Successfully transitioned to discrete animations
This commit is contained in:
parent
ecca6c691a
commit
b816474dd5
|
@ -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;
|
||||||
|
|
|
@ -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
|
|
@ -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:
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue