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
AddAnimationBlockAtCurrentTime (u32 AnimationProcHandle, u32 Layer, animation_system* System)
AddAnimationBlockAtCurrentTime (u32 AnimationProcHandle, u32 LayerHandle, animation_system* System)
{
u32 NewBlockStart = System->CurrentFrame;
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;
}
@ -58,7 +59,8 @@ FOLDHAUS_INPUT_COMMAND_PROC(DeleteAnimationBlockCommand)
{
if(ListHandleIsValid(State->SelectedAnimationBlockHandle))
{
RemoveAnimationBlock(State->SelectedAnimationBlockHandle, &State->AnimationSystem);
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
Animation_RemoveBlock(ActiveAnim, State->SelectedAnimationBlockHandle);
State->SelectedAnimationBlockHandle = {0};
}
}
@ -135,6 +137,8 @@ OPERATION_RENDER_PROC(UpdateDragAnimationClip)
{
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);
u32 ClipInitialStartFrameXPosition = LerpR32(ClipInitialStartFrameXPercent,
OpState->TimelineBounds.Min.x,
@ -149,7 +153,7 @@ OPERATION_RENDER_PROC(UpdateDragAnimationClip)
u32 FrameAtMouseX = GetFrameFromPointInAnimationPanel(Mouse.Pos, OpState->TimelineBounds, OpState->VisibleRange);
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)
{
EndCurrentOperationMode(State, {}, Mouse, Context);
@ -161,9 +165,9 @@ OPERATION_RENDER_PROC(UpdateDragAnimationClip)
s32 NewStartFrame = OpState->ClipRange.Min + FrameOffset;
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; }
animation_block OtherBlock = OtherBlockEntry->Value;
NewStartFrame = AttemptToSnapPosition(NewStartFrame, OtherBlock.Range.Max);
@ -183,9 +187,9 @@ OPERATION_RENDER_PROC(UpdateDragAnimationClip)
r32 NewEndFrame = OpState->ClipRange.Max + FrameOffset;
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; }
animation_block OtherBlock = OtherBlockEntry->Value;
NewEndFrame = AttemptToSnapPosition(NewEndFrame, OtherBlock.Range.Min);
@ -204,9 +208,9 @@ OPERATION_RENDER_PROC(UpdateDragAnimationClip)
{
u32 NewStartFrame = OpState->ClipRange.Min + 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; }
animation_block OtherBlock = OtherBlockEntry->Value;
@ -228,8 +232,8 @@ OPERATION_RENDER_PROC(UpdateDragAnimationClip)
AnimationBlock->Range.Max = NewEndFrame;
}
s32 PlayableStartFrame = State->AnimationSystem.PlayableRange.Min;
s32 PlayableEndFrame = State->AnimationSystem.PlayableRange.Max;
s32 PlayableStartFrame = ActiveAnim->PlayableRange.Min;
s32 PlayableEndFrame = ActiveAnim->PlayableRange.Max;
AnimationBlock->Range.Min = (u32)Clamp(PlayableStartFrame, (s32)AnimationBlock->Range.Min, 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);
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
operation_mode* DragAnimationClipMode = ActivateOperationModeWithCommands(&State->Modes, DragAnimationClipCommands, UpdateDragAnimationClip);
drag_animation_clip_state* OpState = CreateOperationState(DragAnimationClipMode,
@ -251,17 +256,20 @@ SelectAndBeginDragAnimationBlock(gs_list_handle BlockHandle, frame_range Visible
OpState->TimelineBounds = TimelineBounds;
OpState->VisibleRange = VisibleRange;
animation_block* SelectedBlock = State->AnimationSystem.Blocks.GetElementWithHandle(BlockHandle);
animation_block* SelectedBlock = ActiveAnim->Blocks.GetElementWithHandle(BlockHandle);
OpState->ClipRange = SelectedBlock->Range;
}
// -------------------
FOLDHAUS_INPUT_COMMAND_PROC(AddAnimationBlockCommand)
{
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
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);
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);
}
@ -277,8 +285,9 @@ internal void
AnimationTimeline_Init(panel* Panel, app_state* State, context Context)
{
// TODO: :FreePanelMemory
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
animation_timeline_state* TimelineState = PushStruct(&State->Permanent, animation_timeline_state);
TimelineState->VisibleRange = State->AnimationSystem.PlayableRange;
TimelineState->VisibleRange = ActiveAnim->PlayableRange;
Panel->PanelStateMemory = (u8*)TimelineState;
}
@ -300,8 +309,6 @@ DrawFrameBar (animation_system* AnimationSystem, ui_interface Interface, frame_r
r32 BarHeight = Rect2Height(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
if (MouseButtonTransitionedDown(Interface.Mouse.LeftButtonState) &&
PointIsInRect(BarBounds, Interface.Mouse.DownPos))
@ -309,6 +316,8 @@ DrawFrameBar (animation_system* AnimationSystem, ui_interface Interface, frame_r
StartDragTimeMarker(BarBounds, VisibleFrames, State);
}
PushRenderQuad2D(Interface.RenderBuffer, BarBounds.Min, BarBounds.Max, v4{.16f, .16f, .16f, 1.f});
// Frame Ticks
u32 TickCount = 10;
for (u32 Tick = 0; Tick < TickCount; Tick++)
@ -323,7 +332,7 @@ DrawFrameBar (animation_system* AnimationSystem, ui_interface Interface, frame_r
}
// Time Slider
if (FrameIsInRange(AnimationSystem->CurrentFrame, VisibleFrames))
if (FrameIsInRange(VisibleFrames, AnimationSystem->CurrentFrame))
{
r32 FrameAtPercentVisibleRange = FrameToPercentRange(AnimationSystem->CurrentFrame, VisibleFrames);
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
DrawTimelineRangeBar (animation_system* AnimationSystem, animation_timeline_state* TimelineState, ui_interface Interface, rect2 BarBounds)
internal bool
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);
r32 BarWidth = Rect2Width(BarBounds);
PushRenderQuad2D(Interface.RenderBuffer, BarBounds.Min, BarBounds.Max, v4{.16f, .16f, .16f, 1.f});
bool ShouldUpdate = false;
*OutHandleValues = HandleValues;
r32 PlayableFrames = (r32)GetFrameCount(AnimationSystem->PlayableRange);
v2 SliderBarDim = v2{25, BarHeight};
v4 BGColor = v4{.16f, .16f, .16f, 1.f};
v4 HandleColor = v4{.8f, .8f, .8f, 1.f};
// Convert Frames To Pixels
r32 VisibleMinPercentPlayable = FrameToPercentRange(TimelineState->VisibleRange.Min, AnimationSystem->PlayableRange);
r32 VisibleMaxPercentPlayable = FrameToPercentRange(TimelineState->VisibleRange.Max, AnimationSystem->PlayableRange);
v2 RangeMinSliderMin = v2{BarBounds.Min.x + (VisibleMinPercentPlayable * Rect2Width(BarBounds)), BarBounds.Min.y};
v2 RangeMaxSliderMin = v2{BarBounds.Min.x + (VisibleMaxPercentPlayable * Rect2Width(BarBounds)) - 25, BarBounds.Min.y};
rect2 SliderBarRange = rect2{ RangeMinSliderMin, RangeMinSliderMin + SliderBarDim };
v2 HandleDim = v2{25, Rect2Height(SliderBounds)};
r32 MinHandleX = RemapR32(HandleValues.x, MinValue, MaxValue, SliderBounds.Min.x, SliderBounds.Max.x);
r32 MaxHandleX = RemapR32(HandleValues.y, MinValue, MaxValue, SliderBounds.Min.x, SliderBounds.Max.x);
rect2 MinHandleBounds = MakeRect2CenterDim(v2{ MinHandleX, Rect2Center(SliderBounds).y }, HandleDim);
rect2 MaxHandleBounds = MakeRect2CenterDim(v2{ MaxHandleX, Rect2Center(SliderBounds).y }, HandleDim);
// Drag the handles
if (MouseButtonHeldDown(Interface.Mouse.LeftButtonState) ||
MouseButtonTransitionedUp(Interface.Mouse.LeftButtonState))
{
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;
RangeMinSliderMin.x = Clamp(BarBounds.Min.x, NewSliderX, RangeMaxSliderMin.x - SliderBarDim.x);
MinHandleBounds = Rect2TranslateX(MinHandleBounds, MouseDragOffset.x);
}
if (PointIsInRect(SliderBarRange, Interface.Mouse.DownPos))
else if (PointIsInRect(MaxHandleBounds, Interface.Mouse.DownPos))
{
r32 NewSliderX = RangeMaxSliderMin.x + MouseDragOffset.x;
RangeMaxSliderMin.x = Clamp(RangeMinSliderMin.x + SliderBarDim.x, NewSliderX, BarBounds.Max.x - SliderBarDim.x);
MaxHandleBounds = Rect2TranslateX(MaxHandleBounds, MouseDragOffset.x);
}
}
v2 RangeMinSliderMax = v2{RangeMinSliderMin.x + 25, BarBounds.Max.y};
v2 RangeMaxSliderMax = v2{RangeMaxSliderMin.x + 25, BarBounds.Max.y};
PushRenderQuad2D(Interface.RenderBuffer, RangeMinSliderMin, RangeMinSliderMax, v4{.8f, .8f, .8f, 1.f});
PushRenderQuad2D(Interface.RenderBuffer, RangeMaxSliderMin, RangeMaxSliderMax, v4{.8f, .8f, .8f, 1.f});
// Draw Background
PushRenderQuad2D(Interface.RenderBuffer, SliderBounds.Min, SliderBounds.Max, BGColor);
// Convert Pixels Back To Frames and store
VisibleMinPercentPlayable = (RangeMinSliderMin.x - BarBounds.Min.x) / BarWidth;
VisibleMaxPercentPlayable = (RangeMaxSliderMax.x - BarBounds.Min.x) / BarWidth;
u32 VisibleFrameCount = GetFrameCount(AnimationSystem->PlayableRange);
Result.Min = VisibleMinPercentPlayable * VisibleFrameCount;
Result.Max = VisibleMaxPercentPlayable * VisibleFrameCount;
// Draw Handles
PushRenderQuad2D(Interface.RenderBuffer, MinHandleBounds.Min, MinHandleBounds.Max, HandleColor);
PushRenderQuad2D(Interface.RenderBuffer, MaxHandleBounds.Min, MaxHandleBounds.Max, HandleColor);
// Update the output range value
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))
{
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
internal u32
DrawLayerMenu(animation_system* AnimationSystem, ui_interface Interface, rect2 PanelDim, u32 SelectedAnimationLayer)
internal void
DrawLayerMenu(animation_system* AnimationSystem, animation ActiveAnim, ui_interface Interface, rect2 PanelDim, u32* SelectedAnimationLayer)
{
v2 LayerDim = { Rect2Width(PanelDim), LAYER_HEIGHT };
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};
LayerBounds.Min = { LayerListMin.x, LayerListMin.y + (LayerDim.y * LayerIndex) };
LayerBounds.Min = { LayerListMin.x, LayerListMin.y + (LayerDim.y * i) };
LayerBounds.Max = LayerBounds.Min + LayerDim;
if (MouseButtonTransitionedDown(Interface.Mouse.LeftButtonState) &&
PointIsInRect(LayerBounds, Interface.Mouse.Pos))
{
SelectedAnimationLayer = LayerIndex;
*SelectedAnimationLayer = i;
}
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);
}
DrawString(Interface.RenderBuffer, Layer->Name, Interface.Style.Font, LayerTextPos, WhiteV4);
}
return SelectedAnimationLayer;
}
internal rect2
@ -458,19 +493,22 @@ DrawAnimationTimeline (animation_system* AnimationSystem, animation_timeline_sta
gs_string Tempgs_string = PushString(State->Transient, 256);
gs_list_handle Result = SelectedBlockHandle;
// TODO(pjs): Animation Selection
animation CurrAnimation = AnimationSystem->Animations.Values[0];
rect2 LayerMenuBounds, TimelineBounds;
RectVSplitAtDistanceFromLeft(PanelBounds, 256, &LayerMenuBounds, &TimelineBounds);
// In Top To Bottom Order
rect2 TimelineFrameBarBounds, TimelineBlockDisplayBounds, TimelineRangeBarBounds;
rect2 TimelineFrameBarBounds;
rect2 TimelineBlockDisplayBounds;
rect2 TimelineRangeBarBounds;
RectHSplitAtDistanceFromTop(TimelineBounds, 32, &TimelineFrameBarBounds, &TimelineBounds);
RectHSplitAtDistanceFromBottom(TimelineBounds, 24, &TimelineBlockDisplayBounds, &TimelineRangeBarBounds);
State->SelectedAnimationLayer = DrawLayerMenu(AnimationSystem, *Interface, LayerMenuBounds, State->SelectedAnimationLayer);
DrawLayerMenu(AnimationSystem, CurrAnimation, *Interface, LayerMenuBounds, &State->SelectedAnimationLayer);
frame_range AdjustedViewRange = {0};
AdjustedViewRange = DrawTimelineRangeBar(AnimationSystem, TimelineState, *Interface, TimelineRangeBarBounds);
s32 VisibleFrameCount = AdjustedViewRange.Max - AdjustedViewRange.Min;
frame_range AdjustedViewRange = DrawTimelineRangeBar(AnimationSystem, CurrAnimation, TimelineState, *Interface, TimelineRangeBarBounds);
DrawFrameBar(AnimationSystem, *Interface, AdjustedViewRange, TimelineFrameBarBounds, State);
@ -479,17 +517,17 @@ DrawAnimationTimeline (animation_system* AnimationSystem, animation_timeline_sta
// Animation Blocks
b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState);
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; }
gs_list_handle CurrentBlockHandle = AnimationBlockEntry->Handle;
animation_block AnimationBlockAt = AnimationBlockEntry->Value;
// If either end is in the range, we should draw it
b32 RangeIsVisible = (FrameIsInRange(AnimationBlockAt.Range.Min, AdjustedViewRange) ||
FrameIsInRange(AnimationBlockAt.Range.Max, AdjustedViewRange));
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 &&
@ -516,7 +554,7 @@ DrawAnimationTimeline (animation_system* AnimationSystem, animation_timeline_sta
}
// Time Slider
if (FrameIsInRange(AnimationSystem->CurrentFrame, AdjustedViewRange))
if (FrameIsInRange(AdjustedViewRange, AnimationSystem->CurrentFrame))
{
r32 FrameAtPercentVisibleRange = FrameToPercentRange(AnimationSystem->CurrentFrame, AdjustedViewRange);
r32 SliderX = LerpR32(FrameAtPercentVisibleRange, TimelineBounds.Min.x, TimelineBounds.Max.x);
@ -554,7 +592,7 @@ animation_clip GlobalAnimationClips[] = {
};
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);
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);
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)
{
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;
ui_interface* Interface = &State->Interface;
animation_system* AnimationSystem = &State->AnimationSystem;
rect2 TitleBarBounds, PanelContentsBounds;
RectHSplitAtDistanceFromTop(PanelBounds, Interface->Style.RowHeight, &TitleBarBounds, &PanelContentsBounds);
rect2 AnimationListBounds, TimelineBounds;
RectHSplitAtDistanceFromTop(PanelBounds, Interface->Style.RowHeight, &TitleBarBounds, &PanelContentsBounds);
RectVSplitAtDistanceFromLeft(PanelContentsBounds, 300, &AnimationListBounds, &TimelineBounds);
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);
{
if (ui_LayoutButton(Interface, &TitleBarLayout, MakeString("Pause")))
{
State->AnimationSystem.TimelineShouldAdvance = true;
}
if (ui_LayoutButton(Interface, &TitleBarLayout, MakeString("Play")))
{
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")))
{
State->AnimationSystem.TimelineShouldAdvance = false;

View File

@ -35,27 +35,47 @@ struct anim_layer
blend_mode BlendMode;
};
struct anim_layer_array
{
anim_layer* Values;
u32 Count;
u32 CountMax;
};
struct animation
{
anim_layer* Layers;
u32 LayersCount;
u32 LayersMax;
anim_layer_array Layers;
gs_list<animation_block> Blocks;
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_BLOCKS_MAX 128
struct animation_system
{
gs_memory_arena* Storage;
animation_array Animations;
gs_list<animation_block> Blocks;
anim_layer* Layers;
u32 LayersCount;
u32 LayersMax;
frame_range PlayableRange_;
gs_list<animation_block> Blocks_;
// 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
@ -65,10 +85,107 @@ struct animation_system
r32 SecondsPerFrame;
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
SecondsToFrames(r32 Seconds, animation_system System)
{
@ -77,7 +194,7 @@ SecondsToFrames(r32 Seconds, animation_system System)
}
inline b32
FrameIsInRange(s32 Frame, frame_range Range)
FrameIsInRange(frame_range Range, s32 Frame)
{
b32 Result = (Frame >= Range.Min) && (Frame <= Range.Max);
return Result;
@ -123,67 +240,64 @@ ClampFrameToRange(s32 Frame, frame_range Range)
// Blocks
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};
NewBlock.Range.Min = StartFrame;
NewBlock.Range.Max = EndFrame;
NewBlock.AnimationProcHandle = AnimationProcHandle;
NewBlock.Layer = Layer;
Result = AnimationSystem->Blocks.PushElementOnList(NewBlock);
NewBlock.Layer = LayerIndex;
gs_list_handle Result = Animation->Blocks.PushElementOnList(NewBlock);
return Result;
}
internal void
RemoveAnimationBlock(gs_list_handle AnimationBlockHandle, animation_system* AnimationSystem)
Animation_RemoveBlock(animation* Animation, gs_list_handle AnimationBlockHandle)
{
Assert(ListHandleIsValid(AnimationBlockHandle));
AnimationSystem->Blocks.FreeElementWithHandle(AnimationBlockHandle);
Animation->Blocks.FreeElementWithHandle(AnimationBlockHandle);
}
// 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;
Result = AnimationSystem->LayersCount++;
anim_layer* NewLayer = AnimationSystem->Layers + Result;
*NewLayer = {0};
NewLayer->Name = MakeString(PushArray(AnimationSystem->Storage, char, Name.Length), Name.Length);
PrintF(&NewLayer->Name, "%S", Name);
NewLayer->BlendMode = BlendMode;
// System
internal animation*
AnimationSystem_GetActiveAnimation(animation_system* System)
{
// TODO(pjs): need a way to specify the active animation
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;
}
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
#endif // FOLDHAUS_ANIMATION

View File

@ -136,16 +136,25 @@ INITIALIZE_APPLICATION(InitializeApplication)
State->Modes = OperationModeSystemInit(&State->Permanent, Context.ThreadContext);
{ // Animation PLAYGROUND
State->AnimationSystem = {};
State->AnimationSystem.Storage = &State->Permanent;
State->AnimationSystem.Animations = AnimationArray_Create(State->AnimationSystem.Storage, 32);
State->AnimationSystem.SecondsPerFrame = 1.f / 24.f;
State->AnimationSystem.PlayableRange.Min = 0;
State->AnimationSystem.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem);
State->AnimationSystem.LayersMax = 32;
State->AnimationSystem.Layers = PushArray(&State->Permanent, anim_layer, State->AnimationSystem.LayersMax);
AddLayer(MakeString("Base Layer"), &State->AnimationSystem, BlendMode_Overwrite);
AddLayer(MakeString("Color Layer"), &State->AnimationSystem, BlendMode_Multiply);
AddLayer(MakeString("Sparkles"), &State->AnimationSystem, BlendMode_Add);
animation Anim = {0};
Anim.Layers.CountMax = 8;
Anim.Layers.Values = PushArray(State->AnimationSystem.Storage, anim_layer, Anim.Layers.CountMax);
Anim.PlayableRange.Min = 0;
Anim.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem);
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
@ -230,16 +239,20 @@ UPDATE_AND_RENDER(UpdateAndRender)
HandleInput(State, State->WindowBounds, InputQueue, Context->Mouse, *Context);
{
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
if (State->AnimationSystem.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
State->AnimationSystem.CurrentFrame += 1;
// Loop back to the beginning
if (State->AnimationSystem.CurrentFrame > State->AnimationSystem.PlayableRange.Max)
if (State->AnimationSystem.CurrentFrame > ActiveAnim->PlayableRange.Max)
{
State->AnimationSystem.CurrentFrame = 0;
}
}
}
s32 CurrentFrame = State->AnimationSystem.CurrentFrame;
if (CurrentFrame != State->AnimationSystem.LastUpdatedFrame)
@ -247,31 +260,18 @@ UPDATE_AND_RENDER(UpdateAndRender)
State->AnimationSystem.LastUpdatedFrame = CurrentFrame;
r32 FrameTime = CurrentFrame * State->AnimationSystem.SecondsPerFrame;
u32 CurrentBlocksMax = State->AnimationSystem.LayersCount;
b8* CurrentBlocksFilled = PushArray(State->Transient, b8, CurrentBlocksMax);
ZeroArray(CurrentBlocksFilled, b8, CurrentBlocksMax);
animation_block* CurrentBlocks = PushArray(State->Transient, animation_block, CurrentBlocksMax);
animation_frame CurrFrame = AnimationSystem_CalculateAnimationFrame(&State->AnimationSystem, State->Transient);
for (u32 i = 0; i < State->AnimationSystem.Blocks.Used; i++)
{
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);
led_buffer* LayerLEDBuffers = PushArray(State->Transient, led_buffer, CurrFrame.BlocksCountMax);
for (u32 AssemblyIndex = 0; AssemblyIndex < State->Assemblies.Count; AssemblyIndex++)
{
assembly* Assembly = &State->Assemblies.Values[AssemblyIndex];
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; }
animation_block Block = CurrentBlocks[Layer];
if (!CurrFrame.BlocksFilled[Layer]) { continue; }
animation_block Block = CurrFrame.Blocks[Layer];
// Prep Temp Buffer
LayerLEDBuffers[Layer] = *AssemblyLedBuffer;
@ -287,11 +287,12 @@ UPDATE_AND_RENDER(UpdateAndRender)
// Consolidate Temp Buffers
// 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:
{

View File

@ -743,6 +743,15 @@ internal rect2 MakeRect2MinDim(v2 Min, v2 Dim)
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)
{
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 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
ClearArena(gs_memory_arena* Arena)
{