Пример #1
0
size_t GetWAVSamples(MFAudioStream *pStream, void *pBuffer, size_t bytes)
{
	MFWAVStream *pWS = (MFWAVStream*)pStream->pStreamData;
	size_t read = MFFile_Read(pWS->pStream, pBuffer, MFMin(bytes, pWS->dataSize - pWS->sampleOffset));
	pWS->sampleOffset += read;
	return read;
}
Пример #2
0
int GetMADSamples(MFAudioStream *pStream, void *pBuffer, uint32 bytes)
{
	MFMADDecoder *pDecoder = (MFMADDecoder*)pStream->pStreamData;

	int written = 0;

	if(pDecoder->overflowBytes)
	{
		// grab from the overflow until we run out...
		uint32 numBytes = MFMin(pDecoder->overflowBytes - pDecoder->overflowOffset, bytes);
		MFCopyMemory(pBuffer, pDecoder->overflow + pDecoder->overflowOffset, numBytes);
		pDecoder->overflowOffset += numBytes;
		if(pDecoder->overflowOffset == pDecoder->overflowBytes)
			pDecoder->overflowBytes = 0;

		// increment timer
		mad_timer_t t = { 0, MAD_TIMER_RESOLUTION / pDecoder->firstHeader.samplerate };
		mad_timer_multiply(&t, numBytes >> 2);
		mad_timer_add(&pDecoder->timer, t);

		if(bytes == numBytes)
			return numBytes;

		bytes -= numBytes;
		(char*&)pBuffer += numBytes;
		written = (int)numBytes;
	}
Пример #3
0
void MFSound_UpdateInternal()
{
	const int NumSamples = 4096;
	float buffer[NumSamples];

	size_t numCaptureDevices = MFDevice_GetNumDevices(MFDT_AudioCapture);
	for(size_t i=0; i<numCaptureDevices; ++i)
	{
		MFDevice *pDevice = MFDevice_GetDeviceByIndex(MFDT_AudioCapture, i);
		AudioDevice &device = *(AudioDevice*)pDevice->pInternal;

		if(device.pDevice && device.bActive)
		{
			// get samples, and feed to callback
			ALint numSamples;
			alcGetIntegerv(device.pDevice, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &numSamples);

			MFDebug_Assert(false, "TODO: check if numSamples is in samples or bytes...");

			while(numSamples > 0)
			{
				ALint samples = MFMin(1024, numSamples);
				alcCaptureSamples(device.pDevice, (ALCvoid *)buffer, samples);
				numSamples -= samples;

				// feed samples to the callback
				device.pSampleCallback((float*)buffer, samples, 1, device.pUserData);
			}
		}
	}
}
Пример #4
0
void * MFPoolHeapExpanding::Alloc()
{
	void *pMem = heap.Alloc();
	if(pMem == NULL)
	{
		MFDebug_Assert(numberTilFull > 0, "The heap has grown larger than the maximum size requested");

		MFPoolHeap *pHeap = (MFPoolHeap*)MFHeap_Alloc(sizeof(MFPoolHeap));
		pHeap->Init(MFMin(numberTilFull, expandNum), heap.Size());
		numberTilFull -= expandNum;

		// hook up the new heap to the chain
		pHeap->pNext = heap.pNext;
		heap.pNext = pHeap;

		// and hook up the free pointer
		heap.pFreeList = pHeap->pFreeList;
		heap.numItems += pHeap->numItems;
		pHeap->pFreeList = NULL;
		pHeap->numItems = 0;

		pMem = heap.Alloc();
	}
	return pMem;
}
Пример #5
0
void ParseID3(MFAudioStream *pStream, MFID3 *pID3, int dataSize)
{
	unsigned char *pData = pID3->pID3Data;

	while(pData - pID3->pID3Data <= dataSize)
	{
		if(pID3->major == 2)
		{
			// for the moment, to be safe...
//			MFDebug_Assert(false, "Make this work!!");
			return;

			//int size = (int)pData[5] | ((int)pData[4] << 7) | ((int)pData[3] << 14);
			//pData += 6 + size;
		}
		if(pID3->major > 2)
		{
			ID3Chunk *pChunkHeader = (ID3Chunk*)pData;
			int size = GetSynchSafeInt(pData + 4);

			if(*pData == 'T')
			{
				//uint8 encoding = pData[10];
				const char *pString = (const char*)pData + 11;

				switch(pChunkHeader->id)
				{
					case MFMAKEFOURCC('T', 'I', 'T', '2'):
						MFString_CopyN(pStream->streamInfo.songName, pString, MFMin(size-1, (int)sizeof(pStream->streamInfo.songName)-1));
						break;
					case MFMAKEFOURCC('T', 'A', 'L', 'B'):
						MFString_CopyN(pStream->streamInfo.albumName, pString, MFMin(size-1, (int)sizeof(pStream->streamInfo.albumName)-1));
						break;
					case MFMAKEFOURCC('T', 'P', 'E', '1'):
					case MFMAKEFOURCC('T', 'P', 'E', '2'):
						MFString_CopyN(pStream->streamInfo.artistName, pString, MFMin(size-1, (int)sizeof(pStream->streamInfo.artistName)-1));
						break;
					case MFMAKEFOURCC('T', 'C', 'O', 'N'):
						MFString_CopyN(pStream->streamInfo.genre, pString, MFMin(size-1, (int)sizeof(pStream->streamInfo.genre)-1));
						break;
				}
			}

			pData += 10 + size;
		}
	}
}
Пример #6
0
MFString& MFString::Replace(int offset, int range, MFString string)
{
	if(!pData)
	{
		pData = string.pData;
		if(pData)
			++pData->refCount;
		return *this;
	}

	// limit within the strings range
	offset = MFMin(offset, (int)pData->bytes);
	int maxChars = (int)pData->bytes - offset;
	if(range > maxChars)
		range = maxChars;

	// bail if we don't need to do anything
	int strLen = string.NumBytes();
	if(range == 0 && strLen == 0)
		return *this;

	int reposition = strLen - range;
	int newSize = (int)pData->bytes + reposition;

	// reserve memory for the new string
	Reserve(newSize);

	// move tail into place
	if(reposition)
	{
		int tailOffset = offset + range;
		char *pSrc = pData->pMemory + tailOffset;
		char *pDest = pSrc + reposition;

		if(pDest < pSrc)
		{
			while(*pSrc)
				*pDest++ = *pSrc++;
			*pDest = 0;
		}
		else
		{
			int len = (int)pData->bytes - tailOffset;
			while(len >= 0)
			{
				pDest[len] = pSrc[len];
				--len;
			}
		}
	}

	// insert string
	if(strLen)
		MFString_CopyN(pData->pMemory + offset, string.pData->pMemory, strLen);

	pData->bytes = newSize;

	return *this;
}
Пример #7
0
void MFParticleSystem_DrawRotating(MFParticleSystem *pParticleSystem, const MFMatrix &ltv)
{
	int numParticles = pParticleSystem->particles.GetLength();
	if(!numParticles)
		return;

	float fadeStart = pParticleSystem->params.life - pParticleSystem->params.fadeDelay;

	MFMaterial_SetMaterial(pParticleSystem->pMaterial);

	MFPrimitive(PT_TriList, 0);
	MFBegin(numParticles * 6);

	MFParticle **ppI = pParticleSystem->particles.Begin();

	while(*ppI)
	{
		MFParticle *pParticle = *ppI;

		float dt = MFSystem_TimeDelta();

		pParticle->rot += pParticleSystem->params.rotationRate * dt;
		pParticle->size += pParticleSystem->params.scaleRate * dt;
		pParticle->velocity += pParticleSystem->params.force * dt;
		pParticle->pos += pParticle->velocity * dt;

		float t = pParticle->size * 0.5f;
		float rad = MFSqrt(t*t*2);
		float xoff = MFCos(-pParticle->rot + 0.7853981f)*rad;
		float yoff = MFSin(-pParticle->rot + 0.7853981f)*rad;
		float alpha = MFMin(pParticle->life / fadeStart, 1.0f);

		MFVector pos = ApplyMatrixH(pParticle->pos, ltv);

		MFSetColourV(MakeVector(pParticle->colour, pParticle->colour.w * alpha));
		MFSetTexCoord1(1, 0);
		MFSetPosition(pos.x + xoff, pos.y + yoff, pos.z);
		MFSetTexCoord1(0, 1);
		MFSetPosition(pos.x - xoff, pos.y - yoff, pos.z);
		MFSetTexCoord1(0, 0);
		MFSetPosition(pos.x - yoff, pos.y + xoff, pos.z);
		MFSetTexCoord1(1, 0);
		MFSetPosition(pos.x + xoff, pos.y + yoff, pos.z);
		MFSetTexCoord1(1, 1);
		MFSetPosition(pos.x + yoff, pos.y - xoff, pos.z);
		MFSetTexCoord1(0, 1);
		MFSetPosition(pos.x - xoff, pos.y - yoff, pos.z);

		pParticle->life -= dt;
		if(pParticle->life < 0.0f)
			pParticleSystem->particles.Destroy(ppI);

		ppI++;
	}

	MFEnd();
}
Пример #8
0
void MFCollision_CalculateDynamicBoundingVolume(MFCollisionItem *pItem)
{
	switch(pItem->pTemplate->type)
	{
		case MFCT_Mesh:
		{
			MFCollisionMesh *pMesh = (MFCollisionMesh*)pItem->pTemplate->pCollisionTemplateData;
			MFBoundingVolume &vol = pItem->pTemplate->boundingVolume;

			vol.min = vol.max = vol.boundingSphere = MakeVector(pMesh->pTriangles[0].verts[0], 0.0f);

			for(int a=0; a<pMesh->numTris; a++)
			{
				MFCollisionTriangle &tri = pMesh->pTriangles[a];

				for(int b=0; b<3; b++)
				{
					vol.min = MFMin(vol.min, tri.verts[b]);
					vol.max = MFMin(vol.max, tri.verts[b]);
					vol.min.w = vol.max.w = 0.0f;

					// if point is outside bounding sphere
					MFVector diff = tri.verts[b] - vol.boundingSphere;
					float mag = diff.MagSquared3();

					if(mag > vol.boundingSphere.w*vol.boundingSphere.w)
					{
						// fit sphere to include point
						mag = MFSqrt(mag) - vol.boundingSphere.w;
						mag *= 0.5f;
						diff.Normalise3();
						vol.boundingSphere.Mad3(diff, mag, vol.boundingSphere);
						vol.boundingSphere.w += mag;
					}
				}
			}
			break;
		}

		default:
			MFDebug_Assert(false, "Invalid item type");
	}
}
Пример #9
0
void HKWidgetListbox::Update()
{
	if(!bDragging)
	{
		if(velocity != 0.f)
		{
			// apply scroll velocity
			velocity *= 1.f - MFSystem_GetTimeDelta()*10.f;
			if(velocity < 0.01f)
				velocity = 0.f;

			scrollOffset += velocity * MFSystem_GetTimeDelta();
		}

		if(scrollOffset > 0.f)
		{
			scrollOffset = MFMax(scrollOffset - MFMax(scrollOffset * 10.f * MFSystem_GetTimeDelta(), 1.f), 0.f);
		}
		else
		{
			float listSize = orientation == Horizontal ? size.x - (padding.x + padding.z) : size.y - (padding.y + padding.w);
			float overflow = MFMin(listSize - (contentSize + scrollOffset), -scrollOffset);
			if(overflow > 0.f)
			{
				scrollOffset = MFMin(scrollOffset + MFMax(overflow * 10.f * MFSystem_GetTimeDelta(), 1.f), scrollOffset + overflow);
			}
		}
	}

	scrollOffset = MFFloor(scrollOffset);
	if(scrollOffset != prevScrollOffset)
	{
		prevScrollOffset = scrollOffset;
		ArrangeChildren();
	}
}
Пример #10
0
void HKStringEntryLogic::ClearSelection()
{
	if(selectionStart == selectionEnd)
		return;

	int selMin = MFMin(selectionStart, selectionEnd);
	int selMax = MFMax(selectionStart, selectionEnd);

	buffer.ClearRange(selMin, selMax - selMin);

	cursorPos = selMin;
	selectionStart = selectionEnd = cursorPos;

	if(changeCallback)
		changeCallback(buffer.CStr());
}
Пример #11
0
bool MFCollision_RaySlabTest(const MFVector& rayPos, const MFVector& rayDir, const MFVector& plane, float slabHalfWidth, MFRayIntersectionResult *pResult)
{
	float a = plane.Dot3(rayDir);

	// if ray is parallel to plane
	if(a > -MFALMOST_ZERO && a < MFALMOST_ZERO)
	{
		// TODO: this is intentionally BROKEN
		// this is a near impossible case, and it adds a lot of junk to the function
/*
		if(MFAbs(rayPos.DotH(plane)) <= slabHalfWidth)
		{
			if(pResult)
			{
				pResult->time = 0.0f;
			}

			return true;
		}
*/
		return false;
	}

	// otherwise we can do the conventional test
	float inva = MFRcp(a);
	float t = -rayPos.DotH(plane);
	float t1 = (t + slabHalfWidth) * inva;
	float t2 = (t - slabHalfWidth) * inva;

	t = MFMin(t1, t2);
	t2 = MFMax(t1, t2);

	if(t > 1.0f || t2 < 0.0f)
		return false;

	if(pResult)
	{
		pResult->time = MFMax(t, 0.0f);
		pResult->surfaceNormal = a > 0.0f ? -plane : plane;
	}

	return true;
}
Пример #12
0
MF_API MFCollisionItem* MFCollision_SweepSphereTest(const MFVector &sweepSpherePos, const MFVector &sweepSphereVelocity, float sweepSphereRadius, MFCollisionItem *pItem, MFSweepSphereResult *pResult)
{
	MFBoundingVolume rayVolume;

	MFVector radiusVector = MakeVector(sweepSphereRadius, sweepSphereRadius, sweepSphereRadius, 0.0f);
	MFVector sweepEnd = sweepSpherePos + sweepSphereVelocity;
	rayVolume.min = MFMin(sweepSpherePos, sweepEnd) - radiusVector;
	rayVolume.max = MFMax(sweepSpherePos, sweepEnd) + radiusVector;
	rayVolume.boundingSphere = MakeVector(sweepSpherePos + sweepSphereVelocity*0.5f, sweepSphereVelocity.Magnitude3()*0.5f + sweepSphereRadius);

	if(0)//!MFBoundingVolume_Test(rayVolume, pItem->pTemplate->boundingVolume))
	{
		return NULL;
	}

	switch(pItem->pTemplate->type)
	{
		case MFCT_Sphere:
		{
			MFCollisionSphere *pSphere = (MFCollisionSphere*)pItem->pTemplate->pCollisionTemplateData;
			if(!MFCollision_SweepSphereSphereTest(sweepSpherePos, sweepSphereVelocity, sweepSphereRadius, pItem->worldPos.GetTrans(), pSphere->radius, pResult))
				return NULL;
			break;
		}
		case MFCT_Mesh:
		{
			if(!MFCollision_SweepSphereMeshTest(sweepSpherePos, sweepSphereVelocity, sweepSphereRadius, pItem, pResult))
				return NULL;
			break;
		}
		case MFCT_Field:
		{
			pItem = MFCollision_SweepSphereFieldTest(sweepSpherePos, sweepSphereVelocity, sweepSphereRadius, pItem, pResult);
			break;
		}
		default:
			MFDebug_Assert(false, "Invalid target primitive");
	}

	return pItem;
}
Пример #13
0
MF_API size_t MFMidi_GetEvents(MFDevice *pDevice, MFMidiEvent *pEvents, size_t maxEvents, bool bPeek)
{
	MFMidiPC_MidiInputDevice *pMidi = (MFMidiPC_MidiInputDevice*)pDevice->pInternal;

	if (pMidi->numEvents == 0)
		return 0;
	if (!pEvents)
		return pMidi->numEvents - pMidi->numEventsRead;

	uint32 toRead = MFMin((uint32)maxEvents, pMidi->numEvents - pMidi->numEventsRead);
	MFCopyMemory(pEvents, pMidi->pEvents + pMidi->numEventsRead, sizeof(MFMidiEvent)*toRead);

	if (!bPeek)
	{
		pMidi->numEventsRead += toRead;
		if (pMidi->numEventsRead == pMidi->numEvents)
			pMidi->numEvents = pMidi->numEventsRead = 0;
	}

	return toRead;
}
Пример #14
0
int MFFileZipFile_Seek(MFFile* pFile, int64 bytes, MFFileSeek relativity)
{
	MFCALLSTACK;

	int64 newPos = 0;

	switch(relativity)
	{
		case MFSeek_Begin:
			newPos = MFMin(bytes, pFile->length);
			break;
		case MFSeek_End:
			newPos = MFMax((int64)0, pFile->length - bytes);
			break;
		case MFSeek_Current:
			newPos = MFClamp((int64)0, pFile->offset + bytes, pFile->length);
			break;
		default:
			MFDebug_Assert(false, "Invalid 'relativity' for file seeking.");
	}

	unzFile f = (unzFile)pFile->pFilesysData;

	unzCloseCurrentFile(f);
	unzOpenCurrentFile(f);

	pFile->offset = newPos;

	// read to the desired position.
	char buffer[256];

	while(newPos)
	{
		int64 bytes = newPos < 256 ? newPos : 256;
		unzReadCurrentFile(f, buffer, (uint32)bytes);
		newPos -= bytes;
	}

	return (int)pFile->offset;
}
Пример #15
0
MF_API MFCollisionItem* MFCollision_RayTest(const MFVector& rayPos, const MFVector& rayDir, MFCollisionItem *pItem, MFRayIntersectionResult *pResult)
{
	MFBoundingVolume rayVolume;

	MFVector rayEnd = rayPos+rayDir;
	rayVolume.min = MFMin(rayPos, rayEnd);
	rayVolume.max = MFMax(rayPos, rayEnd);
	rayVolume.boundingSphere = MakeVector(rayPos + rayDir*0.5f, rayDir.Magnitude3()*0.5f);

	if(!MFBoundingVolume_Test(rayVolume, pItem->pTemplate->boundingVolume))
	{
		return NULL;
	}

	switch(pItem->pTemplate->type)
	{
		case MFCT_Sphere:
		{
			MFCollisionSphere *pSphere = (MFCollisionSphere*)pItem->pTemplate->pCollisionTemplateData;
			if(!MFCollision_RaySphereTest(rayPos, rayDir, pItem->worldPos.GetTrans(), pSphere->radius, pResult))
				return NULL;
			break;
		}
		case MFCT_Mesh:
		{
			if(!MFCollision_RayMeshTest(rayPos, rayDir, pItem, pResult))
				return NULL;
			break;
		}
		case MFCT_Field:
		{
			pItem = MFCollision_RayFieldTest(rayPos, rayDir, pItem, pResult);
			break;
		}
		default:
			MFDebug_Assert(false, "Invalid target primitive");
	}

	return pItem;
}
Пример #16
0
MF_API size_t MFInput_GetEvents(int device, int deviceID, MFInputEvent *pEvents, size_t maxEvents, bool bPeek)
{
	MFThread_LockMutex(gInputMutex);

	uint32 toRead = 0;
	if(gNumEvents[device][deviceID] != 0)
	{
		toRead = MFMin((uint32)maxEvents, gNumEvents[device][deviceID] - gNumEventsRead[device][deviceID]);
		MFCopyMemory(pEvents, gInputEvents[device][deviceID] + gNumEventsRead[device][deviceID], sizeof(MFInputEvent)*toRead);

		if(!bPeek)
		{
			gNumEventsRead[device][deviceID] += toRead;
			if(gNumEventsRead[device][deviceID] == gNumEvents[device][deviceID])
				gNumEvents[device][deviceID] = gNumEventsRead[device][deviceID] = 0;
		}
	}

	MFThread_ReleaseMutex(gInputMutex);

	return toRead;
}
Пример #17
0
MF_API void *MFHeap_ReallocInternal(void *pMem, size_t bytes)
{
	MFCALLSTACK;

	if(pMem)
	{
		MFAllocHeader *pHeader = &((MFAllocHeader*)pMem)[-1];
		MFDebug_Assert(MFHeap_ValidateMemory(pMem), MFStr("Memory corruption detected!!\n%s(%d)", pHeader->pFile, pHeader->line));

		void *pNew = MFHeap_AllocInternal(bytes, pHeader->pHeap);
		MFDebug_Assert(pNew, "Failed to allocate memory!");
		if(!pNew)
			return NULL;

		MFCopyMemory(pNew, pMem, MFMin(bytes, (size_t)pHeader->size));
		MFHeap_Free(pMem);
		return pNew;
	}
	else
	{
		return MFHeap_AllocInternal(bytes, NULL);
	}
}
Пример #18
0
MF_API void MFCollision_BuildField(MFCollisionItem *pField)
{
	MFCollisionField *pFieldData = (MFCollisionField*)pField->pTemplate;

	int numItems = pFieldData->itemList.GetLength();

	if(numItems <= 0)
	{
		MFDebug_Warn(4, "EmptyField can not be generated.");
		return;
	}

	// find the min and max range of the objects
	MFVector fieldMin = MakeVector(10e+30f), fieldMax = MakeVector(-10e+30f);

	MFCollisionItem **ppI = pFieldData->itemList.Begin();

	while(*ppI)
	{
		MFCollisionItem *pI = *ppI;
		MFCollisionTemplate *pT = pI->pTemplate;

		MFVector tMin = ApplyMatrixH(pT->boundingVolume.min, pI->worldPos);
		MFVector tMax = ApplyMatrixH(pT->boundingVolume.max, pI->worldPos);

		fieldMin = MFMin(fieldMin, tMin);
		fieldMax = MFMax(fieldMax, tMax);

		ppI++;
	}

	pFieldData->fieldMin = fieldMin;
	pFieldData->fieldMax = fieldMin;

	MFVector numCells;
	MFVector fieldRange = fieldMax - fieldMin;
	numCells.Rcp3(pFieldData->cellSize);
	numCells.Mul3(fieldRange, numCells);

	pFieldData->width = (int)MFCeil(numCells.x);
	pFieldData->height = (int)MFCeil(numCells.y);
	pFieldData->depth = (int)MFCeil(numCells.z);

	// this is TOTALLY broken!! .. if a big object lies in many cell's, it could easilly overflow the array.
	int totalCells = pFieldData->width * pFieldData->height * pFieldData->depth;
	int numPointers = totalCells * 2 + numItems * 16;

	MFCollisionItem **ppItems = (MFCollisionItem**)MFHeap_Alloc(sizeof(MFCollisionItem*) * numPointers);
	pFieldData->pppItems = (MFCollisionItem***)ppItems;
	ppItems += totalCells;

	for(int z=0; z<pFieldData->depth; z++)
	{
		for(int y=0; y<pFieldData->height; y++)
		{
			for(int x=0; x<pFieldData->width; x++)
			{
				pFieldData->pppItems[z*pFieldData->height*pFieldData->width + y*pFieldData->width + x] = ppItems;

				MFVector thisCell = fieldMin + pFieldData->cellSize * MakeVector((float)x, (float)y, (float)z);
				MFVector thisCellEnd = thisCell + pFieldData->cellSize;

				MFCollisionItem **ppI = pFieldData->itemList.Begin();

				while(*ppI)
				{
					MFCollisionItem *pI = *ppI;
					MFCollisionTemplate *pT = pI->pTemplate;

					// if this item fits in this cell, insert it into this cells list.
					MFVector tMin = ApplyMatrixH(pT->boundingVolume.min, pI->worldPos);
					MFVector tMax = ApplyMatrixH(pT->boundingVolume.max, pI->worldPos);

					// test of bounding boxes overlap
					if(MFCollision_TestAABB(tMin, tMax, thisCell, thisCellEnd))
					{
						*ppItems = pI;
						++ppItems;
					}

					ppI++;
				}

				*ppItems = NULL;
				++ppItems;
			}
		}
	}

	MFHeap_ValidateMemory(pFieldData->pppItems);
}
Пример #19
0
MF_API void MFParticleSystem_Draw(MFParticleSystem *pParticleSystem)
{
	int numParticles = pParticleSystem->particles.GetLength();
	if(numParticles == 0)
		return;

	// render particles
	MFView_Push();

	MFMatrix ltv = MFView_GetWorldToViewMatrix();
	MFView_SetCameraMatrix(MFMatrix::identity);

	// update and draw each particle
	if(pParticleSystem->params.rotationRate != 0.0f)
	{
		MFParticleSystem_DrawRotating(pParticleSystem, ltv);
		MFView_Pop();
		return;
	}

	float fadeStart = pParticleSystem->params.life - pParticleSystem->params.fadeDelay;

	MFMaterial_SetMaterial(pParticleSystem->pMaterial);

	MFPrimitive(PT_QuadList, 0);
	MFBegin(numParticles * 2);

	MFParticle **ppI = pParticleSystem->particles.Begin();

	while(*ppI)
	{
		MFParticle *pParticle = *ppI;

		float dt = MFSystem_TimeDelta();

		pParticle->size += pParticleSystem->params.scaleRate * dt;
		pParticle->velocity += pParticleSystem->params.force * dt;
		pParticle->pos += pParticle->velocity * dt;

		float halfSize = pParticle->size * 0.5f;
		float alpha = MFMin(pParticle->life / fadeStart, 1.0f);

		MFVector pos = ApplyMatrixH(pParticle->pos, ltv);

		MFSetColourV(MakeVector(pParticle->colour, pParticle->colour.w * alpha));
		MFSetTexCoord1(0, 0);
		MFSetPosition(pos.x - halfSize, pos.y + halfSize, pos.z);
		MFSetTexCoord1(1, 1);
		MFSetPosition(pos.x + halfSize, pos.y - halfSize, pos.z);

		pParticle->life -= dt;
		if(pParticle->life < 0.0f)
			pParticleSystem->particles.Destroy(ppI);

		ppI++;
	}

	MFEnd();

	MFView_Pop();
}
Пример #20
0
void dBFrame::Draw()
{
	if(!pMat)
	{
		MFPrimitive_DrawUntexturedQuad(rect.x-10, rect.y-10, rect.width+20, rect.height+20, colours[0]);
	}
	else
	{
		// draw background
		float x, y, xRemaining, yRemaining;

		// calculate number of tiles (including edges)
		int h = (int)MFCeil(rect.width / borderWidth);
		int w = (int)MFCeil(rect.height / borderWidth);
		int numTiles = (h + 2) * (w + 2);

		// begin immediate renderer
		MFMaterial_SetMaterial(pMat);
		MFPrimitive(PT_QuadList);
		MFBegin(numTiles*2);

		MFSetColourV(colours[0]);

		// render tiled background
		yRemaining = rect.height;
		for(y = 0.0f; y < rect.height; y += borderWidth)
		{
			xRemaining = rect.width;
			for(x = 0.0f; x < rect.width; x += borderWidth)
			{
				float xuv = xRemaining < borderWidth ? 0.25f * (xRemaining / borderWidth) : 0.25f;
				float yuv = yRemaining < borderWidth ? 0.25f * (yRemaining / borderWidth) : 0.25f;

				MFSetTexCoord1(0.25f, 0.25f);
				MFSetPosition(rect.x + x, rect.y + y, 0);
				MFSetTexCoord1(0.25f + xuv, 0.25f + yuv);		
				MFSetPosition(rect.x + x + MFMin(borderWidth, xRemaining), rect.y + y + MFMin(borderWidth, yRemaining), 0);

				xRemaining -= borderWidth;
			}

			yRemaining -= borderWidth;
		}

		// draw frame
		// draw corners
		MFSetTexCoord1(0.0f, 0.0f);
		MFSetPosition(rect.x - borderWidth, rect.y - borderWidth, 0.0f);
		MFSetTexCoord1(0.25f, 0.25f);		
		MFSetPosition(rect.x, rect.y, 0.0f);
		MFSetTexCoord1(0.5f, 0.0f);
		MFSetPosition(rect.x + rect.width, rect.y - borderWidth, 0.0f);
		MFSetTexCoord1(0.75f, 0.25f);		
		MFSetPosition(rect.x + rect.width + borderWidth, rect.y, 0.0f);
		MFSetTexCoord1(0.0f, 0.50f);
		MFSetPosition(rect.x - borderWidth, rect.y + rect.height, 0.0f);
		MFSetTexCoord1(0.25f, 0.75f);		
		MFSetPosition(rect.x, rect.y + rect.height + borderWidth, 0.0f);
		MFSetTexCoord1(0.5f, 0.5f);
		MFSetPosition(rect.x + rect.width, rect.y + rect.height, 0.0f);
		MFSetTexCoord1(0.75f, 0.75f);		
		MFSetPosition(rect.x + rect.width + borderWidth, rect.y + rect.height + borderWidth, 0.0f);

		// draw vertical edges
		yRemaining = rect.height;
		for(y = 0.0f; y < rect.height; y += borderWidth)
		{
			float yuv = yRemaining < borderWidth ? 0.25f * (yRemaining / borderWidth) : 0.25f;

			MFSetTexCoord1(0.0f, 0.25f);
			MFSetPosition(rect.x - borderWidth, rect.y + y, 0.0f);
			MFSetTexCoord1(0.25f, 0.25f + yuv);		
			MFSetPosition(rect.x, rect.y + y + MFMin(borderWidth, yRemaining), 0.0f);

			MFSetTexCoord1(0.5f, 0.25f);
			MFSetPosition(rect.x + rect.width, rect.y + y, 0.0f);
			MFSetTexCoord1(0.75f, 0.25f + yuv);		
			MFSetPosition(rect.x + rect.width + borderWidth, rect.y + y + MFMin(borderWidth, yRemaining), 0.0f);

			yRemaining -= borderWidth;
		}

		// draw horizontal edges
		xRemaining = rect.width;
		for(x = 0.0f; x < rect.width; x += borderWidth)
		{
			float xuv = xRemaining < borderWidth ? 0.25f * (xRemaining / borderWidth) : 0.25f;

			MFSetTexCoord1(0.25f, 0.0f);
			MFSetPosition(rect.x + x, rect.y - borderWidth, 0.0f);
			MFSetTexCoord1(0.25f + xuv, 0.25f);		
			MFSetPosition(rect.x + x + MFMin(borderWidth, xRemaining), rect.y, 0.0f);

			MFSetTexCoord1(0.25f, 0.5f);
			MFSetPosition(rect.x + x, rect.y + rect.height, 0.0f);
			MFSetTexCoord1(0.25f + xuv, 0.75f);		
			MFSetPosition(rect.x + x + MFMin(borderWidth, xRemaining), rect.y + rect.height + borderWidth, 0.0f);

			xRemaining -= borderWidth;
		}

		MFEnd();
	}
}
Пример #21
0
void HKStringEntryLogic::Update()
{
	bool shiftL = !!MFInput_Read(Key_LShift, IDD_Keyboard);
	bool shiftR = !!MFInput_Read(Key_RShift, IDD_Keyboard);
	bool ctrlL = !!MFInput_Read(Key_LControl, IDD_Keyboard);
	bool ctrlR = !!MFInput_Read(Key_RControl, IDD_Keyboard);

	int keyPressed = 0;

	bool shift = shiftL || shiftR;
	bool ctrl = ctrlL || ctrlR;

#if defined(MF_WINDOWS)
	if(ctrl && MFInput_WasPressed(Key_C, IDD_Keyboard) && selectionStart != selectionEnd)
	{
		BOOL opened = OpenClipboard(apphWnd);

		if(opened)
		{
			int selMin = MFMin(selectionStart, selectionEnd);
			int selMax = MFMax(selectionStart, selectionEnd);

			int numChars = selMax-selMin;

			HANDLE hData = GlobalAlloc(GMEM_MOVEABLE, numChars + 1);
			char *pString = (char*)GlobalLock(hData);

			MFString_Copy(pString, GetRenderString().SubStr(selMin, numChars).CStr());

			GlobalUnlock(hData);

			EmptyClipboard();
			SetClipboardData(CF_TEXT, hData);

			CloseClipboard();
		}
	}
	else if(ctrl && MFInput_WasPressed(Key_X, IDD_Keyboard) && selectionStart != selectionEnd)
	{
		BOOL opened = OpenClipboard(apphWnd);

		if(opened)
		{
			int selMin = MFMin(selectionStart, selectionEnd);
			int selMax = MFMax(selectionStart, selectionEnd);

			int numChars = selMax-selMin;

			HANDLE hData = GlobalAlloc(GMEM_MOVEABLE, numChars + 1);
			char *pString = (char*)GlobalLock(hData);

			MFString_Copy(pString, GetRenderString().SubStr(selMin, numChars).CStr());

			GlobalUnlock(hData);

			EmptyClipboard();
			SetClipboardData(CF_TEXT, hData);

			CloseClipboard();

			ClearSelection();
		}
	}
	else if(ctrl && MFInput_WasPressed(Key_V, IDD_Keyboard))
	{
		BOOL opened = OpenClipboard(apphWnd);

		if(opened)
		{
			int selMin = MFMin(selectionStart, selectionEnd);
			int selMax = MFMax(selectionStart, selectionEnd);

			int numChars = selMax-selMin;

			HANDLE hData = GetClipboardData(CF_TEXT);
			MFString paste((const char*)GlobalLock(hData), true);

			buffer.Replace(selMin, numChars, paste);

			GlobalUnlock(hData);

			cursorPos = selMin + paste.NumBytes();
			selectionStart = selectionEnd = cursorPos;

			GlobalUnlock(hData);

			CloseClipboard();

			if((numChars || cursorPos != selMin) && changeCallback)
				changeCallback(buffer.CStr());
		}
	}
	else
#endif
	{
		// check for new keypresses
		for(int a=0; a<255; a++)
		{
			if(MFInput_WasPressed(a, IDD_Keyboard))
			{
				keyPressed = a;
				holdKey = a;
				repeatDelay = gRepeatDelay;
				break;
			}
		}

		// handle repeat keys
		if(holdKey && MFInput_Read(holdKey, IDD_Keyboard))
		{
			repeatDelay -= MFSystem_TimeDelta();
			if(repeatDelay <= 0.f)
			{
				keyPressed = holdKey;
				repeatDelay += gRepeatRate;
			}
		}
		else
			holdKey = 0;

		// if there was a new key press
		if(keyPressed)
		{
			switch(keyPressed)
			{
				case Key_Backspace:
				case Key_Delete:
				{
					if(selectionStart != selectionEnd)
					{
						ClearSelection();
					}
					else
					{
						if(keyPressed == Key_Backspace && cursorPos > 0)
						{
							buffer.ClearRange(--cursorPos, 1);
							selectionStart = selectionEnd = cursorPos;

							if(changeCallback)
								changeCallback(buffer.CStr());
						}
						else if(keyPressed == Key_Delete && cursorPos < buffer.NumBytes())
						{
							buffer.ClearRange(cursorPos, 1);
							selectionStart = selectionEnd = cursorPos;

							if(changeCallback)
								changeCallback(buffer.CStr());
						}
					}
					break;
				}

				case Key_Left:
				case Key_Right:
				case Key_Home:
				case Key_End:
				{
					if(ctrl)
					{
						if(keyPressed == Key_Left)
						{
							while(cursorPos && MFIsWhite(buffer[cursorPos-1]))
								--cursorPos;
							if(MFIsAlphaNumeric(buffer[cursorPos-1]))
							{
								while(cursorPos && MFIsAlphaNumeric(buffer[cursorPos-1]))
									--cursorPos;
							}
							else if(cursorPos)
							{
								--cursorPos;
								while(cursorPos && buffer[cursorPos-1] == buffer[cursorPos])
									--cursorPos;
							}
						}
						else if(keyPressed == Key_Right)
						{
							while(cursorPos < buffer.NumBytes() && MFIsWhite(buffer[cursorPos]))
								++cursorPos;
							if(MFIsAlphaNumeric(buffer[cursorPos]))
							{
								while(cursorPos < buffer.NumBytes() && MFIsAlphaNumeric(buffer[cursorPos]))
									++cursorPos;
							}
							else if(cursorPos < buffer.NumBytes())
							{
								++cursorPos;
								while(cursorPos < buffer.NumBytes() && buffer[cursorPos] == buffer[cursorPos-1])
									++cursorPos;
							}
						}
						else if(keyPressed == Key_Home)
							cursorPos = 0;
						else if(keyPressed == Key_End)
							cursorPos = buffer.NumBytes();
					}
					else
					{
						if(keyPressed == Key_Left)
							cursorPos = (!shift && selectionStart != selectionEnd ? MFMin(selectionStart, selectionEnd) : MFMax(cursorPos-1, 0));
						else if(keyPressed == Key_Right)
							cursorPos = (!shift && selectionStart != selectionEnd ? MFMax(selectionStart, selectionEnd) : MFMin(cursorPos+1, buffer.NumBytes()));
						else if(keyPressed == Key_Home)
							cursorPos = 0;	// TODO: if multiline, go to start of line..
						else if(keyPressed == Key_End)
							cursorPos = buffer.NumBytes();	// TODO: if multiline, go to end of line...
					}

					if(shift)
						selectionEnd = cursorPos;
					else
						selectionStart = selectionEnd = cursorPos;

					break;
				}

				default:
				{
					bool caps = MFInput_GetKeyboardStatusState(KSS_CapsLock);
					int ascii = MFInput_KeyToAscii(keyPressed, shift, caps);

					if(ascii && (!maxLen || buffer.NumBytes() < maxLen-1))
					{
						// check character exclusions
						if(MFIsNewline(ascii) && type != ST_MultiLine)
							break;
						if(ascii == '\t' && type != ST_MultiLine)
							break;
						if(type == ST_Numeric && !MFIsNumeric(ascii))
							break;
						if(include)
						{
							if(include.FindChar(ascii) < 0)
								break;
						}
						if(exclude)
						{
							if(exclude.FindChar(ascii) >= 0)
								break;
						}

						int selMin = MFMin(selectionStart, selectionEnd);
						int selMax = MFMax(selectionStart, selectionEnd);
						int selRange = selMax - selMin;

						const uint16 wstr[] = { (uint16)ascii, 0 };
						char insert[5];
						MFString_CopyUTF16ToUTF8(insert, wstr);
						buffer.Replace(selMin, selRange, insert);
						cursorPos = selMin + 1;

						selectionStart = selectionEnd = cursorPos;

						if(changeCallback)
							changeCallback(buffer.CStr());
					}
					break;
				}
			}
		}
	}
}
Пример #22
0
void ParseOBJFile(const char *pFilePtr)
{
	const char *pToken = GetNextToken(pFilePtr);

	while(*pToken != 0)
	{
		if(!MFString_CaseCmp(pToken, "o"))
		{
			const char *pName = GetRestOfLine(pFilePtr);

			pModel->name = pName;
		}
		else if(!MFString_CaseCmp(pToken, "g"))
		{
			const char *pName = GetRestOfLine(pFilePtr);

			if(!vertsInGroup)
			{
				// we'll just rename the current subobject, since theres nothing in it..
				F3DSubObject &sub = pModel->GetMeshChunk()->subObjects[subObject];
				sub.name = pName;
			}
			else
			{
				// probably wanna copy vertex data in at this point..
				// and subtract the min from each of the components indices..
				CopyDataIntoSubobject(subObject);

				++subObject;

				matSub = 0;

				minVertIndex = -1;
				minUVIndex = -1;
				minNormIndex = -1;
				maxVertIndex = -1;
				maxUVIndex = -1;
				maxNormIndex = -1;

				vertsInGroup = false;
				vertsInMatSub = false;

				F3DSubObject &sub = pModel->GetMeshChunk()->subObjects[subObject];
				sub.name = pName;
			}
		}
		else if(!MFString_CaseCmp(pToken, "v"))
		{
			const char *pX = GetNextToken(pFilePtr);
			const char *pY = GetNextToken(pFilePtr);
			const char *pZ = GetNextToken(pFilePtr);
			pFilePtr = MFSeekNewline(pFilePtr);

			MFVector v;
			v.x = (float)atof(pX);
			v.y = (float)atof(pY);
			v.z = (float)atof(pZ);
			v.w = 1.0f;

			verts.push(v);
		}
		else if(!MFString_CaseCmp(pToken, "vt"))
		{
			const char *pU = GetNextToken(pFilePtr);
			const char *pV = GetNextToken(pFilePtr);
			pFilePtr = MFSeekNewline(pFilePtr);

			MFVector v;
			v.x = (float)atof(pU);
			v.y = (float)atof(pV);
			v.z = 0.0f;
			v.w = 1.0f;

			uvs.push(v);
		}
		else if(!MFString_CaseCmp(pToken, "vn"))
		{
			const char *pX = GetNextToken(pFilePtr);
			const char *pY = GetNextToken(pFilePtr);
			const char *pZ = GetNextToken(pFilePtr);
			pFilePtr = MFSeekNewline(pFilePtr);

			MFVector v;
			v.x = (float)atof(pX);
			v.y = (float)atof(pY);
			v.z = (float)atof(pZ);
			v.w = 1.0f;

			normals.push(v);
		}
		else if(!MFString_CaseCmp(pToken, "f"))
		{
			vertsInGroup = true;
			vertsInMatSub = true;

			F3DSubObject &sub = pModel->GetMeshChunk()->subObjects[subObject];

			const char *pRestOfLine = GetRestOfLine(pFilePtr);

			int firstVert = (int)sub.matSubobjects[matSub].vertices.size();

			pToken = GetNextToken(pRestOfLine);

			while(*pToken)
			{
				const char *pPos = GetNextIndex(pToken);
				const char *pUV = GetNextIndex(pToken);
				const char *pNorm = GetNextIndex(pToken);

				int posid = atoi(pPos);
				int texid = atoi(pUV);
				int normid = atoi(pNorm);

				if(posid < 0)
					posid = (int)verts.size() - posid;
				else
					posid = posid - 1;

				if(texid < 0)
					texid = (int)uvs.size() - texid;
				else
					texid = texid - 1;

				if(normid < 0)
					normid = (int)normals.size() - normid;
				else
					normid = normid - 1;

				minVertIndex = minVertIndex == -1 ? posid : MFMin(minVertIndex, posid);
				minUVIndex = minUVIndex == -1 ? texid : MFMin(minUVIndex, texid);
				minNormIndex = minNormIndex == -1 ? normid : MFMin(minNormIndex, normid);
				maxVertIndex = minVertIndex == -1 ? posid : MFMax(maxVertIndex, posid);
				maxUVIndex = maxUVIndex == -1 ? texid : MFMax(maxUVIndex, texid);
				maxNormIndex = maxNormIndex == -1 ? normid : MFMax(maxNormIndex, normid);

				int vi = (int)sub.matSubobjects[matSub].vertices.size();
				int f = vi - firstVert;

				F3DVertex &vert = sub.matSubobjects[matSub].vertices[firstVert + f];
				vert.position = posid;
				vert.uv[0] = texid;
				vert.normal = normid;

				// add a triangle if we are up to the third vert or beyond
				if(f >= 2)
				{
					F3DTriangle &tri = sub.matSubobjects[matSub].triangles.push();

					tri.v[0] = firstVert;
					tri.v[1] = vi-1;
					tri.v[2] = vi;
				}

				pToken = GetNextToken(pRestOfLine);
			}
		}
		else if(!MFString_CaseCmp(pToken, "usemtl"))
		{
			F3DSubObject &sub = pModel->GetMeshChunk()->subObjects[subObject];

			if(vertsInGroup && vertsInMatSub)
			{
				++matSub;
				vertsInMatSub = false;
			}

			const char *pName = GetRestOfLine(pFilePtr);

			sub.matSubobjects[matSub].materialIndex = GetMaterialID(pName);
		}
		else if(!MFString_CaseCmp(pToken, "mtllib"))
		{
			// load material info?
			//..
			
			pFilePtr = MFSeekNewline(pFilePtr);
		}
		else if(pToken[0] == '#')
		{
			pFilePtr = MFSeekNewline(pFilePtr);
		}
		else
		{
			MFDebug_Warn(2, MFStr("Unknown token encountered in obj file '%s'!", pToken));
			pFilePtr = MFSeekNewline(pFilePtr);
		}

		pToken = GetNextToken(pFilePtr);
	}

	// want to copy vertex data into the last subobject at this point...
	if(vertsInGroup)
	{
		CopyDataIntoSubobject(subObject);
	}
}
Пример #23
0
void MFInput_GetGamepadStateInternal(int id, MFGamepadState *pGamepadState)
{
    MFCALLSTACK;

	MFZeroMemory(pGamepadState, sizeof(MFGamepadState));

	LinuxGamepad *pPad = &gGamepads[id];
	const int *pButtonMap = pPad->pGamepadInfo->pButtonMap;

	// if this is a virtual device, find the source device
	while(pPad->bVirtualDevice)
		--pPad;

	if(pPad->joyFD)
	{
		// convert input to float data
		for(int a=0; a<GamepadType_Max; a++)
		{
			if(pButtonMap[a] == -1)
				continue;

			int axisID = MFGETAXIS(pButtonMap[a]);

			// if we are not reading the analog axis
			if(!(pButtonMap[a] & AID_Analog))
			{
				if(axisID >= 60)// && gPCJoysticks[id].bHasPOV)
				{
/*
					if(POVCentered)
					{
						// POV is centered
						pGamepadState->values[a] = 0.0f;
					}
					else
					{
						// read POV (or more appropriately titled, POS)
						if(axisID == 60)
							pGamepadState->values[Button_DUp] = ((pov >= 31500 && pov <= 36000) || (pov >= 0 && pov <= 4500)) ? 1.0f : 0.0f;
						else if(axisID == 61)
							pGamepadState->values[Button_DDown] = (pov >= 13500 && pov <= 22500) ? 1.0f : 0.0f;
						else if(axisID == 62)
							pGamepadState->values[Button_DLeft] = (pov >= 22500 && pov <= 31500) ? 1.0f : 0.0f;
						else if(axisID == 63)
							pGamepadState->values[Button_DRight] = (pov >= 4500 && pov <= 13500) ? 1.0f : 0.0f;
					}
*/
				}
				else
				{
					// read digital button
					pGamepadState->values[a] = pPad->button[pButtonMap[a] & AID_ButtonMask] ? 1.0f : 0.0f;
				}
			}
			else
			{
				// read an analog axis
//				pGamepadState->values[a] = MFMin(pad.axis[pad.axisMap[MFGETAXIS(pButtonMap[a])]] * (1.0f/32767.0f), 1.0f);
				pGamepadState->values[a] = MFMin(pPad->axis[axisID] * (1.0f/32767.0f), 1.0f);
			}

			// invert any buttons with the AID_Negative flag
			if(pButtonMap[a] & AID_Negative)
				pGamepadState->values[a] = -pGamepadState->values[a];
			// clamp any butons with the AID_Clamp flag to the positive range
			if(pButtonMap[a] & AID_Clamp)
				pGamepadState->values[a] = MFMax(0.0f, pGamepadState->values[a]);
			// use the full range for any buttons with the AID_Full flag
			if(pButtonMap[a] & AID_Full)
				pGamepadState->values[a] = (pGamepadState->values[a] + 1.0f) * 0.5f;
		}
	}
}
Пример #24
0
void UpdateEditor()
{
	static GHEvent *pHold[8] = { (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1, (GHEvent*)(size_t)-1 };

	bool ctrlState = MFInput_Read(Key_LControl, IDD_Keyboard) || MFInput_Read(Key_RControl, IDD_Keyboard);
	bool shiftState = MFInput_Read(Key_LShift, IDD_Keyboard) || MFInput_Read(Key_LShift, IDD_Keyboard);
	bool altState = MFInput_Read(Key_LAlt, IDD_Keyboard) || MFInput_Read(Key_RAlt, IDD_Keyboard);

	int res = gEditor.pSong->GetRes();

	if(TestControl(dBCtrl_Edit_Save, GHCT_Once))
	{
		gEditor.pSong->SaveChart();
		MFVoice *pVoice = MFSound_Play(gEditor.pSaveSound, MFPF_BeginPaused);
		MFSound_SetVolume(pVoice, gConfig.sound.fxLevel);
		MFSound_Pause(pVoice, false);

		if(gConfig.editor.saveAction[0])
			system(gConfig.editor.saveAction);
	}

	if(TestControl(dBCtrl_Edit_Event, GHCT_Once))
		PlaceEvent();
	else if(TestControl(dBCtrl_Edit_TrackEvent, GHCT_Once))
		PlaceTrackEvent();
	else if(TestControl(dBCtrl_Edit_Section, GHCT_Once))
		PlaceSection();
	else if(TestControl(dBCtrl_Edit_Quantise, GHCT_Once))
		gpStringBox->Show(MFTranslation_GetString(pStrings, MENU_SETQUANTISE), "", SetCustomQuantisation);
	else if(TestControl(dBCtrl_Edit_Mixer, GHCT_Once))
		((EditorScreen*)dBScreen::GetCurrent())->gMixer.Push();

	// check selection
	if(bSelecting && !TestControl(dBCtrl_Edit_RangeSelect, GHCT_Hold))
	{
		gEditor.selectEnd = gEditor.offset;
		bSelecting = false;
	}
	else if(!bSelecting && TestControl(dBCtrl_Edit_RangeSelect, GHCT_Hold))
	{
		gEditor.selectStart = gEditor.selectEnd = gEditor.offset;
		bSelecting = true;
	}

	if(TestControl(dBCtrl_Edit_Cut, GHCT_Once) || TestControl(dBCtrl_Edit_Copy, GHCT_Once))
	{
		if(gEditor.selectStart != gEditor.selectEnd)
		{
			gEditor.copyLen = gEditor.selectEnd - gEditor.selectStart;

			gEditor.selectEvents.Clear();

			GHEventManager &noteStream = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]];
			GHEvent *pEv = noteStream.GetNextEvent(gEditor.selectStart);

			while(pEv && pEv->tick < gEditor.selectEnd)
			{
				// copy the next pointer incase we cut the note.
				GHEvent *pNext = pEv->Next();

				// just cut/copy notes
				if(pEv->event == GHE_Note)
				{
					// copy to clipboard
					gEditor.selectEvents.AddEvent(pEv->event, pEv->tick - gEditor.selectStart, pEv->key, pEv->parameter);

					// if we cut
					if(TestControl(dBCtrl_Edit_Cut, GHCT_Once))
						pNext = noteStream.RemoveEvent(pEv);
				}

				pEv = pNext;
			}
		}
	}
	else if(TestControl(dBCtrl_Edit_Paste, GHCT_Once))
	{
		GHEvent *pEv = gEditor.selectEvents.First();

		if(pEv)
		{
			int curStream = gEditor.currentStream[gEditor.selectedStream];
			GHEventManager &noteStream = gEditor.pSong->notes[curStream];

			// delete notes in paste range
			GHEvent *pDel = noteStream.GetNextEvent(gEditor.offset);
			while(pDel && pDel->tick < gEditor.offset + gEditor.copyLen)
			{
				if(pDel->event == GHE_Note)
					pDel = noteStream.RemoveEvent(pDel);
				else
					pDel = pDel->Next();
			}

			// paste notes
			while(pEv)
			{
				noteStream.AddEvent(pEv->event, pEv->tick + gEditor.offset, pEv->key, pEv->parameter);
				pEv = pEv->Next();
			}

			gEditor.pSong->CalculateNoteTimes(curStream, gEditor.offset);
		}
	}

	if(TestControl(dBCtrl_Edit_Delete, GHCT_Once))
	{
		GHEventManager &notes = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]];
		if(gEditor.selectStart != gEditor.selectEnd)
		{
			// delete notes in selected range
			GHEvent *pDel = notes.GetNextEvent(gEditor.selectStart);
			while(pDel && pDel->tick < gEditor.selectEnd)
			{
				if(pDel->event == GHE_Note)
					pDel = notes.RemoveEvent(pDel);
				else
					pDel = pDel->Next();
			}
		}
		else
		{
			int numEvents = 0;
			uint32 eventTypes = 0;

			// find note events
			GHEvent *pEvent = notes.GetNextEvent(gEditor.offset);
			while(pEvent && pEvent->tick == gEditor.offset)
			{
				uint32 bit = 1 << pEvent->event;
				if(!(eventTypes & bit))
				{
					++numEvents;
					eventTypes |= bit;
				}
				pEvent = pEvent->Next();
			}

			// find sync events
			if(gEditor.offset > 0)
			{
				pEvent = gEditor.pSong->sync.GetNextEvent(gEditor.offset);
				while(pEvent && pEvent->tick == gEditor.offset)
				{
					uint32 bit = 1 << pEvent->event;
					if(!(eventTypes & bit))
					{
						++numEvents;
						eventTypes |= bit;
					}
					pEvent = pEvent->Next();
				}
			}

			// find global events
			pEvent = gEditor.pSong->events.GetNextEvent(gEditor.offset);
			while(pEvent && pEvent->tick == gEditor.offset)
			{
				uint32 bit = 1 << pEvent->event;
				if(!(eventTypes & bit))
				{
					++numEvents;
					eventTypes |= bit;
				}
				pEvent = pEvent->Next();
			}

			// if there are multiple event types to remove, show a list box
			if(numEvents > 1)
				gpListBox->Show(MFTranslation_GetString(pStrings, SELECT_EVENT_REMOVE), RemoveEventCallback, 200.0f, 100.0f);

			for(int a=0; a<GHE_Max; ++a)
			{
				if(eventTypes & (1 << a))
				{
					if(numEvents > 1)
						gpListBox->AddItem(MFTranslation_GetString(pStrings, EVENT_TYPE_UNKNOWN+a), (void*)(size_t)a);
					else
						RemoveEventCallback(false, 0, NULL, (void*)(size_t)a);
				}
			}
		}
	}

	// shift notes left or right
	int selStart, selEnd;

	if(gEditor.selectStart != gEditor.selectEnd)
	{
		selStart = gEditor.selectStart;
		selEnd = gEditor.selectEnd;
	}
	else
	{
		selStart = gEditor.offset;
		selEnd = gEditor.pSong->GetLastNoteTick();
	}

	if(TestControl(dBCtrl_Edit_ShiftForwards, GHCT_Delay))
	{
		int offset = 4*res / gEditor.quantisation;

		// TODO: gotta remove notes after the selection that will be overwritten as the selection shifts..

		if(altState)
		{
			// shift events, sync and other tracks too
			GHEvent *pEv = gEditor.pSong->sync.GetNextEvent(selStart);
			while(pEv && pEv->tick < selEnd)
			{
				if(pEv->tick != 0)
					pEv->tick += offset;
				pEv = pEv->Next();
			}

			pEv = gEditor.pSong->events.GetNextEvent(selStart);
			while(pEv && pEv->tick < selEnd)
			{
				pEv->tick += offset;
				pEv = pEv->Next();
			}

			for(int i=0; i<GHS_Max; ++i)
			{
				pEv = gEditor.pSong->notes[i].GetNextEvent(selStart);
				while(pEv && pEv->tick < selEnd)
				{
					pEv->tick += offset;
					pEv = pEv->Next();
				}
			}
		}
		else
		{
			GHEvent *pEv = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]].GetNextEvent(selStart);
			while(pEv && pEv->tick < selEnd)
			{
				pEv->tick += offset;
				pEv = pEv->Next();
			}
		}

		gEditor.selectStart += offset;
		gEditor.selectEnd += offset;
		gEditor.offset += offset;

		gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[gEditor.selectedStream], gEditor.offset);
	}
	if(TestControl(dBCtrl_Edit_ShiftBackwards, GHCT_Delay))
	{
		int offset = MFMin(4*res / gEditor.quantisation, gEditor.selectStart);

		// TODO: gotta remove notes before the selection that will be overwritten as the selection shifts..

		if(altState)
		{
			// shift events, sync and other tracks too
			GHEvent *pEv = gEditor.pSong->sync.GetNextEvent(selStart);
			while(pEv && pEv->tick < selEnd)
			{
				if(pEv->tick != 0)
					pEv->tick -= offset;
				pEv = pEv->Next();
			}

			pEv = gEditor.pSong->events.GetNextEvent(selStart);
			while(pEv && pEv->tick < selEnd)
			{
				pEv->tick -= offset;
				pEv = pEv->Next();
			}

			for(int i=0; i<GHS_Max; ++i)
			{
				pEv = gEditor.pSong->notes[i].GetNextEvent(selStart);
				while(pEv && pEv->tick < selEnd)
				{
					pEv->tick -= offset;
					pEv = pEv->Next();
				}
			}
		}
		else
		{
			GHEvent *pEv = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]].GetNextEvent(selStart);
			while(pEv && pEv->tick < selEnd)
			{
				pEv->tick -= offset;
				pEv = pEv->Next();
			}
		}

		gEditor.selectStart -= offset;
		gEditor.selectEnd -= offset;
		gEditor.offset -= offset;

		gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[gEditor.selectedStream], gEditor.offset);
	}
	if(TestControl(dBCtrl_Edit_ShiftLeft, GHCT_Once))
	{
		GHEvent *pEv = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]].GetNextEvent(selStart);

		while(pEv && pEv->tick < selEnd)
		{
			if(pEv->event == GHE_Note)
				pEv->key = MFMax(0, pEv->key - 1);

			pEv = pEv->Next();
		}
	}
	if(TestControl(dBCtrl_Edit_ShiftRight, GHCT_Once))
	{
		GHEvent *pEv = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]].GetNextEvent(selStart);

		while(pEv && pEv->tick < selEnd)
		{
			if(pEv->event == GHE_Note)
				pEv->key = MFMin(4, pEv->key + 1);

			pEv = pEv->Next();
		}
	}

	// change quantisation
	if(TestControl(dBCtrl_Edit_QuantiseDown, GHCT_Delay))
	{
		gEditor.quantiseStep = MFMax(0, gEditor.quantiseStep-1);
		gEditor.quantisation = gQuantiseSteps[gEditor.quantiseStep];
		OffsetToMeasureAndBeat(gEditor.offset, &gEditor.measure, &gEditor.beat);

		MFVoice *pVoice = MFSound_Play(gEditor.pChangeSound, MFPF_BeginPaused);
		MFSound_SetVolume(pVoice, gConfig.sound.fxLevel);
		MFSound_Pause(pVoice, false);
	}
	if(TestControl(dBCtrl_Edit_QuantiseUp, GHCT_Delay))
	{
		gEditor.quantiseStep = MFMin((int)(sizeof(gQuantiseSteps)/sizeof(gQuantiseSteps[0]))-1, gEditor.quantiseStep+1);
		gEditor.quantisation = gQuantiseSteps[gEditor.quantiseStep];
		OffsetToMeasureAndBeat(gEditor.offset, &gEditor.measure, &gEditor.beat);

		MFVoice *pVoice = MFSound_Play(gEditor.pChangeSound, MFPF_BeginPaused);
		MFSound_SetVolume(pVoice, gConfig.sound.fxLevel);
		MFSound_Pause(pVoice, false);
	}

	// move the track
	if(TestControl(dBCtrl_Edit_Forward, GHCT_Delay))
	{
		// forward one step
		++gEditor.beat;
		if(gEditor.beat == gEditor.quantisation)
		{
			++gEditor.measure;
			gEditor.beat = 0;
		}
	}
	if(TestControl(dBCtrl_Edit_Back, GHCT_Delay))
	{
		// back one step
		if(gEditor.measure || gEditor.beat)
		{
			--gEditor.beat;
			if(gEditor.beat < 0)
			{
				--gEditor.measure;
				gEditor.beat += gEditor.quantisation;
			}
		}
	}
	if(TestControl(dBCtrl_Edit_Start, GHCT_Once))
	{
		// go to start
		gEditor.measure = gEditor.beat = 0;
	}
	if(TestControl(dBCtrl_Edit_End, GHCT_Once))
	{
		// go to the last note...
		OffsetToMeasureAndBeat(gEditor.pSong->GetLastNoteTick(), &gEditor.measure, &gEditor.beat);
	}
	if(TestControl(dBCtrl_Edit_UpMeasure, GHCT_Delay))
	{
		// forward one measure
		// TODO: consider bar lengths while moving
		++gEditor.measure;
	}
	if(TestControl(dBCtrl_Edit_DownMeasure, GHCT_Delay))
	{
		// back one measure
		// TODO: consider bar lengths while moving
		if(gEditor.measure < 1)
			gEditor.measure = gEditor.beat = 0;
		else
			--gEditor.measure;
	}
	if(TestControl(dBCtrl_Edit_NextSection, GHCT_Delay))
	{
		GHEvent *pEv = gEditor.pSong->events.GetNextEvent(gEditor.offset);

		while(pEv)
		{
			if(pEv->tick >= gEditor.offset + gEditor.pSong->resolution*4 / gEditor.quantisation && !MFString_CompareN(pEv->GetString(), "section ", 8))
			{
				OffsetToMeasureAndBeat(pEv->tick, &gEditor.measure, &gEditor.beat);
				break;
			}

			pEv = pEv->Next();
		}

		if(!pEv)
			OffsetToMeasureAndBeat(gEditor.pSong->GetLastNoteTick(), &gEditor.measure, &gEditor.beat);
	}
	if(TestControl(dBCtrl_Edit_PrevSection, GHCT_Delay))
	{
		GHEvent *pMostRecent = NULL;
		GHEvent *pEv = gEditor.pSong->events.First();

		while(pEv && pEv->tick < gEditor.offset)
		{
			if(!MFString_CompareN(pEv->GetString(), "section ", 8))
				pMostRecent = pEv;

			pEv = pEv->Next();
		}

		if(pMostRecent)
			OffsetToMeasureAndBeat(pMostRecent->tick, &gEditor.measure, &gEditor.beat);
		else
			gEditor.measure = gEditor.beat = 0;
	}

	int newOffset = MeasureAndBeatToOffset(gEditor.measure, gEditor.beat);
	int shift = newOffset - gEditor.offset;
	shift = MFMax(gEditor.offset + shift, 0) - gEditor.offset;

	if(shift)
	{
		// update BPM if applicable
		int shiftStart, shiftEnd;

		if(shift > 0)
		{
			shiftStart = gEditor.offset;
			shiftEnd = gEditor.offset+shift + 1;
		}
		else
		{
			shiftStart = 0;
			shiftEnd = gEditor.offset+shift + 1;
		}

		gEditor.offset += shift;

		if(bSelecting)
			gEditor.selectEnd = gEditor.offset;

		GHEvent *pEv = gEditor.pSong->sync.GetNextEvent(shiftStart);

		while(pEv && pEv->tick < shiftEnd)
		{
			if(pEv->event == GHE_BPM || pEv->event == GHE_Anchor)
				gEditor.currentBPM = pEv->parameter;
			else if(pEv->event == GHE_TimeSignature)
			{
				gEditor.currentTimeSignature = pEv->parameter;
				gEditor.lastTimeSignature = pEv->tick;
			}

			pEv = pEv->Next();
		}

		gEditor.playingTime = gEditor.pSong->CalculateTimeOfTick(gEditor.offset);

		MFVoice *pVoice = MFSound_Play(gEditor.pStepSound, MFPF_BeginPaused);
		if(pVoice)
		{
			MFSound_SetVolume(pVoice, gConfig.sound.fxLevel);
			MFSound_Pause(pVoice, false);
		}
	}

	// increase/decrease BPM
	if(gEditor.currentBPM > 1000 && TestControl(dBCtrl_Edit_DecreaseBPM, GHCT_Delay))
	{
		// reduce BPM
		int inc = 1000;

		if(shiftState)
			inc /= 10;
		if(ctrlState)
			inc /= 100;
		if(altState)
			inc *= 10;

		GHEvent *pEv = gEditor.pSong->sync.FindEvent(GHE_BPM, gEditor.offset, 0);
		if(!pEv) pEv = gEditor.pSong->sync.FindEvent(GHE_Anchor, gEditor.offset, 0);

		if(!pEv)
			pEv = gEditor.pSong->sync.AddEvent(GHE_BPM, gEditor.offset, 0, MFMax(gEditor.currentBPM - inc, 1000));
		else
			pEv->parameter = MFMax(pEv->parameter - inc, 1000);

		gEditor.currentBPM = pEv->parameter;

		// remove this BPM marker if its the same as the previous one..
		if(pEv->event != GHE_Anchor)
		{
			GHEvent *pPrev = gEditor.pSong->sync.GetMostRecentEvent(GHE_BPM, gEditor.offset);
			if(pPrev && pPrev->parameter == pEv->parameter)
				gEditor.pSong->sync.RemoveEvent(pEv);
		}

		// recalculate the note times from this point on
		gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[0], gEditor.offset);
		if(gEditor.currentStream[1] != -1)
			gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[1], gEditor.offset);
	}

	if(gEditor.currentBPM < 9999000 && TestControl(dBCtrl_Edit_IncreaseBPM, GHCT_Delay))
	{
		// increase BPM
		int inc = 1000;

		if(shiftState)
			inc /= 10;
		if(ctrlState)
			inc /= 100;
		if(altState)
			inc *= 10;

		GHEvent *pEv = gEditor.pSong->sync.FindEvent(GHE_BPM, gEditor.offset, 0);
		if(!pEv) pEv = gEditor.pSong->sync.FindEvent(GHE_Anchor, gEditor.offset, 0);

		if(!pEv)
			pEv = gEditor.pSong->sync.AddEvent(GHE_BPM, gEditor.offset, 0, MFMin(gEditor.currentBPM + inc, 9999000));
		else
			pEv->parameter = MFMin(pEv->parameter + inc, 9999000);

		gEditor.currentBPM = pEv->parameter;

		// remove this BPM marker if its the same as the previous one..
		if(pEv->event != GHE_Anchor)
		{
			GHEvent *pPrev = gEditor.pSong->sync.GetMostRecentEvent(GHE_BPM, gEditor.offset);
			if(pEv->event != GHE_Anchor && pPrev && pPrev->parameter == pEv->parameter)
				gEditor.pSong->sync.RemoveEvent(pEv);
		}

		// recalculate the note times from this point on
		gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[0], gEditor.offset);
		if(gEditor.currentStream[1] != -1)
			gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[1], gEditor.offset);
	}

	// place anchor
	if(TestControl(dBCtrl_Edit_Anchor, GHCT_Once) && gEditor.offset > 0)
	{
		GHEvent *pEv = gEditor.pSong->sync.FindEvent(GHE_BPM, gEditor.offset, 0);

		if(pEv)
		{
			pEv->event = GHE_Anchor;
			pEv->time = gEditor.pSong->CalculateTimeOfTick(pEv->tick);
		}
		else
		{
			pEv = gEditor.pSong->sync.FindEvent(GHE_Anchor, gEditor.offset, 0);

			if(!pEv)
			{
				pEv = gEditor.pSong->sync.AddEvent(GHE_Anchor, gEditor.offset, 0, gEditor.currentBPM);
				pEv->time = gEditor.pSong->CalculateTimeOfTick(pEv->tick);
			}
			else
			{
				GHEvent *pLast = gEditor.pSong->sync.GetMostRecentSyncEvent(pEv->tick);

				if(pLast && pLast->parameter == pEv->parameter)
					gEditor.pSong->sync.RemoveEvent(pEv);
				else
					pEv->event = GHE_BPM;
			}
		}

		// recalculate the note times
		gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[0], 0);
		if(gEditor.currentStream[1] != -1)
			gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[1], 0);
	}

	// change time signature
	if(TestControl(dBCtrl_Edit_DecreaseTS, GHCT_Delay) && gEditor.currentTimeSignature > 1)
	{
		int tsTime = gEditor.offset - ((gEditor.offset - gEditor.lastTimeSignature) % (res*gEditor.currentTimeSignature));

		GHEvent *pEv = gEditor.pSong->sync.FindEvent(GHE_TimeSignature, tsTime, 0);

		if(!pEv)
			pEv = gEditor.pSong->sync.AddEvent(GHE_TimeSignature, tsTime, 0, gEditor.currentTimeSignature - 1);
		else
			--pEv->parameter;

		gEditor.currentTimeSignature = pEv->parameter;
		gEditor.lastTimeSignature = tsTime;

		// remove this BPM marker if its the same as the previous one..
		GHEvent *pPrev = gEditor.pSong->sync.GetMostRecentEvent(GHE_TimeSignature, tsTime);
		if(pPrev && pPrev->parameter == pEv->parameter)
		{
			gEditor.lastTimeSignature = pPrev->tick;
			gEditor.pSong->sync.RemoveEvent(pEv);
		}

		// recalculate the note times from this point on
		gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[0], tsTime);
		if(gEditor.currentStream[1] != -1)
			gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[1], tsTime);
	}
	else if(TestControl(dBCtrl_Edit_IncreaseTS, GHCT_Delay) && gEditor.currentTimeSignature < 99)
	{
		int tsTime = gEditor.offset - ((gEditor.offset - gEditor.lastTimeSignature) % (res*gEditor.currentTimeSignature));

		GHEvent *pEv = gEditor.pSong->sync.FindEvent(GHE_TimeSignature, tsTime, 0);

		if(!pEv)
			pEv = gEditor.pSong->sync.AddEvent(GHE_TimeSignature, tsTime, 0, gEditor.currentTimeSignature + 1);
		else
			++pEv->parameter;

		gEditor.currentTimeSignature = pEv->parameter;
		gEditor.lastTimeSignature = tsTime;

		// remove this BPM marker if its the same as the previous one..
		GHEvent *pPrev = gEditor.pSong->sync.GetMostRecentEvent(GHE_TimeSignature, tsTime);
		if(pPrev && pPrev->parameter == pEv->parameter)
		{
			gEditor.lastTimeSignature = pPrev->tick;
			gEditor.pSong->sync.RemoveEvent(pEv);
		}

		// recalculate the note times from this point on
		gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[0], tsTime);
		if(gEditor.currentStream[1] != -1)
			gEditor.pSong->CalculateNoteTimes(gEditor.currentStream[1], tsTime);
	}

	// add/remove notes
	GHEventManager &noteStream = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]];
	dBControlType keys_righty[] = { dBCtrl_Edit_Note0, dBCtrl_Edit_Note1, dBCtrl_Edit_Note2, dBCtrl_Edit_Note3, dBCtrl_Edit_Note4, dBCtrl_Edit_PS1, dBCtrl_Edit_PS2, dBCtrl_Edit_Note5 };
	dBControlType keys_lefty[] = { dBCtrl_Edit_Note4, dBCtrl_Edit_Note3, dBCtrl_Edit_Note2, dBCtrl_Edit_Note1, dBCtrl_Edit_Note0, dBCtrl_Edit_PS1, dBCtrl_Edit_PS2, dBCtrl_Edit_Note5 };
	dBControlType *keys = gConfig.controls.leftyFlip[0] ? keys_lefty : keys_righty;

	for(int a=0; a<8; a++)
	{
		GHEventType ev = a < 5 ? GHE_Note : GHE_Special;
		int key = a < 5 ? GHEK_Green + a : GHS_Player1 + (a - 5);

		if(TestControl(keys[a], GHCT_Hold))
		{
			if(pHold[a])
			{
				if(pHold[a] != (GHEvent*)(size_t)0xFFFFFFFF)
				{
					pHold[a]->parameter = MFMax(gEditor.offset - pHold[a]->tick, 0);
				}
			}
			else
			{
				GHEvent *pEv = noteStream.FindEvent(ev, gEditor.offset, key);

				if(pEv)
				{
					noteStream.RemoveEvent(pEv);
					pHold[a] = (GHEvent*)(size_t)0xFFFFFFFF;
					for(int i=0; i<8; ++i)
					{
						if(pHold[i] && pHold[i] != (GHEvent*)(size_t)0xFFFFFFFF)
						{
							if(pHold[i] > pEv)
								--pHold[i];
						}
					}
				}
				else
				{
					// check if we are intersecting a hold note
					pEv = noteStream.GetMostRecentEvent(GHE_Note, gEditor.offset);

					if(pEv && pEv->parameter > gEditor.offset - pEv->tick)
					{
						// the last note was a hold note, we'll cut it short...
						do
						{
							pEv->parameter = gEditor.offset - pEv->tick;
							pEv = pEv->Prev();
						}
						while(pEv && pEv->tick == pEv->Next()->tick);
					}

					pEv = noteStream.AddEvent(ev, gEditor.offset, key);
					pEv->time = gEditor.pSong->CalculateTimeOfTick(gEditor.offset);
					pHold[a] = pEv;
				}
			}
		}
		else
		{
			if(a<5)
			{
				// check if we have just released a hold note
				if(pHold[a] && pHold[a] != (GHEvent*)(size_t)0xFFFFFFFF && pHold[a]->parameter != 0)
				{
					// remove any other notes within the hold range
					GHEvent *pEv = gEditor.pSong->notes[gEditor.currentStream[gEditor.selectedStream]].GetNextEvent(pHold[a]->tick);

					while(pEv && pEv->tick < pHold[a]->tick+pHold[a]->parameter)
					{
						GHEvent *pNext = pEv->Next();

						if(pEv->event == GHE_Note)
						{
							// and make sure we dont remove chords
							if(pHold[a]->tick != pEv->tick || pHold[a]->parameter != pEv->parameter)
							{
								pNext = noteStream.RemoveEvent(pEv);
								for(int i=0; i<8; ++i)
								{
									if(pHold[i] && pHold[i] != (GHEvent*)(size_t)0xFFFFFFFF)
									{
										if(pHold[i] > pEv)
											--pHold[i];
									}
								}
							}
						}

						pEv = pNext;
					}
				}
			}
			else
			{
				// remove zero length special events...
				if(pHold[a] && pHold[a] != (GHEvent*)(size_t)0xFFFFFFFF && pHold[a]->parameter == 0)
				{
					noteStream.RemoveEvent(pHold[a]);
				}
			}

			pHold[a] = NULL;
		}
	}
}
Пример #25
0
void Fretboard::Draw(float time, dBChart *pSong, int track)
{
	MFCALLSTACKc;

	MFView_Push();

	MFRect rect;
	MFView_GetViewport(&rect);

	float aspect = rect.width / rect.height;
	aspect = MFClamp(0.82f, aspect, 2.0f);
	MFView_SetAspectRatio(aspect);

	if(viewPoint == 0)
	{
		// incoming
		MFView_ConfigureProjection(MFDEGREES(65.0f)/aspect, 1.0f, 100.0f);

		// Setup the Camera in 3D space.
		MFMatrix cameraMatrix;
		MFVector start = MakeVector( 0, 8, 3 );
		MFVector dir = MakeVector( 0, 5, -8 );
		dir = (dir-start) * (1.0f/1.777777777f);
		cameraMatrix.LookAt(start + dir*aspect, MakeVector(0.0f, 0.0f, 5.0f));
		MFView_SetCameraMatrix(cameraMatrix);
	}
	else if(viewPoint == 1)
	{
		// overhead
		MFView_ConfigureProjection(MFDEGREES(45.0f), 1.0f, 100.0f);
/*
		float aspect = MFDisplay_GetNativeAspectRatio();
		MFView_SetAspectRatio(aspect);

		MFRect projRect;
		projRect.y = 15;
		projRect.height = -30;
		projRect.x = -projRect.y * aspect;
		projRect.width = -projRect.height * aspect;
		MFView_SetOrtho(&projRect);
*/

		// Setup the Camera in 3D space.
		MFMatrix cameraMatrix;
		cameraMatrix.LookAt(MakeVector(0, 30, 10), MakeVector(0, 0, 10), MakeVector(0, 0, 1));
		MFView_SetCameraMatrix(cameraMatrix);
	}
	else if(viewPoint == 2)
	{
		// overhead
		MFView_ConfigureProjection(MFDEGREES(45.0f), 1.0f, 100.0f);
/*
		float aspect = MFDisplay_GetNativeAspectRatio();
		MFView_SetAspectRatio(aspect);

		MFRect projRect;
		projRect.y = 15;
		projRect.height = -30;
		projRect.x = -projRect.y * aspect;
		projRect.width = -projRect.height * aspect;
		MFView_SetOrtho(&projRect);
*/

		// Setup the Camera in 3D space.
		MFMatrix cameraMatrix;
		cameraMatrix.LookAt(MakeVector(0, 20, 13), MakeVector(0, 0, 13), MakeVector(-1, 0, 0));
		MFView_SetCameraMatrix(cameraMatrix);
	}

	MFView_SetProjection();

	MFMaterial *pFB = pSong->pFretboard ? pSong->pFretboard : pFretboard;
	MFMaterial_SetMaterial(pFB);
	MFPrimitive(PT_TriStrip, 0);

	int start = -4;
	int end = 60;
	int fadeStart = end - 10;

	float fretboardRepeat = 15.0f;
	float fretboardWidth = 7.0f;

	float columnWidth = fretboardWidth / 5.0f;
	float ringBorder = 0.1f;

	// draw the fretboard...
	MFBegin(((end-start) / 4) * 2 + 2);
	MFSetColourV(MFVector::white);

	float halfFB = fretboardWidth*0.5f;

	float offset = time*scrollSpeed;
	float topTime = time + end/scrollSpeed;
	float bottomTime = time + start/scrollSpeed;

	int a;
	float textureOffset = fmodf(offset, fretboardRepeat);
	for(a=start; a<end; a+=4)
	{
		float z = (float)a;
		MFSetTexCoord1(1.0f, 1.0f - (z+textureOffset) / fretboardRepeat);
		MFSetPosition(halfFB, 0.0f, z);
		MFSetTexCoord1(0.0f, 1.0f - (z+textureOffset) / fretboardRepeat);
		MFSetPosition(-halfFB, 0.0f, z);
	}

	float z = (float)a;
	MFSetTexCoord1(1.0f, 1.0f - (z+textureOffset) / fretboardRepeat);
	MFSetPosition(halfFB, 0.0f, z);
	MFSetTexCoord1(0.0f, 1.0f - (z+textureOffset) / fretboardRepeat);
	MFSetPosition(-halfFB, 0.0f, z);

	MFEnd();

	// draw the selection region
	MFMaterial_SetMaterial(pFrets);
	MFPrimitive(PT_TriStrip, 0);

	if(gEditor.selectStart != gEditor.selectEnd)
	{
		float selectStartTime = GETSECONDS(pSong->CalculateTimeOfTick(gEditor.selectStart));
		float selectEndTime = GETSECONDS(pSong->CalculateTimeOfTick(gEditor.selectEnd));

		if(selectStartTime < topTime && selectEndTime > bottomTime)
		{
			selectStartTime = (MFMax(bottomTime, selectStartTime) - time) * scrollSpeed;
			selectEndTime = (MFMin(topTime, selectEndTime) - time) * scrollSpeed;

			MFBegin(4);
			MFSetColour(1.0f, 0.0f, 0.0f, 0.5f);
			MFSetPosition(-halfFB, 0.0f, selectEndTime);
			MFSetPosition(halfFB, 0.0f, selectEndTime);
			MFSetPosition(-halfFB, 0.0f, selectStartTime);
			MFSetPosition(halfFB, 0.0f, selectStartTime);
			MFEnd();
		}
	}

	// draw the fretboard edges and bar lines
	const float barWidth = 0.2f;

	MFMaterial_SetMaterial(pBar);
	MFPrimitive(PT_TriStrip, 0);

	MFBegin(4);
	MFSetColour(0.0f, 0.0f, 0.0f, 0.8f);
	MFSetTexCoord1(0,0);
	MFSetPosition(-halfFB, 0.0f, barWidth);
	MFSetTexCoord1(1,0);
	MFSetPosition(halfFB, 0.0f, barWidth);
	MFSetTexCoord1(0,1);
	MFSetPosition(-halfFB, 0.0f, -barWidth);
	MFSetTexCoord1(1,1);
	MFSetPosition(halfFB, 0.0f, -barWidth);
	MFEnd();

	MFMaterial_SetMaterial(pEdge);
	MFPrimitive(PT_TriStrip, 0);
	MFBegin(34);

	MFSetColour(0.0f, 0.0f, 0.0f, 0.3f);
	for(int col=1; col<5; col++)
	{
		if(col > 1)
			MFSetPosition(-halfFB + columnWidth*(float)col - 0.02f, 0.0f, (float)end);

		MFSetTexCoord1(0,0);
		MFSetPosition(-halfFB + columnWidth*(float)col - 0.02f, 0.0f, (float)end);
		MFSetTexCoord1(1,0);
		MFSetPosition(-halfFB + columnWidth*(float)col + 0.02f, 0.0f, (float)end);
		MFSetTexCoord1(0,1);
		MFSetPosition(-halfFB + columnWidth*(float)col - 0.02f, 0.0f, (float)start);
		MFSetTexCoord1(1,1);
		MFSetPosition(-halfFB + columnWidth*(float)col + 0.02f, 0.0f, (float)start);

		MFSetPosition(-halfFB + columnWidth*(float)col + 0.02f, 0.0f, (float)start);
	}
	MFSetColourV(MFVector::white);
	MFSetPosition(-halfFB - 0.1f, 0.0f, (float)end);

	MFSetTexCoord1(0,0);
	MFSetPosition(-halfFB - 0.1f, 0.0f, (float)end);
	MFSetTexCoord1(1,0);
	MFSetPosition(-halfFB + 0.1f, 0.0f, (float)end);
	MFSetTexCoord1(0,1);
	MFSetPosition(-halfFB - 0.1f, 0.0f, (float)start);
	MFSetTexCoord1(1,1);
	MFSetPosition(-halfFB + 0.1f, 0.0f, (float)start);

	MFSetPosition(-halfFB + 0.1f, 0.0f, (float)start);
	MFSetPosition(halfFB - 0.1f, 0.0f, (float)end);

	MFSetTexCoord1(0,0);
	MFSetPosition(halfFB - 0.1f, 0.0f, (float)end);
	MFSetTexCoord1(1,0);
	MFSetPosition(halfFB + 0.1f, 0.0f, (float)end);
	MFSetTexCoord1(0,1);
	MFSetPosition(halfFB - 0.1f, 0.0f, (float)start);
	MFSetTexCoord1(1,1);
	MFSetPosition(halfFB + 0.1f, 0.0f, (float)start);

	MFEnd();

	// draw the frets....
	MFMaterial_SetMaterial(pBar);
	MFPrimitive(PT_TriStrip, 0);

	int bottomTick = pSong->CalculateTickAtTime((int64)(bottomTime*1000000.0f));
	int res = pSong->GetRes();
	int ticks = bHalfFrets ? res/2 : res;
	int fretBeat = bottomTick + ticks - 1;
	fretBeat -= fretBeat % ticks;
	float fretTime = GETSECONDS(pSong->CalculateTimeOfTick(fretBeat));

	while(fretTime < topTime)
	{
		bool halfBeat = (fretBeat % res) != 0;
		bool bar = false;

		if(!halfBeat)
		{
			GHEvent *pLastTS = pSong->sync.GetMostRecentEvent(GHE_TimeSignature, fretBeat);

			if(pLastTS)
				bar = ((fretBeat - pLastTS->tick) % (pLastTS->parameter*res)) == 0;
			else if(fretBeat == 0)
				bar = true;
		}

		float bw = bar ? barWidth : barWidth*0.5f;
		MFBegin(4);

		float position = (fretTime - time) * scrollSpeed;

		if(!halfBeat)
			MFSetColourV(MFVector::white);
		else
			MFSetColourV(MakeVector(1,1,1,0.3f));
		MFSetTexCoord1(0,0);
		MFSetPosition(-halfFB, 0.0f, position + bw);
		MFSetTexCoord1(1,0);
		MFSetPosition(halfFB, 0.0f, position + bw);
		MFSetTexCoord1(0,1);
		MFSetPosition(-halfFB, 0.0f, position + -bw);
		MFSetTexCoord1(1,1);
		MFSetPosition(halfFB, 0.0f, position + -bw);

		MFEnd();

		fretBeat += ticks;
		fretTime = GETSECONDS(pSong->CalculateTimeOfTick(fretBeat));
	}

	// draw the notes...
	GHEventManager &noteStream = pSong->notes[track];
	GHEvent *pEv = noteStream.First();

	int64 topTimeus = (int64)(topTime*1000000.0f);
	while(pEv && pEv->time < topTimeus)
	{
		if((pEv->event == GHE_Note || pEv->event == GHE_Special) && pEv->tick + pEv->parameter >= bottomTick)
		{
			float evTime = GETSECONDS(pEv->time);

			// TODO: we need to calculate the end of the hold...
			float noteEnd = evTime;
			if(pEv->parameter)
				noteEnd = GETSECONDS(pSong->CalculateTimeOfTick(pEv->tick + pEv->parameter));

			if(pEv->event == GHE_Note && pEv->played != 1)
			{
				// draw a note
				int key = pEv->key;
				bool tap = false;

				// check if there is a previous note, and it is in range
				if(pEv->Prev() && pEv->Prev()->tick < pEv->tick && pEv->Prev()->tick > pEv->tick - (res/2) && pEv->Prev()->key != pEv->key
					&& (!pEv->Next() || !(pEv->Next()->tick == pEv->tick))
					&& !pEv->Prev()->parameter && !(pEv->Prev()->Prev() && pEv->Prev()->Prev()->tick == pEv->Prev()->tick))
				{
					tap = true;
				}

				int noteX = gConfig.controls.leftyFlip[0] ? 4-key : key;

				float position = (GETSECONDS(pEv->time) - time)*scrollSpeed;
				float xoffset = -halfFB + columnWidth*0.5f + (float)noteX*columnWidth;

				if(pEv->parameter)
				{
					MFMaterial_SetMaterial(pFrets);

					float whammyTop = (noteEnd - time)*scrollSpeed;

					MFPrimitive(PT_TriStrip, 0);

					// TODO: we could consider not drawing this part of the hold line.. seems reasonable that it terminates at the line...
					if(gEditor.state == GHPS_Stopped)
					{
						if(position < 0.0f)
						{
							MFBegin(4);
							MFSetColourV(gColours[key]);
							MFSetPosition(xoffset - 0.2f, 0.0f, MFMin(whammyTop, 0.0f));
							MFSetPosition(xoffset + 0.2f, 0.0f, MFMin(whammyTop, 0.0f));
							MFSetPosition(xoffset - 0.2f, 0.0f, position);
							MFSetPosition(xoffset + 0.2f, 0.0f, position);
							MFEnd();
						}
					}

					if(whammyTop > 0.0f)
					{
						// this half could have waves cruising down it if we wanted to support the whammy...
						MFBegin(4);
						MFSetColourV(gColours[key]);
						MFSetPosition(xoffset - 0.2f, 0.0f, MFMin(whammyTop, (float)end));
						MFSetPosition(xoffset + 0.2f, 0.0f, MFMin(whammyTop, (float)end));
						MFSetPosition(xoffset - 0.2f, 0.0f, MFMax(position, 0.0f));
						MFSetPosition(xoffset + 0.2f, 0.0f, MFMax(position, 0.0f));
						MFEnd();
					}
				}

				if(evTime >= bottomTime)
				{
					MFMatrix mat;
					mat.SetScale(MakeVector(0.5f/20, 0.5f/20, 0.5f/20));
					mat.Translate(MakeVector(xoffset, 0.03f, position));
					MFModel_SetWorldMatrix(pButton, mat);

					MFStateBlock *pSB = MFStateBlock_CreateTemporary(64);
					MFStateBlock_SetVector(pSB, MFSCV_DiffuseColour, pEv->played == -1 ? MakeVector(0.3f, 0.3f, 0.3f, 1.0f) : MFVector::white);
//					MFStateBlock_SetVector(pSB, MFSCV_DiffuseColour, position < 0.0f ? MakeVector(0.3f, 0.3f, 0.3f, 1.0f) : MFVector::white);

//					MFRenderer_SetRenderStateOverride(MFRS_MaterialOverride, (uint32&)(tap ? pButtonMat[key] : pButtonRing[key]));
					MFRenderer_AddModel(pButton, pSB, MFView_GetViewState());

					// render the note time
					if(bRenderNoteTimes)
					{
						MFView_Push();
						MFView_SetOrtho(&rect);

						MFVector pos;
						MFView_TransformPoint3DTo2D(MakeVector(xoffset, 0.0f, position), &pos);
						pos.x += 16.0f;
						pos.y -= 26.0f;

						int minutes = (int)(pEv->time / 60000000);
						int seconds = (int)((pEv->time % 60000000) / 1000000);
						int milliseconds = (int)((pEv->time % 1000000) / 1000);
						MFFont_DrawTextf(pText, pos, 20.0f, MFVector::yellow, "%s: %d:%02d.%d\nTick: %g", MFTranslation_GetString(pStrings, TRACK_TIME), minutes, seconds, milliseconds, (float)pEv->tick/res);

						MFView_Pop();
					}
				}
			}

			if(pEv->event == GHE_Special)
			{
//				static MFVector specialColours[3] = { MakeVector(1,0,0,1), MakeVector(1,1,0,1), MakeVector(0,0,1,1) };
//				static float specialX[3] = { halfFB + 0.2f, halfFB + 1, -halfFB - 1 };
//				static float specialWidth[3] = { 0.8f, 0.8f, 0.8f };
				static MFVector specialColours[3] = { MakeVector(1,0,0,0.5f), MakeVector(1,1,0,0.5f), MakeVector(0,0,1,0.5f) };
				static float specialX[3] = { -halfFB, halfFB - 0.8f, -halfFB };
				static float specialWidth[3] = { 0.8f, 0.8f, fretboardWidth };

				float bottom = (evTime - time)*scrollSpeed;
				float top = (noteEnd - time)*scrollSpeed;

				int key = pEv->key;

				MFMaterial_SetMaterial(pFrets);

				MFPrimitive(PT_TriStrip, 0);
				MFBegin(4);
				MFSetColourV(specialColours[key]);
				MFSetPosition(specialX[key], 0.0f, MFMin(top, (float)end));
				MFSetPosition(specialX[key]+specialWidth[key], 0.0f, MFMin(top, (float)end));
				MFSetPosition(specialX[key], 0.0f, MFMax(bottom, (float)start));
				MFSetPosition(specialX[key]+specialWidth[key], 0.0f, MFMax(bottom, (float)start));
				MFEnd();
			}
		}

		pEv = pEv->Next();
	}

//	MFRenderer_SetRenderStateOverride(MFRS_MaterialOverride, NULL);

	// draw circles at the bottom..
	MFMaterial_SetMaterial(pRing);
	for(int a=0; a<5; a++)
	{
		DrawRing(-halfFB + (float)a*columnWidth + columnWidth*ringBorder, 0.0f, columnWidth*(1.0f-ringBorder*2));
	}

	for(int a=0; a<5; a++)
	{
		dBControlType keys_righty[] = { dBCtrl_Edit_Note0, dBCtrl_Edit_Note1, dBCtrl_Edit_Note2, dBCtrl_Edit_Note3, dBCtrl_Edit_Note4 };
		dBControlType keys_lefty[] = { dBCtrl_Edit_Note4, dBCtrl_Edit_Note3, dBCtrl_Edit_Note2, dBCtrl_Edit_Note1, dBCtrl_Edit_Note0 };
		dBControlType *keys = gConfig.controls.leftyFlip[0] ? keys_lefty : keys_righty;

		int ringPos = gConfig.controls.leftyFlip[0] ? 4-a : a;

		MFMaterial_SetMaterial(pColourRing[a]);
		DrawRing(-halfFB + (float)ringPos*columnWidth, 0.0f, columnWidth, !TestControl(keys[a], GHCT_Hold));
	}

	// render trigger particles
	MFParticleSystem_Draw(pParticles);

	// render text and stuff
	MFView_SetOrtho(&rect);

	pEv = pSong->sync.GetNextEvent(bottomTick);

	while(pEv && pEv->time < topTimeus)
	{
		float evTime = GETSECONDS(pEv->time);

		if(evTime > bottomTime)
		{
			if(pEv->event == GHE_BPM)
			{
				float position = (evTime - time) * scrollSpeed;

				MFVector pos;
				MFView_TransformPoint3DTo2D(MakeVector(halfFB + 0.2f, 0.0f, position), &pos);
				pos.y -= 12.0f;
				MFFont_DrawTextf(pText, pos, 24.0f, MakeVector(0,0.5f,0,1), "%s: %g", MFTranslation_GetString(pStrings, TRACK_BPM), (float)pEv->parameter * 0.001f);
			}
			if(pEv->event == GHE_Anchor)
			{
				int minutes = (int)(pEv->time / 60000000);
				int seconds = (int)((pEv->time%60000000)/1000000);
				int milliseconds = (int)((pEv->time%1000000)/1000);

				float position = (evTime - time) * scrollSpeed;

				MFVector pos;
				MFView_TransformPoint3DTo2D(MakeVector(halfFB + 0.2f, 0.0f, position), &pos);
				pos.y -= 12.0f;
				MFFont_DrawTextf(pText, pos, 24.0f, MakeVector(0,0.5f,0,1), "A: %02d:%02d.%03d\n   %s: %g", minutes, seconds, milliseconds, MFTranslation_GetString(pStrings, TRACK_BPM), (float)pEv->parameter * 0.001f);
			}
			else if(pEv->event == GHE_TimeSignature)
			{
				float position = (evTime - time) * scrollSpeed;

				MFVector pos;
				MFView_TransformPoint3DTo2D(MakeVector(-halfFB - 0.2f, 0.0f, position), &pos);
				const char *pString = MFStr("TS: %d/4", pEv->parameter);
				pos.x -= MFFont_GetStringWidth(pText, pString, 24.0f);
				pos.y -= 12.0f;
				MFFont_DrawTextf(pText, pos, 24.0f, MFVector::yellow, pString);
			}
		}

		pEv = pEv->Next();
	}

	// render events
	pEv = pSong->events.GetNextEvent(bottomTick);

	int lastChecked = -1;
	float yEventOffset = -12.0f;
	while(pEv && pEv->time < topTimeus)
	{
		float evTime = GETSECONDS(pEv->time);

		if(evTime > bottomTime)
		{
			if(pEv->event == GHE_Event)
			{
				if(lastChecked != pEv->tick)
				{
					yEventOffset = -12.0f;
					lastChecked = pEv->tick;

					if(pSong->sync.FindEvent(GHE_TimeSignature, pEv->tick, 0))
					{
						yEventOffset -= 24.0f;
					}
				}

				float position = (evTime - time) * scrollSpeed;

				MFVector pos;
				MFView_TransformPoint3DTo2D(MakeVector(-halfFB - 0.2f, 0.0f, position), &pos);

				if(!MFString_CompareN(pEv->GetString(), "section ", 8))
				{
					// draw a line across?

					pos.x -= MFFont_GetStringWidth(pText, &pEv->GetString()[8], 24.0f);
					pos.y += yEventOffset;
					MFFont_DrawTextf(pText, pos, 24.0f, MFVector::blue, &pEv->GetString()[8]);
				}
				else
				{
					pos.x -= MFFont_GetStringWidth(pText, pEv->GetString(), 24.0f);
					pos.y += yEventOffset;
					MFFont_DrawTextf(pText, pos, 24.0f, MFVector::white, pEv->GetString());
				}

				yEventOffset -= 24.0f;
			}
		}

		pEv = pEv->Next();
	}

	// render track events
	pEv = pSong->notes[track].GetNextEvent(bottomTick);

	lastChecked = -1;
	yEventOffset = -12.0f;
	while(pEv && pEv->time < topTimeus)
	{
		float evTime = GETSECONDS(pEv->time);

		if(evTime > bottomTime)
		{
			if(pEv->event == GHE_Event)
			{
				if(lastChecked != pEv->tick)
				{
					yEventOffset = -12.0f;
					lastChecked = pEv->tick;

					if(pSong->sync.FindEvent(GHE_TimeSignature, pEv->tick, 0))
					{
						yEventOffset -= 24.0f;
					}

					GHEvent *pOther = pSong->events.FindEvent(GHE_Event, pEv->tick, 0);
					while(pOther && pOther->tick == pEv->tick)
					{
						yEventOffset -= 24.0f;
						pOther = pOther->Next();
					}
				}

				float position = (evTime - time) * scrollSpeed;

				MFVector pos;
				MFView_TransformPoint3DTo2D(MakeVector(-halfFB - 0.2f, 0.0f, position), &pos);

				pos.x -= MFFont_GetStringWidth(pText, pEv->GetString(), 24.0f);
				pos.y += yEventOffset;
				MFFont_DrawTextf(pText, pos, 24.0f, MakeVector(0.6f, 0.8f, 1.0f, 1.0f), pEv->GetString());

				yEventOffset -= 24.0f;
			}
		}

		pEv = pEv->Next();
	}

	MFView_Pop();
}