diff --git a/src/app/editor/interface.h b/src/app/editor/interface.h index 6b91bef..f06c16a 100644 --- a/src/app/editor/interface.h +++ b/src/app/editor/interface.h @@ -752,6 +752,17 @@ ui_PushOverlayLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction return Result; } +static gs_string +ui_PushLayoutCategoryName(ui_interface* Interface, gs_string Category, gs_string Identifier) +{ + gs_string Result = PushStringF(Interface->PerFrameMemory, + Category.Length + Identifier.Length, + "%S%S", + Category.ConstString, + Identifier.ConstString); + return Result; +} + static ui_widget* ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Name) { @@ -770,6 +781,12 @@ ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir Interface->ActiveLayout = Result; return Result; } +static ui_widget* +ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Category, gs_string Identifier) +{ + gs_string Name = ui_PushLayoutCategoryName(Interface, Category, Identifier); + return ui_PushLayout(Interface, Bounds, FillDir, Name); +} static ui_widget* ui_PushLayout(ui_interface* Interface, gs_string Name, bool Inset = true) @@ -851,6 +868,12 @@ ui_PopLayout(ui_interface* Interface, gs_string LayoutName) ui_CommitBounds(Interface->ActiveLayout, Layout->Bounds); } } +static void +ui_PopLayout(ui_interface* Interface, gs_string Category, gs_string Identifier) +{ + gs_string Name = ui_PushLayoutCategoryName(Interface, Category, Identifier); + ui_PopLayout(Interface, Name); +} static ui_widget* ui_BeginRow(ui_interface* Interface, u32 ColumnsMax) diff --git a/src/app/editor/panels/foldhaus_panel_animation_timeline.h b/src/app/editor/panels/foldhaus_panel_animation_timeline.h index 886b1a3..b0b7dcc 100644 --- a/src/app/editor/panels/foldhaus_panel_animation_timeline.h +++ b/src/app/editor/panels/foldhaus_panel_animation_timeline.h @@ -15,6 +15,8 @@ struct animation_timeline_state handle SelectedBlockHandle; animation_handle EditingAnimationHandle; u32 SelectedAnimationLayer; + + animation_handle NextActiveAnim; }; inline u32 @@ -504,13 +506,10 @@ PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback) if (FileInfo.Path.Length > 0) { - gs_file AnimFile = ReadEntireFile(Context.ThreadContext.FileHandler, FileInfo.Path); - - gs_string AnimFileString = MakeString((char*)AnimFile.Data.Memory, AnimFile.Data.Size); - animation NewAnim = AnimParser_Parse(AnimFileString, State->AnimationSystem.Storage, State->Patterns); - NewAnim.FileInfo = AnimFile.FileInfo; - - animation_handle NewAnimHandle = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim); + animation_handle NewAnimHandle = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + FileInfo.Path); State->AnimationSystem.ActiveFadeGroup.From = NewAnimHandle; } } @@ -762,11 +761,9 @@ PANEL_MODAL_OVERRIDE_CALLBACK(AnimInfoView_SaveAnimFileCallback) } internal void -AnimationTimeline_SetActiveAnimation (animation_handle Handle, animation_timeline_state* TimelineState, - animation_system* System) +AnimationTimeline_SetActiveAnimation (animation_handle Handle, animation_timeline_state* TimelineState) { - System->ActiveFadeGroup.From = Handle; - TimelineState->EditingAnimationHandle = Handle; + TimelineState->NextActiveAnim = Handle; } internal void @@ -775,7 +772,7 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAn animation_system* AnimSystem = &State->AnimationSystem; ui_interface* Interface = &State->Interface; - ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("AnimInfo Layout")); + ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("AnimInfo Layout"), ActiveAnim->Name); ui_FillRect(&State->Interface, Bounds, Interface->Style.PanelBG); @@ -796,7 +793,7 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAn { animation_handle NewHandle = {}; NewHandle.Index = i; - AnimationTimeline_SetActiveAnimation(NewHandle, TimelineState, AnimSystem); + AnimationTimeline_SetActiveAnimation(NewHandle, TimelineState); } } } @@ -899,7 +896,7 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAn AnimationTimeline_AddAnimationBlockCommand(TimelineState, State, Context); } } - ui_PopLayout(Interface, MakeString("AnimInfo Layout")); + ui_PopLayout(Interface, MakeString("AnimInfo Layout"), ActiveAnim->Name); } internal void @@ -917,11 +914,12 @@ AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* animation* ActiveAnim = 0; animation_handle Handle = State->AnimationSystem.ActiveFadeGroup.From; - TimelineState->EditingAnimationHandle = Handle; if (IsValid(Handle)) { animation_array Animations = State->AnimationSystem.Animations; ActiveAnim = AnimationArray_GetSafe(Animations, Handle); + TimelineState->EditingAnimationHandle = Handle; + TimelineState->NextActiveAnim = Handle; } ui_FillRect(&State->Interface, PanelBounds, v4{.1f,.1f,.1f,1.f}); @@ -944,6 +942,13 @@ AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* LayerList_Render(TimelineState, ActiveAnim, LayersBounds, Panel, RenderBuffer, State, Context); TimeRange_Render(TimelineState, ActiveAnim, TimeRangeBounds, RenderBuffer, State, Context); AnimInfoView_Render(TimelineState, ActiveAnim, InfoBounds, Panel, RenderBuffer, State, Context); + + if (!AnimHandlesAreEqual(TimelineState->NextActiveAnim, + Handle)) + { + State->AnimationSystem.ActiveFadeGroup.From = TimelineState->NextActiveAnim; + TimelineState->EditingAnimationHandle = TimelineState->NextActiveAnim; + } } #define FOLDHAUS_PANEL_ANIMATION_TIMELINE_H diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index 30c0589..12281fe 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -164,6 +164,8 @@ struct animation_system r32 SecondsPerFrame; b32 TimelineShouldAdvance; + // Settings + bool Multithreaded; }; // NOTE(pjs): A Pattern is a named procedure which can be used as @@ -174,6 +176,7 @@ struct animation_pattern char* Name; s32 NameLength; animation_proc* Proc; + bool Multithreaded; }; struct animation_pattern_array @@ -250,9 +253,14 @@ Patterns_Create(gs_memory_arena* Arena, s32 CountMax) return Result; } -#define Patterns_PushPattern(array, proc) Patterns_PushPattern_((array), (proc), Stringify(proc), sizeof(Stringify(proc)) - 1) +#define PATTERN_MULTITHREADED true +#define PATTERN_SINGLETHREADED false + +#define Patterns_PushPattern(array, proc, multithread) \ +Patterns_PushPattern_((array), (proc), Stringify(proc), sizeof(Stringify(proc)) - 1, (multithread)) + internal void -Patterns_PushPattern_(animation_pattern_array* Array, animation_proc* Proc, char* Name, u32 NameLength) +Patterns_PushPattern_(animation_pattern_array* Array, animation_proc* Proc, char* Name, u32 NameLength, bool Multithreaded) { Assert(Array->Count < Array->CountMax); @@ -260,6 +268,7 @@ Patterns_PushPattern_(animation_pattern_array* Array, animation_proc* Proc, char Pattern.Name = Name; Pattern.NameLength = NameLength; Pattern.Proc = Proc; + Pattern.Multithreaded = Multithreaded; Array->Values[Array->Count++] = Pattern; } @@ -604,6 +613,9 @@ AnimationSystem_Init(animation_system_desc Desc) Clear(&Result.ActiveFadeGroup.To); Result.ActiveFadeGroup.FadeElapsed = 0; + // Settings + Result.Multithreaded = true; + return Result; } diff --git a/src/app/engine/animation/foldhaus_animation_renderer.cpp b/src/app/engine/animation/foldhaus_animation_renderer.cpp index a22c417..f43184b 100644 --- a/src/app/engine/animation/foldhaus_animation_renderer.cpp +++ b/src/app/engine/animation/foldhaus_animation_renderer.cpp @@ -123,37 +123,45 @@ AnimationSystem_BeginRenderBlockToLedBuffer(animation_system* System, animation_ animation_pattern Pattern = Patterns_GetPattern(Patterns, Block.AnimationProcHandle); -#if MULTITHREAD_PATTERN_RENDERING - u32 JobsCount = 4; - u32 LedsPerJob = Buffer->LedCount / JobsCount; - - for (u32 i = 0; i < JobsCount; i++) + if (System->Multithreaded && Pattern.Multithreaded) { - gs_data Data = PushSizeToData(Context.ThreadContext.Transient, sizeof(render_anim_to_led_buffer_job_data)); - render_anim_to_led_buffer_job_data* JobData = (render_anim_to_led_buffer_job_data*)Data.Memory; - JobData->Pattern = Pattern; - JobData->Buffer = *Buffer; - JobData->BufferRange.First = LedsPerJob * i; - JobData->BufferRange.OnePastLast = LedsPerJob * (i + 1); - JobData->PatternArgs = PatternArgs; - JobData->SecondsIntoBlock = SecondsIntoBlock; + u32 JobsCount = 4; + u32 LedsPerJob = Buffer->LedCount / JobsCount; - Context.GeneralWorkQueue->PushWorkOnQueue(Context.GeneralWorkQueue, - (thread_proc*)AnimationSystem_RenderAnimationToLedBufferJob, - Data, - ConstString("Render Pattern To Buffer")); + for (u32 i = 0; i < JobsCount; i++) + { + gs_data Data = PushSizeToData(Context.ThreadContext.Transient, sizeof(render_anim_to_led_buffer_job_data)); + render_anim_to_led_buffer_job_data* JobData = (render_anim_to_led_buffer_job_data*)Data.Memory; + JobData->Pattern = Pattern; + JobData->Buffer = *Buffer; + JobData->BufferRange.First = LedsPerJob * i; + JobData->BufferRange.OnePastLast = LedsPerJob * (i + 1); + JobData->PatternArgs = PatternArgs; + JobData->SecondsIntoBlock = SecondsIntoBlock; + + Context.GeneralWorkQueue->PushWorkOnQueue(Context.GeneralWorkQueue, + (thread_proc*)AnimationSystem_RenderAnimationToLedBufferJob, + Data, + ConstString("Render Pattern To Buffer")); + } + } + else + { + led_buffer_range Range = {}; + Range.First = 0; + Range.OnePastLast = Buffer->LedCount; + + Pattern.Proc(Buffer, Range, PatternArgs.Assembly, SecondsIntoBlock, PatternArgs.Transient, PatternArgs.UserData); } -#else - Pattern.Proc(Buffer, PatternArgs.Assembly, SecondsIntoBlock, PatternArgs.Transient, PatternArgs.UserData); -#endif } internal void -AnimationSystem_EndRenderBlockToLedBuffer (context Context) +AnimationSystem_EndRenderBlockToLedBuffer (animation_system* System, context Context) { -#if MULTITHREAD_PATTERN_RENDERING - Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext); -#endif + if (System->Multithreaded) + { + Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext); + } } // NOTE(pjs): This mirrors animation_layer_frame to account @@ -212,7 +220,7 @@ RenderAnimationToLedBuffer (animation_system* System, AnimationSystem_BeginRenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs, Context); } - AnimationSystem_EndRenderBlockToLedBuffer(Context); + AnimationSystem_EndRenderBlockToLedBuffer(System, Context); } // Blend together any layers that have a hot and next hot buffer diff --git a/src/app/engine/animation/foldhaus_animation_serializer.cpp b/src/app/engine/animation/foldhaus_animation_serializer.cpp index effbcda..9227328 100644 --- a/src/app/engine/animation/foldhaus_animation_serializer.cpp +++ b/src/app/engine/animation/foldhaus_animation_serializer.cpp @@ -182,9 +182,23 @@ AnimParser_Parse(gs_string File, gs_memory_arena* Arena, animation_pattern_array } } } - return Result; } +internal animation +AnimParser_Parse(gs_data File, gs_memory_arena* Arena, animation_pattern_array AnimPatterns) +{ + gs_string FileString = MakeString((char*)File.Memory, File.Size); + return AnimParser_Parse(FileString, Arena, AnimPatterns); +} +internal animation_handle +AnimationSystem_LoadAnimationFromFile(animation_system* System, animation_pattern_array AnimPatterns, context Context, gs_const_string FilePath) +{ + gs_file AnimFile = ReadEntireFile(Context.ThreadContext.FileHandler, FilePath); + animation NewAnim = AnimParser_Parse(AnimFile.Data, System->Storage, AnimPatterns); + NewAnim.FileInfo = AnimFile.FileInfo; + animation_handle NewAnimHandle = AnimationArray_Push(&System->Animations, NewAnim); + return NewAnimHandle; +} #define FOLDHAUS_ANIMATION_SERIALIZER_CPP #endif // FOLDHAUS_ANIMATION_SERIALIZER_CPP \ No newline at end of file diff --git a/src/app/patterns/blumen_patterns.h b/src/app/patterns/blumen_patterns.h index a0d46e6..ee59638 100644 --- a/src/app/patterns/blumen_patterns.h +++ b/src/app/patterns/blumen_patterns.h @@ -1187,6 +1187,41 @@ internal void Pattern_WavyPatchy(led_buffer* Leds, led_buffer_range Range, assembly Assembly, r32 Time, gs_memory_arena* Transient, u8* UserData) { DEBUG_TRACK_FUNCTION; + + gs_random_series Random = InitRandomSeries(24601); + + r32 LightSpeedMin = 1; + r32 LightSpeedMax = 5; + + r32 LightHueMin = (ModR32(Time, 10) / 10) * 360; + r32 LightHueMax = ModR32((LightHueMin + 45), 360) ; + + s32 LightTailLength = 10; + for (u32 StripIndex = 0; StripIndex < Assembly.StripCount; StripIndex++) + { + v2_strip Strip = Assembly.Strips[StripIndex]; + + r32 LightHue = LerpR32(NextRandomUnilateral(&Random), + LightHueMin, + LightHueMax); + r32 LightStartHeight = NextRandomUnilateral(&Random); + r32 LightSpeed = LerpR32(NextRandomUnilateral(&Random), + LightSpeedMin, + LightSpeedMax); + r32 LightCurrentHeight = LightStartHeight + (LightSpeed * Time * 0.1f); + s32 StartIndex = (s32)(LightCurrentHeight * (r32)Strip.LedCount) % Strip.LedCount; + + for (s32 i = 0; i < LightTailLength; i++) + { + s32 StripLedIndex = StartIndex + i; + if (StripLedIndex >= (s32)Strip.LedCount) continue; + + u32 LedIndex = Strip.LedLUT[StripLedIndex]; + r32 PctTail = ((r32)i / (r32)LightTailLength); + v4 C = HSVToRGB(v4{LightHue, 1, 1, 1}) * PctTail; + Leds->Colors[LedIndex] = V4ToRGBPixel(C); + } + } } #define BLUMEN_PATTERNS_H diff --git a/src/app/ss_blumen_lumen/blumen_lumen.cpp b/src/app/ss_blumen_lumen/blumen_lumen.cpp index 2a0e150..29bf6a1 100644 --- a/src/app/ss_blumen_lumen/blumen_lumen.cpp +++ b/src/app/ss_blumen_lumen/blumen_lumen.cpp @@ -43,6 +43,9 @@ MessageQueue_Write(blumen_network_msg_queue* Queue, gs_data Msg) Assert(Msg.Size <= DEFAULT_QUEUE_ENTRY_SIZE); u32 Index = Queue->WriteHead; + Assert(Index >= 0 && + Index < BLUMEN_MESSAGE_QUEUE_COUNT); + gs_data* Dest = Queue->Buffers + Index; CopyMemoryTo(Msg.Memory, Dest->Memory, Msg.Size); Dest->Size = Msg.Size; @@ -50,7 +53,7 @@ MessageQueue_Write(blumen_network_msg_queue* Queue, gs_data Msg) // NOTE(pjs): We increment write head at the end of writing so that // a reader thread doesn't pull the message off before we've finished // filling it out - Queue->WriteHead++; + Queue->WriteHead = (Queue->WriteHead + 1) % BLUMEN_MESSAGE_QUEUE_COUNT; return true; } @@ -116,28 +119,28 @@ BlumenLumen_LoadPatterns(app_state* State) } Patterns->Count = 0; - Patterns_PushPattern(Patterns, TestPatternOne); - Patterns_PushPattern(Patterns, TestPatternTwo); - Patterns_PushPattern(Patterns, TestPatternThree); - Patterns_PushPattern(Patterns, Pattern_AllGreen); - Patterns_PushPattern(Patterns, Pattern_HueShift); - Patterns_PushPattern(Patterns, Pattern_HueFade); - Patterns_PushPattern(Patterns, Pattern_Spots); - Patterns_PushPattern(Patterns, Pattern_LighthouseRainbow); - Patterns_PushPattern(Patterns, Pattern_SmoothGrowRainbow); - Patterns_PushPattern(Patterns, Pattern_GrowAndFade); - Patterns_PushPattern(Patterns, Pattern_ColorToWhite); - Patterns_PushPattern(Patterns, Pattern_Blue); - Patterns_PushPattern(Patterns, Pattern_Green); - Patterns_PushPattern(Patterns, Pattern_FlowerColors); - Patterns_PushPattern(Patterns, Pattern_FlowerColorToWhite); - Patterns_PushPattern(Patterns, Pattern_BasicFlowers); + Patterns_PushPattern(Patterns, TestPatternOne, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, TestPatternTwo, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, TestPatternThree, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_AllGreen, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_HueShift, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_HueFade, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Spots, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_LighthouseRainbow, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_SmoothGrowRainbow, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_GrowAndFade, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_ColorToWhite, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Blue, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Green, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_FlowerColors, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_FlowerColorToWhite, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_BasicFlowers, PATTERN_MULTITHREADED); // 15 - Patterns_PushPattern(Patterns, Pattern_Wavy); - Patterns_PushPattern(Patterns, Pattern_Patchy); - Patterns_PushPattern(Patterns, Pattern_Leafy); - Patterns_PushPattern(Patterns, Pattern_LeafyPatchy); - Patterns_PushPattern(Patterns, Pattern_WavyPatchy); + Patterns_PushPattern(Patterns, Pattern_Wavy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Patchy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_Leafy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_LeafyPatchy, PATTERN_MULTITHREADED); + Patterns_PushPattern(Patterns, Pattern_WavyPatchy, PATTERN_SINGLETHREADED); } internal v4 @@ -183,6 +186,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) gs_const_string SculpturePath = ConstString("data/test_blumen.fold"); LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath, State->GlobalLog); +#if 0 { // Animation PLAYGROUND animation Anim0 = {0}; Anim0.Name = PushStringF(&State->Permanent, 256, "test_anim_zero"); @@ -216,13 +220,19 @@ BlumenLumen_CustomInit(app_state* State, context Context) Anim2.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem); Animation_AddLayer(&Anim2, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem); - Animation_AddBlock(&Anim2, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(16), 0); + Animation_AddBlock(&Anim2, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(20), 0); BLState->AnimHandles[2] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim2); State->AnimationSystem.ActiveFadeGroup.From = BLState->AnimHandles[2]; - State->AnimationSystem.TimelineShouldAdvance = true; } // End Animation Playground +#endif + animation_handle DemoPatternsAnim = AnimationSystem_LoadAnimationFromFile(&State->AnimationSystem, + State->Patterns, + Context, + ConstString("data/demo_patterns.foldanim")); + State->AnimationSystem.ActiveFadeGroup.From = DemoPatternsAnim; + State->AnimationSystem.TimelineShouldAdvance = true; for (u32 i = 0; i < FLOWER_COLORS_COUNT; i++) {