2020-01-02 02:41:43 +00:00
|
|
|
//
|
|
|
|
// File: foldhaus_panel_animation_timeline.h
|
|
|
|
// Author: Peter Slattery
|
|
|
|
// Creation Date: 2020-01-01
|
|
|
|
//
|
|
|
|
#ifndef FOLDHAUS_PANEL_ANIMATION_TIMELINE_H
|
|
|
|
|
2020-03-01 23:24:12 +00:00
|
|
|
// Colors
|
|
|
|
global_variable v4 TimeSliderColor = v4{.36f, .52f, .78f, 1.f};
|
|
|
|
|
|
|
|
//
|
|
|
|
struct animation_timeline_state
|
|
|
|
{
|
2020-03-02 01:19:30 +00:00
|
|
|
frame_range VisibleRange;
|
2020-03-01 23:24:12 +00:00
|
|
|
};
|
|
|
|
|
2020-03-01 01:11:15 +00:00
|
|
|
inline u32
|
2020-03-02 01:19:30 +00:00
|
|
|
GetFrameFromPointInAnimationPanel(v2 Point, rect PanelBounds, frame_range VisibleRange)
|
2019-12-28 19:31:21 +00:00
|
|
|
{
|
2020-03-01 01:11:15 +00:00
|
|
|
r32 HorizontalPercentOfBounds = (Point.x - PanelBounds.Min.x) / (PanelBounds.Max.x - PanelBounds.Min.x);
|
2020-03-01 23:44:58 +00:00
|
|
|
u32 VisibleFramesCount = GetFrameCount(VisibleRange);
|
2020-03-02 01:19:30 +00:00
|
|
|
u32 TimeAtPoint = (u32)(HorizontalPercentOfBounds * VisibleFramesCount) + VisibleRange.Min;
|
2019-12-28 19:31:21 +00:00
|
|
|
return TimeAtPoint;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline s32
|
2020-03-02 01:19:30 +00:00
|
|
|
GetXPositionFromFrameInAnimationPanel (u32 Frame, rect PanelBounds, frame_range VisibleRange)
|
2019-12-28 19:31:21 +00:00
|
|
|
{
|
2020-03-02 01:19:30 +00:00
|
|
|
r32 PercentOfTimeline = (r32)(Frame - VisibleRange.Min) / (r32)GetFrameCount(VisibleRange);
|
2020-03-01 01:11:15 +00:00
|
|
|
s32 XPositionAtFrame = (PercentOfTimeline * Width(PanelBounds)) + PanelBounds.Min.x;
|
|
|
|
return XPositionAtFrame;
|
2019-12-28 19:31:21 +00:00
|
|
|
}
|
|
|
|
|
2020-03-01 01:11:15 +00:00
|
|
|
internal gs_list_handle
|
2020-03-08 00:06:10 +00:00
|
|
|
AddAnimationBlockAtCurrentTime (u32 AnimationProcHandle, u32 Layer, animation_system* System)
|
2019-12-29 00:01:34 +00:00
|
|
|
{
|
2020-03-01 01:11:15 +00:00
|
|
|
u32 NewBlockStart = System->CurrentFrame;
|
|
|
|
u32 NewBlockEnd = NewBlockStart + SecondsToFrames(3, *System);
|
2020-03-08 00:06:10 +00:00
|
|
|
gs_list_handle Result = AddAnimationBlock(NewBlockStart, NewBlockEnd, AnimationProcHandle, Layer, System);
|
2020-03-01 01:11:15 +00:00
|
|
|
return Result;
|
2019-12-29 00:01:34 +00:00
|
|
|
}
|
|
|
|
|
2019-12-28 18:51:47 +00:00
|
|
|
internal void
|
2019-12-31 04:26:28 +00:00
|
|
|
SelectAnimationBlock(gs_list_handle BlockHandle, app_state* State)
|
2019-12-28 18:51:47 +00:00
|
|
|
{
|
|
|
|
State->SelectedAnimationBlockHandle = BlockHandle;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void
|
|
|
|
DeselectCurrentAnimationBlock(app_state* State)
|
|
|
|
{
|
|
|
|
State->SelectedAnimationBlockHandle = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
FOLDHAUS_INPUT_COMMAND_PROC(DeleteAnimationBlockCommand)
|
|
|
|
{
|
2020-03-01 01:11:15 +00:00
|
|
|
if(ListHandleIsValid(State->SelectedAnimationBlockHandle))
|
|
|
|
{
|
2020-03-08 00:06:10 +00:00
|
|
|
RemoveAnimationBlock(State->SelectedAnimationBlockHandle, &State->AnimationSystem);
|
|
|
|
State->SelectedAnimationBlockHandle = {0};
|
2020-03-01 01:11:15 +00:00
|
|
|
}
|
2019-12-28 18:51:47 +00:00
|
|
|
}
|
|
|
|
|
2019-12-28 22:14:00 +00:00
|
|
|
//
|
|
|
|
// Drag Time Marker
|
|
|
|
//
|
|
|
|
|
|
|
|
OPERATION_STATE_DEF(drag_time_marker_operation_state)
|
|
|
|
{
|
|
|
|
rect TimelineBounds;
|
|
|
|
s32 StartFrame;
|
|
|
|
s32 EndFrame;
|
|
|
|
};
|
|
|
|
|
|
|
|
OPERATION_RENDER_PROC(UpdateDragTimeMarker)
|
|
|
|
{
|
|
|
|
drag_time_marker_operation_state* OpState = (drag_time_marker_operation_state*)Operation.OpStateMemory;
|
2020-03-08 00:06:10 +00:00
|
|
|
frame_range Range = { OpState->StartFrame, OpState->EndFrame };
|
2020-03-01 23:44:58 +00:00
|
|
|
u32 FrameAtMouseX = GetFrameFromPointInAnimationPanel(Mouse.Pos, OpState->TimelineBounds, Range);
|
2020-03-01 01:11:15 +00:00
|
|
|
State->AnimationSystem.CurrentFrame = FrameAtMouseX;
|
2019-12-28 22:14:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FOLDHAUS_INPUT_COMMAND_PROC(EndDragTimeMarker)
|
|
|
|
{
|
|
|
|
DeactivateCurrentOperationMode(&State->Modes);
|
|
|
|
}
|
|
|
|
|
|
|
|
input_command DragTimeMarkerCommands [] = {
|
|
|
|
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndDragTimeMarker },
|
|
|
|
};
|
|
|
|
|
|
|
|
internal void
|
2020-03-02 01:19:30 +00:00
|
|
|
StartDragTimeMarker(rect TimelineBounds, frame_range VisibleFrames, app_state* State)
|
2019-12-28 22:14:00 +00:00
|
|
|
{
|
|
|
|
operation_mode* DragTimeMarkerMode = ActivateOperationModeWithCommands(&State->Modes, DragTimeMarkerCommands, UpdateDragTimeMarker);
|
|
|
|
|
|
|
|
drag_time_marker_operation_state* OpState = CreateOperationState(DragTimeMarkerMode,
|
|
|
|
&State->Modes,
|
|
|
|
drag_time_marker_operation_state);
|
2020-03-02 01:19:30 +00:00
|
|
|
OpState->StartFrame = VisibleFrames.Min;
|
|
|
|
OpState->EndFrame = VisibleFrames.Max;
|
2019-12-28 22:14:00 +00:00
|
|
|
OpState->TimelineBounds = TimelineBounds;
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------
|
|
|
|
|
2019-12-28 19:31:21 +00:00
|
|
|
//
|
|
|
|
// Drag Animation Clip
|
|
|
|
//
|
|
|
|
|
2019-12-28 19:41:00 +00:00
|
|
|
#define CLICK_ANIMATION_BLOCK_EDGE_MAX_SCREEN_DISTANCE 10
|
|
|
|
|
2019-12-28 19:31:21 +00:00
|
|
|
OPERATION_STATE_DEF(drag_animation_clip_state)
|
|
|
|
{
|
2019-12-29 00:01:34 +00:00
|
|
|
rect TimelineBounds;
|
2020-03-02 01:19:30 +00:00
|
|
|
frame_range VisibleRange;
|
|
|
|
frame_range ClipRange;
|
2019-12-28 19:31:21 +00:00
|
|
|
};
|
|
|
|
|
2020-03-01 01:11:15 +00:00
|
|
|
internal u32
|
|
|
|
AttemptToSnapPosition(u32 SnappingFrame, u32 SnapToFrame)
|
2020-02-29 23:43:06 +00:00
|
|
|
{
|
2020-03-01 01:11:15 +00:00
|
|
|
u32 Result = SnappingFrame;
|
|
|
|
s32 SnapDistance = 5;
|
|
|
|
if (GSAbs((s32)SnappingFrame - (s32)SnapToFrame) <= SnapDistance)
|
2020-02-29 23:43:06 +00:00
|
|
|
{
|
2020-03-01 01:11:15 +00:00
|
|
|
Result = SnapToFrame;
|
2020-02-29 23:43:06 +00:00
|
|
|
}
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
2019-12-28 19:31:21 +00:00
|
|
|
OPERATION_RENDER_PROC(UpdateDragAnimationClip)
|
|
|
|
{
|
|
|
|
drag_animation_clip_state* OpState = (drag_animation_clip_state*)Operation.OpStateMemory;
|
|
|
|
|
2020-03-02 01:19:30 +00:00
|
|
|
r32 ClipInitialStartFrameXPercent = FrameToPercentRange(OpState->ClipRange.Min, OpState->VisibleRange);
|
2020-03-01 23:44:58 +00:00
|
|
|
u32 ClipInitialStartFrameXPosition = GSLerp(OpState->TimelineBounds.Min.x,
|
|
|
|
OpState->TimelineBounds.Max.x,
|
|
|
|
ClipInitialStartFrameXPercent);
|
2020-03-02 01:19:30 +00:00
|
|
|
r32 ClipInitialEndFrameXPercent = FrameToPercentRange(OpState->ClipRange.Max, OpState->VisibleRange);
|
2020-03-01 23:44:58 +00:00
|
|
|
u32 ClipInitialEndFrameXPosition = GSLerp(OpState->TimelineBounds.Min.x,
|
|
|
|
OpState->TimelineBounds.Max.x,
|
|
|
|
ClipInitialEndFrameXPercent);
|
2020-03-01 01:11:15 +00:00
|
|
|
|
2020-03-01 23:44:58 +00:00
|
|
|
u32 FrameAtMouseDownX = GetFrameFromPointInAnimationPanel(Mouse.DownPos, OpState->TimelineBounds, OpState->VisibleRange);
|
2019-12-28 19:31:21 +00:00
|
|
|
|
2020-03-01 23:44:58 +00:00
|
|
|
u32 FrameAtMouseX = GetFrameFromPointInAnimationPanel(Mouse.Pos, OpState->TimelineBounds, OpState->VisibleRange);
|
2020-03-01 01:11:15 +00:00
|
|
|
s32 FrameOffset = (s32)FrameAtMouseX - (s32)FrameAtMouseDownX;
|
2019-12-28 19:31:21 +00:00
|
|
|
|
2019-12-31 04:26:28 +00:00
|
|
|
animation_block* AnimationBlock = State->AnimationSystem.Blocks.GetElementWithHandle(State->SelectedAnimationBlockHandle);
|
2020-03-01 00:02:30 +00:00
|
|
|
if (!AnimationBlock)
|
|
|
|
{
|
|
|
|
EndCurrentOperationMode(State, {}, Mouse);
|
|
|
|
return;
|
|
|
|
}
|
2019-12-28 19:41:00 +00:00
|
|
|
|
2020-03-01 01:11:15 +00:00
|
|
|
if (GSAbs(Mouse.DownPos.x - ClipInitialStartFrameXPosition) < CLICK_ANIMATION_BLOCK_EDGE_MAX_SCREEN_DISTANCE)
|
2019-12-28 19:41:00 +00:00
|
|
|
{
|
2020-03-08 00:06:10 +00:00
|
|
|
s32 NewStartFrame = OpState->ClipRange.Min + FrameOffset;
|
2020-03-01 01:11:15 +00:00
|
|
|
if (FrameOffset < 0)
|
2020-02-29 23:43:06 +00:00
|
|
|
{
|
2020-03-01 00:02:30 +00:00
|
|
|
for (u32 i = 0; i < State->AnimationSystem.Blocks.Used; i++)
|
|
|
|
{
|
|
|
|
gs_list_entry<animation_block>* OtherBlockEntry = State->AnimationSystem.Blocks.GetEntryAtIndex(i);
|
2020-03-08 00:06:10 +00:00
|
|
|
if (EntryIsFree(OtherBlockEntry)) { continue; }
|
2020-03-01 00:02:30 +00:00
|
|
|
animation_block OtherBlock = OtherBlockEntry->Value;
|
2020-03-02 01:19:30 +00:00
|
|
|
NewStartFrame = AttemptToSnapPosition(NewStartFrame, OtherBlock.Range.Max);
|
2020-03-01 00:02:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-03-02 01:19:30 +00:00
|
|
|
if (NewStartFrame >= AnimationBlock->Range.Max)
|
2020-03-01 00:02:30 +00:00
|
|
|
{
|
2020-03-02 01:19:30 +00:00
|
|
|
NewStartFrame = AnimationBlock->Range.Max - 1;
|
2020-03-01 00:02:30 +00:00
|
|
|
}
|
2020-02-29 23:43:06 +00:00
|
|
|
}
|
2020-03-02 01:19:30 +00:00
|
|
|
AnimationBlock->Range.Min = NewStartFrame;
|
2019-12-28 19:41:00 +00:00
|
|
|
}
|
2020-03-01 01:11:15 +00:00
|
|
|
else if (GSAbs(Mouse.DownPos.x - ClipInitialEndFrameXPosition) < CLICK_ANIMATION_BLOCK_EDGE_MAX_SCREEN_DISTANCE)
|
2019-12-28 19:41:00 +00:00
|
|
|
{
|
2020-03-02 01:19:30 +00:00
|
|
|
r32 NewEndFrame = OpState->ClipRange.Max + FrameOffset;
|
2020-03-01 01:11:15 +00:00
|
|
|
if (FrameOffset > 0)
|
2020-02-29 23:43:06 +00:00
|
|
|
{
|
2020-03-01 00:02:30 +00:00
|
|
|
for (u32 i = 0; i < State->AnimationSystem.Blocks.Used; i++)
|
|
|
|
{
|
|
|
|
gs_list_entry<animation_block>* OtherBlockEntry = State->AnimationSystem.Blocks.GetEntryAtIndex(i);
|
2020-03-08 00:06:10 +00:00
|
|
|
if (EntryIsFree(OtherBlockEntry)) { continue; }
|
2020-03-01 00:02:30 +00:00
|
|
|
animation_block OtherBlock = OtherBlockEntry->Value;
|
2020-03-02 01:19:30 +00:00
|
|
|
NewEndFrame = AttemptToSnapPosition(NewEndFrame, OtherBlock.Range.Min);
|
2020-03-01 00:02:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-03-02 01:19:30 +00:00
|
|
|
if(NewEndFrame <= AnimationBlock->Range.Min)
|
2020-03-01 00:02:30 +00:00
|
|
|
{
|
2020-03-02 01:19:30 +00:00
|
|
|
NewEndFrame = AnimationBlock->Range.Min + 1;
|
2020-03-01 00:02:30 +00:00
|
|
|
}
|
2020-02-29 23:43:06 +00:00
|
|
|
}
|
2020-03-02 01:19:30 +00:00
|
|
|
AnimationBlock->Range.Max = NewEndFrame;
|
2019-12-28 19:41:00 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-03-02 01:19:30 +00:00
|
|
|
u32 NewStartFrame = OpState->ClipRange.Min + FrameOffset;
|
|
|
|
u32 NewEndFrame = OpState->ClipRange.Max + FrameOffset;
|
2020-02-29 23:43:06 +00:00
|
|
|
for (u32 i = 0; i < State->AnimationSystem.Blocks.Used; i++)
|
|
|
|
{
|
|
|
|
gs_list_entry<animation_block>* OtherBlockEntry = State->AnimationSystem.Blocks.GetEntryAtIndex(i);
|
2020-03-08 00:06:10 +00:00
|
|
|
if (EntryIsFree(OtherBlockEntry)) { continue; }
|
2020-02-29 23:43:06 +00:00
|
|
|
animation_block OtherBlock = OtherBlockEntry->Value;
|
|
|
|
|
2020-03-01 01:11:15 +00:00
|
|
|
u32 SnapFramesAmount = 0;
|
|
|
|
if (FrameOffset > 0)
|
2020-02-29 23:43:06 +00:00
|
|
|
{
|
2020-03-02 01:19:30 +00:00
|
|
|
u32 FinalEndFrame = AttemptToSnapPosition(NewEndFrame, OtherBlock.Range.Min);
|
2020-03-01 01:11:15 +00:00
|
|
|
SnapFramesAmount = FinalEndFrame - NewEndFrame;
|
2020-02-29 23:43:06 +00:00
|
|
|
}
|
2020-03-01 01:11:15 +00:00
|
|
|
else if (FrameOffset < 0)
|
2020-02-29 23:43:06 +00:00
|
|
|
{
|
2020-03-02 01:19:30 +00:00
|
|
|
u32 FinalStartFrame = AttemptToSnapPosition(NewStartFrame, OtherBlock.Range.Max);
|
2020-03-01 01:11:15 +00:00
|
|
|
SnapFramesAmount = FinalStartFrame - NewStartFrame;
|
2020-02-29 23:43:06 +00:00
|
|
|
}
|
2020-03-01 01:11:15 +00:00
|
|
|
NewEndFrame += SnapFramesAmount;
|
|
|
|
NewStartFrame += SnapFramesAmount;
|
2020-02-29 23:43:06 +00:00
|
|
|
}
|
2020-03-02 01:19:30 +00:00
|
|
|
AnimationBlock->Range.Min = NewStartFrame;
|
|
|
|
AnimationBlock->Range.Max = NewEndFrame;
|
2019-12-28 19:41:00 +00:00
|
|
|
}
|
2020-03-01 00:02:30 +00:00
|
|
|
|
2020-03-02 01:19:30 +00:00
|
|
|
s32 PlayableStartFrame = State->AnimationSystem.PlayableRange.Min;
|
|
|
|
s32 PlayableEndFrame = State->AnimationSystem.PlayableRange.Max;
|
|
|
|
AnimationBlock->Range.Min = (u32)GSClamp(PlayableStartFrame, (s32)AnimationBlock->Range.Min, PlayableEndFrame);
|
|
|
|
AnimationBlock->Range.Max = (u32)GSClamp(PlayableStartFrame, (s32)AnimationBlock->Range.Max, PlayableEndFrame);
|
2019-12-28 19:31:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
input_command DragAnimationClipCommands [] = {
|
2019-12-31 04:26:28 +00:00
|
|
|
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndCurrentOperationMode },
|
2019-12-28 19:31:21 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
internal void
|
2020-03-02 01:19:30 +00:00
|
|
|
SelectAndBeginDragAnimationBlock(gs_list_handle BlockHandle, frame_range VisibleRange, rect TimelineBounds, app_state* State)
|
2019-12-28 19:31:21 +00:00
|
|
|
{
|
|
|
|
SelectAnimationBlock(BlockHandle, State);
|
|
|
|
|
|
|
|
operation_mode* DragAnimationClipMode = ActivateOperationModeWithCommands(&State->Modes, DragAnimationClipCommands, UpdateDragAnimationClip);
|
|
|
|
|
|
|
|
drag_animation_clip_state* OpState = CreateOperationState(DragAnimationClipMode,
|
|
|
|
&State->Modes,
|
|
|
|
drag_animation_clip_state);
|
2019-12-29 00:01:34 +00:00
|
|
|
OpState->TimelineBounds = TimelineBounds;
|
2020-03-01 23:44:58 +00:00
|
|
|
OpState->VisibleRange = VisibleRange;
|
2019-12-28 19:31:21 +00:00
|
|
|
|
2019-12-31 04:26:28 +00:00
|
|
|
animation_block* SelectedBlock = State->AnimationSystem.Blocks.GetElementWithHandle(BlockHandle);
|
2020-03-02 01:19:30 +00:00
|
|
|
OpState->ClipRange = SelectedBlock->Range;
|
2019-12-28 19:31:21 +00:00
|
|
|
}
|
|
|
|
// -------------------
|
|
|
|
|
2019-12-28 18:51:47 +00:00
|
|
|
FOLDHAUS_INPUT_COMMAND_PROC(AddAnimationBlockCommand)
|
|
|
|
{
|
2019-12-28 21:02:19 +00:00
|
|
|
panel_and_bounds ActivePanel = GetPanelContainingPoint(Mouse.Pos, &State->PanelSystem, State->WindowBounds);
|
2020-03-02 01:19:30 +00:00
|
|
|
frame_range Range = State->AnimationSystem.PlayableRange;
|
2020-03-01 23:44:58 +00:00
|
|
|
u32 MouseDownFrame = GetFrameFromPointInAnimationPanel(Mouse.Pos, ActivePanel.Bounds, Range);
|
2020-03-08 00:06:10 +00:00
|
|
|
gs_list_handle NewBlockHandle = AddAnimationBlock(MouseDownFrame, MouseDownFrame + SecondsToFrames(3, State->AnimationSystem), 4, State->SelectedAnimationLayer, &State->AnimationSystem);
|
2019-12-28 18:51:47 +00:00
|
|
|
SelectAnimationBlock(NewBlockHandle, State);
|
|
|
|
}
|
|
|
|
|
|
|
|
input_command AnimationTimeline_Commands[] = {
|
|
|
|
{ KeyCode_X, KeyCode_Invalid, Command_Began, DeleteAnimationBlockCommand },
|
|
|
|
{ KeyCode_A, KeyCode_Invalid, Command_Began, AddAnimationBlockCommand },
|
|
|
|
};
|
2020-02-29 22:23:46 +00:00
|
|
|
s32 AnimationTimeline_CommandsCount = 2;
|
2020-02-02 03:15:04 +00:00
|
|
|
|
|
|
|
GSMetaTag(panel_init);
|
2020-02-29 22:23:46 +00:00
|
|
|
GSMetaTag(panel_type_animation_timeline);
|
2020-02-02 03:15:04 +00:00
|
|
|
internal void
|
|
|
|
AnimationTimeline_Init(panel* Panel, app_state* State)
|
2019-12-26 20:42:55 +00:00
|
|
|
{
|
2020-03-01 23:24:12 +00:00
|
|
|
// TODO: :FreePanelMemory
|
|
|
|
animation_timeline_state* TimelineState = PushStruct(&State->Permanent, animation_timeline_state);
|
2020-03-02 01:19:30 +00:00
|
|
|
TimelineState->VisibleRange = State->AnimationSystem.PlayableRange;
|
2020-03-01 23:24:12 +00:00
|
|
|
Panel->PanelStateMemory = (u8*)TimelineState;
|
2019-12-26 20:42:55 +00:00
|
|
|
}
|
|
|
|
|
2020-02-02 03:15:04 +00:00
|
|
|
GSMetaTag(panel_cleanup);
|
2020-02-29 22:23:46 +00:00
|
|
|
GSMetaTag(panel_type_animation_timeline);
|
2020-02-02 03:15:04 +00:00
|
|
|
internal void
|
|
|
|
AnimationTimeline_Cleanup(panel* Panel, app_state* State)
|
2019-12-26 20:42:55 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-03-01 23:24:12 +00:00
|
|
|
internal void
|
2020-03-02 01:19:30 +00:00
|
|
|
DrawFrameBar (animation_system* AnimationSystem, render_command_buffer* RenderBuffer, frame_range VisibleFrames, rect BarBounds, mouse_state Mouse, app_state* State)
|
2019-12-26 20:42:55 +00:00
|
|
|
{
|
2019-12-27 02:40:14 +00:00
|
|
|
MakeStringBuffer(TempString, 256);
|
2019-12-26 20:42:55 +00:00
|
|
|
|
2020-03-02 01:19:30 +00:00
|
|
|
s32 VisibleFrameCount = VisibleFrames.Max - VisibleFrames.Min;
|
2019-12-26 20:42:55 +00:00
|
|
|
|
2020-03-01 23:24:12 +00:00
|
|
|
r32 BarHeight = Height(BarBounds);
|
|
|
|
r32 BarWidth = Width(BarBounds);
|
2019-12-27 02:40:14 +00:00
|
|
|
|
2020-03-01 23:24:12 +00:00
|
|
|
PushRenderQuad2D(RenderBuffer, RectExpand(BarBounds), v4{.16f, .16f, .16f, 1.f});
|
2019-12-26 20:42:55 +00:00
|
|
|
|
2019-12-27 02:40:14 +00:00
|
|
|
// Mouse clicked inside frame nubmer bar -> change current frame on timeline
|
2020-03-01 23:24:12 +00:00
|
|
|
if (MouseButtonTransitionedDown(Mouse.LeftButtonState) &&
|
|
|
|
PointIsInRange(Mouse.DownPos, RectExpand(BarBounds)))
|
2019-12-27 00:23:43 +00:00
|
|
|
{
|
2020-03-01 23:24:12 +00:00
|
|
|
StartDragTimeMarker(BarBounds, VisibleFrames, State);
|
2019-12-27 00:23:43 +00:00
|
|
|
}
|
|
|
|
|
2019-12-27 02:40:14 +00:00
|
|
|
// Frame Ticks
|
2020-03-01 23:24:12 +00:00
|
|
|
u32 TickCount = 10;
|
|
|
|
for (u32 Tick = 0; Tick < TickCount; Tick++)
|
2019-12-27 00:23:43 +00:00
|
|
|
{
|
2020-03-01 23:24:12 +00:00
|
|
|
r32 Percent = (r32)Tick / (r32)TickCount;
|
|
|
|
u32 Frame = PercentToFrameInRange(Percent, VisibleFrames);
|
2019-12-27 00:23:43 +00:00
|
|
|
PrintF(&TempString, "%d", Frame);
|
2020-03-01 23:24:12 +00:00
|
|
|
r32 FramePercent = FrameToPercentRange(Frame, VisibleFrames);
|
|
|
|
r32 FrameX = GSLerp(BarBounds.Min.x, BarBounds.Max.x, FramePercent);
|
|
|
|
v2 FrameTextPos = v2{FrameX, BarBounds.Min.y + 2};
|
2019-12-28 22:14:00 +00:00
|
|
|
DrawString(RenderBuffer, TempString, State->Interface.Font, FrameTextPos, WhiteV4);
|
2019-12-27 00:23:43 +00:00
|
|
|
}
|
|
|
|
|
2020-03-01 23:24:12 +00:00
|
|
|
// Time Slider
|
|
|
|
if (FrameIsInRange(AnimationSystem->CurrentFrame, VisibleFrames))
|
|
|
|
{
|
|
|
|
r32 FrameAtPercentVisibleRange = FrameToPercentRange(AnimationSystem->CurrentFrame, VisibleFrames);
|
|
|
|
r32 SliderX = GSLerp(BarBounds.Min.x, BarBounds.Max.x, FrameAtPercentVisibleRange);
|
|
|
|
|
|
|
|
u32 FrameNumberCharCount = GetU32NumberOfCharactersNeeded(AnimationSystem->CurrentFrame);
|
|
|
|
// space for each character + a margin on either side
|
|
|
|
r32 SliderWidth = (8 * FrameNumberCharCount) + 8;
|
|
|
|
r32 SliderHalfWidth = SliderWidth / 2.f;
|
|
|
|
v2 HeadMin = v2{SliderX - SliderHalfWidth, BarBounds.Min.y};
|
|
|
|
v2 HeadMax = v2{SliderX + SliderHalfWidth, BarBounds.Max.y};
|
|
|
|
PushRenderQuad2D(RenderBuffer, HeadMin, HeadMax, TimeSliderColor);
|
|
|
|
|
|
|
|
PrintF(&TempString, "%d", AnimationSystem->CurrentFrame);
|
|
|
|
DrawString(RenderBuffer, TempString, State->Interface.Font, HeadMin + v2{6, 4}, WhiteV4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-02 01:19:30 +00:00
|
|
|
internal frame_range
|
2020-03-01 23:24:12 +00:00
|
|
|
DrawTimelineRangeBar (animation_system* AnimationSystem, animation_timeline_state* TimelineState, render_command_buffer* RenderBuffer, rect BarBounds, mouse_state Mouse)
|
|
|
|
{
|
2020-03-02 01:19:30 +00:00
|
|
|
frame_range Result = {0};
|
2020-03-01 23:24:12 +00:00
|
|
|
|
|
|
|
r32 BarHeight = Height(BarBounds);
|
|
|
|
r32 BarWidth = Width(BarBounds);
|
|
|
|
PushRenderQuad2D(RenderBuffer, RectExpand(BarBounds), v4{.16f, .16f, .16f, 1.f});
|
|
|
|
|
2020-03-02 01:19:30 +00:00
|
|
|
r32 PlayableFrames = (r32)GetFrameCount(AnimationSystem->PlayableRange);
|
2020-03-01 23:24:12 +00:00
|
|
|
v2 SliderBarDim = v2{25, BarHeight};
|
|
|
|
|
|
|
|
// Convert Frames To Pixels
|
2020-03-08 00:06:10 +00:00
|
|
|
r32 VisibleMinPercentPlayable = FrameToPercentRange(TimelineState->VisibleRange.Min, AnimationSystem->PlayableRange);
|
|
|
|
r32 VisibleMaxPercentPlayable = FrameToPercentRange(TimelineState->VisibleRange.Max, AnimationSystem->PlayableRange);
|
|
|
|
v2 RangeMinSliderMin = v2{BarBounds.Min.x + (VisibleMinPercentPlayable * Width(BarBounds)), BarBounds.Min.y};
|
|
|
|
v2 RangeMaxSliderMin = v2{BarBounds.Min.x + (VisibleMaxPercentPlayable * Width(BarBounds)) - 25, BarBounds.Min.y};
|
2020-03-01 23:24:12 +00:00
|
|
|
|
|
|
|
if (MouseButtonHeldDown(Mouse.LeftButtonState) ||
|
|
|
|
MouseButtonTransitionedUp(Mouse.LeftButtonState))
|
|
|
|
{
|
|
|
|
v2 MouseDragOffset = Mouse.Pos - Mouse.DownPos;
|
|
|
|
if (PointIsInRange(Mouse.DownPos, RangeMinSliderMin, RangeMinSliderMin + SliderBarDim))
|
|
|
|
{
|
|
|
|
r32 NewSliderX = RangeMinSliderMin.x + MouseDragOffset.x;
|
|
|
|
RangeMinSliderMin.x = GSClamp(BarBounds.Min.x, NewSliderX, RangeMaxSliderMin.x - SliderBarDim.x);
|
|
|
|
}
|
|
|
|
if (PointIsInRange(Mouse.DownPos, RangeMaxSliderMin, RangeMaxSliderMin + SliderBarDim))
|
|
|
|
{
|
|
|
|
r32 NewSliderX = RangeMaxSliderMin.x + MouseDragOffset.x;
|
|
|
|
RangeMaxSliderMin.x = GSClamp(RangeMinSliderMin.x + SliderBarDim.x, NewSliderX, BarBounds.Max.x - SliderBarDim.x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
v2 RangeMinSliderMax = v2{RangeMinSliderMin.x + 25, BarBounds.Max.y};
|
|
|
|
v2 RangeMaxSliderMax = v2{RangeMaxSliderMin.x + 25, BarBounds.Max.y};
|
|
|
|
PushRenderQuad2D(RenderBuffer, RangeMinSliderMin, RangeMinSliderMax, v4{.8f, .8f, .8f, 1.f});
|
|
|
|
PushRenderQuad2D(RenderBuffer, RangeMaxSliderMin, RangeMaxSliderMax, v4{.8f, .8f, .8f, 1.f});
|
|
|
|
|
|
|
|
// Convert Pixels Back To Frames and store
|
2020-03-08 00:06:10 +00:00
|
|
|
VisibleMinPercentPlayable = (RangeMinSliderMin.x - BarBounds.Min.x) / BarWidth;
|
|
|
|
VisibleMaxPercentPlayable = (RangeMaxSliderMax.x - BarBounds.Min.x) / BarWidth;
|
2020-03-02 01:19:30 +00:00
|
|
|
u32 VisibleFrameCount = GetFrameCount(AnimationSystem->PlayableRange);
|
2020-03-08 00:06:10 +00:00
|
|
|
Result.Min = VisibleMinPercentPlayable * VisibleFrameCount;
|
|
|
|
Result.Max = VisibleMaxPercentPlayable * VisibleFrameCount;
|
2020-03-01 23:24:12 +00:00
|
|
|
|
2020-03-08 00:06:10 +00:00
|
|
|
if (MouseButtonTransitionedUp(Mouse.LeftButtonState))
|
2020-03-01 23:24:12 +00:00
|
|
|
{
|
2020-03-02 01:19:30 +00:00
|
|
|
TimelineState->VisibleRange = Result;
|
2020-03-01 23:24:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return Result;
|
2019-12-27 02:40:14 +00:00
|
|
|
}
|
|
|
|
|
2020-03-08 00:06:10 +00:00
|
|
|
#define LAYER_HEIGHT 52
|
|
|
|
|
|
|
|
internal void
|
|
|
|
DrawLayerMenu(animation_system* AnimationSystem, rect PanelDim, render_command_buffer* RenderBuffer, app_state* State, mouse_state Mouse)
|
|
|
|
{
|
|
|
|
v2 LayerDim = { Width(PanelDim), LAYER_HEIGHT };
|
|
|
|
v2 LayerListMin = PanelDim.Min + v2{0, 24};
|
|
|
|
for (u32 LayerIndex = 0; LayerIndex < AnimationSystem->LayersCount; LayerIndex++)
|
|
|
|
{
|
|
|
|
anim_layer* Layer = AnimationSystem->Layers + LayerIndex;
|
|
|
|
rect LayerBounds = {0};
|
|
|
|
LayerBounds.Min = { LayerListMin.x, LayerListMin.y + (LayerDim.y * LayerIndex) };
|
|
|
|
LayerBounds.Max = LayerBounds.Min + LayerDim;
|
|
|
|
|
|
|
|
if (MouseButtonTransitionedDown(Mouse.LeftButtonState) &&
|
|
|
|
PointIsInRect(Mouse.Pos, LayerBounds))
|
|
|
|
{
|
|
|
|
State->SelectedAnimationLayer = LayerIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
v2 LayerTextPos = { LayerBounds.Min.x + 6, LayerBounds.Max.y - 16};
|
|
|
|
if (State->SelectedAnimationLayer == LayerIndex)
|
|
|
|
{
|
|
|
|
PushRenderBoundingBox2D(RenderBuffer, RectExpand(LayerBounds), 1, WhiteV4);
|
|
|
|
}
|
|
|
|
DrawString(RenderBuffer, Layer->Name, State->Interface.Font, LayerTextPos, WhiteV4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-27 02:40:14 +00:00
|
|
|
internal rect
|
2020-03-02 01:19:30 +00:00
|
|
|
DrawAnimationBlock (animation_block AnimationBlock, v4 BlockColor, frame_range VisibleFrames, rect TimelineBounds, render_command_buffer* RenderBuffer)
|
2019-12-27 02:40:14 +00:00
|
|
|
{
|
|
|
|
rect BlockBounds = {};
|
|
|
|
|
2019-12-29 00:01:34 +00:00
|
|
|
r32 TimelineWidth = Width(TimelineBounds);
|
2019-12-27 02:40:14 +00:00
|
|
|
|
2020-03-02 01:19:30 +00:00
|
|
|
u32 ClampedBlockStartFrame = ClampFrameToRange(AnimationBlock.Range.Min, VisibleFrames);
|
2020-03-01 23:24:12 +00:00
|
|
|
r32 StartFramePercent = FrameToPercentRange(ClampedBlockStartFrame, VisibleFrames);
|
2019-12-27 02:40:14 +00:00
|
|
|
r32 StartPosition = TimelineWidth * StartFramePercent;
|
|
|
|
|
2020-03-02 01:19:30 +00:00
|
|
|
u32 ClampedBlockEndFrame = ClampFrameToRange(AnimationBlock.Range.Max, VisibleFrames);
|
2020-03-01 23:24:12 +00:00
|
|
|
r32 EndFramePercent = FrameToPercentRange(ClampedBlockEndFrame, VisibleFrames);
|
2019-12-27 02:40:14 +00:00
|
|
|
r32 EndPosition = TimelineWidth * EndFramePercent;
|
|
|
|
|
2020-03-08 00:06:10 +00:00
|
|
|
r32 LayerYOffset = LAYER_HEIGHT * AnimationBlock.Layer;
|
|
|
|
BlockBounds.Min = TimelineBounds.Min + v2{StartPosition, LayerYOffset};
|
|
|
|
BlockBounds.Max = TimelineBounds.Min + v2{EndPosition, LayerYOffset + LAYER_HEIGHT};
|
2019-12-27 02:40:14 +00:00
|
|
|
|
|
|
|
PushRenderQuad2D(RenderBuffer, BlockBounds.Min, BlockBounds.Max, BlockColor);
|
|
|
|
PushRenderBoundingBox2D(RenderBuffer, BlockBounds.Min, BlockBounds.Max, 1, WhiteV4);
|
|
|
|
|
|
|
|
return BlockBounds;
|
|
|
|
}
|
|
|
|
|
2019-12-31 04:26:28 +00:00
|
|
|
internal gs_list_handle
|
2020-03-01 23:24:12 +00:00
|
|
|
DrawAnimationTimeline (animation_system* AnimationSystem, animation_timeline_state* TimelineState, rect PanelBounds, gs_list_handle SelectedBlockHandle, render_command_buffer* RenderBuffer, app_state* State, mouse_state Mouse)
|
2019-12-27 02:40:14 +00:00
|
|
|
{
|
|
|
|
string TempString = MakeString(PushArray(&State->Transient, char, 256), 256);
|
2019-12-31 04:26:28 +00:00
|
|
|
gs_list_handle Result = SelectedBlockHandle;
|
2019-12-27 02:40:14 +00:00
|
|
|
|
2019-12-28 19:31:21 +00:00
|
|
|
r32 AnimationPanelHeight = PanelBounds.Max.y - PanelBounds.Min.y;
|
|
|
|
r32 AnimationPanelWidth = PanelBounds.Max.x - PanelBounds.Min.x;
|
2019-12-27 02:40:14 +00:00
|
|
|
|
2020-03-02 01:19:30 +00:00
|
|
|
rect LayerMenuBounds = {0};
|
|
|
|
LayerMenuBounds.Min = PanelBounds.Min;
|
|
|
|
LayerMenuBounds.Max = { PanelBounds.Min.x + 256, PanelBounds.Max.y };
|
|
|
|
|
2020-03-01 23:24:12 +00:00
|
|
|
rect TimeRangeBarBounds = {0};
|
2020-03-02 01:19:30 +00:00
|
|
|
TimeRangeBarBounds.Min = BottomRight(LayerMenuBounds);
|
2020-03-01 23:24:12 +00:00
|
|
|
TimeRangeBarBounds.Max = { PanelBounds.Max.x, PanelBounds.Min.y + 24 };
|
|
|
|
|
|
|
|
rect FrameBarBounds = {0};
|
2020-03-02 01:19:30 +00:00
|
|
|
FrameBarBounds.Min = { LayerMenuBounds.Max.x, PanelBounds.Max.y - 32 };
|
2020-03-01 23:24:12 +00:00
|
|
|
FrameBarBounds.Max = PanelBounds.Max;
|
|
|
|
|
|
|
|
rect TimelineBounds = {0};
|
|
|
|
TimelineBounds.Min = TopLeft(TimeRangeBarBounds);
|
|
|
|
TimelineBounds.Max = BottomRight(FrameBarBounds);
|
|
|
|
|
2020-03-02 01:19:30 +00:00
|
|
|
DrawLayerMenu(AnimationSystem, LayerMenuBounds, RenderBuffer, State, Mouse);
|
|
|
|
|
|
|
|
frame_range AdjustedViewRange = {0};
|
2020-03-01 23:24:12 +00:00
|
|
|
AdjustedViewRange = DrawTimelineRangeBar(AnimationSystem, TimelineState, RenderBuffer, TimeRangeBarBounds, Mouse);
|
2020-03-02 01:19:30 +00:00
|
|
|
s32 VisibleFrameCount = AdjustedViewRange.Max - AdjustedViewRange.Min;
|
2019-12-27 02:40:14 +00:00
|
|
|
|
2020-03-01 23:24:12 +00:00
|
|
|
DrawFrameBar(AnimationSystem, RenderBuffer, AdjustedViewRange, FrameBarBounds, Mouse, State);
|
2019-12-27 02:40:14 +00:00
|
|
|
|
2020-03-02 01:19:30 +00:00
|
|
|
// Timeline
|
|
|
|
PushRenderQuad2D(RenderBuffer, RectExpand(TimelineBounds), v4{.25f, .25f, .25f, 1.f});
|
|
|
|
|
2019-12-27 00:23:43 +00:00
|
|
|
// Animation Blocks
|
|
|
|
b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Mouse.LeftButtonState);
|
2020-03-08 21:57:34 +00:00
|
|
|
gs_list_handle DragBlockHandle = {0};
|
2019-12-31 04:26:28 +00:00
|
|
|
for (u32 i = 0; i < AnimationSystem->Blocks.Used; i++)
|
2019-12-27 00:23:43 +00:00
|
|
|
{
|
2019-12-31 04:26:28 +00:00
|
|
|
gs_list_entry<animation_block>* AnimationBlockEntry = AnimationSystem->Blocks.GetEntryAtIndex(i);
|
2020-03-08 00:06:10 +00:00
|
|
|
if (EntryIsFree(AnimationBlockEntry)) { continue; }
|
2019-12-26 20:42:55 +00:00
|
|
|
|
2019-12-31 04:26:28 +00:00
|
|
|
gs_list_handle CurrentBlockHandle = AnimationBlockEntry->Handle;
|
|
|
|
animation_block AnimationBlockAt = AnimationBlockEntry->Value;
|
2019-12-27 00:23:43 +00:00
|
|
|
|
2020-03-01 23:24:12 +00:00
|
|
|
// If either end is in the range, we should draw it
|
2020-03-02 01:19:30 +00:00
|
|
|
b32 RangeIsVisible = (FrameIsInRange(AnimationBlockAt.Range.Min, AdjustedViewRange) ||
|
|
|
|
FrameIsInRange(AnimationBlockAt.Range.Max, AdjustedViewRange));
|
2020-03-01 23:24:12 +00:00
|
|
|
// If neither end is in the range, but the ends surround the visible range,
|
|
|
|
// we should still draw it.
|
2020-03-02 01:19:30 +00:00
|
|
|
RangeIsVisible |= (AnimationBlockAt.Range.Min <= AdjustedViewRange.Min &&
|
|
|
|
AnimationBlockAt.Range.Max>= AdjustedViewRange.Max);
|
2020-03-01 23:24:12 +00:00
|
|
|
if (RangeIsVisible)
|
2019-12-26 20:42:55 +00:00
|
|
|
{
|
2020-03-01 23:24:12 +00:00
|
|
|
v4 BlockColor = BlackV4;
|
|
|
|
if (GSListHandlesAreEqual(SelectedBlockHandle, CurrentBlockHandle))
|
|
|
|
{
|
|
|
|
BlockColor = PinkV4;
|
|
|
|
}
|
|
|
|
rect BlockBounds = DrawAnimationBlock(AnimationBlockAt, BlockColor, AdjustedViewRange, TimelineBounds, RenderBuffer);
|
2020-03-08 21:57:34 +00:00
|
|
|
if (PointIsInRange(Mouse.Pos, BlockBounds.Min, BlockBounds.Max))
|
2020-03-01 23:24:12 +00:00
|
|
|
{
|
2020-03-08 21:57:34 +00:00
|
|
|
DragBlockHandle = CurrentBlockHandle;
|
2020-03-01 23:24:12 +00:00
|
|
|
}
|
2019-12-26 20:42:55 +00:00
|
|
|
}
|
2019-12-27 00:23:43 +00:00
|
|
|
}
|
2019-12-26 20:42:55 +00:00
|
|
|
|
2020-03-08 21:57:34 +00:00
|
|
|
if (MouseDownAndNotHandled && ListHandleIsValid(DragBlockHandle))
|
|
|
|
{
|
|
|
|
MouseDownAndNotHandled = false;
|
|
|
|
SelectAndBeginDragAnimationBlock(DragBlockHandle, AdjustedViewRange, TimelineBounds, State);
|
|
|
|
}
|
|
|
|
|
2019-12-27 00:23:43 +00:00
|
|
|
// Time Slider
|
2020-03-01 23:24:12 +00:00
|
|
|
if (FrameIsInRange(AnimationSystem->CurrentFrame, AdjustedViewRange))
|
|
|
|
{
|
|
|
|
r32 FrameAtPercentVisibleRange = FrameToPercentRange(AnimationSystem->CurrentFrame, AdjustedViewRange);
|
|
|
|
r32 SliderX = GSLerp(TimelineBounds.Min.x, TimelineBounds.Max.x, FrameAtPercentVisibleRange);
|
|
|
|
v2 SliderMin = v2{SliderX, TimelineBounds.Min.y};
|
|
|
|
v2 SliderMax = v2{SliderX + 1, TimelineBounds.Max.y};
|
|
|
|
PushRenderQuad2D(RenderBuffer, SliderMin, SliderMax, TimeSliderColor);
|
|
|
|
}
|
|
|
|
|
|
|
|
PushRenderBoundingBox2D(RenderBuffer, RectExpand(TimeRangeBarBounds), 1.f, RedV4);
|
|
|
|
PushRenderBoundingBox2D(RenderBuffer, RectExpand(FrameBarBounds), 1.f, TealV4);
|
|
|
|
PushRenderBoundingBox2D(RenderBuffer, RectExpand(TimelineBounds), 1.f, PinkV4);
|
|
|
|
|
2019-12-29 00:01:34 +00:00
|
|
|
if (MouseDownAndNotHandled && PointIsInRect(Mouse.Pos, TimelineBounds))
|
2019-12-26 20:42:55 +00:00
|
|
|
{
|
2019-12-28 18:51:47 +00:00
|
|
|
DeselectCurrentAnimationBlock(State);
|
2019-12-26 20:42:55 +00:00
|
|
|
}
|
2020-03-08 00:06:10 +00:00
|
|
|
|
2019-12-26 20:42:55 +00:00
|
|
|
return Result;
|
2019-12-27 00:23:43 +00:00
|
|
|
}
|
2019-12-26 20:42:55 +00:00
|
|
|
|
2019-12-29 00:01:34 +00:00
|
|
|
struct animation_clip
|
|
|
|
{
|
|
|
|
char* Name;
|
|
|
|
s32 NameLength;
|
|
|
|
animation_proc* Proc;
|
|
|
|
};
|
|
|
|
|
|
|
|
s32 GlobalAnimationClipsCount = 3;
|
|
|
|
animation_clip GlobalAnimationClips[] = {
|
|
|
|
{ "Test Pattern One", 16, TestPatternOne },
|
|
|
|
{ "Test Pattern Two", 16, TestPatternTwo },
|
|
|
|
{ "Test Pattern Three", 18, TestPatternThree },
|
|
|
|
};
|
|
|
|
|
|
|
|
internal void
|
|
|
|
DrawAnimationClipsList(rect PanelBounds, mouse_state Mouse, render_command_buffer* RenderBuffer, app_state* State)
|
|
|
|
{
|
|
|
|
v4 LineBGColors[] = {
|
|
|
|
{ .16f, .16f, .16f, 1.f },
|
|
|
|
{ .18f, .18f, .18f, 1.f },
|
|
|
|
};
|
|
|
|
|
|
|
|
interface_list List = {};
|
|
|
|
|
|
|
|
List.LineBGColors = LineBGColors;
|
|
|
|
List.LineBGColorsCount = sizeof(LineBGColors) / sizeof(LineBGColors[0]);
|
|
|
|
List.LineBGHoverColor = v4{ .22f, .22f, .22f, 1.f };
|
|
|
|
List.TextColor = WhiteV4;
|
|
|
|
List.ListBounds = PanelBounds;
|
|
|
|
List.ListElementDimensions = v2{
|
|
|
|
Width(PanelBounds),
|
|
|
|
(r32)(State->Interface.Font->PixelHeight + 8),
|
|
|
|
};
|
|
|
|
List.ElementLabelIndent = v2{10, 4};
|
|
|
|
|
|
|
|
string TitleString = MakeStringLiteral("Animation Clips");
|
|
|
|
DrawListElement(TitleString, &List, Mouse, RenderBuffer, State->Interface);
|
|
|
|
|
|
|
|
for (s32 i = 0; i < GlobalAnimationClipsCount; i++)
|
|
|
|
{
|
|
|
|
animation_clip Clip = GlobalAnimationClips[i];
|
|
|
|
string ClipName = MakeString(Clip.Name, Clip.NameLength);
|
|
|
|
rect ElementBounds = DrawListElement(ClipName, &List, Mouse, RenderBuffer, State->Interface);
|
|
|
|
|
|
|
|
if (MouseButtonTransitionedDown(Mouse.LeftButtonState)
|
|
|
|
&& PointIsInRect(Mouse.DownPos, ElementBounds))
|
|
|
|
{
|
2020-03-08 00:06:10 +00:00
|
|
|
AddAnimationBlockAtCurrentTime(i + 1, State->SelectedAnimationLayer, &State->AnimationSystem);
|
2019-12-29 00:01:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-02-02 03:15:04 +00:00
|
|
|
GSMetaTag(panel_render);
|
2020-02-29 22:23:46 +00:00
|
|
|
GSMetaTag(panel_type_animation_timeline);
|
2020-02-02 03:15:04 +00:00
|
|
|
internal void
|
|
|
|
AnimationTimeline_Render(panel Panel, rect PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context, mouse_state Mouse)
|
2019-12-26 20:42:55 +00:00
|
|
|
{
|
2020-03-01 23:24:12 +00:00
|
|
|
animation_timeline_state* TimelineState = (animation_timeline_state*)Panel.PanelStateMemory;
|
|
|
|
|
2019-12-31 04:26:28 +00:00
|
|
|
gs_list_handle SelectedBlockHandle = State->SelectedAnimationBlockHandle;
|
2019-12-26 20:42:55 +00:00
|
|
|
|
2019-12-27 00:23:43 +00:00
|
|
|
r32 OptionsRowHeight = 25;
|
2019-12-29 00:01:34 +00:00
|
|
|
rect AnimationClipListBounds = rect{
|
2019-12-28 19:31:21 +00:00
|
|
|
PanelBounds.Min,
|
2019-12-29 00:01:34 +00:00
|
|
|
v2{PanelBounds.Min.x + 300, PanelBounds.Max.y - OptionsRowHeight},
|
|
|
|
};
|
|
|
|
rect TimelineBounds = rect{
|
|
|
|
v2{AnimationClipListBounds.Max.x, PanelBounds.Min.y},
|
|
|
|
v2{PanelBounds.Max.x, PanelBounds.Max.y - OptionsRowHeight},
|
2019-12-28 19:31:21 +00:00
|
|
|
};
|
2020-03-01 23:24:12 +00:00
|
|
|
|
2019-12-28 19:31:21 +00:00
|
|
|
if (Height(TimelineBounds) > 0)
|
2019-12-26 20:42:55 +00:00
|
|
|
{
|
|
|
|
SelectedBlockHandle = DrawAnimationTimeline(&State->AnimationSystem,
|
2020-03-01 23:24:12 +00:00
|
|
|
TimelineState,
|
2019-12-28 19:31:21 +00:00
|
|
|
TimelineBounds,
|
2019-12-27 00:23:43 +00:00
|
|
|
SelectedBlockHandle,
|
|
|
|
RenderBuffer, State, Mouse);
|
2020-03-01 23:24:12 +00:00
|
|
|
DrawAnimationClipsList(AnimationClipListBounds, Mouse, RenderBuffer, State);
|
2019-12-26 20:42:55 +00:00
|
|
|
}
|
|
|
|
|
2019-12-28 19:31:21 +00:00
|
|
|
v2 OptionsRowMin = v2{ PanelBounds.Min.x, TimelineBounds.Max.y };
|
|
|
|
v2 OptionsRowMax = PanelBounds.Max;
|
2020-02-15 23:06:51 +00:00
|
|
|
panel_result AnimationPanel = EvaluatePanel(RenderBuffer, OptionsRowMin, OptionsRowMax, State->Interface);
|
2019-12-26 20:42:55 +00:00
|
|
|
|
|
|
|
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;
|
2019-12-27 00:23:43 +00:00
|
|
|
button_result PlayResult = EvaluateButton(RenderBuffer,
|
|
|
|
ButtonAt + ButtonMin, ButtonAt + ButtonMax,
|
|
|
|
MakeStringLiteral("Play"),
|
2019-12-26 20:42:55 +00:00
|
|
|
State->Interface, Mouse);
|
|
|
|
ButtonAt.x += ButtonWidth + 2;
|
2019-12-27 00:23:43 +00:00
|
|
|
button_result StopResult = EvaluateButton(RenderBuffer,
|
|
|
|
ButtonAt + ButtonMin, ButtonAt + ButtonMax,
|
|
|
|
MakeStringLiteral("Stop"),
|
2019-12-26 20:42:55 +00:00
|
|
|
State->Interface, Mouse);
|
|
|
|
|
|
|
|
if (PauseResult.Pressed)
|
|
|
|
{
|
|
|
|
State->AnimationSystem.TimelineShouldAdvance = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PlayResult.Pressed)
|
|
|
|
{
|
|
|
|
State->AnimationSystem.TimelineShouldAdvance = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (StopResult.Pressed)
|
|
|
|
{
|
|
|
|
State->AnimationSystem.TimelineShouldAdvance = false;
|
2020-03-01 01:11:15 +00:00
|
|
|
State->AnimationSystem.CurrentFrame = 0;
|
2019-12-26 20:42:55 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-02 02:41:43 +00:00
|
|
|
|
|
|
|
#define FOLDHAUS_PANEL_ANIMATION_TIMELINE_H
|
|
|
|
#endif // FOLDHAUS_PANEL_ANIMATION_TIMELINE_H
|