static keyframe* KeyframeLookup(property_channel *Property, int16 i) { int16 b = i / MAX_KEYFRAMES_PER_BLOCK; int16 k = i - b*MAX_KEYFRAMES_PER_BLOCK; return &Property->KeyframeBlock[b]->Keyframe[k]; } static keyframe* PushKeyframe(property_channel *Property) { int16 i = Property->NumberOfTotalKeyframes; int16 b = i / MAX_KEYFRAMES_PER_BLOCK; int16 k = i - b*MAX_KEYFRAMES_PER_BLOCK; return &Property->KeyframeBlock[b]->Keyframe[k]; } static temp_keyframe_list GetSelectedKeyframes(project_data *File) { temp_keyframe_list KeyframeList; int z = 0; for (int i = 0; i < File->NumberOfLayers; i++) { for (int a = 0; a < AmountOf(File->Layer[i]->Property); a++) { for (int l = 0; l < File->Layer[i]->Property[a].NumberOfTotalKeyframes; l++) { keyframe *Keyframe = KeyframeLookup(&File->Layer[i]->Property[a], l); if (Keyframe->IsSelected) { KeyframeList.SelectedKeyframe[z] = Keyframe; z++; } } } } KeyframeList.Amount = z; return KeyframeList; } static void SelectKeyframe(project_data *File, project_layer *Layer, property_channel *Property, keyframe *Keyframe) { Layer->IsSelected = true; File->NumberOfSelectedLayers++; Property->NumberOfSelectedKeyframes++; Keyframe->IsSelected = true; } static void DeselectKeyframe(project_data *File, project_layer *Layer, property_channel *Property, keyframe *Keyframe) { Layer->IsSelected = true; File->NumberOfSelectedLayers++; Property->NumberOfSelectedKeyframes++; Keyframe->IsSelected = true; } static void CheckKeyframeSort(property_channel *Property, int32 Increment, int32 b) { /* int32 i = KeyframeMemoryToIndex(Property, b); if (Increment > 0) { if (i+1 != Property->NumberOfTotalKeyframes) { keyframe *CurrentKeyframe = KeyframeLookupIndex(Property, i); keyframe *NextKeyframe = KeyframeLookupIndex(Property, i + 1); if (NextKeyframe->FrameNumber < CurrentKeyframe->FrameNumber) { uint16 Temp = Property->SortedIndex[i]; Property->SortedIndex[i] = Property->SortedIndex[i + 1]; Property->SortedIndex[i + 1] = Temp; } } } else { if (i != 0) { keyframe *CurrentKeyframe = KeyframeLookupIndex(Property, i); keyframe *LastKeyframe = KeyframeLookupIndex(Property, i - 1); if (CurrentKeyframe->FrameNumber < LastKeyframe->FrameNumber) { uint16 Temp = Property->SortedIndex[i]; Property->SortedIndex[i] = Property->SortedIndex[i - 1]; Property->SortedIndex[i - 1] = Temp; } } } */ } static void ResortPropertyChannel(property_channel *Property) { /* for (int16 i = 0; i < Property->NumberOfTotalKeyframes; i++) { Property->SortedIndex[i] = i; } for (;;) { int16 Swaps = 0; for (int16 i = 0; i < Property->NumberOfTotalKeyframes - 1; i++) { keyframe *Keyframe = KeyframeLookupIndex(Property, i); keyframe *NextKeyframe = KeyframeLookupIndex(Property, i + 1); if (Keyframe->FrameNumber > NextKeyframe->FrameNumber) { uint16 Temp = Property->SortedIndex[i]; Property->SortedIndex[i] = Property->SortedIndex[i + 1]; Property->SortedIndex[i + 1] = Temp; Swaps++; } } if (Swaps == 0) break; } */ } static void Keyframe_ShiftPointers(property_channel *Property, int16 Increment, int16 StopAt) { if (Increment > 0) { int16 i = Property->NumberOfTotalKeyframes - 1; while (i >= StopAt) { keyframe *CurrentKeyframe = KeyframeLookup(Property, i); keyframe *NextKeyframe = KeyframeLookup(Property, i + Increment); *NextKeyframe = *CurrentKeyframe; i--; } } else { int16 i = StopAt; while (i <= Property->NumberOfTotalKeyframes - 1) { keyframe *CurrentKeyframe = KeyframeLookup(Property, i); keyframe *NextKeyframe = KeyframeLookup(Property, i - Increment); *CurrentKeyframe = *NextKeyframe; i++; } } } static void DeleteSelectedKeyframes(project_data *File, memory *Memory) { for (int i = 0; i < File->NumberOfLayers; i++) { for (int a = 0; a < AmountOf(File->Layer[i]->Property); a++) { property_channel *Property = &File->Layer[i]->Property[a]; for (int l = 0; l < Property->NumberOfTotalKeyframes; l++) { keyframe *Keyframe = KeyframeLookup(Property, l); if (Keyframe->IsSelected) { int16 ToShift = 1; bool32 Until = true; while (Until) { keyframe *KeyframeN = KeyframeLookup(Property, l + ToShift); if (KeyframeN->IsSelected) { ToShift += 1; } else { Until = false; } } Keyframe_ShiftPointers(Property, -ToShift, l); } } ResortPropertyChannel(Property); } } } static void IncrementKeyframes(property_channel *Property, int16 Increment) { /* for (int i = 0; i < Property->NumberOfTotalKeyframes; i++) { keyframe *Keyframe = KeyframeLookupIndex(Property, i); Keyframe->FrameNumber += Increment; } */ } static void IncrementKeyframesInLayer(project_layer *Layer, int16 Increment) { for (int a = 0; a < AmountOf(Layer->Property); a++) IncrementKeyframes(&Layer->Property[a], Increment); for (int e = 0; e < Layer->NumberOfEffects; e++) for (int a = 0; a < Layer->Effect[e]->NumberOfProperties; a++) IncrementKeyframes(&Layer->Effect[e]->Property[a], Increment); } static void CreateKeyframeBlock(property_channel *, memory *); // dir 0 left, 1 right static void ClampKeyframeHandles(property_channel *Property, int16 b, int16 dir) { if (dir == 0) { if (b > 0) { keyframe *Keyframe = KeyframeLookup(Property, b - 1); keyframe *NextKeyframe = KeyframeLookup(Property, b); real32 XSpan = NextKeyframe->FrameNumber - Keyframe->FrameNumber; // TODO(fox): Fix this! #if WINDOWS if (NextKeyframe->TangentLeft.x > XSpan) #else if (abs(NextKeyframe->TangentLeft.x) > XSpan) #endif NextKeyframe->TangentLeft.x = -XSpan; if (NextKeyframe->TangentLeft.x > 0) NextKeyframe->TangentLeft.x = 0; } } if (dir == 1) { if (b < Property->NumberOfTotalKeyframes - 1) { keyframe *Keyframe = KeyframeLookup(Property, b); keyframe *NextKeyframe = KeyframeLookup(Property, b + 1); real32 XSpan = NextKeyframe->FrameNumber - Keyframe->FrameNumber; if (Keyframe->TangentRight.x > XSpan) Keyframe->TangentRight.x = XSpan; if (Keyframe->TangentRight.x < 0) Keyframe->TangentRight.x = 0; } } } static void ClampSurroundingKeyframeHandles(property_channel *Property, int16 b) { ClampKeyframeHandles(Property, b, 0); ClampKeyframeHandles(Property, b, 1); if (b > 0) { ClampKeyframeHandles(Property, b-1, 0); ClampKeyframeHandles(Property, b-1, 1); } if (b < Property->NumberOfTotalKeyframes - 1) { ClampKeyframeHandles(Property, b+1, 0); ClampKeyframeHandles(Property, b+1, 1); } } static uint32 Keyframe_FindClosestIndex(property_channel *Property, int32 CurrentFrame, bool32 *Overlapping) { if (Property->NumberOfTotalKeyframes == 0) { *Overlapping = false; return 0; } else { uint32 Index = Property->NumberOfTotalKeyframes; for (;;) { keyframe *PreviousKeyframe = KeyframeLookup(Property, Index - 1); keyframe *NextKeyframe = KeyframeLookup(Property, Index + 1); if (PreviousKeyframe->FrameNumber < CurrentFrame) { if (NextKeyframe->FrameNumber >= CurrentFrame) { keyframe *CurrentKeyframe = KeyframeLookup(Property, Index); if (CurrentKeyframe->FrameNumber == CurrentFrame) { *Overlapping = true; return Index; } else { if (CurrentKeyframe->FrameNumber > CurrentFrame) { *Overlapping = false; return Index; } else { *Overlapping = false; return Index + 1; } } } else if (Index == Property->NumberOfTotalKeyframes) { *Overlapping = false; return Index; } else { Index += (Property->NumberOfTotalKeyframes - Index) / 2; } // We can only progress from this first if statement if // NextKeyframe is valid, so we need to check for these conditions. } else if (Property->NumberOfTotalKeyframes == 1) { keyframe *Keyframe = KeyframeLookup(Property, 0); *Overlapping = (Keyframe->FrameNumber == CurrentFrame); Index = 0; return Index; } else if (Property->NumberOfTotalKeyframes == 2) { // We know we're smaller than index 1, so we just need to compare 0. keyframe *FirstKeyframe = KeyframeLookup(Property, 0); Index = (FirstKeyframe->FrameNumber > CurrentFrame) ? 0 : 1; keyframe *Keyframe = KeyframeLookup(Property, Index); *Overlapping = (Keyframe->FrameNumber == CurrentFrame); return Index; } else if (Index == 0) { keyframe *CurrentKeyframe = KeyframeLookup(Property, Index); if (CurrentKeyframe->FrameNumber == CurrentFrame) { *Overlapping = true; return Index; } else { Assert(0); } } else { Index = Index / 2; } } } } // NOTE(fox): Delete calls need to increment the total number before shifting! static void Keyframe_Delete(property_channel *Property, memory *Memory, uint32 Index) { History_Entry_Commit(Memory, action_entry_default, "Delete keyframe"); keyframe *KeyframeIndex = KeyframeLookup(Property, Index); History_Action_StoreData(Memory, KeyframeIndex, sizeof(keyframe)); History_Action_Change_Decrement(Memory, &Property->NumberOfTotalKeyframes, action_type_change_u16); History_Action_Shift(Memory, action_type_shift_keyframe, Property, -1, Index); Keyframe_ShiftPointers(Property, -1, Index); History_Entry_End(Memory); } static void Keyframe_Insert(property_channel *Property, memory *Memory, int32 CurrentFrame, real32 Val) { if (!(Property->NumberOfTotalKeyframes % MAX_KEYFRAMES_PER_BLOCK)) { CreateKeyframeBlock(Property, Memory); } keyframe *Keyframe = NULL; bool32 Overlapping = 0; uint32 Index = Keyframe_FindClosestIndex(Property, CurrentFrame, &Overlapping); Keyframe = KeyframeLookup(Property, Index); if (Overlapping && Val == Keyframe->Value.f) return; if (!Overlapping) { History_Entry_Commit(Memory, action_entry_default, "Insert keyframe"); if (Index != Property->NumberOfTotalKeyframes) { History_Action_Shift(Memory, action_type_shift_keyframe, Property, 1, Index); Keyframe_ShiftPointers(Property, 1, Index); } History_Action_Change_Increment(Memory, &Property->NumberOfTotalKeyframes, action_type_change_u16); History_Action_Change(Memory, &Keyframe->FrameNumber, &Keyframe->FrameNumber, &CurrentFrame, action_type_change_i32); History_Action_Change_Increment(Memory, &RandomGlobalIncrement, action_type_change_u32); History_Action_Change(Memory, &Keyframe->ImguiID, &Keyframe->ImguiID, &RandomGlobalIncrement, action_type_change_u32); Keyframe->Type = bezier; Keyframe->TangentLeft = V2(-1, 0); Keyframe->TangentRight = V2(1, 0); } else { History_Entry_Commit(Memory, action_entry_default, "Adjust keyframe value"); } History_Action_Change(Memory, &Keyframe->Value.f, &Keyframe->Value.f, &Val, action_type_change_r32); // Keyframe->Value.f = Val; History_Entry_End(Memory); } static void CalculateKeyframesLinearly(uint16 CurrentFrame, struct property_channel *Property) { keyframe *FirstKeyframe = KeyframeLookup(Property, 0); keyframe *LastKeyframe = KeyframeLookup(Property, Property->NumberOfTotalKeyframes - 1); if (Property->NumberOfTotalKeyframes == 0) { // do nothing } // check if current frame is before first keyframe or after last else if (Property->NumberOfTotalKeyframes == 1 || CurrentFrame < FirstKeyframe->FrameNumber) { Property->CurrentValue = FirstKeyframe->Value; } else if (CurrentFrame > LastKeyframe->FrameNumber) { Property->CurrentValue = LastKeyframe->Value; } else if (Property->NumberOfTotalKeyframes > 1) { for (int i = 0; i < (Property->NumberOfTotalKeyframes - 1); i++) { keyframe *Keyframe = KeyframeLookup(Property, i); keyframe *NextKeyframe = KeyframeLookup(Property, i+1); if (CurrentFrame >= Keyframe->FrameNumber && CurrentFrame <= NextKeyframe->FrameNumber) { real32 FakeVelocity = ((CurrentFrame - Keyframe->FrameNumber) / (NextKeyframe->FrameNumber - (real32)Keyframe->FrameNumber)); real32 t = FakeVelocity; // real32 u = 1.0f - t; // real32 w1 = u * u * u; // real32 w2 = 3 * u * u * t; // real32 w3 = 3 * u * t * t; // real32 w4 = t * t * t; real32 t2 = t * t; real32 t3 = t2 * t; real32 mt = 1-t; real32 mt2 = mt * mt; real32 mt3 = mt2 * mt; if (Property->VarType == type_real ) { real32 OldValue = Property->CurrentValue.f; real32 CurrentVal = Keyframe->Value.f; real32 CurrentTan = CurrentVal + Keyframe->TangentRight.y; real32 NextVal = NextKeyframe->Value.f; real32 NextTan = NextVal + NextKeyframe->TangentLeft.y; // Property->CurrentValue.f = w1 * CurrentVal + w2 * CurrentTan + w3 * NextTan + w4 * NextVal; Property->CurrentValue.f = CurrentVal*mt3 + 3*CurrentTan*mt2*t + 3*NextTan*mt*t2 + NextVal*t3; //Property->CurrentValue.f = CurrentVal + ((NextVal - CurrentVal) * FakeVelocity); } else if (Property->VarType == type_color) { v4 OldValue = Property->CurrentValue.col; v4 CurrentVal = Keyframe->Value.col; v4 NextVal = NextKeyframe->Value.col; Property->CurrentValue.col = CurrentVal + ((NextVal - CurrentVal) * FakeVelocity); } break; } } } }