Converted thinsg over to using time ranges and implemented zoom. Also restructured how different parts of the timeline view know where they are. The timeline now constructs all the bounds, and each element just draws itself inside the provided bounds.

This commit is contained in:
Peter Slattery 2020-03-01 15:24:12 -08:00
parent 197b6accc7
commit 780ccbd1a3
6 changed files with 2473 additions and 2232 deletions

File diff suppressed because it is too large Load Diff

View File

@ -627,20 +627,14 @@ v4 HSVToRGB (v4 In)
} }
static bool static bool
PointIsInRange ( PointIsInRange (v2 _P, v2 _Min, v2 _Max)
v2 _P,
v2 _Min, v2 _Max
)
{ {
return (_P.x >= _Min.x && _P.x <= _Max.x && return (_P.x >= _Min.x && _P.x <= _Max.x &&
_P.y >= _Min.y && _P.y <= _Max.y); _P.y >= _Min.y && _P.y <= _Max.y);
} }
static bool static bool
PointIsInRangeSafe ( PointIsInRangeSafe (v2 _P, v2 _Min, v2 _Max)
v2 _P,
v2 _Min, v2 _Max
)
{ {
s32 MinX = GSMin(_Min.x, _Max.x); s32 MinX = GSMin(_Min.x, _Max.x);
s32 MinY = GSMin(_Min.y, _Max.y); s32 MinY = GSMin(_Min.y, _Max.y);
@ -666,6 +660,15 @@ PointToPercentRange (v2 P, v2 Min, v2 Max)
// RECT // RECT
////////////////////////////////////// //////////////////////////////////////
// NOTE(Peter): This is useful when you have a function that has a call signature like:
// void Foo(v2 Min, v2 Max)
// because instead of having to do:
// Foo(MyRect.Min, MyRect.Max)
// you can just do:
// Foo(RectExpand(MyRect))
// which makes refactoring easier as you only have to change the identifier in one place
#define RectExpand(r) (r).Min, (r).Max
inline float inline float
Width (rect Rect) Width (rect Rect)
{ {
@ -680,12 +683,43 @@ Height (rect Rect)
return Result; return Result;
} }
inline v2
TopLeft(rect Rect)
{
v2 Result = {0};
Result.x = Rect.Min.x;
Result.y = Rect.Max.y;
return Result;
}
inline v2
TopRight(rect Rect)
{
return Rect.Max;
}
inline v2
BottomLeft(rect Rect)
{
return Rect.Min;
}
inline v2
BottomRight(rect Rect)
{
v2 Result = {0};
Result.x = Rect.Max.x;
Result.y = Rect.Min.y;
return Result;
}
inline float inline float
AspectRatio (rect Rect) AspectRatio (rect Rect)
{ {
float Result = Width(Rect) / Height(Rect); float Result = Width(Rect) / Height(Rect);
return Result; return Result;
} }
inline v2 inline v2
CalculateRectCenter (rect Rect) CalculateRectCenter (rect Rect)
{ {

View File

@ -32,6 +32,7 @@ struct animation_system
b32 TimelineShouldAdvance; b32 TimelineShouldAdvance;
// Timeline // Timeline
// TODO(Peter): Reverse these. Should be FrameStart and FrameEnd for consistency
r32 StartFrame; r32 StartFrame;
r32 EndFrame; r32 EndFrame;
}; };

View File

@ -5,6 +5,70 @@
// //
#ifndef FOLDHAUS_PANEL_ANIMATION_TIMELINE_H #ifndef FOLDHAUS_PANEL_ANIMATION_TIMELINE_H
// Colors
global_variable v4 TimeSliderColor = v4{.36f, .52f, .78f, 1.f};
//
struct animation_timeline_state
{
s32 VisibleFrameRangeMin;
s32 VisibleFrameRangeMax;
};
// TODO(Peter): Move this over to the animation system just as frame_range
struct timeline_frame_range
{
u32 FrameMin;
u32 FrameMax;
};
inline b32
FrameIsInRange(u32 Frame, timeline_frame_range Range)
{
b32 Result = (Frame >= Range.FrameMin) && (Frame <= Range.FrameMax);
return Result;
}
internal u32
GetFrameCount(timeline_frame_range Range)
{
u32 Result = Range.FrameMax - Range.FrameMin;
return Result;
}
internal r32
FrameToPercentRange(s32 Frame, timeline_frame_range Range)
{
r32 Result = (r32)(Frame - Range.FrameMin);
Result = Result / GetFrameCount(Range);
return Result;
}
internal u32
PercentToFrameInRange(r32 Percent, timeline_frame_range Range)
{
u32 Result = Range.FrameMin + (u32)(Percent * GetFrameCount(Range));
return Result;
}
internal u32
ClampFrameToRange(u32 Frame, timeline_frame_range Range)
{
u32 Result = Frame;
if (Result < Range.FrameMin)
{
Result = Range.FrameMin;
}
else if (Result > Range.FrameMax)
{
Result = Range.FrameMax;
}
return Result;
}
//
//
inline u32 inline u32
GetFrameFromPointInAnimationPanel(v2 Point, rect PanelBounds, u32 StartFrame, u32 EndFrame) GetFrameFromPointInAnimationPanel(v2 Point, rect PanelBounds, u32 StartFrame, u32 EndFrame)
{ {
@ -97,15 +161,15 @@ input_command DragTimeMarkerCommands [] = {
}; };
internal void internal void
StartDragTimeMarker(rect TimelineBounds, s32 PanelStartFrame, s32 PanelEndFrame, app_state* State) StartDragTimeMarker(rect TimelineBounds, timeline_frame_range VisibleFrames, app_state* State)
{ {
operation_mode* DragTimeMarkerMode = ActivateOperationModeWithCommands(&State->Modes, DragTimeMarkerCommands, UpdateDragTimeMarker); operation_mode* DragTimeMarkerMode = ActivateOperationModeWithCommands(&State->Modes, DragTimeMarkerCommands, UpdateDragTimeMarker);
drag_time_marker_operation_state* OpState = CreateOperationState(DragTimeMarkerMode, drag_time_marker_operation_state* OpState = CreateOperationState(DragTimeMarkerMode,
&State->Modes, &State->Modes,
drag_time_marker_operation_state); drag_time_marker_operation_state);
OpState->StartFrame = PanelStartFrame; OpState->StartFrame = VisibleFrames.FrameMin;
OpState->EndFrame = PanelEndFrame ; OpState->EndFrame = VisibleFrames.FrameMax;
OpState->TimelineBounds = TimelineBounds; OpState->TimelineBounds = TimelineBounds;
} }
@ -240,7 +304,7 @@ input_command DragAnimationClipCommands [] = {
}; };
internal void internal void
SelectAndBeginDragAnimationBlock(gs_list_handle BlockHandle, s32 PanelStartFrame, s32 PanelEndFrame, rect TimelineBounds, app_state* State) SelectAndBeginDragAnimationBlock(gs_list_handle BlockHandle, timeline_frame_range VisibleRange, rect TimelineBounds, app_state* State)
{ {
SelectAnimationBlock(BlockHandle, State); SelectAnimationBlock(BlockHandle, State);
@ -250,8 +314,8 @@ SelectAndBeginDragAnimationBlock(gs_list_handle BlockHandle, s32 PanelStartFrame
&State->Modes, &State->Modes,
drag_animation_clip_state); drag_animation_clip_state);
OpState->TimelineBounds = TimelineBounds; OpState->TimelineBounds = TimelineBounds;
OpState->AnimationPanel_StartFrame = PanelStartFrame; OpState->AnimationPanel_StartFrame = VisibleRange.FrameMin;
OpState->AnimationPanel_EndFrame = PanelEndFrame ; OpState->AnimationPanel_EndFrame = VisibleRange.FrameMax;
animation_block* SelectedBlock = State->AnimationSystem.Blocks.GetElementWithHandle(BlockHandle); animation_block* SelectedBlock = State->AnimationSystem.Blocks.GetElementWithHandle(BlockHandle);
OpState->SelectedClip_InitialStartFrame = SelectedBlock->StartFrame; OpState->SelectedClip_InitialStartFrame = SelectedBlock->StartFrame;
@ -278,7 +342,11 @@ GSMetaTag(panel_type_animation_timeline);
internal void internal void
AnimationTimeline_Init(panel* Panel, app_state* State) AnimationTimeline_Init(panel* Panel, app_state* State)
{ {
// TODO: :FreePanelMemory
animation_timeline_state* TimelineState = PushStruct(&State->Permanent, animation_timeline_state);
TimelineState->VisibleFrameRangeMin = State->AnimationSystem.StartFrame;
TimelineState->VisibleFrameRangeMax = State->AnimationSystem.EndFrame;
Panel->PanelStateMemory = (u8*)TimelineState;
} }
GSMetaTag(panel_cleanup); GSMetaTag(panel_cleanup);
@ -289,57 +357,131 @@ AnimationTimeline_Cleanup(panel* Panel, app_state* State)
} }
internal r32 internal void
DrawFrameBar (animation_system* AnimationSystem, render_command_buffer* RenderBuffer, s32 StartFrame, s32 EndFrame, rect PanelBounds, mouse_state Mouse, app_state* State) DrawFrameBar (animation_system* AnimationSystem, render_command_buffer* RenderBuffer, timeline_frame_range VisibleFrames, rect BarBounds, mouse_state Mouse, app_state* State)
{ {
MakeStringBuffer(TempString, 256); MakeStringBuffer(TempString, 256);
s32 FrameCount = EndFrame - StartFrame; s32 VisibleFrameCount = VisibleFrames.FrameMax - VisibleFrames.FrameMin;
r32 FrameBarHeight = 24; r32 BarHeight = Height(BarBounds);
v2 FrameBarMin = v2{PanelBounds.Min.x, PanelBounds.Max.y - FrameBarHeight}; r32 BarWidth = Width(BarBounds);
v2 FrameBarMax = PanelBounds.Max;
PushRenderQuad2D(RenderBuffer, FrameBarMin, FrameBarMax, v4{.16f, .16f, .16f, 1.f}); PushRenderQuad2D(RenderBuffer, RectExpand(BarBounds), 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(Mouse.LeftButtonState) if (MouseButtonTransitionedDown(Mouse.LeftButtonState) &&
&& PointIsInRange(Mouse.DownPos, FrameBarMin, FrameBarMax)) PointIsInRange(Mouse.DownPos, RectExpand(BarBounds)))
{ {
StartDragTimeMarker(rect{FrameBarMin, FrameBarMax}, StartFrame, EndFrame, State); StartDragTimeMarker(BarBounds, VisibleFrames, State);
} }
// Frame Ticks // Frame Ticks
for (s32 f = 0; f < FrameCount; f += 10) u32 TickCount = 10;
for (u32 Tick = 0; Tick < TickCount; Tick++)
{ {
s32 Frame = StartFrame + f; r32 Percent = (r32)Tick / (r32)TickCount;
u32 Frame = PercentToFrameInRange(Percent, VisibleFrames);
PrintF(&TempString, "%d", Frame); PrintF(&TempString, "%d", Frame);
r32 FramePercent = FrameToPercentRange(Frame, VisibleFrames);
r32 FramePercent = (r32)f / (r32)FrameCount; r32 FrameX = GSLerp(BarBounds.Min.x, BarBounds.Max.x, FramePercent);
r32 FrameX = GSLerp(PanelBounds.Min.x, PanelBounds.Max.x, FramePercent); v2 FrameTextPos = v2{FrameX, BarBounds.Min.y + 2};
v2 FrameTextPos = v2{FrameX, FrameBarMin.y + 2};
DrawString(RenderBuffer, TempString, State->Interface.Font, FrameTextPos, WhiteV4); DrawString(RenderBuffer, TempString, State->Interface.Font, FrameTextPos, WhiteV4);
// Frame Vertical Slices // Frame Vertical Slices
v2 LineTop = v2{FrameX, FrameBarMin.y}; v2 LineTop = v2{FrameX, BarBounds.Min.y};
v2 LineBottom = v2{FrameX + 1, PanelBounds.Min.y}; v2 LineBottom = v2{FrameX + 1, BarBounds.Min.y};
PushRenderQuad2D(RenderBuffer, LineTop, LineBottom, v4{.16f, .16f, .16f, 1.f}); PushRenderQuad2D(RenderBuffer, LineTop, LineBottom, v4{.16f, .16f, .16f, 1.f});
} }
return FrameBarMin.y; // 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);
}
}
internal timeline_frame_range
DrawTimelineRangeBar (animation_system* AnimationSystem, animation_timeline_state* TimelineState, render_command_buffer* RenderBuffer, rect BarBounds, mouse_state Mouse)
{
timeline_frame_range Result = {0};
r32 BarHeight = Height(BarBounds);
r32 BarWidth = Width(BarBounds);
PushRenderQuad2D(RenderBuffer, RectExpand(BarBounds), v4{.16f, .16f, .16f, 1.f});
r32 PlayableFrames = (r32)(AnimationSystem->EndFrame - AnimationSystem->StartFrame);
v2 SliderBarDim = v2{25, BarHeight};
// Convert Frames To Pixels
// TODO(Peter): When the animation system is storing a frame range rather than individual values
// come back and use the utility functions here
r32 RangeMinPercentPlayable = (r32)(TimelineState->VisibleFrameRangeMin - AnimationSystem->StartFrame) / PlayableFrames;
r32 RangeMaxPercentPlayable = (r32)(TimelineState->VisibleFrameRangeMax - AnimationSystem->StartFrame) / PlayableFrames;
v2 RangeMinSliderMin = v2{BarBounds.Min.x + (RangeMinPercentPlayable * Width(BarBounds)), BarBounds.Min.y};
v2 RangeMaxSliderMin = v2{BarBounds.Min.x + (RangeMaxPercentPlayable * Width(BarBounds)) - 25, BarBounds.Min.y};
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
RangeMinPercentPlayable = (RangeMinSliderMin.x - BarBounds.Min.x) / BarWidth;
RangeMaxPercentPlayable = (RangeMaxSliderMax.x - BarBounds.Min.x) / BarWidth;
u32 VisibleFrameCount = AnimationSystem->EndFrame - AnimationSystem->StartFrame;
Result.FrameMin = RangeMinPercentPlayable * VisibleFrameCount;
Result.FrameMax = RangeMaxPercentPlayable * VisibleFrameCount;
if (MouseButtonTransitionedUp(Mouse.LeftButtonState))
{
TimelineState->VisibleFrameRangeMin = Result.FrameMin;
TimelineState->VisibleFrameRangeMax = Result.FrameMax;
}
return Result;
} }
internal rect internal rect
DrawAnimationBlock (animation_block AnimationBlock, v4 BlockColor, s32 FrameCount, s32 StartFrame, rect TimelineBounds, render_command_buffer* RenderBuffer) DrawAnimationBlock (animation_block AnimationBlock, v4 BlockColor, timeline_frame_range VisibleFrames, rect TimelineBounds, render_command_buffer* RenderBuffer)
{ {
rect BlockBounds = {}; rect BlockBounds = {};
r32 TimelineWidth = Width(TimelineBounds); r32 TimelineWidth = Width(TimelineBounds);
r32 StartFramePercent = (r32)(AnimationBlock.StartFrame - StartFrame) / (r32)FrameCount; u32 ClampedBlockStartFrame = ClampFrameToRange(AnimationBlock.StartFrame, VisibleFrames);
r32 StartFramePercent = FrameToPercentRange(ClampedBlockStartFrame, VisibleFrames);
r32 StartPosition = TimelineWidth * StartFramePercent; r32 StartPosition = TimelineWidth * StartFramePercent;
r32 EndFramePercent = (r32)(AnimationBlock.EndFrame - StartFrame) / (r32)FrameCount; u32 ClampedBlockEndFrame = ClampFrameToRange(AnimationBlock.EndFrame, VisibleFrames);
r32 EndFramePercent = FrameToPercentRange(ClampedBlockEndFrame, VisibleFrames);
r32 EndPosition = TimelineWidth * EndFramePercent; r32 EndPosition = TimelineWidth * EndFramePercent;
BlockBounds.Min = TimelineBounds.Min + v2{StartPosition, 25}; BlockBounds.Min = TimelineBounds.Min + v2{StartPosition, 25};
@ -352,31 +494,33 @@ DrawAnimationBlock (animation_block AnimationBlock, v4 BlockColor, s32 FrameCoun
} }
internal gs_list_handle internal gs_list_handle
DrawAnimationTimeline (animation_system* AnimationSystem, s32 VisibleStartFrame, s32 VisibleEndFrame, rect PanelBounds, gs_list_handle SelectedBlockHandle, render_command_buffer* RenderBuffer, app_state* State, mouse_state Mouse) DrawAnimationTimeline (animation_system* AnimationSystem, animation_timeline_state* TimelineState, rect PanelBounds, gs_list_handle SelectedBlockHandle, render_command_buffer* RenderBuffer, app_state* State, mouse_state Mouse)
{ {
string TempString = MakeString(PushArray(&State->Transient, char, 256), 256); string TempString = MakeString(PushArray(&State->Transient, char, 256), 256);
s32 VisibleFrameCount = VisibleEndFrame - VisibleStartFrame;
gs_list_handle Result = SelectedBlockHandle; gs_list_handle Result = SelectedBlockHandle;
r32 AnimationPanelHeight = PanelBounds.Max.y - PanelBounds.Min.y; r32 AnimationPanelHeight = PanelBounds.Max.y - PanelBounds.Min.y;
r32 AnimationPanelWidth = PanelBounds.Max.x - PanelBounds.Min.x; r32 AnimationPanelWidth = PanelBounds.Max.x - PanelBounds.Min.x;
{ rect TimeRangeBarBounds = {0};
r32 FirstPlayablePercentX = ((r32)(AnimationSystem->StartFrame - VisibleStartFrame) / (r32)VisibleFrameCount); TimeRangeBarBounds.Min = PanelBounds.Min;
r32 LastPlayablePercentX = ((r32)(AnimationSystem->EndFrame - VisibleStartFrame) / (r32)VisibleFrameCount); TimeRangeBarBounds.Max = { PanelBounds.Max.x, PanelBounds.Min.y + 24 };
v2 PlayableMin = v2{(FirstPlayablePercentX * AnimationPanelWidth) + PanelBounds.Min.x, PanelBounds.Min.y }; rect FrameBarBounds = {0};
v2 PlayableMax = v2{(LastPlayablePercentX * AnimationPanelWidth) + PanelBounds.Min.x, PanelBounds.Max.y }; FrameBarBounds.Min = { PanelBounds.Min.x, PanelBounds.Max.y - 32 };
FrameBarBounds.Max = PanelBounds.Max;
PushRenderQuad2D(RenderBuffer, PanelBounds.Min, PanelBounds.Max, v4{.16f, .16f, .16f, 1.f}); rect TimelineBounds = {0};
PushRenderQuad2D(RenderBuffer, PlayableMin, PlayableMax, v4{.22f, .22f, .22f, 1.f}); TimelineBounds.Min = TopLeft(TimeRangeBarBounds);
} TimelineBounds.Max = BottomRight(FrameBarBounds);
r32 FrameBarBottom = DrawFrameBar(AnimationSystem, RenderBuffer, VisibleStartFrame, VisibleEndFrame, PanelBounds, Mouse, State); timeline_frame_range AdjustedViewRange = {0};
AdjustedViewRange = DrawTimelineRangeBar(AnimationSystem, TimelineState, RenderBuffer, TimeRangeBarBounds, Mouse);
s32 VisibleFrameCount = AdjustedViewRange.FrameMax - AdjustedViewRange.FrameMin;
DrawFrameBar(AnimationSystem, RenderBuffer, AdjustedViewRange, FrameBarBounds, Mouse, State);
// Animation Blocks // Animation Blocks
rect TimelineBounds = rect{ PanelBounds.Min, v2{PanelBounds.Max.x, FrameBarBottom} };
b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Mouse.LeftButtonState); b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Mouse.LeftButtonState);
for (u32 i = 0; i < AnimationSystem->Blocks.Used; i++) for (u32 i = 0; i < AnimationSystem->Blocks.Used; i++)
{ {
@ -386,38 +530,58 @@ DrawAnimationTimeline (animation_system* AnimationSystem, s32 VisibleStartFrame,
gs_list_handle CurrentBlockHandle = AnimationBlockEntry->Handle; gs_list_handle CurrentBlockHandle = AnimationBlockEntry->Handle;
animation_block AnimationBlockAt = AnimationBlockEntry->Value; animation_block AnimationBlockAt = AnimationBlockEntry->Value;
v4 BlockColor = BlackV4; // If either end is in the range, we should draw it
if (GSListHandlesAreEqual(SelectedBlockHandle, CurrentBlockHandle)) b32 RangeIsVisible = (FrameIsInRange(AnimationBlockAt.StartFrame, AdjustedViewRange) ||
FrameIsInRange(AnimationBlockAt.EndFrame, AdjustedViewRange));
// If neither end is in the range, but the ends surround the visible range,
// we should still draw it.
RangeIsVisible |= (AnimationBlockAt.StartFrame <= AdjustedViewRange.FrameMin &&
AnimationBlockAt.EndFrame >= AdjustedViewRange.FrameMax);
if (RangeIsVisible)
{ {
BlockColor = PinkV4; v4 BlockColor = BlackV4;
} if (GSListHandlesAreEqual(SelectedBlockHandle, CurrentBlockHandle))
{
rect BlockBounds = DrawAnimationBlock(AnimationBlockAt, BlockColor, VisibleFrameCount, VisibleStartFrame, TimelineBounds, RenderBuffer); BlockColor = PinkV4;
}
if (PointIsInRange(Mouse.Pos, BlockBounds.Min, BlockBounds.Max) rect BlockBounds = DrawAnimationBlock(AnimationBlockAt, BlockColor, AdjustedViewRange, TimelineBounds, RenderBuffer);
&& MouseButtonTransitionedDown(Mouse.LeftButtonState)) if (PointIsInRange(Mouse.Pos, BlockBounds.Min, BlockBounds.Max)
{ && MouseButtonTransitionedDown(Mouse.LeftButtonState))
MouseDownAndNotHandled = false; {
SelectAndBeginDragAnimationBlock(CurrentBlockHandle, VisibleStartFrame, VisibleEndFrame, TimelineBounds, State); MouseDownAndNotHandled = false;
SelectAndBeginDragAnimationBlock(CurrentBlockHandle, AdjustedViewRange, TimelineBounds, State);
}
} }
} }
// Time Slider // Time Slider
r32 TimePercent = (r32)(AnimationSystem->CurrentFrame - VisibleStartFrame) / (r32)VisibleFrameCount; if (FrameIsInRange(AnimationSystem->CurrentFrame, AdjustedViewRange))
r32 SliderX = PanelBounds.Min.x + (AnimationPanelWidth * TimePercent); {
v2 SliderMin = v2{SliderX, PanelBounds.Min.y}; r32 FrameAtPercentVisibleRange = FrameToPercentRange(AnimationSystem->CurrentFrame, AdjustedViewRange);
v2 SliderMax = v2{SliderX + 1, PanelBounds.Max.y - 25}; r32 SliderX = GSLerp(TimelineBounds.Min.x, TimelineBounds.Max.x, FrameAtPercentVisibleRange);
v4 TimeSliderColor = v4{.36f, .52f, .78f, 1.f}; v2 SliderMin = v2{SliderX, TimelineBounds.Min.y};
v2 SliderMax = v2{SliderX + 1, TimelineBounds.Max.y};
PushRenderQuad2D(RenderBuffer, SliderMin, SliderMax, TimeSliderColor);
}
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);
r32 SliderHalfWidth = 10; return Result;
v2 HeadMin = v2{SliderX - SliderHalfWidth, SliderMax.y};
v2 HeadMax = v2{SliderX + SliderHalfWidth, PanelBounds.Max.y};
PushRenderQuad2D(RenderBuffer, HeadMin, HeadMax, TimeSliderColor);
PrintF(&TempString, "%d", AnimationSystem->CurrentFrame);
DrawString(RenderBuffer, TempString, State->Interface.Font, HeadMin + v2{4, 4}, WhiteV4);
{
r32 FirstPlayablePercentX = FrameToPercentRange(AnimationSystem->StartFrame, AdjustedViewRange);
r32 LastPlayablePercentX = FrameToPercentRange(AnimationSystem->EndFrame, AdjustedViewRange);
v2 PlayableMin = v2{(FirstPlayablePercentX * AnimationPanelWidth) + PanelBounds.Min.x, PanelBounds.Min.y };
v2 PlayableMax = v2{(LastPlayablePercentX * AnimationPanelWidth) + PanelBounds.Min.x, PanelBounds.Max.y };
PushRenderQuad2D(RenderBuffer, PanelBounds.Min, PanelBounds.Max, v4{.16f, .16f, .16f, 1.f});
PushRenderQuad2D(RenderBuffer, PlayableMin, PlayableMax, v4{.22f, .22f, .22f, 1.f});
}
if (MouseDownAndNotHandled && PointIsInRect(Mouse.Pos, TimelineBounds)) if (MouseDownAndNotHandled && PointIsInRect(Mouse.Pos, TimelineBounds))
{ {
@ -484,6 +648,8 @@ GSMetaTag(panel_type_animation_timeline);
internal void internal void
AnimationTimeline_Render(panel Panel, rect PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context, mouse_state Mouse) AnimationTimeline_Render(panel Panel, rect PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context, mouse_state Mouse)
{ {
animation_timeline_state* TimelineState = (animation_timeline_state*)Panel.PanelStateMemory;
gs_list_handle SelectedBlockHandle = State->SelectedAnimationBlockHandle; gs_list_handle SelectedBlockHandle = State->SelectedAnimationBlockHandle;
r32 OptionsRowHeight = 25; r32 OptionsRowHeight = 25;
@ -495,16 +661,15 @@ AnimationTimeline_Render(panel Panel, rect PanelBounds, render_command_buffer* R
v2{AnimationClipListBounds.Max.x, PanelBounds.Min.y}, v2{AnimationClipListBounds.Max.x, PanelBounds.Min.y},
v2{PanelBounds.Max.x, PanelBounds.Max.y - OptionsRowHeight}, v2{PanelBounds.Max.x, PanelBounds.Max.y - OptionsRowHeight},
}; };
if (Height(TimelineBounds) > 0) if (Height(TimelineBounds) > 0)
{ {
DrawAnimationClipsList(AnimationClipListBounds, Mouse, RenderBuffer, State);
SelectedBlockHandle = DrawAnimationTimeline(&State->AnimationSystem, SelectedBlockHandle = DrawAnimationTimeline(&State->AnimationSystem,
State->AnimationSystem.StartFrame - 20, TimelineState,
State->AnimationSystem.EndFrame + 20,
TimelineBounds, TimelineBounds,
SelectedBlockHandle, SelectedBlockHandle,
RenderBuffer, State, Mouse); RenderBuffer, State, Mouse);
DrawAnimationClipsList(AnimationClipListBounds, Mouse, RenderBuffer, State);
} }
v2 OptionsRowMin = v2{ PanelBounds.Min.x, TimelineBounds.Max.y }; v2 OptionsRowMin = v2{ PanelBounds.Min.x, TimelineBounds.Max.y };

View File

@ -180,6 +180,7 @@ NodeGraph_Init(panel* Panel, app_state* State)
// TODO(Peter): We aren't able to free this memory. We need a system for // TODO(Peter): We aren't able to free this memory. We need a system for
// taking fixed size chunks off the Memory stack and then reusing them. THis // taking fixed size chunks off the Memory stack and then reusing them. THis
// should probably live outside the paneling system. // should probably live outside the paneling system.
// TODO: :FreePanelMemory
Panel->PanelStateMemory = (u8*)PushStruct(&State->Permanent, node_graph_state); Panel->PanelStateMemory = (u8*)PushStruct(&State->Permanent, node_graph_state);
node_graph_state* GraphState = (node_graph_state*)Panel->PanelStateMemory; node_graph_state* GraphState = (node_graph_state*)Panel->PanelStateMemory;
GraphState->LayoutIsDirty = true; GraphState->LayoutIsDirty = true;

View File

@ -65,8 +65,8 @@ Ground Up Reengineering
- Animation System - Animation System
x snapping clips x snapping clips
- convert everything from time to frames x convert everything from time to frames
- zoom in and out x zoom in and out
- blending between animation - blending between animation
- layers - layers
- display more than one layer - display more than one layer