Lumenarium/src/panels/foldhaus_panel_animation_ti...

378 lines
16 KiB
C
Raw Normal View History

// TODO
// [x] - Moving animation blocks
// [x] - dragging beginning and end of time blocks
// [] - creating a timeblock with a specific animation
// [x] - play, pause, stop,
// [] - setting the start and end of the animation system
// [] - displaying multiple layers
// [] -
2019-12-28 19:31:21 +00:00
inline r32
GetTimeFromPointInAnimationPanel(v2 Point, rect PanelBounds, s32 StartFrame, s32 EndFrame, r32 SecondsPerFrame)
{
r32 StartFrameTime = (r32)StartFrame * SecondsPerFrame;
r32 EndFrameTime = (r32)EndFrame * SecondsPerFrame;
r32 TimeAtPoint = GSRemap(Point.x, PanelBounds.Min.x, PanelBounds.Max.x, StartFrameTime, EndFrameTime);
return TimeAtPoint;
}
inline s32
GetFrameFromPointInAnimationPanel (v2 Point, rect PanelBounds, s32 StartFrame, s32 EndFrame, r32 SecondsPerFrame)
{
r32 TimeAtPoint = GetTimeFromPointInAnimationPanel(Point, PanelBounds, StartFrame, EndFrame, SecondsPerFrame);
s32 FrameAtPoint = (s32)(TimeAtPoint * SecondsPerFrame);
return FrameAtPoint;
}
inline s32
GetXPositionFromTimeInAnimationPanel (r32 Time, rect PanelBounds, s32 StartFrame, s32 EndFrame, r32 SecondsPerFrame)
{
r32 StartFrameTime = (r32)StartFrame * SecondsPerFrame;
r32 EndFrameTime = (r32)EndFrame * SecondsPerFrame;
s32 XPositionAtTime = GSRemap(Time, StartFrameTime, EndFrameTime, PanelBounds.Min.x, PanelBounds.Max.x);
return XPositionAtTime;
}
internal void
DeleteAnimationBlock(animation_block_handle AnimationBlockHandle, app_state* State)
{
RemoveAnimationBlock(State->SelectedAnimationBlockHandle, &State->AnimationSystem);
State->SelectedAnimationBlockHandle = {0};
}
internal void
SelectAnimationBlock(animation_block_handle BlockHandle, app_state* State)
{
State->SelectedAnimationBlockHandle = BlockHandle;
}
internal void
DeselectCurrentAnimationBlock(app_state* State)
{
State->SelectedAnimationBlockHandle = {};
}
FOLDHAUS_INPUT_COMMAND_PROC(DeleteAnimationBlockCommand)
{
if (AnimationBlockHandleIsValid(State->SelectedAnimationBlockHandle))
{
DeleteAnimationBlock(State->SelectedAnimationBlockHandle, State);
}
}
2019-12-28 19:31:21 +00:00
//
// Drag Animation Clip
//
#define CLICK_ANIMATION_BLOCK_EDGE_MAX_SCREEN_DISTANCE 10
2019-12-28 19:31:21 +00:00
OPERATION_STATE_DEF(drag_animation_clip_state)
{
r32 AnimationPanel_StartFrame;
r32 AnimationPanel_EndFrame;
r32 SelectedClip_InitialStartTime;
r32 SelectedClip_InitialEndTime;
};
OPERATION_RENDER_PROC(UpdateDragAnimationClip)
{
drag_animation_clip_state* OpState = (drag_animation_clip_state*)Operation.OpStateMemory;
panel_and_bounds AnimationPanel = GetPanelContainingPoint(Mouse.DownPos, &State->PanelLayout, State->WindowBounds);
Assert(AnimationPanel.Panel);
s32 ClipInitialStartTimeXPosition = GetXPositionFromTimeInAnimationPanel(OpState->SelectedClip_InitialStartTime, AnimationPanel.Bounds, OpState->AnimationPanel_StartFrame, OpState->AnimationPanel_EndFrame, State->AnimationSystem.SecondsPerFrame);
s32 ClipInitialEndTimeXPosition = GetXPositionFromTimeInAnimationPanel(OpState->SelectedClip_InitialEndTime, AnimationPanel.Bounds, OpState->AnimationPanel_StartFrame, OpState->AnimationPanel_EndFrame, State->AnimationSystem.SecondsPerFrame);
2019-12-28 19:31:21 +00:00
r32 TimeAtMouseDownX = GetTimeFromPointInAnimationPanel(Mouse.DownPos, AnimationPanel.Bounds, OpState->AnimationPanel_StartFrame, OpState->AnimationPanel_EndFrame, State->AnimationSystem.SecondsPerFrame);
r32 TimeAtMouseX = GetTimeFromPointInAnimationPanel(Mouse.Pos, AnimationPanel.Bounds, OpState->AnimationPanel_StartFrame, OpState->AnimationPanel_EndFrame, State->AnimationSystem.SecondsPerFrame);
r32 TimeOffset = TimeAtMouseX - TimeAtMouseDownX;
animation_block* AnimationBlock = GetAnimationBlockWithHandle(State->SelectedAnimationBlockHandle, &State->AnimationSystem);
if (GSAbs(Mouse.DownPos.x - ClipInitialStartTimeXPosition) < CLICK_ANIMATION_BLOCK_EDGE_MAX_SCREEN_DISTANCE)
{
AnimationBlock->StartTime = OpState->SelectedClip_InitialStartTime + TimeOffset;
}
else if (GSAbs(Mouse.DownPos.x - ClipInitialEndTimeXPosition) < CLICK_ANIMATION_BLOCK_EDGE_MAX_SCREEN_DISTANCE)
{
AnimationBlock->EndTime = OpState->SelectedClip_InitialEndTime + TimeOffset;
}
else
{
AnimationBlock->StartTime = OpState->SelectedClip_InitialStartTime + TimeOffset;
AnimationBlock->EndTime = OpState->SelectedClip_InitialEndTime + TimeOffset;
}
2019-12-28 19:31:21 +00:00
}
FOLDHAUS_INPUT_COMMAND_PROC(EndDragAnimationClip)
{
DeactivateCurrentOperationMode(&State->Modes);
}
input_command DragAnimationClipCommands [] = {
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndDragAnimationClip },
};
internal void
SelectAndBeginDragAnimationBlock(animation_block_handle BlockHandle, s32 PanelStartFrame, s32 PanelEndFrame, app_state* State)
{
SelectAnimationBlock(BlockHandle, State);
operation_mode* DragAnimationClipMode = ActivateOperationModeWithCommands(&State->Modes, DragAnimationClipCommands, UpdateDragAnimationClip);
drag_animation_clip_state* OpState = CreateOperationState(DragAnimationClipMode,
&State->Modes,
drag_animation_clip_state);
OpState->AnimationPanel_StartFrame = PanelStartFrame;
OpState->AnimationPanel_EndFrame = PanelEndFrame ;
animation_block* SelectedBlock = GetAnimationBlockWithHandle(BlockHandle, &State->AnimationSystem);
OpState->SelectedClip_InitialStartTime = SelectedBlock->StartTime;
OpState->SelectedClip_InitialEndTime = SelectedBlock->EndTime;
}
// -------------------
FOLDHAUS_INPUT_COMMAND_PROC(AddAnimationBlockCommand)
{
panel_and_bounds ActivePanel = GetPanelContainingPoint(Mouse.Pos, &State->PanelLayout, State->WindowBounds);
r32 MouseDownPositionPercent = (Mouse.Pos.x - ActivePanel.Bounds.Min.x) / Width(ActivePanel.Bounds);
r32 NewBlockTimeStart = MouseDownPositionPercent * State->AnimationSystem.AnimationEnd;
#define NEW_BLOCK_DURATION 1
r32 NewBlockTimeEnd = NewBlockTimeStart + NEW_BLOCK_DURATION;
animation_block Block = {0};
Block.StartTime = NewBlockTimeStart;
Block.EndTime = NewBlockTimeEnd;
Block.Proc = TestPatternThree;
animation_block_handle NewBlockHandle = AddAnimationBlock(Block, &State->AnimationSystem);
SelectAnimationBlock(NewBlockHandle, State);
}
input_command AnimationTimeline_Commands[] = {
{ KeyCode_X, KeyCode_Invalid, Command_Began, DeleteAnimationBlockCommand },
{ KeyCode_A, KeyCode_Invalid, Command_Began, AddAnimationBlockCommand },
};
PANEL_INIT_PROC(AnimationTimeline_Init)
{
}
PANEL_CLEANUP_PROC(AnimationTimeline_Cleanup)
{
}
internal r32
2019-12-28 19:31:21 +00:00
DrawFrameBar (animation_system* AnimationSystem, render_command_buffer* RenderBuffer, s32 StartFrame, s32 EndFrame, rect PanelBounds, interface_config Interface, mouse_state Mouse)
{
MakeStringBuffer(TempString, 256);
s32 FrameCount = EndFrame - StartFrame;
r32 FrameBarHeight = 24;
2019-12-28 19:31:21 +00:00
v2 FrameBarMin = v2{PanelBounds.Min.x, PanelBounds.Max.y - FrameBarHeight};
v2 FrameBarMax = PanelBounds.Max;
PushRenderQuad2D(RenderBuffer, FrameBarMin, FrameBarMax, v4{.16f, .16f, .16f, 1.f});
// Mouse clicked inside frame nubmer bar -> change current frame on timeline
if (MouseButtonHeldDown(Mouse.LeftButtonState)
&& PointIsInRange(Mouse.DownPos, FrameBarMin, FrameBarMax))
{
2019-12-28 19:31:21 +00:00
r32 TimeAtMouseX = GetTimeFromPointInAnimationPanel(Mouse.DownPos, PanelBounds, StartFrame, EndFrame, AnimationSystem->SecondsPerFrame);
AnimationSystem->Time = TimeAtMouseX;
}
// Frame Ticks
for (s32 f = 0; f < FrameCount; f += 10)
{
s32 Frame = StartFrame + f;
PrintF(&TempString, "%d", Frame);
r32 FramePercent = (r32)f / (r32)FrameCount;
2019-12-28 19:31:21 +00:00
r32 FrameX = GSLerp(PanelBounds.Min.x, PanelBounds.Max.x, FramePercent);
v2 FrameTextPos = v2{FrameX, FrameBarMin.y + 2};
DrawString(RenderBuffer, TempString, Interface.Font, FrameTextPos, WhiteV4);
// Frame Vertical Slices
v2 LineTop = v2{FrameX, FrameBarMin.y};
2019-12-28 19:31:21 +00:00
v2 LineBottom = v2{FrameX + 1, PanelBounds.Min.y};
PushRenderQuad2D(RenderBuffer, LineTop, LineBottom, v4{.16f, .16f, .16f, 1.f});
}
return FrameBarMin.y;
}
internal rect
DrawAnimationBlock (animation_block AnimationBlock, v4 BlockColor, r32 SecondsPerFrame, s32 FrameCount, s32 StartFrame, v2 TimelineMin, v2 TimelineMax, render_command_buffer* RenderBuffer)
{
rect BlockBounds = {};
r32 TimelineWidth = TimelineMax.x - TimelineMin.x;
s32 BlockStartFrame = AnimationBlock.StartTime / SecondsPerFrame;
r32 StartFramePercent = (r32)(BlockStartFrame - StartFrame) / (r32)FrameCount;
r32 StartPosition = TimelineWidth * StartFramePercent;
s32 BlockEndFrame = AnimationBlock.EndTime / SecondsPerFrame;
r32 EndFramePercent = (r32)(BlockEndFrame - StartFrame) / (r32)FrameCount;
r32 EndPosition = TimelineWidth * EndFramePercent;
BlockBounds.Min = TimelineMin + v2{StartPosition, 25};
BlockBounds.Max = TimelineMin + v2{EndPosition, 75};
PushRenderQuad2D(RenderBuffer, BlockBounds.Min, BlockBounds.Max, BlockColor);
PushRenderBoundingBox2D(RenderBuffer, BlockBounds.Min, BlockBounds.Max, 1, WhiteV4);
return BlockBounds;
}
internal animation_block_handle
2019-12-28 19:31:21 +00:00
DrawAnimationTimeline (animation_system* AnimationSystem, s32 StartFrame, s32 EndFrame, rect PanelBounds, animation_block_handle SelectedBlockHandle, render_command_buffer* RenderBuffer, app_state* State, mouse_state Mouse)
{
string TempString = MakeString(PushArray(&State->Transient, char, 256), 256);
s32 FrameCount = EndFrame - StartFrame;
animation_block_handle Result = SelectedBlockHandle;
2019-12-28 19:31:21 +00:00
r32 AnimationPanelHeight = PanelBounds.Max.y - PanelBounds.Min.y;
r32 AnimationPanelWidth = PanelBounds.Max.x - PanelBounds.Min.x;
{
s32 FirstPlayableFrame = (AnimationSystem->AnimationStart / AnimationSystem->SecondsPerFrame);
s32 LastPlayableFrame = (AnimationSystem->AnimationEnd / AnimationSystem->SecondsPerFrame);
r32 FirstPlayablePercentX = ((r32)(FirstPlayableFrame - StartFrame) / (r32)FrameCount);
r32 LastPlayablePercentX = ((r32)(LastPlayableFrame - StartFrame) / (r32)FrameCount);
2019-12-28 19:31:21 +00:00
v2 PlayableMin = v2{(FirstPlayablePercentX * AnimationPanelWidth) + PanelBounds.Min.x, PanelBounds.Min.y };
v2 PlayableMax = v2{(LastPlayablePercentX * AnimationPanelWidth) + PanelBounds.Min.x, PanelBounds.Max.y };
2019-12-28 19:31:21 +00:00
PushRenderQuad2D(RenderBuffer, PanelBounds.Min, PanelBounds.Max, v4{.16f, .16f, .16f, 1.f});
PushRenderQuad2D(RenderBuffer, PlayableMin, PlayableMax, v4{.22f, .22f, .22f, 1.f});
}
2019-12-28 19:31:21 +00:00
r32 FrameBarBottom = DrawFrameBar(AnimationSystem, RenderBuffer, StartFrame, EndFrame, PanelBounds, State->Interface, Mouse);
// Animation Blocks
2019-12-28 19:31:21 +00:00
v2 TimelineMin = PanelBounds.Min;
v2 TimelineMax = v2{PanelBounds.Max.x, FrameBarBottom};
b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Mouse.LeftButtonState);
for (u32 i = 0; i < AnimationSystem->BlocksCount; i++)
{
2019-12-28 19:31:21 +00:00
animation_block_entry* AnimationBlockEntry = GetEntryAtIndex(i, AnimationSystem);
if (AnimationBlockIsFree(*AnimationBlockEntry)) { continue; }
animation_block_handle CurrentBlockHandle = {};
CurrentBlockHandle.Index = i;
2019-12-28 19:31:21 +00:00
CurrentBlockHandle.Generation = AnimationBlockEntry->Generation;
2019-12-28 19:31:21 +00:00
animation_block AnimationBlockAt = AnimationBlockEntry->Block;
v4 BlockColor = BlackV4;
if (AnimationBlockHandlesAreEqual(SelectedBlockHandle, CurrentBlockHandle))
{
BlockColor = PinkV4;
}
rect BlockBounds = DrawAnimationBlock(AnimationBlockAt, BlockColor, AnimationSystem->SecondsPerFrame, FrameCount, StartFrame, TimelineMin, TimelineMax, RenderBuffer);
if (PointIsInRange(Mouse.Pos, BlockBounds.Min, BlockBounds.Max)
&& MouseButtonTransitionedDown(Mouse.LeftButtonState))
{
MouseDownAndNotHandled = false;
2019-12-28 19:31:21 +00:00
SelectAndBeginDragAnimationBlock(CurrentBlockHandle, StartFrame, EndFrame, State);
}
}
// Time Slider
s32 SliderFrame = AnimationSystem->Time / AnimationSystem->SecondsPerFrame;
r32 TimePercent = (r32)(SliderFrame - StartFrame) / (r32)FrameCount;
2019-12-28 19:31:21 +00:00
r32 SliderX = PanelBounds.Min.x + (AnimationPanelWidth * TimePercent);
v2 SliderMin = v2{SliderX, PanelBounds.Min.y};
v2 SliderMax = v2{SliderX + 1, PanelBounds.Max.y - 25};
v4 TimeSliderColor = v4{.36f, .52f, .78f, 1.f};
PushRenderQuad2D(RenderBuffer, SliderMin, SliderMax, TimeSliderColor);
r32 SliderHalfWidth = 10;
v2 HeadMin = v2{SliderX - SliderHalfWidth, SliderMax.y};
2019-12-28 19:31:21 +00:00
v2 HeadMax = v2{SliderX + SliderHalfWidth, PanelBounds.Max.y};
PushRenderQuad2D(RenderBuffer, HeadMin, HeadMax, TimeSliderColor);
PrintF(&TempString, "%d", SliderFrame);
DrawString(RenderBuffer, TempString, State->Interface.Font, HeadMin + v2{4, 4}, WhiteV4);
if (MouseDownAndNotHandled && PointIsInRange(Mouse.Pos, TimelineMin, TimelineMax))
{
DeselectCurrentAnimationBlock(State);
}
return Result;
}
PANEL_RENDER_PROC(AnimationTimeline_Render)
{
animation_block_handle SelectedBlockHandle = State->SelectedAnimationBlockHandle;
r32 OptionsRowHeight = 25;
2019-12-28 19:31:21 +00:00
rect TimelineBounds = rect{
PanelBounds.Min,
v2{PanelBounds.Max.x, PanelBounds.Max.y - OptionsRowHeight}
};
if (Height(TimelineBounds) > 0)
{
s32 FrameStart = (s32)(State->AnimationSystem.AnimationStart / State->AnimationSystem.SecondsPerFrame);
s32 FrameEnd = (s32)(State->AnimationSystem.AnimationEnd / State->AnimationSystem.SecondsPerFrame);
SelectedBlockHandle = DrawAnimationTimeline(&State->AnimationSystem,
FrameStart - 20, FrameEnd + 20,
2019-12-28 19:31:21 +00:00
TimelineBounds,
SelectedBlockHandle,
RenderBuffer, State, Mouse);
}
2019-12-28 19:31:21 +00:00
v2 OptionsRowMin = v2{ PanelBounds.Min.x, TimelineBounds.Max.y };
v2 OptionsRowMax = PanelBounds.Max;
panel_result AnimationPanel = EvaluatePanel(RenderBuffer, OptionsRowMin, OptionsRowMax,
0, State->Interface);
r32 ButtonWidth = 35;
v2 ButtonMin = v2{0, 0};
v2 ButtonMax = v2{35, OptionsRowHeight - 2};
v2 ButtonAt = v2{OptionsRowMin.x + 1, OptionsRowMin.y + 1};
button_result PauseResult = EvaluateButton(RenderBuffer,
ButtonAt + ButtonMin, ButtonAt + ButtonMax,
MakeStringLiteral("Pause"),
State->Interface, Mouse);
ButtonAt.x += ButtonWidth + 2;
button_result PlayResult = EvaluateButton(RenderBuffer,
ButtonAt + ButtonMin, ButtonAt + ButtonMax,
MakeStringLiteral("Play"),
State->Interface, Mouse);
ButtonAt.x += ButtonWidth + 2;
button_result StopResult = EvaluateButton(RenderBuffer,
ButtonAt + ButtonMin, ButtonAt + ButtonMax,
MakeStringLiteral("Stop"),
State->Interface, Mouse);
if (PauseResult.Pressed)
{
State->AnimationSystem.TimelineShouldAdvance = false;
}
if (PlayResult.Pressed)
{
State->AnimationSystem.TimelineShouldAdvance = true;
}
if (StopResult.Pressed)
{
State->AnimationSystem.TimelineShouldAdvance = false;
State->AnimationSystem.Time = 0;
}
}