From 59cb48c9f07e152240a72ccb44ed89bfba97074a Mon Sep 17 00:00:00 2001 From: PS Date: Wed, 17 Mar 2021 22:15:37 -0700 Subject: [PATCH] Implemented Crossfading between animations --- src/app/blumen_lumen.cpp | 25 +- src/app/blumen_lumen.h | 5 + .../foldhaus_panel_animation_timeline.h | 382 ++++++++---------- .../editor/panels/foldhaus_panel_file_view.h | 23 +- src/app/engine/animation/foldhaus_animation.h | 183 +++++++-- .../animation/foldhaus_animation_renderer.cpp | 272 ++++++++----- src/app/engine/assembly/foldhaus_assembly.h | 40 ++ src/app/foldhaus_app.cpp | 17 +- .../platform_win32/win32_foldhaus_socket.h | 4 + 9 files changed, 594 insertions(+), 357 deletions(-) diff --git a/src/app/blumen_lumen.cpp b/src/app/blumen_lumen.cpp index 4f9791c..d85e3bc 100644 --- a/src/app/blumen_lumen.cpp +++ b/src/app/blumen_lumen.cpp @@ -144,7 +144,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) Animation_AddBlock(&Anim0, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(15), 0); - AnimationArray_Push(&State->AnimationSystem.Animations, Anim0); + BLState->AnimHandles[0] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim0); animation Anim1 = {0}; Anim1.Name = PushStringF(&State->Permanent, 256, "test_anim_one"); @@ -156,7 +156,7 @@ BlumenLumen_CustomInit(app_state* State, context Context) Animation_AddBlock(&Anim1, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(12), 0); - AnimationArray_Push(&State->AnimationSystem.Animations, Anim1); + BLState->AnimHandles[1] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim1); animation Anim2 = {0}; Anim2.Name = PushStringF(&State->Permanent, 256, "i_love_you"); @@ -166,10 +166,12 @@ 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(10), 0); + Animation_AddBlock(&Anim2, 0, 100, Patterns_IndexToHandle(5), 0); + Animation_AddBlock(&Anim2, 50, Anim0.PlayableRange.Max, Patterns_IndexToHandle(10), 0); - AnimationArray_Push(&State->AnimationSystem.Animations, Anim2); + BLState->AnimHandles[2] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim2); + State->AnimationSystem.ActiveFadeGroup.From = BLState->AnimHandles[2]; State->AnimationSystem.TimelineShouldAdvance = true; } // End Animation Playground @@ -189,6 +191,15 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; MotorTimeElapsed += Context->DeltaTime; + BLState->TimeElapsed += Context->DeltaTime; + + if (BLState->TimeElapsed > 5) + { + u32 NextIndex = ++BLState->CurrAnim % 3; + animation_handle Next = BLState->AnimHandles[NextIndex]; + AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, Next, 5); + BLState->TimeElapsed = 0; + } gs_string BlueString = MakeString("blue"); gs_string GreenString = MakeString("green"); @@ -207,15 +218,15 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context) u32 NameLen = CStringLength(Packet.AnimationFileName); if (StringEqualsCharArray(BlueString.ConstString, Packet.AnimationFileName, NameLen)) { - State->AnimationSystem.ActiveAnimationIndex = 0; + State->AnimationSystem.ActiveFadeGroup.From.Index = 0; } else if (StringEqualsCharArray(GreenString.ConstString, Packet.AnimationFileName, NameLen)) { - State->AnimationSystem.ActiveAnimationIndex = 1; + State->AnimationSystem.ActiveFadeGroup.From.Index = 1; } else if (StringEqualsCharArray(ILoveYouString.ConstString, Packet.AnimationFileName, NameLen)) { - State->AnimationSystem.ActiveAnimationIndex = 2; + State->AnimationSystem.ActiveFadeGroup.From.Index = 2; } OutputDebugStringA("Received Pattern Packet\n"); diff --git a/src/app/blumen_lumen.h b/src/app/blumen_lumen.h index 2c47b22..56ad6b6 100644 --- a/src/app/blumen_lumen.h +++ b/src/app/blumen_lumen.h @@ -70,6 +70,11 @@ struct blumen_lumen_state mic_listen_job_data MicListenJobData; motor_packet LastKnownMotorState; + + r64 TimeElapsed; + + animation_handle AnimHandles[3]; + u32 CurrAnim; }; diff --git a/src/app/editor/panels/foldhaus_panel_animation_timeline.h b/src/app/editor/panels/foldhaus_panel_animation_timeline.h index 6464628..9b350c9 100644 --- a/src/app/editor/panels/foldhaus_panel_animation_timeline.h +++ b/src/app/editor/panels/foldhaus_panel_animation_timeline.h @@ -13,6 +13,7 @@ struct animation_timeline_state { frame_range VisibleRange; handle SelectedBlockHandle; + animation_handle EditingAnimationHandle; u32 SelectedAnimationLayer; }; @@ -33,22 +34,13 @@ GetXPositionFromFrameInAnimationPanel (u32 Frame, rect2 PanelBounds, frame_range return XPositionAtFrame; } -internal handle -AddAnimationBlockAtCurrentTime (animation_pattern_handle AnimationProcHandle, u32 LayerHandle, animation_system* System) -{ - u32 NewBlockStart = System->CurrentFrame; - u32 NewBlockEnd = NewBlockStart + SecondsToFrames(3, *System); - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); - handle AnimHandle = Animation_AddBlock(ActiveAnim, NewBlockStart, NewBlockEnd, AnimationProcHandle, LayerHandle); - return AnimHandle; -} - FOLDHAUS_INPUT_COMMAND_PROC(DeleteAnimationBlockCommand) { animation_timeline_state* PanelState = Panel_GetStateStruct(Panel, animation_timeline_state); handle SelectedBlockHandle = PanelState->SelectedBlockHandle; - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); + animation* ActiveAnim = AnimationArray_GetSafe(State->AnimationSystem.Animations, PanelState->EditingAnimationHandle); + if(SelectedBlockHandle.Index < ActiveAnim->Blocks_.Count && ActiveAnim->Blocks_.Generations[SelectedBlockHandle.Index] == SelectedBlockHandle.Generation) { @@ -112,6 +104,7 @@ StartDragTimeMarker(rect2 TimelineBounds, frame_range VisibleFrames, app_state* OPERATION_STATE_DEF(drag_animation_block_state) { rect2 TimelineBounds; + animation_handle EditingAnimationHandle; handle BlockHandle; frame_range VisibleRange; frame_range ClipRange; @@ -133,7 +126,9 @@ OPERATION_RENDER_PROC(UpdateDragAnimationBlock) { drag_animation_block_state* OpState = (drag_animation_block_state*)Operation.OpStateMemory; - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); + animation_array Animations = State->AnimationSystem.Animations; + animation_handle Handle = OpState->EditingAnimationHandle; + animation* ActiveAnim = AnimationArray_GetSafe(Animations, Handle); r32 ClipInitialStartFrameXPercent = FrameToPercentRange(OpState->ClipRange.Min, OpState->VisibleRange); u32 ClipInitialStartFrameXPosition = LerpR32(ClipInitialStartFrameXPercent, @@ -237,7 +232,10 @@ SelectAndBeginDragAnimationBlock(animation_timeline_state* TimelineState, handle { TimelineState->SelectedBlockHandle = BlockHandle; - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); + animation_handle Handle = TimelineState->EditingAnimationHandle; + animation_array Animations = State->AnimationSystem.Animations; + animation* ActiveAnim = AnimationArray_GetSafe(Animations, Handle); + operation_mode* DragAnimationBlockMode = ActivateOperationModeWithCommands(&State->Modes, DragAnimationBlockCommands, UpdateDragAnimationBlock); animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, BlockHandle); @@ -245,29 +243,39 @@ SelectAndBeginDragAnimationBlock(animation_timeline_state* TimelineState, handle &State->Modes, drag_animation_block_state); OpState->TimelineBounds = TimelineBounds; + OpState->EditingAnimationHandle = Handle; OpState->BlockHandle = BlockHandle; OpState->VisibleRange = VisibleRange; OpState->ClipRange = SelectedBlock->Range; } // ------------------- -FOLDHAUS_INPUT_COMMAND_PROC(AddAnimationBlockCommand) +internal void +AnimationTimeline_AddAnimationBlockCommand(animation_timeline_state* TimelineState, app_state* State, context Context) { - animation_timeline_state* TimelineState = Panel_GetStateStruct(Panel, animation_timeline_state); + animation_handle Handle = TimelineState->EditingAnimationHandle; + animation_array Animations = State->AnimationSystem.Animations; + animation* ActiveAnim = AnimationArray_GetSafe(Animations, Handle); - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); - - frame_range Range = ActiveAnim->PlayableRange; - u32 MouseDownFrame = GetFrameFromPointInAnimationPanel(Mouse.Pos, Panel->Bounds, Range); - - animation_pattern_handle PatternHandle = Patterns_IndexToHandle(4); - handle NewBlockHandle = Animation_AddBlock(ActiveAnim, MouseDownFrame, MouseDownFrame + SecondsToFrames(3, State->AnimationSystem), PatternHandle, TimelineState->SelectedAnimationLayer); - TimelineState->SelectedBlockHandle = NewBlockHandle; + s32 StartFrame = State->AnimationSystem.CurrentFrame; + s32 EndFrame = StartFrame + SecondsToFrames(3, State->AnimationSystem); + EndFrame = Clamp(0, EndFrame, ActiveAnim->PlayableRange.Max); + if ((EndFrame - StartFrame) > 0) + { + animation_pattern_handle PatternHandle = Patterns_IndexToHandle(0); + u32 Layer = TimelineState->SelectedAnimationLayer; + + handle NewBlockHandle = Animation_AddBlock(ActiveAnim, StartFrame, EndFrame, PatternHandle, Layer); + + TimelineState->SelectedBlockHandle = NewBlockHandle; + } else { + // TODO(pjs): we don't want to create a block of zero frames + // since you won't be able to delete it. What do we do here?? + } } input_command AnimationTimeline_Commands[] = { { KeyCode_X, KeyCode_Invalid, Command_Began, DeleteAnimationBlockCommand }, - { KeyCode_A, KeyCode_Invalid, Command_Began, AddAnimationBlockCommand }, }; s32 AnimationTimeline_CommandsCount = 2; @@ -276,10 +284,17 @@ GSMetaTag(panel_type_animation_timeline); internal void AnimationTimeline_Init(panel* Panel, app_state* State, context Context) { + animation_handle Handle = State->AnimationSystem.ActiveFadeGroup.From; + // TODO: :FreePanelMemory - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); animation_timeline_state* TimelineState = PushStruct(&State->Permanent, animation_timeline_state); - TimelineState->VisibleRange = ActiveAnim->PlayableRange; + TimelineState->EditingAnimationHandle = Handle; + + if (IsValid(Handle)) { + animation_array Animations = State->AnimationSystem.Animations; + animation* ActiveAnim = AnimationArray_GetSafe(Animations, Handle); + TimelineState->VisibleRange = ActiveAnim->PlayableRange; + } Panel->StateMemory = StructToData(TimelineState, animation_timeline_state); } @@ -481,92 +496,6 @@ DrawAnimationBlock (animation_block AnimationBlock, v4 BlockColor, frame_range V return BlockBounds; } -internal handle -DrawAnimationTimeline (animation_system* AnimationSystem, animation_timeline_state* TimelineState, rect2 PanelBounds, handle SelectedBlockHandle, ui_interface* Interface, app_state* State) -{ - gs_string Tempgs_string = PushString(State->Transient, 256); - handle Result = SelectedBlockHandle; - - animation CurrAnimation = *AnimationSystem_GetActiveAnimation(AnimationSystem); - - rect2 LayerMenuBounds, TimelineBounds; - RectVSplitAtDistanceFromLeft(PanelBounds, 256, &LayerMenuBounds, &TimelineBounds); - - // In Top To Bottom Order - rect2 TimelineFrameBarBounds; - rect2 TimelineBlockDisplayBounds; - rect2 TimelineRangeBarBounds; - RectHSplitAtDistanceFromTop(TimelineBounds, 32, &TimelineFrameBarBounds, &TimelineBounds); - RectHSplitAtDistanceFromBottom(TimelineBounds, 24, &TimelineBlockDisplayBounds, &TimelineRangeBarBounds); - - DrawLayerMenu(AnimationSystem, CurrAnimation, *Interface, LayerMenuBounds, &TimelineState->SelectedAnimationLayer); - - frame_range AdjustedViewRange = DrawTimelineRangeBar(AnimationSystem, CurrAnimation, TimelineState, *Interface, TimelineRangeBarBounds); - - DrawFrameBar(AnimationSystem, *Interface, AdjustedViewRange, TimelineFrameBarBounds, State); - - ui_FillRect(Interface, TimelineBlockDisplayBounds, v4{.25f, .25f, .25f, 1.0f}); - - // Animation Blocks - b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState); - handle DragBlockHandle = {0}; - for (u32 i = 0; i < CurrAnimation.Blocks_.Count; i++) - { - animation_block* AnimationBlockAt = CurrAnimation.Blocks_.Values + i; - - // If either end is in the range, we should draw it - b32 RangeIsVisible = (FrameIsInRange(AdjustedViewRange, AnimationBlockAt->Range.Min) || - FrameIsInRange(AdjustedViewRange, AnimationBlockAt->Range.Max)); - // If neither end is in the range, but the ends surround the visible range, - // we should still draw it. - RangeIsVisible |= (AnimationBlockAt->Range.Min <= AdjustedViewRange.Min && - AnimationBlockAt->Range.Max>= AdjustedViewRange.Max); - if (RangeIsVisible) - { - v4 BlockColor = BlackV4; - if (SelectedBlockHandle.Index == i && SelectedBlockHandle.Generation == CurrAnimation.Blocks_.Generations[i]) - { - BlockColor = PinkV4; - } - rect2 BlockBounds = DrawAnimationBlock(*AnimationBlockAt, BlockColor, AdjustedViewRange, TimelineBounds, Interface->RenderBuffer); - if (PointIsInRect(BlockBounds, Interface->Mouse.Pos)) - { - DragBlockHandle.Index = i; - DragBlockHandle.Generation = CurrAnimation.Blocks_.Generations[i]; - } - } - } - - if (MouseDownAndNotHandled && Handle_IsValid(DragBlockHandle)) - { - MouseDownAndNotHandled = false; - SelectAndBeginDragAnimationBlock(TimelineState, DragBlockHandle, AdjustedViewRange, TimelineBounds, State); - } - - // Time Slider - if (FrameIsInRange(AdjustedViewRange, AnimationSystem->CurrentFrame)) - { - r32 FrameAtPercentVisibleRange = FrameToPercentRange(AnimationSystem->CurrentFrame, AdjustedViewRange); - r32 SliderX = LerpR32(FrameAtPercentVisibleRange, TimelineBounds.Min.x, TimelineBounds.Max.x); - rect2 SliderBounds = { - v2{ SliderX, TimelineBounds.Min.y }, - v2{ SliderX + 1, TimelineBounds.Max.y } - }; - ui_FillRect(Interface, SliderBounds, TimeSliderColor); - } - - ui_OutlineRect(Interface, TimelineRangeBarBounds, 1.f, RedV4); - ui_OutlineRect(Interface, TimelineFrameBarBounds, 1.f, RedV4); - ui_OutlineRect(Interface, TimelineBlockDisplayBounds, 1.f, RedV4); - - if (MouseDownAndNotHandled && PointIsInRect(TimelineBounds, Interface->Mouse.Pos)) - { - TimelineState->SelectedBlockHandle = {0}; - } - - return Result; -} - PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback) { Assert(ReturningFrom->TypeIndex == PanelType_FileView); @@ -581,28 +510,11 @@ PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback) animation NewAnim = AnimParser_Parse(AnimFileString, State->AnimationSystem.Storage, State->Patterns); NewAnim.FileInfo = AnimFile.FileInfo; - u32 NewAnimIndex = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim); - State->AnimationSystem.ActiveAnimationIndex = NewAnimIndex; + animation_handle NewAnimHandle = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim); + State->AnimationSystem.ActiveFadeGroup.From = NewAnimHandle; } } -internal void -DrawAnimationPatternList(rect2 PanelBounds, ui_interface* Interface, u32 SelectedAnimationLayerHandle, animation_system* AnimationSystem, animation_pattern_array Patterns) -{ - ui_PushLayout(Interface, PanelBounds, LayoutDirection_TopDown, MakeString("AnimClips Layout")); - for (u32 i = 0; i < Patterns.Count; i++) - { - animation_pattern Pattern = Patterns.Values[i]; - gs_string PatternName = MakeString(Pattern.Name, Pattern.NameLength); - if (ui_Button(Interface, PatternName)) - { - animation_pattern_handle PatternHandle = Patterns_IndexToHandle(i); - AddAnimationBlockAtCurrentTime(PatternHandle, SelectedAnimationLayerHandle, AnimationSystem); - } - } - ui_PopLayout(Interface, MakeString("AnimClips Layout")); -} - internal void PlayBar_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) { @@ -634,14 +546,18 @@ PlayBar_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Pan } internal void -FrameCount_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context) +FrameCount_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context) { ui_interface* Interface = &State->Interface; gs_string TempString = PushString(State->Transient, 256); + // :FrameRange // frame_range VisibleFrames = TimelineState->VisibleRange; - animation ActiveAnim = *AnimationSystem_GetActiveAnimation(&State->AnimationSystem); - frame_range VisibleFrames = ActiveAnim.PlayableRange; + + frame_range VisibleFrames = {}; + if (ActiveAnim) { + VisibleFrames = ActiveAnim->PlayableRange; + } s32 VisibleFrameCount = VisibleFrames.Max - VisibleFrames.Min; @@ -687,10 +603,9 @@ FrameCount_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_ } internal void -LayerList_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) +LayerList_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) { ui_interface* Interface = &State->Interface; - animation ActiveAnim = *AnimationSystem_GetActiveAnimation(&State->AnimationSystem); ui_FillRect(Interface, Bounds, Interface->Style.PanelBG); @@ -698,38 +613,45 @@ LayerList_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* P rect2 LayerBounds = {0}; LayerBounds.Min = Bounds.Min; LayerBounds.Max = LayerBounds.Min + LayerDim; - for (u32 i = 0; i < ActiveAnim.Layers.Count; i++) + + if (ActiveAnim) { - anim_layer* Layer = ActiveAnim.Layers.Values + i; - - if (ui_MouseClickedRect(*Interface, LayerBounds)) + for (u32 i = 0; i < ActiveAnim->Layers.Count; i++) { - TimelineState->SelectedAnimationLayer = i; + anim_layer* Layer = ActiveAnim->Layers.Values + i; + + if (ui_MouseClickedRect(*Interface, LayerBounds)) + { + TimelineState->SelectedAnimationLayer = i; + } + + v2 LayerTextPos = { LayerBounds.Min.x + 6, LayerBounds.Max.y - 16}; + if (TimelineState->SelectedAnimationLayer == i) + { + PushRenderBoundingBox2D(Interface->RenderBuffer, LayerBounds.Min, LayerBounds.Max, 1, WhiteV4); + } + DrawString(Interface->RenderBuffer, Layer->Name, Interface->Style.Font, LayerTextPos, WhiteV4); + + LayerBounds = Rect2TranslateY(LayerBounds, LayerDim.y); } - - v2 LayerTextPos = { LayerBounds.Min.x + 6, LayerBounds.Max.y - 16}; - if (TimelineState->SelectedAnimationLayer == i) - { - PushRenderBoundingBox2D(Interface->RenderBuffer, LayerBounds.Min, LayerBounds.Max, 1, WhiteV4); - } - DrawString(Interface->RenderBuffer, Layer->Name, Interface->Style.Font, LayerTextPos, WhiteV4); - - LayerBounds = Rect2TranslateY(LayerBounds, LayerDim.y); } } internal void -TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context) +TimeRange_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context) { ui_interface* Interface = &State->Interface; - animation ActiveAnim = *AnimationSystem_GetActiveAnimation(&State->AnimationSystem); // TODO(pjs): setting the timeline to show the entire range // of the current animation until I reimplement the range // slider bars // :FrameRange // frame_range ViewRange = TimelineState->VisibleRange; - frame_range ViewRange = ActiveAnim.PlayableRange; + frame_range ViewRange = {}; + if (ActiveAnim) + { + ViewRange = ActiveAnim->PlayableRange; + } handle SelectedBlockHandle = TimelineState->SelectedBlockHandle; s32 CurrentFrame = State->AnimationSystem.CurrentFrame; @@ -737,30 +659,33 @@ TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_c // Animation Blocks b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState); handle DragBlockHandle = {0}; - for (u32 i = 0; i < ActiveAnim.Blocks_.Count; i++) + if (ActiveAnim) { - animation_block* AnimationBlockAt = ActiveAnim.Blocks_.Values + i; - - // If either end is in the range, we should draw it - b32 RangeIsVisible = (FrameIsInRange(ViewRange, AnimationBlockAt->Range.Min) || - FrameIsInRange(ViewRange, AnimationBlockAt->Range.Max)); - // If neither end is in the range, but the ends surround the visible range, - // we should still draw it. - RangeIsVisible |= (AnimationBlockAt->Range.Min <= ViewRange.Min && - AnimationBlockAt->Range.Max>= ViewRange.Max); - if (RangeIsVisible) + for (u32 i = 0; i < ActiveAnim->Blocks_.Count; i++) { - v4 BlockColor = BlackV4; - if (SelectedBlockHandle.Index == i && SelectedBlockHandle.Generation == ActiveAnim.Blocks_.Generations[i]) - { - BlockColor = PinkV4; - } - rect2 BlockBounds = DrawAnimationBlock(*AnimationBlockAt, BlockColor, ViewRange, Bounds, Interface->RenderBuffer); + animation_block* AnimationBlockAt = ActiveAnim->Blocks_.Values + i; - if (PointIsInRect(BlockBounds, Interface->Mouse.Pos)) + // If either end is in the range, we should draw it + b32 RangeIsVisible = (FrameIsInRange(ViewRange, AnimationBlockAt->Range.Min) || + FrameIsInRange(ViewRange, AnimationBlockAt->Range.Max)); + // If neither end is in the range, but the ends surround the visible range, + // we should still draw it. + RangeIsVisible |= (AnimationBlockAt->Range.Min <= ViewRange.Min && + AnimationBlockAt->Range.Max>= ViewRange.Max); + if (RangeIsVisible) { - DragBlockHandle.Index = i; - DragBlockHandle.Generation = ActiveAnim.Blocks_.Generations[i]; + v4 BlockColor = BlackV4; + if (SelectedBlockHandle.Index == i && SelectedBlockHandle.Generation == ActiveAnim->Blocks_.Generations[i]) + { + BlockColor = PinkV4; + } + rect2 BlockBounds = DrawAnimationBlock(*AnimationBlockAt, BlockColor, ViewRange, Bounds, Interface->RenderBuffer); + + if (PointIsInRect(BlockBounds, Interface->Mouse.Pos)) + { + DragBlockHandle.Index = i; + DragBlockHandle.Generation = ActiveAnim->Blocks_.Generations[i]; + } } } } @@ -795,10 +720,10 @@ TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_c internal void AnimInfoView_SaveAnimFile (gs_const_string Path, app_state* State, context Context) { - u32 ActiveAnimIndex = State->AnimationSystem.ActiveAnimationIndex; - animation ActiveAnimation = State->AnimationSystem.Animations.Values[ActiveAnimIndex]; + animation_handle ActiveAnimHandle = State->AnimationSystem.ActiveFadeGroup.From; + animation ActiveAnim = *AnimationArray_GetSafe(State->AnimationSystem.Animations, ActiveAnimHandle); - gs_string FileText = AnimSerializer_Serialize(ActiveAnimation, State->Patterns, State->Transient); + gs_string FileText = AnimSerializer_Serialize(ActiveAnim, State->Patterns, State->Transient); if (!WriteEntireFile(Context.ThreadContext.FileHandler, Path, StringToData(FileText))) { @@ -816,24 +741,41 @@ PANEL_MODAL_OVERRIDE_CALLBACK(AnimInfoView_SaveAnimFileCallback) } internal void -AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) +AnimationTimeline_SetActiveAnimation (animation_handle Handle, animation_timeline_state* TimelineState, + animation_system* System) +{ + System->ActiveFadeGroup.From = Handle; + TimelineState->EditingAnimationHandle = Handle; +} + +internal void +AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) { animation_system* AnimSystem = &State->AnimationSystem; - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(AnimSystem); ui_interface* Interface = &State->Interface; ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("AnimInfo Layout")); ui_FillRect(&State->Interface, Bounds, Interface->Style.PanelBG); - if (ui_BeginLabeledDropdown(Interface, MakeString("Active Animation"), ActiveAnim->Name)) + gs_string AnimName = {}; + if (ActiveAnim) + { + AnimName = ActiveAnim->Name; + } else { + AnimName = MakeString("[None]"); + } + + if (ui_BeginLabeledDropdown(Interface, MakeString("Active Animation"), AnimName)) { for (u32 i = 0; i < AnimSystem->Animations.Count; i++) { animation Animation = AnimSystem->Animations.Values[i]; if (ui_Button(Interface, Animation.Name)) { - AnimSystem->ActiveAnimationIndex = i; + animation_handle NewHandle = {}; + NewHandle.Index = i; + AnimationTimeline_SetActiveAnimation(NewHandle, TimelineState, AnimSystem); } } } @@ -846,16 +788,16 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel animation NewAnim = {}; NewAnim.Name = PushString(State->AnimationSystem.Storage, 256); - u32 NewAnimIndex = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim); - State->AnimationSystem.ActiveAnimationIndex = NewAnimIndex; + animation_handle NewAnimHandle = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim); + State->AnimationSystem.ActiveFadeGroup.From = NewAnimHandle; } - if (ui_Button(Interface, MakeString("Save"))) + if (ActiveAnim && ui_Button(Interface, MakeString("Save"))) { // Save Animation File // TODO(pjs): If you created the animation via the "new" button, there won't be a file attached. // need to use the file browser to create a file - u32 ActiveAnimIndex = State->AnimationSystem.ActiveAnimationIndex; - animation ActiveAnimation = State->AnimationSystem.Animations.Values[ActiveAnimIndex]; + animation_handle ActiveAnimHandle = State->AnimationSystem.ActiveFadeGroup.From; + animation ActiveAnimation = *AnimationArray_GetSafe(State->AnimationSystem.Animations, ActiveAnimHandle); if (!ActiveAnimation.FileInfo.Path.Str) { @@ -875,39 +817,46 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel } ui_EndRow(Interface); - ui_TextEntry(Interface, MakeString("Anim Name"), &ActiveAnim->Name); - - ui_Label(Interface, MakeString("Frame Range")); - ui_BeginRow(Interface, 3); + if (ActiveAnim) { - ActiveAnim->PlayableRange.Min = ui_TextEntryU64(Interface, MakeString("StartFrame"), ActiveAnim->PlayableRange.Min); - ActiveAnim->PlayableRange.Max = ui_TextEntryU64(Interface, MakeString("EndFrame"), ActiveAnim->PlayableRange.Max); - - } - ui_EndRow(Interface); - - animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, TimelineState->SelectedBlockHandle); - if (SelectedBlock) - { - animation_pattern BlockPattern = Patterns_GetPattern(State->Patterns, SelectedBlock->AnimationProcHandle); + ui_TextEntry(Interface, MakeString("Anim Name"), &ActiveAnim->Name); + ui_Label(Interface, MakeString("Frame Range")); ui_BeginRow(Interface, 3); - ui_Label(Interface, MakeString("Selected Pattern")); - //if (ui_BeginLabeledDropdown(Interface, MakeString("Selected Pattern"), MakeString(BlockPattern.Name, BlockPattern.NameLength))) - if (ui_BeginDropdown(Interface, MakeString(BlockPattern.Name, BlockPattern.NameLength))) { - for (u32 i = 0; i < State->Patterns.Count; i++) + ActiveAnim->PlayableRange.Min = ui_TextEntryU64(Interface, MakeString("StartFrame"), ActiveAnim->PlayableRange.Min); + ActiveAnim->PlayableRange.Max = ui_TextEntryU64(Interface, MakeString("EndFrame"), ActiveAnim->PlayableRange.Max); + + } + ui_EndRow(Interface); + + animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, TimelineState->SelectedBlockHandle); + if (SelectedBlock) + { + animation_pattern BlockPattern = Patterns_GetPattern(State->Patterns, SelectedBlock->AnimationProcHandle); + + ui_BeginRow(Interface, 3); + ui_Label(Interface, MakeString("Selected Pattern")); + //if (ui_BeginLabeledDropdown(Interface, MakeString("Selected Pattern"), MakeString(BlockPattern.Name, BlockPattern.NameLength))) + if (ui_BeginDropdown(Interface, MakeString(BlockPattern.Name, BlockPattern.NameLength))) { - animation_pattern Pattern = State->Patterns.Values[i]; - if (ui_Button(Interface, MakeString(Pattern.Name, Pattern.NameLength))) + for (u32 i = 0; i < State->Patterns.Count; i++) { - SelectedBlock->AnimationProcHandle = Patterns_IndexToHandle(i); + animation_pattern Pattern = State->Patterns.Values[i]; + if (ui_Button(Interface, MakeString(Pattern.Name, Pattern.NameLength))) + { + SelectedBlock->AnimationProcHandle = Patterns_IndexToHandle(i); + } } } + ui_EndLabeledDropdown(Interface); + } + + if (ui_Button(Interface, MakeString("+ Add Block"))) + { + AnimationTimeline_AddAnimationBlockCommand(TimelineState, State, Context); } - ui_EndLabeledDropdown(Interface); } - ui_PopLayout(Interface, MakeString("AnimInfo Layout")); } @@ -924,6 +873,15 @@ AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* { animation_timeline_state* TimelineState = Panel_GetStateStruct(Panel, animation_timeline_state); + 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); + } + ui_FillRect(&State->Interface, PanelBounds, v4{.1f,.1f,.1f,1.f}); rect2 TimelineBounds, InfoBounds; @@ -940,10 +898,10 @@ AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RectHSplitAtDistanceFromTop(TimeRangePanelBounds, TitleBarHeight, &FrameCountBounds, &TimeRangeBounds); PlayBar_Render(TimelineState, PlayBarBounds, Panel, RenderBuffer, State, Context); - FrameCount_Render(TimelineState, FrameCountBounds, RenderBuffer, State, Context); - LayerList_Render(TimelineState, LayersBounds, Panel, RenderBuffer, State, Context); - TimeRange_Render(TimelineState, TimeRangeBounds, RenderBuffer, State, Context); - AnimInfoView_Render(TimelineState, InfoBounds, Panel, RenderBuffer, State, Context); + FrameCount_Render(TimelineState, ActiveAnim, FrameCountBounds, RenderBuffer, State, Context); + 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); } #define FOLDHAUS_PANEL_ANIMATION_TIMELINE_H diff --git a/src/app/editor/panels/foldhaus_panel_file_view.h b/src/app/editor/panels/foldhaus_panel_file_view.h index aee1c19..0910e66 100644 --- a/src/app/editor/panels/foldhaus_panel_file_view.h +++ b/src/app/editor/panels/foldhaus_panel_file_view.h @@ -16,6 +16,8 @@ struct file_view_state file_view_mode Mode; gs_string WorkingDirectory; + gs_string DisplayDirectory; + gs_memory_arena FileNamesArena; gs_file_info_array FileNames; @@ -55,6 +57,7 @@ FileView_UpdateWorkingDirectory(gs_const_string WorkingDirectory, file_view_stat Assert(LastChar == '\\' || LastChar == '/'); ClearArena(&State->FileNamesArena); + gs_string SanitizedDir = PushString(Context.ThreadContext.Transient, WorkingDirectory.Length + 2); SanitizePath(WorkingDirectory, &SanitizedDir, Context.ThreadContext.Transient); if (SanitizedDir.Str[SanitizedDir.Length - 1] != '\\') @@ -62,6 +65,8 @@ FileView_UpdateWorkingDirectory(gs_const_string WorkingDirectory, file_view_stat AppendPrintF(&SanitizedDir, "\\"); } + gs_const_string SanitizedDisplayDir = SanitizedDir.ConstString; + gs_file_info NewWorkingDir = GetFileInfo(Context.ThreadContext.FileHandler, SanitizedDir.ConstString); if (NewWorkingDir.IsDirectory) { @@ -73,7 +78,7 @@ FileView_UpdateWorkingDirectory(gs_const_string WorkingDirectory, file_view_stat // NOTE(pjs): we might be printing from State->WorkingDirectory to State->WorkingDirectory // in some cases. Shouldn't be a problem but it is unnecessary PrintF(&State->WorkingDirectory, "%S", SanitizedDir.ConstString); - + PrintF(&State->DisplayDirectory, "%S", SanitizedDisplayDir); } } @@ -88,7 +93,9 @@ FileView_Init(panel* Panel, app_state* State, context Context) FileViewState->FileNamesArena = CreateMemoryArena(Context.ThreadContext.Allocator); // TODO(pjs): this shouldn't be stored in permanent - FileViewState->WorkingDirectory = PushString(&State->Permanent, 256); + FileViewState->DisplayDirectory = PushString(&State->Permanent, 1024); + FileViewState->WorkingDirectory = PushString(&State->Permanent, 1024); + FileView_UpdateWorkingDirectory(ConstString(".\\"), FileViewState, Context); } @@ -109,13 +116,17 @@ FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBu ui_PushLayout(&State->Interface, PanelBounds, LayoutDirection_TopDown, MakeString("FileView Layout")); { - ui_BeginRow(&State->Interface, 3); { if (FileViewState->Mode == FileViewMode_Save) { if (ui_Button(&State->Interface, MakeString("Save"))) { + if (!FileViewState->SelectedFile.Path.Str) + { + FileViewState->SelectedFile.Path = FileViewState->DisplayDirectory.ConstString; + } + FileView_Exit_(Panel, State, Context); } } @@ -128,11 +139,11 @@ FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBu ui_EndRow(&State->Interface); // Header - if (ui_TextEntry(&State->Interface, MakeString("pwd"), &FileViewState->WorkingDirectory)) + if (ui_TextEntry(&State->Interface, MakeString("pwd"), &FileViewState->DisplayDirectory)) { // if last character is a slash, update pwd, and clear the filter string // otherwise update the filter string - gs_string Pwd = FileViewState->WorkingDirectory; + gs_string Pwd = FileViewState->DisplayDirectory; char LastChar = Pwd.Str[Pwd.Length - 1]; if (LastChar == '\\' || LastChar == '/') { @@ -163,9 +174,9 @@ FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBu } else { + FileViewState->SelectedFile = File; switch (FileViewState->Mode) { - FileViewState->SelectedFile = File; case FileViewMode_Load: { FileView_Exit_(Panel, State, Context); diff --git a/src/app/engine/animation/foldhaus_animation.h b/src/app/engine/animation/foldhaus_animation.h index d93f3f1..85e541a 100644 --- a/src/app/engine/animation/foldhaus_animation.h +++ b/src/app/engine/animation/foldhaus_animation.h @@ -46,9 +46,7 @@ enum blend_mode BlendMode_Count, }; -// TODO(pjs): Add Opacity to this -typedef pixel led_blend_proc(pixel PixelA, pixel PixelB); - +// TODO(pjs): This really doesn't belong here global gs_const_string BlendModeStrings[] = { ConstString("Overwrite"), ConstString("Add"), @@ -86,6 +84,18 @@ struct animation gs_file_info FileInfo; }; +struct animation_handle +{ + s32 Index; +}; + +internal bool IsValid (animation_handle H) { return H.Index >= 0; } +internal void Clear (animation_handle* H) { H->Index = -1; } +internal bool AnimHandlesAreEqual (animation_handle A, animation_handle B) +{ + return A.Index == B.Index; +} + struct animation_array { animation* Values; @@ -101,7 +111,9 @@ struct animation_layer_frame animation_block NextHot; bool HasNextHot; - r32 HotOpacity; + r32 NextHotOpacity; + + blend_mode BlendMode; }; // NOTE(pjs): This is an evaluated frame - across all layers in an @@ -112,6 +124,27 @@ struct animation_frame u32 LayersCount; }; +enum animation_repeat_mode +{ + AnimationRepeat_Single, + AnimationRepeat_Loop, + AnimationRepeat_Invalid, +}; + +global gs_const_string AnimationRepeatModeStrings[] = { + ConstString("Repeat Single"), + ConstString("Loop"), + ConstString("Invalid"), +}; + +struct animation_fade_group +{ + animation_handle From; + animation_handle To; + r32 FadeElapsed; + r32 FadeDuration; +}; + #define ANIMATION_SYSTEM_LAYERS_MAX 128 #define ANIMATION_SYSTEM_BLOCKS_MAX 128 struct animation_system @@ -119,10 +152,14 @@ struct animation_system gs_memory_arena* Storage; animation_array Animations; + animation_repeat_mode RepeatMode; + // NOTE(Peter): The frame currently being displayed/processed. you // can see which frame you're on by looking at the time slider on the timeline // panel - u32 ActiveAnimationIndex; + animation_handle ActiveAnimationHandle_; + animation_fade_group ActiveFadeGroup; + s32 CurrentFrame; s32 LastUpdatedFrame; r32 SecondsPerFrame; @@ -214,7 +251,7 @@ Patterns_Create(gs_memory_arena* Arena, s32 CountMax) return Result; } -#define Patterns_PushPattern(array, proc) Patterns_PushPattern_((array), (proc), Stringify(proc), sizeof(Stringify(proc))) +#define Patterns_PushPattern(array, proc) Patterns_PushPattern_((array), (proc), Stringify(proc), sizeof(Stringify(proc)) - 1) internal void Patterns_PushPattern_(animation_pattern_array* Array, animation_proc* Proc, char* Name, u32 NameLength) { @@ -341,13 +378,33 @@ AnimationArray_Create(gs_memory_arena* Storage, u32 CountMax) return Result; } -internal u32 +internal animation_handle AnimationArray_Push(animation_array* Array, animation Value) { Assert(Array->Count < Array->CountMax); - u32 Index = Array->Count++; - Array->Values[Index] = Value; - return Index; + animation_handle Result = {0}; + Result.Index = Array->Count++; + Array->Values[Result.Index] = Value; + return Result; +} + +internal animation* +AnimationArray_Get(animation_array Array, animation_handle Handle) +{ + animation* Result = 0; + if (IsValid(Handle) && Handle.Index < (s32)Array.Count) + { + Result = Array.Values + Handle.Index; + } + return Result; +} + +internal animation* +AnimationArray_GetSafe(animation_array Array, animation_handle Handle) +{ + Assert(IsValid(Handle)); + Assert(Handle.Index < (s32)Array.Count); + return AnimationArray_Get(Array, Handle); } ////////////////////////// @@ -482,6 +539,49 @@ ClampFrameToRange(s32 Frame, frame_range Range) // Layers +// Fade Group + +internal bool +AnimationFadeGroup_ShouldRender (animation_fade_group FadeGroup) +{ + return IsValid(FadeGroup.From); +} + +internal void +AnimationFadeGroup_Advance(animation_fade_group* Group) +{ + Group->From = Group->To; + Clear(&Group->To); + Group->FadeElapsed = 0; + Group->FadeDuration = 0; +} + +internal void +AnimationFadeGroup_Update(animation_fade_group* Group, r32 DeltaTime) +{ + if (IsValid(Group->To)) + { + Group->FadeElapsed += DeltaTime; + if (Group->FadeElapsed >= Group->FadeDuration) + { + AnimationFadeGroup_Advance(Group); + } + } +} + +internal void +AnimationFadeGroup_FadeTo(animation_fade_group* Group, animation_handle To, r32 Duration) +{ + // complete current fade if there is one in progress + if (IsValid(Group->To)) + { + AnimationFadeGroup_Advance(Group); + } + + Group->To = To; + Group->FadeDuration = Duration; +} + // System struct animation_system_desc @@ -499,29 +599,38 @@ AnimationSystem_Init(animation_system_desc Desc) Result.Animations = AnimationArray_Create(Result.Storage, Desc.AnimArrayCount); Result.SecondsPerFrame = Desc.SecondsPerFrame; + Clear(&Result.ActiveFadeGroup.From); + Clear(&Result.ActiveFadeGroup.To); + Result.ActiveFadeGroup.FadeElapsed = 0; + return Result; } internal animation* AnimationSystem_GetActiveAnimation(animation_system* System) { - // TODO(pjs): need a way to specify the active animation - return System->Animations.Values + System->ActiveAnimationIndex; + return AnimationArray_Get(System->Animations, System->ActiveFadeGroup.From); } internal animation_frame -AnimationSystem_CalculateAnimationFrame(animation_system* System, gs_memory_arena* Arena) +AnimationSystem_CalculateAnimationFrame(animation_system* System, + animation* Animation, + gs_memory_arena* Arena) { - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); - animation_frame Result = {0}; - Result.LayersCount = ActiveAnim->Layers.Count; + Result.LayersCount = Animation->Layers.Count; Result.Layers = PushArray(Arena, animation_layer_frame, Result.LayersCount); ZeroArray(Result.Layers, animation_layer_frame, Result.LayersCount); - for (u32 i = 0; i < ActiveAnim->Blocks_.Count; i++) + for (u32 l = 0; l < Animation->Layers.Count; l++) { - animation_block Block = ActiveAnim->Blocks_.Values[i]; + animation_layer_frame* Layer = Result.Layers + l; + Layer->BlendMode = Animation->Layers.Values[l].BlendMode; + } + + for (u32 i = 0; i < Animation->Blocks_.Count; i++) + { + animation_block Block = Animation->Blocks_.Values[i]; if (FrameIsInRange(Block.Range, System->CurrentFrame)) { @@ -548,12 +657,12 @@ AnimationSystem_CalculateAnimationFrame(animation_system* System, gs_memory_aren frame_range BlendRange = {}; BlendRange.Min = Layer->NextHot.Range.Min; BlendRange.Max = Layer->Hot.Range.Max; - Layer->HotOpacity = 1.0f - FrameToPercentRange(System->CurrentFrame, BlendRange); + Layer->NextHotOpacity = FrameToPercentRange(System->CurrentFrame, BlendRange); } else { Layer->Hot = Block; - Layer->HotOpacity = 1.0f; + Layer->NextHotOpacity = 0.0f; Layer->HasHot = true; } } @@ -563,18 +672,34 @@ AnimationSystem_CalculateAnimationFrame(animation_system* System, gs_memory_aren } internal void -AnimationSystem_Update(animation_system* System) +AnimationSystem_Update(animation_system* System, r32 DeltaTime) { + if (!System->TimelineShouldAdvance) { return; } + if (!AnimationFadeGroup_ShouldRender(System->ActiveFadeGroup)) { return; } + + AnimationFadeGroup_Update(&System->ActiveFadeGroup, DeltaTime); + animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); - if (System->TimelineShouldAdvance) { - // TODO(Peter): Revisit this. This implies that the framerate of the animation system - // is tied to the framerate of the simulation. That seems correct to me, but I'm not sure - System->CurrentFrame += 1; - - // Loop back to the beginning - if (System->CurrentFrame > ActiveAnim->PlayableRange.Max) + // TODO(Peter): Revisit this. This implies that the framerate of the animation system + // is tied to the framerate of the simulation. That seems correct to me, but I'm not sure + System->CurrentFrame += 1; + + // Loop back to the beginning + if (System->CurrentFrame > ActiveAnim->PlayableRange.Max) + { + switch (System->RepeatMode) { - System->CurrentFrame = 0; + case AnimationRepeat_Single: + { + System->CurrentFrame = 0; + }break; + + case AnimationRepeat_Loop: + { + // TODO(pjs): + }break; + + InvalidDefaultCase; } } } diff --git a/src/app/engine/animation/foldhaus_animation_renderer.cpp b/src/app/engine/animation/foldhaus_animation_renderer.cpp index 8915d59..16072ad 100644 --- a/src/app/engine/animation/foldhaus_animation_renderer.cpp +++ b/src/app/engine/animation/foldhaus_animation_renderer.cpp @@ -6,25 +6,27 @@ #ifndef FOLDHAUS_ANIMATION_RENDERER_CPP internal pixel -LedBlend_Overwrite(pixel PixelA, pixel PixelB) +LedBlend_Overwrite(pixel PixelA, pixel PixelB, u8* UserData) { return PixelB; } internal pixel -LedBlend_Overwrite(pixel PixelA, pixel PixelB, r32 Opacity) +LedBlend_Lerp(pixel PixelA, pixel PixelB, u8* UserData) { + r32 BOpacity = *(r32*)UserData; + pixel Result = {}; - r32 BOpacity = 1.0f - Opacity; - Result.R = (u8)((PixelA.R * Opacity) + (PixelB.R * BOpacity)); - Result.G = (u8)((PixelA.G * Opacity) + (PixelB.G * BOpacity)); - Result.B = (u8)((PixelA.B * Opacity) + (PixelB.B * BOpacity)); + r32 AOpacity = 1.0f - BOpacity; + Result.R = (u8)((PixelA.R * AOpacity) + (PixelB.R * BOpacity)); + Result.G = (u8)((PixelA.G * AOpacity) + (PixelB.G * BOpacity)); + Result.B = (u8)((PixelA.B * AOpacity) + (PixelB.B * BOpacity)); return Result; } internal pixel -LedBlend_Add(pixel PixelA, pixel PixelB) +LedBlend_Add(pixel PixelA, pixel PixelB, u8* UserData) { pixel Result = {}; @@ -40,7 +42,7 @@ LedBlend_Add(pixel PixelA, pixel PixelB) } internal pixel -LedBlend_Multiply(pixel PixelA, pixel PixelB) +LedBlend_Multiply(pixel PixelA, pixel PixelB, u8* UserData) { pixel Result = {}; @@ -60,7 +62,7 @@ LedBlend_Multiply(pixel PixelA, pixel PixelB) } internal pixel -LedBlend_Overlay(pixel PixelA, pixel PixelB) +LedBlend_Overlay(pixel PixelA, pixel PixelB, u8* UserData) { pixel Result = {}; return Result; @@ -80,15 +82,110 @@ LedBlend_GetProc(blend_mode BlendMode) return Result; } +struct pattern_args +{ + assembly Assembly; + gs_memory_arena* Transient; + u8* UserData; +}; + internal void -AnimationSystem_RenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, assembly Assembly, animation_pattern_array Patterns, gs_memory_arena* Transient, - u8* UserData) +AnimationSystem_RenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, animation_pattern_array Patterns, pattern_args PatternArgs) { u32 FramesIntoBlock = System->CurrentFrame - Block.Range.Min; r32 SecondsIntoBlock = FramesIntoBlock * System->SecondsPerFrame; animation_pattern Pattern = Patterns_GetPattern(Patterns, Block.AnimationProcHandle); - Pattern.Proc(Buffer, Assembly, SecondsIntoBlock, Transient, UserData); + Pattern.Proc(Buffer, PatternArgs.Assembly, SecondsIntoBlock, PatternArgs.Transient, PatternArgs.UserData); +} + +// NOTE(pjs): This mirrors animation_layer_frame to account +// for overlapping +struct layer_led_buffer +{ + led_buffer HotBuffer; + led_buffer NextHotBuffer; +}; + +internal led_buffer +RenderAnimationToLedBuffer (animation_system* System, + pattern_args PatternArgs, + animation_frame CurrFrame, + layer_led_buffer* LayerBuffers, + led_buffer* AssemblyLedBuffer, + animation_pattern_array Patterns, + gs_memory_arena* Transient) +{ + led_buffer AccBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient); + + // Create the LayerLEDBuffers + for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) + { + layer_led_buffer TempBuffer = {}; + + if (CurrFrame.Layers[Layer].HasHot) + { + TempBuffer.HotBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient); + + if (CurrFrame.Layers[Layer].HasNextHot) + { + TempBuffer.NextHotBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient); + } + } + + LayerBuffers[Layer] = TempBuffer; + } + + // Render Each layer's block to the appropriate temp buffer + for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) + { + animation_layer_frame LayerFrame = CurrFrame.Layers[Layer]; + if (LayerFrame.HasHot) + { + led_buffer TempBuffer = LayerBuffers[Layer].HotBuffer; + animation_block Block = LayerFrame.Hot; + AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs); + } + + if (LayerFrame.HasNextHot) + { + led_buffer TempBuffer = LayerBuffers[Layer].NextHotBuffer; + animation_block Block = LayerFrame.NextHot; + AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs); + } + } + + // Blend together any layers that have a hot and next hot buffer + for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) + { + animation_layer_frame LayerFrame = CurrFrame.Layers[Layer]; + layer_led_buffer LayerBuffer = LayerBuffers[Layer]; + if (LayerFrame.HasNextHot) + { + LedBuffer_Blend(LayerBuffer.HotBuffer, + LayerBuffer.NextHotBuffer, + &LayerBuffer.HotBuffer, + LedBlend_Lerp, + (u8*)&LayerFrame.NextHotOpacity); + } + } + + // Consolidate Temp Buffers back into AssemblyLedBuffer + // We do this in reverse order so that they go from top to bottom + for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) + { + if (CurrFrame.Layers[Layer].HasHot) + { + led_blend_proc* Blend = LedBlend_GetProc(CurrFrame.Layers[Layer].BlendMode); + LedBuffer_Blend(AccBuffer, + LayerBuffers[Layer].HotBuffer, + &AccBuffer, + Blend, + 0); + } + } + + return AccBuffer; } internal void @@ -101,100 +198,85 @@ AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Asse s32 CurrentFrame = System->CurrentFrame; r32 FrameTime = CurrentFrame * System->SecondsPerFrame; - animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); - animation_frame CurrFrame = AnimationSystem_CalculateAnimationFrame(System, Transient); +#if 1 + animation_array Animations = System->Animations; + animation_fade_group FadeGroup = System->ActiveFadeGroup; - // NOTE(pjs): This mirrors animation_layer_frame to account - // for overlapping - struct layer_led_buffer + animation* FromAnim = AnimationArray_Get(Animations, FadeGroup.From); + animation_frame FromFrame = AnimationSystem_CalculateAnimationFrame(System, FromAnim, Transient); + layer_led_buffer* FromLayerBuffers = PushArray(Transient, layer_led_buffer, FromFrame.LayersCount); + + animation* ToAnim = AnimationArray_Get(Animations, FadeGroup.To); + animation_frame ToFrame = {0}; + layer_led_buffer* ToLayerBuffers = 0; + if (ToAnim) { - led_buffer HotBuffer; - led_buffer NextHotBuffer; - }; - - layer_led_buffer* LayerBuffers = PushArray(Transient, layer_led_buffer, CurrFrame.LayersCount); + ToFrame = AnimationSystem_CalculateAnimationFrame(System, ToAnim, Transient); + ToLayerBuffers = PushArray(Transient, layer_led_buffer, ToFrame.LayersCount); + } for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++) { - assembly* Assembly = &Assemblies.Values[AssemblyIndex]; - led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly->LedBufferIndex); + assembly Assembly = Assemblies.Values[AssemblyIndex]; + led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex); - // Create the LayerLEDBuffers - for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) - { - layer_led_buffer TempBuffer = {}; - if (CurrFrame.Layers[Layer].HasHot) - { - TempBuffer.HotBuffer.LedCount = AssemblyLedBuffer->LedCount; - TempBuffer.HotBuffer.Positions = AssemblyLedBuffer->Positions; - TempBuffer.HotBuffer.Colors = PushArray(Transient, pixel, TempBuffer.HotBuffer.LedCount); - LedBuffer_ClearToBlack(&TempBuffer.HotBuffer); - } + pattern_args PatternArgs = {}; + PatternArgs.Assembly = Assembly; + PatternArgs.Transient = Transient; + PatternArgs.UserData = UserData; + + led_buffer FromBuffer = RenderAnimationToLedBuffer(System, + PatternArgs, + FromFrame, + FromLayerBuffers, + AssemblyLedBuffer, + Patterns, + Transient); + led_buffer ConsolidatedBuffer = FromBuffer; + + if (ToAnim) { + led_buffer ToBuffer = RenderAnimationToLedBuffer(System, + PatternArgs, + ToFrame, + ToLayerBuffers, + AssemblyLedBuffer, + Patterns, + Transient); - if (CurrFrame.Layers[Layer].HasNextHot) - { - TempBuffer.NextHotBuffer.LedCount = AssemblyLedBuffer->LedCount; - TempBuffer.NextHotBuffer.Positions = AssemblyLedBuffer->Positions; - TempBuffer.NextHotBuffer.Colors = PushArray(Transient, pixel, TempBuffer.HotBuffer.LedCount); - LedBuffer_ClearToBlack(&TempBuffer.NextHotBuffer); - } - - LayerBuffers[Layer] = TempBuffer; + r32 BlendPercent = FadeGroup.FadeElapsed / FadeGroup.FadeDuration; + LedBuffer_Blend(FromBuffer, ToBuffer, &ConsolidatedBuffer, LedBlend_Lerp, (u8*)&BlendPercent); } - // Render Each layer's block to the appropriate temp buffer - for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) - { - animation_layer_frame LayerFrame = CurrFrame.Layers[Layer]; - if (LayerFrame.HasHot) - { - led_buffer TempBuffer = LayerBuffers[Layer].HotBuffer; - animation_block Block = LayerFrame.Hot; - AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, *Assembly, Patterns, Transient, UserData); - } - - if (LayerFrame.HasNextHot) - { - led_buffer TempBuffer = LayerBuffers[Layer].NextHotBuffer; - animation_block Block = LayerFrame.NextHot; - AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, *Assembly, Patterns, Transient, UserData); - } - } - - // Blend together any layers that have a hot and next hot buffer - for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) - { - animation_layer_frame LayerFrame = CurrFrame.Layers[Layer]; - layer_led_buffer LayerBuffer = LayerBuffers[Layer]; - if (LayerFrame.HasNextHot) - { - for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++) - { - pixel A = LayerBuffer.HotBuffer.Colors[LED]; - pixel B = LayerBuffer.NextHotBuffer.Colors[LED]; - LayerBuffer.HotBuffer.Colors[LED] = LedBlend_Overwrite(A, B, LayerFrame.HotOpacity); - } - } - } - - // Consolidate Temp Buffers - // We do this in reverse order so that they go from top to bottom - for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) - { - if (CurrFrame.Layers[Layer].HasHot) - { - led_blend_proc* Blend = LedBlend_GetProc(ActiveAnim->Layers.Values[Layer].BlendMode); - Assert(Blend != 0); - for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++) - { - pixel A = AssemblyLedBuffer->Colors[LED]; - pixel B = LayerBuffers[Layer].HotBuffer.Colors[LED]; - AssemblyLedBuffer->Colors[LED] = Blend(A, B); - } - } - } + LedBuffer_Copy(ConsolidatedBuffer, AssemblyLedBuffer); } +#else + + animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); + animation_frame CurrFrame = AnimationSystem_CalculateAnimationFrame(System, ActiveAnim, Transient); + + for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++) + { + assembly Assembly = Assemblies.Values[AssemblyIndex]; + led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex); + + pattern_args PatternArgs = {}; + PatternArgs.Assembly = Assembly; + PatternArgs.Transient = Transient; + PatternArgs.UserData = UserData; + + led_buffer AccBuffer = RenderAnimationToLedBuffer(System, + PatternArgs, + CurrFrame, + LayerBuffers, + AssemblyLedBuffer, + Patterns, + Transient); + LedBuffer_Copy(AccBuffer, AssemblyLedBuffer); + } + +#endif + System->LastUpdatedFrame = System->CurrentFrame; } diff --git a/src/app/engine/assembly/foldhaus_assembly.h b/src/app/engine/assembly/foldhaus_assembly.h index 677d4ff..a14268e 100644 --- a/src/app/engine/assembly/foldhaus_assembly.h +++ b/src/app/engine/assembly/foldhaus_assembly.h @@ -160,6 +160,8 @@ struct assembly_array assembly* Values; }; +typedef pixel led_blend_proc(pixel A, pixel B, u8* UserData); + internal led_buffer* LedSystemGetBuffer(led_system* System, u32 Index) { @@ -178,6 +180,44 @@ LedBuffer_ClearToBlack(led_buffer* Buffer) } } +internal void +LedBuffer_Copy(led_buffer From, led_buffer* To) +{ + Assert(From.LedCount == To->LedCount); + u32 LedCount = To->LedCount; + for (u32 i = 0; i < LedCount; i++) + { + To->Colors[i] = From.Colors[i]; + } +} + +internal void +LedBuffer_Blend(led_buffer A, led_buffer B, led_buffer* Dest, led_blend_proc* BlendProc, u8* UserData) +{ + Assert(A.LedCount == B.LedCount); + Assert(Dest->LedCount == A.LedCount); + Assert(BlendProc); + + u32 LedCount = Dest->LedCount; + for (u32 i = 0; i < LedCount; i++) + { + pixel PA = A.Colors[i]; + pixel PB = B.Colors[i]; + Dest->Colors[i] = BlendProc(PA, PB, UserData); + } +} + +internal led_buffer +LedBuffer_CreateCopyCleared (led_buffer Buffer, gs_memory_arena* Arena) +{ + led_buffer Result = {}; + Result.LedCount = Buffer.LedCount; + Result.Positions = Buffer.Positions; + Result.Colors = PushArray(Arena, pixel, Buffer.LedCount); + LedBuffer_ClearToBlack(&Result); + return Result; +} + internal u32 StripGenData_CountLeds(strip_gen_data Data) { diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index 9112a93..df1df6a 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -62,6 +62,14 @@ INITIALIZE_APPLICATION(InitializeApplication) GlobalDebugServices->Interface.RenderSculpture = true; PanelSystem_Init(&State->PanelSystem, GlobalPanelDefs, GlobalPanelDefsCount, &State->Permanent); + + State->Modes = OperationModeSystemInit(&State->Permanent, Context.ThreadContext); + + State->UserSpaceDesc = BlumenLumen_UserSpaceCreate(); + + ReloadStaticData(Context, GlobalDebugServices); + US_CustomInit(&State->UserSpaceDesc, State, Context); + { // NOTE(pjs): This just sets up the default panel layout panel* RootPanel = PanelSystem_PushPanel(&State->PanelSystem, PanelType_SculptureView, State, Context); @@ -83,13 +91,6 @@ INITIALIZE_APPLICATION(InitializeApplication) Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_AssemblyDebug, State, Context); } - - State->Modes = OperationModeSystemInit(&State->Permanent, Context.ThreadContext); - - State->UserSpaceDesc = BlumenLumen_UserSpaceCreate(); - - ReloadStaticData(Context, GlobalDebugServices); - US_CustomInit(&State->UserSpaceDesc, State, Context); } UPDATE_AND_RENDER(UpdateAndRender) @@ -105,7 +106,7 @@ UPDATE_AND_RENDER(UpdateAndRender) Editor_Update(State, Context, InputQueue); - AnimationSystem_Update(&State->AnimationSystem); + AnimationSystem_Update(&State->AnimationSystem, Context->DeltaTime); if (AnimationSystem_NeedsRender(State->AnimationSystem)) { AnimationSystem_RenderToLedBuffers(&State->AnimationSystem, diff --git a/src/app/platform_win32/win32_foldhaus_socket.h b/src/app/platform_win32/win32_foldhaus_socket.h index b42c8c9..1a51d46 100644 --- a/src/app/platform_win32/win32_foldhaus_socket.h +++ b/src/app/platform_win32/win32_foldhaus_socket.h @@ -260,6 +260,10 @@ Win32SocketPeek(platform_socket* Socket) { }break; + case WSAECONNRESET: + { + }break; + InvalidDefaultCase; } }