Successfully transitioned to discrete animations

This commit is contained in:
PS 2020-10-09 22:08:51 -07:00
parent ecca6c691a
commit b816474dd5
4 changed files with 347 additions and 169 deletions

View File

@ -34,11 +34,12 @@ GetXPositionFromFrameInAnimationPanel (u32 Frame, rect2 PanelBounds, frame_range
} }
internal gs_list_handle internal gs_list_handle
AddAnimationBlockAtCurrentTime (u32 AnimationProcHandle, u32 Layer, animation_system* System) AddAnimationBlockAtCurrentTime (u32 AnimationProcHandle, u32 LayerHandle, animation_system* System)
{ {
u32 NewBlockStart = System->CurrentFrame; u32 NewBlockStart = System->CurrentFrame;
u32 NewBlockEnd = NewBlockStart + SecondsToFrames(3, *System); u32 NewBlockEnd = NewBlockStart + SecondsToFrames(3, *System);
gs_list_handle Result = AddAnimationBlock(NewBlockStart, NewBlockEnd, AnimationProcHandle, Layer, System); animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System);
gs_list_handle Result = Animation_AddBlock(ActiveAnim, NewBlockStart, NewBlockEnd, AnimationProcHandle, LayerHandle);
return Result; return Result;
} }
@ -58,7 +59,8 @@ FOLDHAUS_INPUT_COMMAND_PROC(DeleteAnimationBlockCommand)
{ {
if(ListHandleIsValid(State->SelectedAnimationBlockHandle)) if(ListHandleIsValid(State->SelectedAnimationBlockHandle))
{ {
RemoveAnimationBlock(State->SelectedAnimationBlockHandle, &State->AnimationSystem); animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
Animation_RemoveBlock(ActiveAnim, State->SelectedAnimationBlockHandle);
State->SelectedAnimationBlockHandle = {0}; State->SelectedAnimationBlockHandle = {0};
} }
} }
@ -135,6 +137,8 @@ OPERATION_RENDER_PROC(UpdateDragAnimationClip)
{ {
drag_animation_clip_state* OpState = (drag_animation_clip_state*)Operation.OpStateMemory; drag_animation_clip_state* OpState = (drag_animation_clip_state*)Operation.OpStateMemory;
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
r32 ClipInitialStartFrameXPercent = FrameToPercentRange(OpState->ClipRange.Min, OpState->VisibleRange); r32 ClipInitialStartFrameXPercent = FrameToPercentRange(OpState->ClipRange.Min, OpState->VisibleRange);
u32 ClipInitialStartFrameXPosition = LerpR32(ClipInitialStartFrameXPercent, u32 ClipInitialStartFrameXPosition = LerpR32(ClipInitialStartFrameXPercent,
OpState->TimelineBounds.Min.x, OpState->TimelineBounds.Min.x,
@ -149,7 +153,7 @@ OPERATION_RENDER_PROC(UpdateDragAnimationClip)
u32 FrameAtMouseX = GetFrameFromPointInAnimationPanel(Mouse.Pos, OpState->TimelineBounds, OpState->VisibleRange); u32 FrameAtMouseX = GetFrameFromPointInAnimationPanel(Mouse.Pos, OpState->TimelineBounds, OpState->VisibleRange);
s32 FrameOffset = (s32)FrameAtMouseX - (s32)FrameAtMouseDownX; s32 FrameOffset = (s32)FrameAtMouseX - (s32)FrameAtMouseDownX;
animation_block* AnimationBlock = State->AnimationSystem.Blocks.GetElementWithHandle(State->SelectedAnimationBlockHandle); animation_block* AnimationBlock = ActiveAnim->Blocks.GetElementWithHandle(State->SelectedAnimationBlockHandle);
if (!AnimationBlock) if (!AnimationBlock)
{ {
EndCurrentOperationMode(State, {}, Mouse, Context); EndCurrentOperationMode(State, {}, Mouse, Context);
@ -161,9 +165,9 @@ OPERATION_RENDER_PROC(UpdateDragAnimationClip)
s32 NewStartFrame = OpState->ClipRange.Min + FrameOffset; s32 NewStartFrame = OpState->ClipRange.Min + FrameOffset;
if (FrameOffset < 0) if (FrameOffset < 0)
{ {
for (u32 i = 0; i < State->AnimationSystem.Blocks.Used; i++) for (u32 i = 0; i < ActiveAnim->Blocks.Used; i++)
{ {
gs_list_entry<animation_block>* OtherBlockEntry = State->AnimationSystem.Blocks.GetEntryAtIndex(i); gs_list_entry<animation_block>* OtherBlockEntry = ActiveAnim->Blocks.GetEntryAtIndex(i);
if (EntryIsFree(OtherBlockEntry)) { continue; } if (EntryIsFree(OtherBlockEntry)) { continue; }
animation_block OtherBlock = OtherBlockEntry->Value; animation_block OtherBlock = OtherBlockEntry->Value;
NewStartFrame = AttemptToSnapPosition(NewStartFrame, OtherBlock.Range.Max); NewStartFrame = AttemptToSnapPosition(NewStartFrame, OtherBlock.Range.Max);
@ -183,9 +187,9 @@ OPERATION_RENDER_PROC(UpdateDragAnimationClip)
r32 NewEndFrame = OpState->ClipRange.Max + FrameOffset; r32 NewEndFrame = OpState->ClipRange.Max + FrameOffset;
if (FrameOffset > 0) if (FrameOffset > 0)
{ {
for (u32 i = 0; i < State->AnimationSystem.Blocks.Used; i++) for (u32 i = 0; i < ActiveAnim->Blocks.Used; i++)
{ {
gs_list_entry<animation_block>* OtherBlockEntry = State->AnimationSystem.Blocks.GetEntryAtIndex(i); gs_list_entry<animation_block>* OtherBlockEntry = ActiveAnim->Blocks.GetEntryAtIndex(i);
if (EntryIsFree(OtherBlockEntry)) { continue; } if (EntryIsFree(OtherBlockEntry)) { continue; }
animation_block OtherBlock = OtherBlockEntry->Value; animation_block OtherBlock = OtherBlockEntry->Value;
NewEndFrame = AttemptToSnapPosition(NewEndFrame, OtherBlock.Range.Min); NewEndFrame = AttemptToSnapPosition(NewEndFrame, OtherBlock.Range.Min);
@ -204,9 +208,9 @@ OPERATION_RENDER_PROC(UpdateDragAnimationClip)
{ {
u32 NewStartFrame = OpState->ClipRange.Min + FrameOffset; u32 NewStartFrame = OpState->ClipRange.Min + FrameOffset;
u32 NewEndFrame = OpState->ClipRange.Max + FrameOffset; u32 NewEndFrame = OpState->ClipRange.Max + FrameOffset;
for (u32 i = 0; i < State->AnimationSystem.Blocks.Used; i++) for (u32 i = 0; i < ActiveAnim->Blocks.Used; i++)
{ {
gs_list_entry<animation_block>* OtherBlockEntry = State->AnimationSystem.Blocks.GetEntryAtIndex(i); gs_list_entry<animation_block>* OtherBlockEntry = ActiveAnim->Blocks.GetEntryAtIndex(i);
if (EntryIsFree(OtherBlockEntry)) { continue; } if (EntryIsFree(OtherBlockEntry)) { continue; }
animation_block OtherBlock = OtherBlockEntry->Value; animation_block OtherBlock = OtherBlockEntry->Value;
@ -228,8 +232,8 @@ OPERATION_RENDER_PROC(UpdateDragAnimationClip)
AnimationBlock->Range.Max = NewEndFrame; AnimationBlock->Range.Max = NewEndFrame;
} }
s32 PlayableStartFrame = State->AnimationSystem.PlayableRange.Min; s32 PlayableStartFrame = ActiveAnim->PlayableRange.Min;
s32 PlayableEndFrame = State->AnimationSystem.PlayableRange.Max; s32 PlayableEndFrame = ActiveAnim->PlayableRange.Max;
AnimationBlock->Range.Min = (u32)Clamp(PlayableStartFrame, (s32)AnimationBlock->Range.Min, PlayableEndFrame); AnimationBlock->Range.Min = (u32)Clamp(PlayableStartFrame, (s32)AnimationBlock->Range.Min, PlayableEndFrame);
AnimationBlock->Range.Max = (u32)Clamp(PlayableStartFrame, (s32)AnimationBlock->Range.Max, PlayableEndFrame); AnimationBlock->Range.Max = (u32)Clamp(PlayableStartFrame, (s32)AnimationBlock->Range.Max, PlayableEndFrame);
} }
@ -243,6 +247,7 @@ SelectAndBeginDragAnimationBlock(gs_list_handle BlockHandle, frame_range Visible
{ {
SelectAnimationBlock(BlockHandle, State); SelectAnimationBlock(BlockHandle, State);
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
operation_mode* DragAnimationClipMode = ActivateOperationModeWithCommands(&State->Modes, DragAnimationClipCommands, UpdateDragAnimationClip); operation_mode* DragAnimationClipMode = ActivateOperationModeWithCommands(&State->Modes, DragAnimationClipCommands, UpdateDragAnimationClip);
drag_animation_clip_state* OpState = CreateOperationState(DragAnimationClipMode, drag_animation_clip_state* OpState = CreateOperationState(DragAnimationClipMode,
@ -251,17 +256,20 @@ SelectAndBeginDragAnimationBlock(gs_list_handle BlockHandle, frame_range Visible
OpState->TimelineBounds = TimelineBounds; OpState->TimelineBounds = TimelineBounds;
OpState->VisibleRange = VisibleRange; OpState->VisibleRange = VisibleRange;
animation_block* SelectedBlock = State->AnimationSystem.Blocks.GetElementWithHandle(BlockHandle); animation_block* SelectedBlock = ActiveAnim->Blocks.GetElementWithHandle(BlockHandle);
OpState->ClipRange = SelectedBlock->Range; OpState->ClipRange = SelectedBlock->Range;
} }
// ------------------- // -------------------
FOLDHAUS_INPUT_COMMAND_PROC(AddAnimationBlockCommand) FOLDHAUS_INPUT_COMMAND_PROC(AddAnimationBlockCommand)
{ {
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
panel_and_bounds ActivePanel = GetPanelContainingPoint(Mouse.Pos, &State->PanelSystem, State->WindowBounds); panel_and_bounds ActivePanel = GetPanelContainingPoint(Mouse.Pos, &State->PanelSystem, State->WindowBounds);
frame_range Range = State->AnimationSystem.PlayableRange; frame_range Range = ActiveAnim->PlayableRange;
u32 MouseDownFrame = GetFrameFromPointInAnimationPanel(Mouse.Pos, ActivePanel.Bounds, Range); u32 MouseDownFrame = GetFrameFromPointInAnimationPanel(Mouse.Pos, ActivePanel.Bounds, Range);
gs_list_handle NewBlockHandle = AddAnimationBlock(MouseDownFrame, MouseDownFrame + SecondsToFrames(3, State->AnimationSystem), 4, State->SelectedAnimationLayer, &State->AnimationSystem);
gs_list_handle NewBlockHandle = Animation_AddBlock(ActiveAnim, MouseDownFrame, MouseDownFrame + SecondsToFrames(3, State->AnimationSystem), 4, State->SelectedAnimationLayer);
SelectAnimationBlock(NewBlockHandle, State); SelectAnimationBlock(NewBlockHandle, State);
} }
@ -277,8 +285,9 @@ internal void
AnimationTimeline_Init(panel* Panel, app_state* State, context Context) AnimationTimeline_Init(panel* Panel, app_state* State, context Context)
{ {
// TODO: :FreePanelMemory // TODO: :FreePanelMemory
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
animation_timeline_state* TimelineState = PushStruct(&State->Permanent, animation_timeline_state); animation_timeline_state* TimelineState = PushStruct(&State->Permanent, animation_timeline_state);
TimelineState->VisibleRange = State->AnimationSystem.PlayableRange; TimelineState->VisibleRange = ActiveAnim->PlayableRange;
Panel->PanelStateMemory = (u8*)TimelineState; Panel->PanelStateMemory = (u8*)TimelineState;
} }
@ -300,8 +309,6 @@ DrawFrameBar (animation_system* AnimationSystem, ui_interface Interface, frame_r
r32 BarHeight = Rect2Height(BarBounds); r32 BarHeight = Rect2Height(BarBounds);
r32 BarWidth = Rect2Width(BarBounds); r32 BarWidth = Rect2Width(BarBounds);
PushRenderQuad2D(Interface.RenderBuffer, BarBounds.Min, BarBounds.Max, v4{.16f, .16f, .16f, 1.f});
// Mouse clicked inside frame nubmer bar -> change current frame on timeline // Mouse clicked inside frame nubmer bar -> change current frame on timeline
if (MouseButtonTransitionedDown(Interface.Mouse.LeftButtonState) && if (MouseButtonTransitionedDown(Interface.Mouse.LeftButtonState) &&
PointIsInRect(BarBounds, Interface.Mouse.DownPos)) PointIsInRect(BarBounds, Interface.Mouse.DownPos))
@ -309,6 +316,8 @@ DrawFrameBar (animation_system* AnimationSystem, ui_interface Interface, frame_r
StartDragTimeMarker(BarBounds, VisibleFrames, State); StartDragTimeMarker(BarBounds, VisibleFrames, State);
} }
PushRenderQuad2D(Interface.RenderBuffer, BarBounds.Min, BarBounds.Max, v4{.16f, .16f, .16f, 1.f});
// Frame Ticks // Frame Ticks
u32 TickCount = 10; u32 TickCount = 10;
for (u32 Tick = 0; Tick < TickCount; Tick++) for (u32 Tick = 0; Tick < TickCount; Tick++)
@ -323,7 +332,7 @@ DrawFrameBar (animation_system* AnimationSystem, ui_interface Interface, frame_r
} }
// Time Slider // Time Slider
if (FrameIsInRange(AnimationSystem->CurrentFrame, VisibleFrames)) if (FrameIsInRange(VisibleFrames, AnimationSystem->CurrentFrame))
{ {
r32 FrameAtPercentVisibleRange = FrameToPercentRange(AnimationSystem->CurrentFrame, VisibleFrames); r32 FrameAtPercentVisibleRange = FrameToPercentRange(AnimationSystem->CurrentFrame, VisibleFrames);
r32 SliderX = LerpR32(FrameAtPercentVisibleRange, BarBounds.Min.x, BarBounds.Max.x); r32 SliderX = LerpR32(FrameAtPercentVisibleRange, BarBounds.Min.x, BarBounds.Max.x);
@ -340,91 +349,117 @@ DrawFrameBar (animation_system* AnimationSystem, ui_interface Interface, frame_r
} }
} }
internal frame_range internal bool
DrawTimelineRangeBar (animation_system* AnimationSystem, animation_timeline_state* TimelineState, ui_interface Interface, rect2 BarBounds) MinMaxRangeSlider(v2 HandleValues, rect2 SliderBounds, r32 MinValue, r32 MaxValue, ui_interface Interface, v2* OutHandleValues)
{ {
frame_range Result = {0}; // Should Update only gets set to true when the user is finished interacting (ie. on mouse up)
// this allows the continuous use of the value of a handle while it is being dragged, and allows
// for you to know when exactly to update the stored value
r32 BarHeight = Rect2Height(BarBounds); bool ShouldUpdate = false;
r32 BarWidth = Rect2Width(BarBounds); *OutHandleValues = HandleValues;
PushRenderQuad2D(Interface.RenderBuffer, BarBounds.Min, BarBounds.Max, v4{.16f, .16f, .16f, 1.f});
r32 PlayableFrames = (r32)GetFrameCount(AnimationSystem->PlayableRange); v4 BGColor = v4{.16f, .16f, .16f, 1.f};
v2 SliderBarDim = v2{25, BarHeight}; v4 HandleColor = v4{.8f, .8f, .8f, 1.f};
// Convert Frames To Pixels v2 HandleDim = v2{25, Rect2Height(SliderBounds)};
r32 VisibleMinPercentPlayable = FrameToPercentRange(TimelineState->VisibleRange.Min, AnimationSystem->PlayableRange); r32 MinHandleX = RemapR32(HandleValues.x, MinValue, MaxValue, SliderBounds.Min.x, SliderBounds.Max.x);
r32 VisibleMaxPercentPlayable = FrameToPercentRange(TimelineState->VisibleRange.Max, AnimationSystem->PlayableRange); r32 MaxHandleX = RemapR32(HandleValues.y, MinValue, MaxValue, SliderBounds.Min.x, SliderBounds.Max.x);
v2 RangeMinSliderMin = v2{BarBounds.Min.x + (VisibleMinPercentPlayable * Rect2Width(BarBounds)), BarBounds.Min.y}; rect2 MinHandleBounds = MakeRect2CenterDim(v2{ MinHandleX, Rect2Center(SliderBounds).y }, HandleDim);
v2 RangeMaxSliderMin = v2{BarBounds.Min.x + (VisibleMaxPercentPlayable * Rect2Width(BarBounds)) - 25, BarBounds.Min.y}; rect2 MaxHandleBounds = MakeRect2CenterDim(v2{ MaxHandleX, Rect2Center(SliderBounds).y }, HandleDim);
rect2 SliderBarRange = rect2{ RangeMinSliderMin, RangeMinSliderMin + SliderBarDim };
// Drag the handles
if (MouseButtonHeldDown(Interface.Mouse.LeftButtonState) || if (MouseButtonHeldDown(Interface.Mouse.LeftButtonState) ||
MouseButtonTransitionedUp(Interface.Mouse.LeftButtonState)) MouseButtonTransitionedUp(Interface.Mouse.LeftButtonState))
{ {
v2 MouseDragOffset = Interface.Mouse.Pos - Interface.Mouse.DownPos; v2 MouseDragOffset = Interface.Mouse.Pos - Interface.Mouse.DownPos;
if (PointIsInRect(SliderBarRange, Interface.Mouse.DownPos))
// TODO(pjs): We need to make sure that the min handle is always the lower one, etc.
// TODO(pjs): We need to range clamp the handles
if (PointIsInRect(MinHandleBounds, Interface.Mouse.DownPos))
{ {
r32 NewSliderX = RangeMinSliderMin.x + MouseDragOffset.x; MinHandleBounds = Rect2TranslateX(MinHandleBounds, MouseDragOffset.x);
RangeMinSliderMin.x = Clamp(BarBounds.Min.x, NewSliderX, RangeMaxSliderMin.x - SliderBarDim.x);
} }
if (PointIsInRect(SliderBarRange, Interface.Mouse.DownPos)) else if (PointIsInRect(MaxHandleBounds, Interface.Mouse.DownPos))
{ {
r32 NewSliderX = RangeMaxSliderMin.x + MouseDragOffset.x; MaxHandleBounds = Rect2TranslateX(MaxHandleBounds, MouseDragOffset.x);
RangeMaxSliderMin.x = Clamp(RangeMinSliderMin.x + SliderBarDim.x, NewSliderX, BarBounds.Max.x - SliderBarDim.x);
} }
} }
v2 RangeMinSliderMax = v2{RangeMinSliderMin.x + 25, BarBounds.Max.y}; // Draw Background
v2 RangeMaxSliderMax = v2{RangeMaxSliderMin.x + 25, BarBounds.Max.y}; PushRenderQuad2D(Interface.RenderBuffer, SliderBounds.Min, SliderBounds.Max, BGColor);
PushRenderQuad2D(Interface.RenderBuffer, RangeMinSliderMin, RangeMinSliderMax, v4{.8f, .8f, .8f, 1.f});
PushRenderQuad2D(Interface.RenderBuffer, RangeMaxSliderMin, RangeMaxSliderMax, v4{.8f, .8f, .8f, 1.f});
// Convert Pixels Back To Frames and store // Draw Handles
VisibleMinPercentPlayable = (RangeMinSliderMin.x - BarBounds.Min.x) / BarWidth; PushRenderQuad2D(Interface.RenderBuffer, MinHandleBounds.Min, MinHandleBounds.Max, HandleColor);
VisibleMaxPercentPlayable = (RangeMaxSliderMax.x - BarBounds.Min.x) / BarWidth; PushRenderQuad2D(Interface.RenderBuffer, MaxHandleBounds.Min, MaxHandleBounds.Max, HandleColor);
u32 VisibleFrameCount = GetFrameCount(AnimationSystem->PlayableRange);
Result.Min = VisibleMinPercentPlayable * VisibleFrameCount; // Update the output range value
Result.Max = VisibleMaxPercentPlayable * VisibleFrameCount; r32 MinHandleXOut = Rect2Center(MinHandleBounds).x;
r32 MaxHandleXOut = Rect2Center(MaxHandleBounds).x;
r32 MinHandleValue = RemapR32(MinHandleXOut, SliderBounds.Min.x, SliderBounds.Max.x, MinValue, MaxValue);
r32 MaxHandleValue = RemapR32(MaxHandleXOut, SliderBounds.Min.x, SliderBounds.Max.x, MinValue, MaxValue);
*OutHandleValues = v2{ Min(MinHandleValue, MaxHandleValue), Max(MinHandleValue, MaxHandleValue) };
if (MouseButtonTransitionedUp(Interface.Mouse.LeftButtonState)) if (MouseButtonTransitionedUp(Interface.Mouse.LeftButtonState))
{ {
TimelineState->VisibleRange = Result; ShouldUpdate = true;
} }
return Result; return ShouldUpdate;
}
internal frame_range
DrawTimelineRangeBar (animation_system* AnimationSystem, animation Animation, animation_timeline_state* TimelineState, ui_interface Interface, rect2 BarBounds)
{
frame_range VisibleRangeAfterInteraction = {};
r32 MinFrame = (r32)Animation.PlayableRange.Min;
r32 MaxFrame = (r32)Animation.PlayableRange.Max;
v2 RangeHandles = v2{ (r32)TimelineState->VisibleRange.Min, (r32)TimelineState->VisibleRange.Max };
bool ApplyUpdate = MinMaxRangeSlider(RangeHandles, BarBounds, MinFrame, MaxFrame, Interface, &RangeHandles);
VisibleRangeAfterInteraction.Min = (s32)RangeHandles.x;
VisibleRangeAfterInteraction.Max = (s32)RangeHandles.y;
if (ApplyUpdate)
{
TimelineState->VisibleRange = VisibleRangeAfterInteraction;
}
return VisibleRangeAfterInteraction;
} }
#define LAYER_HEIGHT 52 #define LAYER_HEIGHT 52
internal u32 internal void
DrawLayerMenu(animation_system* AnimationSystem, ui_interface Interface, rect2 PanelDim, u32 SelectedAnimationLayer) DrawLayerMenu(animation_system* AnimationSystem, animation ActiveAnim, ui_interface Interface, rect2 PanelDim, u32* SelectedAnimationLayer)
{ {
v2 LayerDim = { Rect2Width(PanelDim), LAYER_HEIGHT }; v2 LayerDim = { Rect2Width(PanelDim), LAYER_HEIGHT };
v2 LayerListMin = PanelDim.Min + v2{0, 24}; v2 LayerListMin = PanelDim.Min + v2{0, 24};
for (u32 LayerIndex = 0; LayerIndex < AnimationSystem->LayersCount; LayerIndex++) for (u32 i = 0; i < ActiveAnim.Layers.Count; i++)
{ {
anim_layer* Layer = AnimationSystem->Layers + LayerIndex; anim_layer* Layer = ActiveAnim.Layers.Values + i;
rect2 LayerBounds = {0}; rect2 LayerBounds = {0};
LayerBounds.Min = { LayerListMin.x, LayerListMin.y + (LayerDim.y * LayerIndex) }; LayerBounds.Min = { LayerListMin.x, LayerListMin.y + (LayerDim.y * i) };
LayerBounds.Max = LayerBounds.Min + LayerDim; LayerBounds.Max = LayerBounds.Min + LayerDim;
if (MouseButtonTransitionedDown(Interface.Mouse.LeftButtonState) && if (MouseButtonTransitionedDown(Interface.Mouse.LeftButtonState) &&
PointIsInRect(LayerBounds, Interface.Mouse.Pos)) PointIsInRect(LayerBounds, Interface.Mouse.Pos))
{ {
SelectedAnimationLayer = LayerIndex; *SelectedAnimationLayer = i;
} }
v2 LayerTextPos = { LayerBounds.Min.x + 6, LayerBounds.Max.y - 16}; v2 LayerTextPos = { LayerBounds.Min.x + 6, LayerBounds.Max.y - 16};
if (SelectedAnimationLayer == LayerIndex) if (*SelectedAnimationLayer == i)
{ {
PushRenderBoundingBox2D(Interface.RenderBuffer, LayerBounds.Min, LayerBounds.Max, 1, WhiteV4); PushRenderBoundingBox2D(Interface.RenderBuffer, LayerBounds.Min, LayerBounds.Max, 1, WhiteV4);
} }
DrawString(Interface.RenderBuffer, Layer->Name, Interface.Style.Font, LayerTextPos, WhiteV4); DrawString(Interface.RenderBuffer, Layer->Name, Interface.Style.Font, LayerTextPos, WhiteV4);
} }
return SelectedAnimationLayer;
} }
internal rect2 internal rect2
@ -458,19 +493,22 @@ DrawAnimationTimeline (animation_system* AnimationSystem, animation_timeline_sta
gs_string Tempgs_string = PushString(State->Transient, 256); gs_string Tempgs_string = PushString(State->Transient, 256);
gs_list_handle Result = SelectedBlockHandle; gs_list_handle Result = SelectedBlockHandle;
// TODO(pjs): Animation Selection
animation CurrAnimation = AnimationSystem->Animations.Values[0];
rect2 LayerMenuBounds, TimelineBounds; rect2 LayerMenuBounds, TimelineBounds;
RectVSplitAtDistanceFromLeft(PanelBounds, 256, &LayerMenuBounds, &TimelineBounds); RectVSplitAtDistanceFromLeft(PanelBounds, 256, &LayerMenuBounds, &TimelineBounds);
// In Top To Bottom Order // In Top To Bottom Order
rect2 TimelineFrameBarBounds, TimelineBlockDisplayBounds, TimelineRangeBarBounds; rect2 TimelineFrameBarBounds;
rect2 TimelineBlockDisplayBounds;
rect2 TimelineRangeBarBounds;
RectHSplitAtDistanceFromTop(TimelineBounds, 32, &TimelineFrameBarBounds, &TimelineBounds); RectHSplitAtDistanceFromTop(TimelineBounds, 32, &TimelineFrameBarBounds, &TimelineBounds);
RectHSplitAtDistanceFromBottom(TimelineBounds, 24, &TimelineBlockDisplayBounds, &TimelineRangeBarBounds); RectHSplitAtDistanceFromBottom(TimelineBounds, 24, &TimelineBlockDisplayBounds, &TimelineRangeBarBounds);
State->SelectedAnimationLayer = DrawLayerMenu(AnimationSystem, *Interface, LayerMenuBounds, State->SelectedAnimationLayer); DrawLayerMenu(AnimationSystem, CurrAnimation, *Interface, LayerMenuBounds, &State->SelectedAnimationLayer);
frame_range AdjustedViewRange = {0}; frame_range AdjustedViewRange = DrawTimelineRangeBar(AnimationSystem, CurrAnimation, TimelineState, *Interface, TimelineRangeBarBounds);
AdjustedViewRange = DrawTimelineRangeBar(AnimationSystem, TimelineState, *Interface, TimelineRangeBarBounds);
s32 VisibleFrameCount = AdjustedViewRange.Max - AdjustedViewRange.Min;
DrawFrameBar(AnimationSystem, *Interface, AdjustedViewRange, TimelineFrameBarBounds, State); DrawFrameBar(AnimationSystem, *Interface, AdjustedViewRange, TimelineFrameBarBounds, State);
@ -479,17 +517,17 @@ DrawAnimationTimeline (animation_system* AnimationSystem, animation_timeline_sta
// Animation Blocks // Animation Blocks
b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState); b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState);
gs_list_handle DragBlockHandle = {0}; gs_list_handle DragBlockHandle = {0};
for (u32 i = 0; i < AnimationSystem->Blocks.Used; i++) for (u32 i = 0; i < CurrAnimation.Blocks.Used; i++)
{ {
gs_list_entry<animation_block>* AnimationBlockEntry = AnimationSystem->Blocks.GetEntryAtIndex(i); gs_list_entry<animation_block>* AnimationBlockEntry = CurrAnimation.Blocks.GetEntryAtIndex(i);
if (EntryIsFree(AnimationBlockEntry)) { continue; } if (EntryIsFree(AnimationBlockEntry)) { continue; }
gs_list_handle CurrentBlockHandle = AnimationBlockEntry->Handle; gs_list_handle CurrentBlockHandle = AnimationBlockEntry->Handle;
animation_block AnimationBlockAt = AnimationBlockEntry->Value; animation_block AnimationBlockAt = AnimationBlockEntry->Value;
// If either end is in the range, we should draw it // If either end is in the range, we should draw it
b32 RangeIsVisible = (FrameIsInRange(AnimationBlockAt.Range.Min, AdjustedViewRange) || b32 RangeIsVisible = (FrameIsInRange(AdjustedViewRange, AnimationBlockAt.Range.Min) ||
FrameIsInRange(AnimationBlockAt.Range.Max, AdjustedViewRange)); FrameIsInRange(AdjustedViewRange, AnimationBlockAt.Range.Max));
// If neither end is in the range, but the ends surround the visible range, // If neither end is in the range, but the ends surround the visible range,
// we should still draw it. // we should still draw it.
RangeIsVisible |= (AnimationBlockAt.Range.Min <= AdjustedViewRange.Min && RangeIsVisible |= (AnimationBlockAt.Range.Min <= AdjustedViewRange.Min &&
@ -516,7 +554,7 @@ DrawAnimationTimeline (animation_system* AnimationSystem, animation_timeline_sta
} }
// Time Slider // Time Slider
if (FrameIsInRange(AnimationSystem->CurrentFrame, AdjustedViewRange)) if (FrameIsInRange(AdjustedViewRange, AnimationSystem->CurrentFrame))
{ {
r32 FrameAtPercentVisibleRange = FrameToPercentRange(AnimationSystem->CurrentFrame, AdjustedViewRange); r32 FrameAtPercentVisibleRange = FrameToPercentRange(AnimationSystem->CurrentFrame, AdjustedViewRange);
r32 SliderX = LerpR32(FrameAtPercentVisibleRange, TimelineBounds.Min.x, TimelineBounds.Max.x); r32 SliderX = LerpR32(FrameAtPercentVisibleRange, TimelineBounds.Min.x, TimelineBounds.Max.x);
@ -554,7 +592,7 @@ animation_clip GlobalAnimationClips[] = {
}; };
internal void internal void
DrawAnimationClipsList(rect2 PanelBounds, ui_interface* Interface, u32 SelectedAnimationLayer, animation_system* AnimationSystem) DrawAnimationClipsList(rect2 PanelBounds, ui_interface* Interface, u32 SelectedAnimationLayerHandle, animation_system* AnimationSystem)
{ {
ui_layout Layout = ui_CreateLayout(*Interface, PanelBounds); ui_layout Layout = ui_CreateLayout(*Interface, PanelBounds);
for (s32 i = 0; i < GlobalAnimationClipsCount; i++) for (s32 i = 0; i < GlobalAnimationClipsCount; i++)
@ -563,7 +601,7 @@ DrawAnimationClipsList(rect2 PanelBounds, ui_interface* Interface, u32 SelectedA
gs_string ClipName = MakeString(Clip.Name, Clip.NameLength); gs_string ClipName = MakeString(Clip.Name, Clip.NameLength);
if (ui_LayoutListEntry(Interface, &Layout, ClipName, i)) if (ui_LayoutListEntry(Interface, &Layout, ClipName, i))
{ {
AddAnimationBlockAtCurrentTime(i + 1, SelectedAnimationLayer, AnimationSystem); AddAnimationBlockAtCurrentTime(i + 1, SelectedAnimationLayerHandle, AnimationSystem);
} }
} }
} }
@ -574,14 +612,15 @@ internal void
AnimationTimeline_Render(panel Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context) AnimationTimeline_Render(panel Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
{ {
animation_timeline_state* TimelineState = (animation_timeline_state*)Panel.PanelStateMemory; animation_timeline_state* TimelineState = (animation_timeline_state*)Panel.PanelStateMemory;
// TODO(pjs): SelectedAnimationBlockHandle should be a property of animation_timeline_state
// unless its used elsewhere. Audit later
gs_list_handle SelectedBlockHandle = State->SelectedAnimationBlockHandle; gs_list_handle SelectedBlockHandle = State->SelectedAnimationBlockHandle;
ui_interface* Interface = &State->Interface; ui_interface* Interface = &State->Interface;
animation_system* AnimationSystem = &State->AnimationSystem; animation_system* AnimationSystem = &State->AnimationSystem;
rect2 TitleBarBounds, PanelContentsBounds; rect2 TitleBarBounds, PanelContentsBounds;
RectHSplitAtDistanceFromTop(PanelBounds, Interface->Style.RowHeight, &TitleBarBounds, &PanelContentsBounds);
rect2 AnimationListBounds, TimelineBounds; rect2 AnimationListBounds, TimelineBounds;
RectHSplitAtDistanceFromTop(PanelBounds, Interface->Style.RowHeight, &TitleBarBounds, &PanelContentsBounds);
RectVSplitAtDistanceFromLeft(PanelContentsBounds, 300, &AnimationListBounds, &TimelineBounds); RectVSplitAtDistanceFromLeft(PanelContentsBounds, 300, &AnimationListBounds, &TimelineBounds);
ui_FillRect(Interface, TitleBarBounds, Interface->Style.PanelBGColors[0]); ui_FillRect(Interface, TitleBarBounds, Interface->Style.PanelBGColors[0]);
@ -589,13 +628,15 @@ AnimationTimeline_Render(panel Panel, rect2 PanelBounds, render_command_buffer*
ui_StartRow(&TitleBarLayout, 3); ui_StartRow(&TitleBarLayout, 3);
{ {
if (ui_LayoutButton(Interface, &TitleBarLayout, MakeString("Pause"))) if (ui_LayoutButton(Interface, &TitleBarLayout, MakeString("Pause")))
{
State->AnimationSystem.TimelineShouldAdvance = true;
}
if (ui_LayoutButton(Interface, &TitleBarLayout, MakeString("Play")))
{ {
State->AnimationSystem.TimelineShouldAdvance = false; State->AnimationSystem.TimelineShouldAdvance = false;
} }
if (ui_LayoutButton(Interface, &TitleBarLayout, MakeString("Play"), (State->AnimationSystem.TimelineShouldAdvance ? PinkV4 : BlackV4), v4{.3f, .3f, .3f, 1.0f}, TealV4))
{
State->AnimationSystem.TimelineShouldAdvance = true;
}
if (ui_LayoutButton(Interface, &TitleBarLayout, MakeString("Stop"))) if (ui_LayoutButton(Interface, &TitleBarLayout, MakeString("Stop")))
{ {
State->AnimationSystem.TimelineShouldAdvance = false; State->AnimationSystem.TimelineShouldAdvance = false;

View File

@ -35,27 +35,47 @@ struct anim_layer
blend_mode BlendMode; blend_mode BlendMode;
}; };
struct anim_layer_array
{
anim_layer* Values;
u32 Count;
u32 CountMax;
};
struct animation struct animation
{ {
anim_layer* Layers; anim_layer_array Layers;
u32 LayersCount;
u32 LayersMax;
gs_list<animation_block> Blocks; gs_list<animation_block> Blocks;
frame_range PlayableRange; frame_range PlayableRange;
}; };
struct animation_array
{
animation* Values;
u32 Count;
u32 CountMax;
};
struct animation_frame
{
// NOTE(pjs): These are all parallel arrays of equal length
animation_block* Blocks;
b8* BlocksFilled;
u32 BlocksCountMax;
u32 BlocksCount;
};
#define ANIMATION_SYSTEM_LAYERS_MAX 128 #define ANIMATION_SYSTEM_LAYERS_MAX 128
#define ANIMATION_SYSTEM_BLOCKS_MAX 128 #define ANIMATION_SYSTEM_BLOCKS_MAX 128
struct animation_system struct animation_system
{ {
gs_memory_arena* Storage; gs_memory_arena* Storage;
animation_array Animations;
gs_list<animation_block> Blocks; frame_range PlayableRange_;
anim_layer* Layers; gs_list<animation_block> Blocks_;
u32 LayersCount;
u32 LayersMax;
// NOTE(Peter): The frame currently being displayed/processed. you // 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 // can see which frame you're on by looking at the time slider on the timeline
@ -65,10 +85,107 @@ struct animation_system
r32 SecondsPerFrame; r32 SecondsPerFrame;
b32 TimelineShouldAdvance; b32 TimelineShouldAdvance;
// Timeline
frame_range PlayableRange;
}; };
//////////////////////////
//
// Anim Layers Array
internal anim_layer_array
AnimLayerArray_Create(gs_memory_arena* Storage, u32 CountMax)
{
anim_layer_array Result = {0};
Result.CountMax = CountMax;
Result.Values = PushArray(Storage, anim_layer, Result.CountMax);
return Result;
}
internal u32
AnimLayerArray_Push(anim_layer_array* Array, anim_layer Value)
{
Assert(Array->Count < Array->CountMax);
u32 Index = Array->Count++;
Array->Values[Index] = Value;
return Index;
}
internal u32
AnimLayerArray_Remove(anim_layer_array* Array, u32 Index)
{
Assert(Index < Array->Count);
for (u32 i = Index; i < Array->Count - 1; i++)
{
Array->Values[i] = Array->Values[i + 1];
}
}
//////////////////////////
//
// Animation Array
internal animation_array
AnimationArray_Create(gs_memory_arena* Storage, u32 CountMax)
{
animation_array Result = {0};
Result.CountMax = CountMax;
Result.Values = PushArray(Storage, animation, Result.CountMax);
return Result;
}
internal u32
AnimationArray_Push(animation_array* Array, animation Value)
{
Assert(Array->Count < Array->CountMax);
u32 Index = Array->Count++;
Array->Values[Index] = Value;
return Index;
}
//////////////////////////
//
// Animation
internal u32
Animation_AddLayer(animation* Animation, anim_layer Layer)
{
return AnimLayerArray_Push(&Animation->Layers, Layer);
}
internal u32
Animation_AddLayer (animation* Animation, gs_string Name, blend_mode BlendMode, animation_system* System)
{
anim_layer NewLayer = {0};
NewLayer.Name = PushStringF(System->Storage, 256, "%S", Name);
NewLayer.BlendMode = BlendMode;
return Animation_AddLayer(Animation, NewLayer);
}
internal void
Animation_RemoveLayer (animation* Animation, u32 LayerIndex)
{
AnimLayerArray_Remove(&Animation->Layers, LayerIndex);
for (u32 i = Animation->Blocks.Used -= 1; i >= 0; i--)
{
gs_list_entry<animation_block>* Entry = Animation->Blocks.GetEntryAtIndex(i);
if (EntryIsFree(Entry)) { continue; }
animation_block* Block = &Entry->Value;
if (Block->Layer > LayerIndex)
{
Block->Layer -= 1;
}
else if (Block->Layer == LayerIndex)
{
Animation->Blocks.FreeElementAtIndex(i);
}
}
}
//////////////////////////
//
//
internal u32 internal u32
SecondsToFrames(r32 Seconds, animation_system System) SecondsToFrames(r32 Seconds, animation_system System)
{ {
@ -77,7 +194,7 @@ SecondsToFrames(r32 Seconds, animation_system System)
} }
inline b32 inline b32
FrameIsInRange(s32 Frame, frame_range Range) FrameIsInRange(frame_range Range, s32 Frame)
{ {
b32 Result = (Frame >= Range.Min) && (Frame <= Range.Max); b32 Result = (Frame >= Range.Min) && (Frame <= Range.Max);
return Result; return Result;
@ -123,67 +240,64 @@ ClampFrameToRange(s32 Frame, frame_range Range)
// Blocks // Blocks
internal gs_list_handle internal gs_list_handle
AddAnimationBlock(u32 StartFrame, s32 EndFrame, u32 AnimationProcHandle, u32 Layer, animation_system* AnimationSystem) Animation_AddBlock(animation* Animation, u32 StartFrame, s32 EndFrame, u32 AnimationProcHandle, u32 LayerIndex)
{ {
gs_list_handle Result = {0}; Assert(LayerIndex < Animation->Layers.Count);
animation_block NewBlock = {0}; animation_block NewBlock = {0};
NewBlock.Range.Min = StartFrame; NewBlock.Range.Min = StartFrame;
NewBlock.Range.Max = EndFrame; NewBlock.Range.Max = EndFrame;
NewBlock.AnimationProcHandle = AnimationProcHandle; NewBlock.AnimationProcHandle = AnimationProcHandle;
NewBlock.Layer = Layer; NewBlock.Layer = LayerIndex;
Result = AnimationSystem->Blocks.PushElementOnList(NewBlock);
gs_list_handle Result = Animation->Blocks.PushElementOnList(NewBlock);
return Result; return Result;
} }
internal void internal void
RemoveAnimationBlock(gs_list_handle AnimationBlockHandle, animation_system* AnimationSystem) Animation_RemoveBlock(animation* Animation, gs_list_handle AnimationBlockHandle)
{ {
Assert(ListHandleIsValid(AnimationBlockHandle)); Assert(ListHandleIsValid(AnimationBlockHandle));
AnimationSystem->Blocks.FreeElementWithHandle(AnimationBlockHandle); Animation->Blocks.FreeElementWithHandle(AnimationBlockHandle);
} }
// Layers // Layers
internal u32
AddLayer (gs_string Name, animation_system* AnimationSystem, blend_mode BlendMode = BlendMode_Overwrite)
{
// NOTE(Peter): If this assert fires its time to make the layer buffer system
// resizable.
Assert(AnimationSystem->LayersCount < AnimationSystem->LayersMax);
u32 Result = 0; // System
Result = AnimationSystem->LayersCount++;
anim_layer* NewLayer = AnimationSystem->Layers + Result; internal animation*
*NewLayer = {0}; AnimationSystem_GetActiveAnimation(animation_system* System)
NewLayer->Name = MakeString(PushArray(AnimationSystem->Storage, char, Name.Length), Name.Length); {
PrintF(&NewLayer->Name, "%S", Name); // TODO(pjs): need a way to specify the active animation
NewLayer->BlendMode = BlendMode; return System->Animations.Values + 0;
}
internal animation_frame
AnimationSystem_CalculateAnimationFrame(animation_system* System, gs_memory_arena* Arena)
{
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System);
animation_frame Result = {0};
Result.BlocksCountMax = ActiveAnim->Layers.Count;
Result.Blocks = PushArray(Arena, animation_block, Result.BlocksCountMax);
Result.BlocksFilled = PushArray(Arena, b8, Result.BlocksCountMax);
for (u32 i = 0; i < ActiveAnim->Blocks.Used; i++)
{
gs_list_entry<animation_block>* BlockEntry = ActiveAnim->Blocks.GetEntryAtIndex(i);
if (EntryIsFree(BlockEntry)) { continue; }
animation_block Block = BlockEntry->Value;
if (FrameIsInRange(Block.Range, System->CurrentFrame)){ continue; }
Result.BlocksFilled[Block.Layer] = true;
Result.Blocks[Block.Layer] = Block;
Result.BlocksCount++;
}
return Result; return Result;
} }
internal void
RemoveLayer (u32 LayerIndex, animation_system* AnimationSystem)
{
Assert(LayerIndex < AnimationSystem->LayersMax);
Assert(LayerIndex < AnimationSystem->LayersCount);
for (u32 i = LayerIndex; i < AnimationSystem->LayersCount - 1; i++)
{
AnimationSystem->Layers[i] = AnimationSystem->Layers[i + 1];
}
for (u32 i = AnimationSystem->Blocks.Used -= 1; i >= 0; i--)
{
gs_list_entry<animation_block>* Entry = AnimationSystem->Blocks.GetEntryAtIndex(i);
if (EntryIsFree(Entry)) { continue; }
animation_block* Block = &Entry->Value;
if (Block->Layer > LayerIndex)
{
Block->Layer -= 1;
}
else if (Block->Layer == LayerIndex)
{
AnimationSystem->Blocks.FreeElementAtIndex(i);
}
}
AnimationSystem->LayersCount -= 1;
}
#define FOLDHAUS_ANIMATION #define FOLDHAUS_ANIMATION
#endif // FOLDHAUS_ANIMATION #endif // FOLDHAUS_ANIMATION

View File

@ -136,16 +136,25 @@ INITIALIZE_APPLICATION(InitializeApplication)
State->Modes = OperationModeSystemInit(&State->Permanent, Context.ThreadContext); State->Modes = OperationModeSystemInit(&State->Permanent, Context.ThreadContext);
{ // Animation PLAYGROUND { // Animation PLAYGROUND
State->AnimationSystem = {}; State->AnimationSystem = {};
State->AnimationSystem.Storage = &State->Permanent; State->AnimationSystem.Storage = &State->Permanent;
State->AnimationSystem.Animations = AnimationArray_Create(State->AnimationSystem.Storage, 32);
State->AnimationSystem.SecondsPerFrame = 1.f / 24.f; State->AnimationSystem.SecondsPerFrame = 1.f / 24.f;
State->AnimationSystem.PlayableRange.Min = 0;
State->AnimationSystem.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem); animation Anim = {0};
State->AnimationSystem.LayersMax = 32; Anim.Layers.CountMax = 8;
State->AnimationSystem.Layers = PushArray(&State->Permanent, anim_layer, State->AnimationSystem.LayersMax); Anim.Layers.Values = PushArray(State->AnimationSystem.Storage, anim_layer, Anim.Layers.CountMax);
AddLayer(MakeString("Base Layer"), &State->AnimationSystem, BlendMode_Overwrite); Anim.PlayableRange.Min = 0;
AddLayer(MakeString("Color Layer"), &State->AnimationSystem, BlendMode_Multiply); Anim.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem);
AddLayer(MakeString("Sparkles"), &State->AnimationSystem, BlendMode_Add); Animation_AddLayer(&Anim, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem);
Animation_AddLayer(&Anim, MakeString("Color Layer"), BlendMode_Multiply, &State->AnimationSystem);
Animation_AddLayer(&Anim, MakeString("Sparkles"), BlendMode_Add, &State->AnimationSystem);
AnimationArray_Push(&State->AnimationSystem.Animations, Anim);
} // End Animation Playground } // End Animation Playground
@ -230,14 +239,18 @@ UPDATE_AND_RENDER(UpdateAndRender)
HandleInput(State, State->WindowBounds, InputQueue, Context->Mouse, *Context); HandleInput(State, State->WindowBounds, InputQueue, Context->Mouse, *Context);
if (State->AnimationSystem.TimelineShouldAdvance) { {
// TODO(Peter): Revisit this. This implies that the framerate of the animation system animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
// is tied to the framerate of the simulation. That seems correct to me, but I'm not sure if (State->AnimationSystem.TimelineShouldAdvance) {
State->AnimationSystem.CurrentFrame += 1; // TODO(Peter): Revisit this. This implies that the framerate of the animation system
// Loop back to the beginning // is tied to the framerate of the simulation. That seems correct to me, but I'm not sure
if (State->AnimationSystem.CurrentFrame > State->AnimationSystem.PlayableRange.Max) State->AnimationSystem.CurrentFrame += 1;
{
State->AnimationSystem.CurrentFrame = 0; // Loop back to the beginning
if (State->AnimationSystem.CurrentFrame > ActiveAnim->PlayableRange.Max)
{
State->AnimationSystem.CurrentFrame = 0;
}
} }
} }
@ -247,31 +260,18 @@ UPDATE_AND_RENDER(UpdateAndRender)
State->AnimationSystem.LastUpdatedFrame = CurrentFrame; State->AnimationSystem.LastUpdatedFrame = CurrentFrame;
r32 FrameTime = CurrentFrame * State->AnimationSystem.SecondsPerFrame; r32 FrameTime = CurrentFrame * State->AnimationSystem.SecondsPerFrame;
u32 CurrentBlocksMax = State->AnimationSystem.LayersCount; animation_frame CurrFrame = AnimationSystem_CalculateAnimationFrame(&State->AnimationSystem, State->Transient);
b8* CurrentBlocksFilled = PushArray(State->Transient, b8, CurrentBlocksMax);
ZeroArray(CurrentBlocksFilled, b8, CurrentBlocksMax);
animation_block* CurrentBlocks = PushArray(State->Transient, animation_block, CurrentBlocksMax);
for (u32 i = 0; i < State->AnimationSystem.Blocks.Used; i++) led_buffer* LayerLEDBuffers = PushArray(State->Transient, led_buffer, CurrFrame.BlocksCountMax);
{
gs_list_entry<animation_block>* BlockEntry = State->AnimationSystem.Blocks.GetEntryAtIndex(i);
if (EntryIsFree(BlockEntry)) { continue; }
animation_block Block = BlockEntry->Value;
if (CurrentFrame < Block.Range.Min || CurrentFrame > Block.Range.Max) { continue; }
CurrentBlocksFilled[Block.Layer] = true;
CurrentBlocks[Block.Layer] = Block;
}
led_buffer* LayerLEDBuffers = PushArray(State->Transient, led_buffer, CurrentBlocksMax);
for (u32 AssemblyIndex = 0; AssemblyIndex < State->Assemblies.Count; AssemblyIndex++) for (u32 AssemblyIndex = 0; AssemblyIndex < State->Assemblies.Count; AssemblyIndex++)
{ {
assembly* Assembly = &State->Assemblies.Values[AssemblyIndex]; assembly* Assembly = &State->Assemblies.Values[AssemblyIndex];
led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(&State->LedSystem, Assembly->LedBufferIndex); led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(&State->LedSystem, Assembly->LedBufferIndex);
for (u32 Layer = 0; Layer < CurrentBlocksMax; Layer++) for (u32 Layer = 0; Layer < CurrFrame.BlocksCountMax; Layer++)
{ {
if (!CurrentBlocksFilled[Layer]) { continue; } if (!CurrFrame.BlocksFilled[Layer]) { continue; }
animation_block Block = CurrentBlocks[Layer]; animation_block Block = CurrFrame.Blocks[Layer];
// Prep Temp Buffer // Prep Temp Buffer
LayerLEDBuffers[Layer] = *AssemblyLedBuffer; LayerLEDBuffers[Layer] = *AssemblyLedBuffer;
@ -287,11 +287,12 @@ UPDATE_AND_RENDER(UpdateAndRender)
// Consolidate Temp Buffers // Consolidate Temp Buffers
// We do this in reverse order so that they go from top to bottom // We do this in reverse order so that they go from top to bottom
for (u32 Layer = 0; Layer < CurrentBlocksMax; Layer++) animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
for (u32 Layer = 0; Layer < CurrFrame.BlocksCountMax; Layer++)
{ {
if (!CurrentBlocksFilled[Layer]) { continue; } if (!CurrFrame.BlocksFilled[Layer]) { continue; }
switch (State->AnimationSystem.Layers[Layer].BlendMode) switch (ActiveAnim->Layers.Values[Layer].BlendMode)
{ {
case BlendMode_Overwrite: case BlendMode_Overwrite:
{ {

View File

@ -743,6 +743,15 @@ internal rect2 MakeRect2MinDim(v2 Min, v2 Dim)
return Result; return Result;
} }
internal rect2 MakeRect2CenterDim(v2 Center, v2 Dim)
{
v2 HalfDim = Dim / 2;
rect2 Result = {0};
Result.Min = Center - HalfDim;
Result.Max = Center + HalfDim;
return Result;
}
internal b32 ValueInRangeR32(r32 Min, r32 Max, r32 V) internal b32 ValueInRangeR32(r32 Min, r32 Max, r32 V)
{ {
return ((V >= Min) && (V <= Max)); return ((V >= Min) && (V <= Max));
@ -2442,6 +2451,19 @@ FreeMemoryArena(gs_memory_arena* Arena)
#define PushArray(arena, type, count) (type*)(PushSize_((arena), sizeof(type) * (count), FileNameAndLineNumberString).Memory) #define PushArray(arena, type, count) (type*)(PushSize_((arena), sizeof(type) * (count), FileNameAndLineNumberString).Memory)
#define PushString(arena, length) MakeString(PushArray((arena), char, (length)), 0, (length)); #define PushString(arena, length) MakeString(PushArray((arena), char, (length)), 0, (length));
internal gs_string
PushStringF(gs_memory_arena* Arena, u32 MaxLength, char* Format, ...)
{
gs_string Result = PushString(Arena, MaxLength);
va_list Args;
va_start(Args, Format);
PrintFArgsList(&Result, Format, Args);
va_end(Args);
return Result;
}
internal void internal void
ClearArena(gs_memory_arena* Arena) ClearArena(gs_memory_arena* Arena)
{ {