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; }
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; }
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); } } } }
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; }
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; } } }
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; }
void MFParticleSystem_DrawRotating(MFParticleSystem *pParticleSystem, const MFMatrix <v) { 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(); }
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"); } }
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(); } }
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()); }
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; }
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; }
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; }
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; }
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; }
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; }
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); } }
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); }
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(); }
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(); } }
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; } } } } }
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); } }
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; } } }
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 ¬eStream = 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 ¬eStream = 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 ¬es = 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 ¬eStream = 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; } } }
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 ¬eStream = 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(); }