global animation_curve_state *Global_AnimationCurveState = 0; //////////////////////////////// //~ sixten: Animation Curve Functions inline animation_curve_state *AC_GetState(void) { return(Global_AnimationCurveState); } inline void AC_SetState(animation_curve_state *State) { Global_AnimationCurveState = State; } static void AC_Init(animation_curve_state *State) { State->Arena = ArenaAlloc(Kilobytes(32), true, "Animation Curve State Arena"); } inline animation_curve_key AC_GenerateKeyFromString(string String) { animation_curve_key Key; Key.Value = HashString(String); return(Key); } static animation_curve_entry *AC_GetEntryByKey(animation_curve_key Key, r32 Initial) { animation_curve_state *State = AC_GetState(); u64 Hash = Key.Value; u64 Slot = Hash % ArrayCount(State->Buckets); animation_curve_bucket *Bucket = State->Buckets + Slot; animation_curve_entry *Result = 0; for(animation_curve_entry *Entry = Bucket->First; Entry != 0; Entry = Entry->Next) { if(AreEqual(Entry->Key, Key)) { Result = Entry; break; } } if(!Result) { if(DLLIsEmpty(State->FirstFreeEntry)) { Result = PushStruct(State->Arena, animation_curve_entry); } else { Result = State->FirstFreeEntry; DLLRemove(State->FirstFreeEntry, State->LastFreeEntry, Result); } DLLInsertLast(Bucket->First, Bucket->Last, Result); Result->Value = Initial; } Result->Key = Key; Result->LastFrameTouched = State->CurrentFrame; return(Result); } inline r32 AC_GetValue(string Name, r32 Initial) { animation_curve_key Key = AC_GenerateKeyFromString(Name); animation_curve_entry *Entry = AC_GetEntryByKey(Key, Initial); r32 Result = Entry->Value; return(Result); } inline void AC_SetValue(string Name, r32 Value) { animation_curve_key Key = AC_GenerateKeyFromString(Name); animation_curve_entry *Entry = AC_GetEntryByKey(Key, Value); Entry->Value = Value; } inline r32 AC_AnimateValueDirect(r32 Target, r32 Duration, r32 *Value) { animation_curve_state *State = AC_GetState(); //- sixten: make sure the value is valid if(*Value != *Value) *Value = 0; r32 Result = *Value; r32 Rate = 1.0 - Pow(2, -(10.0 / Duration * State->dtForFrame)); *Value += (Target - *Value) * Rate; return(Result); } inline r32 AC_AnimateValue(r32 Target, r32 Initial, r32 Duration, string Name) { animation_curve_key Key = AC_GenerateKeyFromString(Name); animation_curve_entry *Entry = AC_GetEntryByKey(Key, Initial); r32 Result = AC_AnimateValueDirect(Target, Duration, &Entry->Value); return(Result); } inline r32 AC_AnimateValueF(r32 Target, r32 Initial, r32 Duration, char *Format, ...) { temp Scratch = GetScratch(0, 0); va_list Arguments; va_start(Arguments, Format); string String = PushFormatVariadic(Scratch.Arena, Format, Arguments); va_end(Arguments); r32 Result = AC_AnimateValue(Target, Initial, Duration, String); ReleaseScratch(Scratch); return(Result); } static void AC_NewFrame(animation_curve_state *State, r32 dtForFrame) { AC_SetState(State); State->dtForFrame = dtForFrame; // sixten: Prune untouched entries. for(s32 BucketIndex = 0; BucketIndex < ArrayCount(State->Buckets); ++BucketIndex) { animation_curve_bucket *Bucket = State->Buckets + BucketIndex; animation_curve_entry *Entry = Bucket->First; while(Entry != 0) { if(Entry->LastFrameTouched != State->CurrentFrame) { animation_curve_entry *ToRemove = Entry; Entry = Entry->Next; DLLRemove(Bucket->First, Bucket->Last, ToRemove); DLLInsertLast(State->FirstFreeEntry, State->LastFreeEntry, ToRemove); } else { Entry = Entry->Next; } } } ++State->CurrentFrame; }