Implemented Crossfading between animations

This commit is contained in:
PS 2021-03-17 22:15:37 -07:00
parent 8a4958938d
commit 59cb48c9f0
9 changed files with 594 additions and 357 deletions

View File

@ -144,7 +144,7 @@ BlumenLumen_CustomInit(app_state* State, context Context)
Animation_AddBlock(&Anim0, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(15), 0); Animation_AddBlock(&Anim0, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(15), 0);
AnimationArray_Push(&State->AnimationSystem.Animations, Anim0); BLState->AnimHandles[0] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim0);
animation Anim1 = {0}; animation Anim1 = {0};
Anim1.Name = PushStringF(&State->Permanent, 256, "test_anim_one"); Anim1.Name = PushStringF(&State->Permanent, 256, "test_anim_one");
@ -156,7 +156,7 @@ BlumenLumen_CustomInit(app_state* State, context Context)
Animation_AddBlock(&Anim1, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(12), 0); Animation_AddBlock(&Anim1, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(12), 0);
AnimationArray_Push(&State->AnimationSystem.Animations, Anim1); BLState->AnimHandles[1] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim1);
animation Anim2 = {0}; animation Anim2 = {0};
Anim2.Name = PushStringF(&State->Permanent, 256, "i_love_you"); Anim2.Name = PushStringF(&State->Permanent, 256, "i_love_you");
@ -166,10 +166,12 @@ BlumenLumen_CustomInit(app_state* State, context Context)
Anim2.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem); Anim2.PlayableRange.Max = SecondsToFrames(15, State->AnimationSystem);
Animation_AddLayer(&Anim2, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem); Animation_AddLayer(&Anim2, MakeString("Base Layer"), BlendMode_Overwrite, &State->AnimationSystem);
Animation_AddBlock(&Anim2, 0, Anim0.PlayableRange.Max, Patterns_IndexToHandle(10), 0); Animation_AddBlock(&Anim2, 0, 100, Patterns_IndexToHandle(5), 0);
Animation_AddBlock(&Anim2, 50, Anim0.PlayableRange.Max, Patterns_IndexToHandle(10), 0);
AnimationArray_Push(&State->AnimationSystem.Animations, Anim2); BLState->AnimHandles[2] = AnimationArray_Push(&State->AnimationSystem.Animations, Anim2);
State->AnimationSystem.ActiveFadeGroup.From = BLState->AnimHandles[2];
State->AnimationSystem.TimelineShouldAdvance = true; State->AnimationSystem.TimelineShouldAdvance = true;
} // End Animation Playground } // End Animation Playground
@ -189,6 +191,15 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context)
blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory; blumen_lumen_state* BLState = (blumen_lumen_state*)UserData.Memory;
MotorTimeElapsed += Context->DeltaTime; MotorTimeElapsed += Context->DeltaTime;
BLState->TimeElapsed += Context->DeltaTime;
if (BLState->TimeElapsed > 5)
{
u32 NextIndex = ++BLState->CurrAnim % 3;
animation_handle Next = BLState->AnimHandles[NextIndex];
AnimationFadeGroup_FadeTo(&State->AnimationSystem.ActiveFadeGroup, Next, 5);
BLState->TimeElapsed = 0;
}
gs_string BlueString = MakeString("blue"); gs_string BlueString = MakeString("blue");
gs_string GreenString = MakeString("green"); gs_string GreenString = MakeString("green");
@ -207,15 +218,15 @@ BlumenLumen_CustomUpdate(gs_data UserData, app_state* State, context* Context)
u32 NameLen = CStringLength(Packet.AnimationFileName); u32 NameLen = CStringLength(Packet.AnimationFileName);
if (StringEqualsCharArray(BlueString.ConstString, Packet.AnimationFileName, NameLen)) if (StringEqualsCharArray(BlueString.ConstString, Packet.AnimationFileName, NameLen))
{ {
State->AnimationSystem.ActiveAnimationIndex = 0; State->AnimationSystem.ActiveFadeGroup.From.Index = 0;
} }
else if (StringEqualsCharArray(GreenString.ConstString, Packet.AnimationFileName, NameLen)) else if (StringEqualsCharArray(GreenString.ConstString, Packet.AnimationFileName, NameLen))
{ {
State->AnimationSystem.ActiveAnimationIndex = 1; State->AnimationSystem.ActiveFadeGroup.From.Index = 1;
} }
else if (StringEqualsCharArray(ILoveYouString.ConstString, Packet.AnimationFileName, NameLen)) else if (StringEqualsCharArray(ILoveYouString.ConstString, Packet.AnimationFileName, NameLen))
{ {
State->AnimationSystem.ActiveAnimationIndex = 2; State->AnimationSystem.ActiveFadeGroup.From.Index = 2;
} }
OutputDebugStringA("Received Pattern Packet\n"); OutputDebugStringA("Received Pattern Packet\n");

View File

@ -70,6 +70,11 @@ struct blumen_lumen_state
mic_listen_job_data MicListenJobData; mic_listen_job_data MicListenJobData;
motor_packet LastKnownMotorState; motor_packet LastKnownMotorState;
r64 TimeElapsed;
animation_handle AnimHandles[3];
u32 CurrAnim;
}; };

View File

@ -13,6 +13,7 @@ struct animation_timeline_state
{ {
frame_range VisibleRange; frame_range VisibleRange;
handle SelectedBlockHandle; handle SelectedBlockHandle;
animation_handle EditingAnimationHandle;
u32 SelectedAnimationLayer; u32 SelectedAnimationLayer;
}; };
@ -33,22 +34,13 @@ GetXPositionFromFrameInAnimationPanel (u32 Frame, rect2 PanelBounds, frame_range
return XPositionAtFrame; return XPositionAtFrame;
} }
internal handle
AddAnimationBlockAtCurrentTime (animation_pattern_handle AnimationProcHandle, u32 LayerHandle, animation_system* System)
{
u32 NewBlockStart = System->CurrentFrame;
u32 NewBlockEnd = NewBlockStart + SecondsToFrames(3, *System);
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System);
handle AnimHandle = Animation_AddBlock(ActiveAnim, NewBlockStart, NewBlockEnd, AnimationProcHandle, LayerHandle);
return AnimHandle;
}
FOLDHAUS_INPUT_COMMAND_PROC(DeleteAnimationBlockCommand) FOLDHAUS_INPUT_COMMAND_PROC(DeleteAnimationBlockCommand)
{ {
animation_timeline_state* PanelState = Panel_GetStateStruct(Panel, animation_timeline_state); animation_timeline_state* PanelState = Panel_GetStateStruct(Panel, animation_timeline_state);
handle SelectedBlockHandle = PanelState->SelectedBlockHandle; handle SelectedBlockHandle = PanelState->SelectedBlockHandle;
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); animation* ActiveAnim = AnimationArray_GetSafe(State->AnimationSystem.Animations, PanelState->EditingAnimationHandle);
if(SelectedBlockHandle.Index < ActiveAnim->Blocks_.Count && if(SelectedBlockHandle.Index < ActiveAnim->Blocks_.Count &&
ActiveAnim->Blocks_.Generations[SelectedBlockHandle.Index] == SelectedBlockHandle.Generation) ActiveAnim->Blocks_.Generations[SelectedBlockHandle.Index] == SelectedBlockHandle.Generation)
{ {
@ -112,6 +104,7 @@ StartDragTimeMarker(rect2 TimelineBounds, frame_range VisibleFrames, app_state*
OPERATION_STATE_DEF(drag_animation_block_state) OPERATION_STATE_DEF(drag_animation_block_state)
{ {
rect2 TimelineBounds; rect2 TimelineBounds;
animation_handle EditingAnimationHandle;
handle BlockHandle; handle BlockHandle;
frame_range VisibleRange; frame_range VisibleRange;
frame_range ClipRange; frame_range ClipRange;
@ -133,7 +126,9 @@ OPERATION_RENDER_PROC(UpdateDragAnimationBlock)
{ {
drag_animation_block_state* OpState = (drag_animation_block_state*)Operation.OpStateMemory; drag_animation_block_state* OpState = (drag_animation_block_state*)Operation.OpStateMemory;
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); animation_array Animations = State->AnimationSystem.Animations;
animation_handle Handle = OpState->EditingAnimationHandle;
animation* ActiveAnim = AnimationArray_GetSafe(Animations, Handle);
r32 ClipInitialStartFrameXPercent = FrameToPercentRange(OpState->ClipRange.Min, OpState->VisibleRange); r32 ClipInitialStartFrameXPercent = FrameToPercentRange(OpState->ClipRange.Min, OpState->VisibleRange);
u32 ClipInitialStartFrameXPosition = LerpR32(ClipInitialStartFrameXPercent, u32 ClipInitialStartFrameXPosition = LerpR32(ClipInitialStartFrameXPercent,
@ -237,7 +232,10 @@ SelectAndBeginDragAnimationBlock(animation_timeline_state* TimelineState, handle
{ {
TimelineState->SelectedBlockHandle = BlockHandle; TimelineState->SelectedBlockHandle = BlockHandle;
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); animation_handle Handle = TimelineState->EditingAnimationHandle;
animation_array Animations = State->AnimationSystem.Animations;
animation* ActiveAnim = AnimationArray_GetSafe(Animations, Handle);
operation_mode* DragAnimationBlockMode = ActivateOperationModeWithCommands(&State->Modes, DragAnimationBlockCommands, UpdateDragAnimationBlock); operation_mode* DragAnimationBlockMode = ActivateOperationModeWithCommands(&State->Modes, DragAnimationBlockCommands, UpdateDragAnimationBlock);
animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, BlockHandle); animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, BlockHandle);
@ -245,29 +243,39 @@ SelectAndBeginDragAnimationBlock(animation_timeline_state* TimelineState, handle
&State->Modes, &State->Modes,
drag_animation_block_state); drag_animation_block_state);
OpState->TimelineBounds = TimelineBounds; OpState->TimelineBounds = TimelineBounds;
OpState->EditingAnimationHandle = Handle;
OpState->BlockHandle = BlockHandle; OpState->BlockHandle = BlockHandle;
OpState->VisibleRange = VisibleRange; OpState->VisibleRange = VisibleRange;
OpState->ClipRange = SelectedBlock->Range; OpState->ClipRange = SelectedBlock->Range;
} }
// ------------------- // -------------------
FOLDHAUS_INPUT_COMMAND_PROC(AddAnimationBlockCommand) internal void
AnimationTimeline_AddAnimationBlockCommand(animation_timeline_state* TimelineState, app_state* State, context Context)
{ {
animation_timeline_state* TimelineState = Panel_GetStateStruct(Panel, animation_timeline_state); animation_handle Handle = TimelineState->EditingAnimationHandle;
animation_array Animations = State->AnimationSystem.Animations;
animation* ActiveAnim = AnimationArray_GetSafe(Animations, Handle);
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); s32 StartFrame = State->AnimationSystem.CurrentFrame;
s32 EndFrame = StartFrame + SecondsToFrames(3, State->AnimationSystem);
EndFrame = Clamp(0, EndFrame, ActiveAnim->PlayableRange.Max);
if ((EndFrame - StartFrame) > 0)
{
animation_pattern_handle PatternHandle = Patterns_IndexToHandle(0);
u32 Layer = TimelineState->SelectedAnimationLayer;
frame_range Range = ActiveAnim->PlayableRange; handle NewBlockHandle = Animation_AddBlock(ActiveAnim, StartFrame, EndFrame, PatternHandle, Layer);
u32 MouseDownFrame = GetFrameFromPointInAnimationPanel(Mouse.Pos, Panel->Bounds, Range);
animation_pattern_handle PatternHandle = Patterns_IndexToHandle(4);
handle NewBlockHandle = Animation_AddBlock(ActiveAnim, MouseDownFrame, MouseDownFrame + SecondsToFrames(3, State->AnimationSystem), PatternHandle, TimelineState->SelectedAnimationLayer);
TimelineState->SelectedBlockHandle = NewBlockHandle; TimelineState->SelectedBlockHandle = NewBlockHandle;
} else {
// TODO(pjs): we don't want to create a block of zero frames
// since you won't be able to delete it. What do we do here??
}
} }
input_command AnimationTimeline_Commands[] = { input_command AnimationTimeline_Commands[] = {
{ KeyCode_X, KeyCode_Invalid, Command_Began, DeleteAnimationBlockCommand }, { KeyCode_X, KeyCode_Invalid, Command_Began, DeleteAnimationBlockCommand },
{ KeyCode_A, KeyCode_Invalid, Command_Began, AddAnimationBlockCommand },
}; };
s32 AnimationTimeline_CommandsCount = 2; s32 AnimationTimeline_CommandsCount = 2;
@ -276,10 +284,17 @@ GSMetaTag(panel_type_animation_timeline);
internal void internal void
AnimationTimeline_Init(panel* Panel, app_state* State, context Context) AnimationTimeline_Init(panel* Panel, app_state* State, context Context)
{ {
animation_handle Handle = State->AnimationSystem.ActiveFadeGroup.From;
// 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->EditingAnimationHandle = Handle;
if (IsValid(Handle)) {
animation_array Animations = State->AnimationSystem.Animations;
animation* ActiveAnim = AnimationArray_GetSafe(Animations, Handle);
TimelineState->VisibleRange = ActiveAnim->PlayableRange; TimelineState->VisibleRange = ActiveAnim->PlayableRange;
}
Panel->StateMemory = StructToData(TimelineState, animation_timeline_state); Panel->StateMemory = StructToData(TimelineState, animation_timeline_state);
} }
@ -481,92 +496,6 @@ DrawAnimationBlock (animation_block AnimationBlock, v4 BlockColor, frame_range V
return BlockBounds; return BlockBounds;
} }
internal handle
DrawAnimationTimeline (animation_system* AnimationSystem, animation_timeline_state* TimelineState, rect2 PanelBounds, handle SelectedBlockHandle, ui_interface* Interface, app_state* State)
{
gs_string Tempgs_string = PushString(State->Transient, 256);
handle Result = SelectedBlockHandle;
animation CurrAnimation = *AnimationSystem_GetActiveAnimation(AnimationSystem);
rect2 LayerMenuBounds, TimelineBounds;
RectVSplitAtDistanceFromLeft(PanelBounds, 256, &LayerMenuBounds, &TimelineBounds);
// In Top To Bottom Order
rect2 TimelineFrameBarBounds;
rect2 TimelineBlockDisplayBounds;
rect2 TimelineRangeBarBounds;
RectHSplitAtDistanceFromTop(TimelineBounds, 32, &TimelineFrameBarBounds, &TimelineBounds);
RectHSplitAtDistanceFromBottom(TimelineBounds, 24, &TimelineBlockDisplayBounds, &TimelineRangeBarBounds);
DrawLayerMenu(AnimationSystem, CurrAnimation, *Interface, LayerMenuBounds, &TimelineState->SelectedAnimationLayer);
frame_range AdjustedViewRange = DrawTimelineRangeBar(AnimationSystem, CurrAnimation, TimelineState, *Interface, TimelineRangeBarBounds);
DrawFrameBar(AnimationSystem, *Interface, AdjustedViewRange, TimelineFrameBarBounds, State);
ui_FillRect(Interface, TimelineBlockDisplayBounds, v4{.25f, .25f, .25f, 1.0f});
// Animation Blocks
b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState);
handle DragBlockHandle = {0};
for (u32 i = 0; i < CurrAnimation.Blocks_.Count; i++)
{
animation_block* AnimationBlockAt = CurrAnimation.Blocks_.Values + i;
// If either end is in the range, we should draw it
b32 RangeIsVisible = (FrameIsInRange(AdjustedViewRange, AnimationBlockAt->Range.Min) ||
FrameIsInRange(AdjustedViewRange, AnimationBlockAt->Range.Max));
// If neither end is in the range, but the ends surround the visible range,
// we should still draw it.
RangeIsVisible |= (AnimationBlockAt->Range.Min <= AdjustedViewRange.Min &&
AnimationBlockAt->Range.Max>= AdjustedViewRange.Max);
if (RangeIsVisible)
{
v4 BlockColor = BlackV4;
if (SelectedBlockHandle.Index == i && SelectedBlockHandle.Generation == CurrAnimation.Blocks_.Generations[i])
{
BlockColor = PinkV4;
}
rect2 BlockBounds = DrawAnimationBlock(*AnimationBlockAt, BlockColor, AdjustedViewRange, TimelineBounds, Interface->RenderBuffer);
if (PointIsInRect(BlockBounds, Interface->Mouse.Pos))
{
DragBlockHandle.Index = i;
DragBlockHandle.Generation = CurrAnimation.Blocks_.Generations[i];
}
}
}
if (MouseDownAndNotHandled && Handle_IsValid(DragBlockHandle))
{
MouseDownAndNotHandled = false;
SelectAndBeginDragAnimationBlock(TimelineState, DragBlockHandle, AdjustedViewRange, TimelineBounds, State);
}
// Time Slider
if (FrameIsInRange(AdjustedViewRange, AnimationSystem->CurrentFrame))
{
r32 FrameAtPercentVisibleRange = FrameToPercentRange(AnimationSystem->CurrentFrame, AdjustedViewRange);
r32 SliderX = LerpR32(FrameAtPercentVisibleRange, TimelineBounds.Min.x, TimelineBounds.Max.x);
rect2 SliderBounds = {
v2{ SliderX, TimelineBounds.Min.y },
v2{ SliderX + 1, TimelineBounds.Max.y }
};
ui_FillRect(Interface, SliderBounds, TimeSliderColor);
}
ui_OutlineRect(Interface, TimelineRangeBarBounds, 1.f, RedV4);
ui_OutlineRect(Interface, TimelineFrameBarBounds, 1.f, RedV4);
ui_OutlineRect(Interface, TimelineBlockDisplayBounds, 1.f, RedV4);
if (MouseDownAndNotHandled && PointIsInRect(TimelineBounds, Interface->Mouse.Pos))
{
TimelineState->SelectedBlockHandle = {0};
}
return Result;
}
PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback) PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback)
{ {
Assert(ReturningFrom->TypeIndex == PanelType_FileView); Assert(ReturningFrom->TypeIndex == PanelType_FileView);
@ -581,28 +510,11 @@ PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback)
animation NewAnim = AnimParser_Parse(AnimFileString, State->AnimationSystem.Storage, State->Patterns); animation NewAnim = AnimParser_Parse(AnimFileString, State->AnimationSystem.Storage, State->Patterns);
NewAnim.FileInfo = AnimFile.FileInfo; NewAnim.FileInfo = AnimFile.FileInfo;
u32 NewAnimIndex = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim); animation_handle NewAnimHandle = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim);
State->AnimationSystem.ActiveAnimationIndex = NewAnimIndex; State->AnimationSystem.ActiveFadeGroup.From = NewAnimHandle;
} }
} }
internal void
DrawAnimationPatternList(rect2 PanelBounds, ui_interface* Interface, u32 SelectedAnimationLayerHandle, animation_system* AnimationSystem, animation_pattern_array Patterns)
{
ui_PushLayout(Interface, PanelBounds, LayoutDirection_TopDown, MakeString("AnimClips Layout"));
for (u32 i = 0; i < Patterns.Count; i++)
{
animation_pattern Pattern = Patterns.Values[i];
gs_string PatternName = MakeString(Pattern.Name, Pattern.NameLength);
if (ui_Button(Interface, PatternName))
{
animation_pattern_handle PatternHandle = Patterns_IndexToHandle(i);
AddAnimationBlockAtCurrentTime(PatternHandle, SelectedAnimationLayerHandle, AnimationSystem);
}
}
ui_PopLayout(Interface, MakeString("AnimClips Layout"));
}
internal void internal void
PlayBar_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) PlayBar_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context)
{ {
@ -634,14 +546,18 @@ PlayBar_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Pan
} }
internal void internal void
FrameCount_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context) FrameCount_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
{ {
ui_interface* Interface = &State->Interface; ui_interface* Interface = &State->Interface;
gs_string TempString = PushString(State->Transient, 256); gs_string TempString = PushString(State->Transient, 256);
// :FrameRange // :FrameRange
// frame_range VisibleFrames = TimelineState->VisibleRange; // frame_range VisibleFrames = TimelineState->VisibleRange;
animation ActiveAnim = *AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
frame_range VisibleFrames = ActiveAnim.PlayableRange; frame_range VisibleFrames = {};
if (ActiveAnim) {
VisibleFrames = ActiveAnim->PlayableRange;
}
s32 VisibleFrameCount = VisibleFrames.Max - VisibleFrames.Min; s32 VisibleFrameCount = VisibleFrames.Max - VisibleFrames.Min;
@ -687,10 +603,9 @@ FrameCount_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_
} }
internal void internal void
LayerList_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) LayerList_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context)
{ {
ui_interface* Interface = &State->Interface; ui_interface* Interface = &State->Interface;
animation ActiveAnim = *AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
ui_FillRect(Interface, Bounds, Interface->Style.PanelBG); ui_FillRect(Interface, Bounds, Interface->Style.PanelBG);
@ -698,9 +613,12 @@ LayerList_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* P
rect2 LayerBounds = {0}; rect2 LayerBounds = {0};
LayerBounds.Min = Bounds.Min; LayerBounds.Min = Bounds.Min;
LayerBounds.Max = LayerBounds.Min + LayerDim; LayerBounds.Max = LayerBounds.Min + LayerDim;
for (u32 i = 0; i < ActiveAnim.Layers.Count; i++)
if (ActiveAnim)
{ {
anim_layer* Layer = ActiveAnim.Layers.Values + i; for (u32 i = 0; i < ActiveAnim->Layers.Count; i++)
{
anim_layer* Layer = ActiveAnim->Layers.Values + i;
if (ui_MouseClickedRect(*Interface, LayerBounds)) if (ui_MouseClickedRect(*Interface, LayerBounds))
{ {
@ -717,19 +635,23 @@ LayerList_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* P
LayerBounds = Rect2TranslateY(LayerBounds, LayerDim.y); LayerBounds = Rect2TranslateY(LayerBounds, LayerDim.y);
} }
} }
}
internal void internal void
TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context) TimeRange_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
{ {
ui_interface* Interface = &State->Interface; ui_interface* Interface = &State->Interface;
animation ActiveAnim = *AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
// TODO(pjs): setting the timeline to show the entire range // TODO(pjs): setting the timeline to show the entire range
// of the current animation until I reimplement the range // of the current animation until I reimplement the range
// slider bars // slider bars
// :FrameRange // :FrameRange
// frame_range ViewRange = TimelineState->VisibleRange; // frame_range ViewRange = TimelineState->VisibleRange;
frame_range ViewRange = ActiveAnim.PlayableRange; frame_range ViewRange = {};
if (ActiveAnim)
{
ViewRange = ActiveAnim->PlayableRange;
}
handle SelectedBlockHandle = TimelineState->SelectedBlockHandle; handle SelectedBlockHandle = TimelineState->SelectedBlockHandle;
s32 CurrentFrame = State->AnimationSystem.CurrentFrame; s32 CurrentFrame = State->AnimationSystem.CurrentFrame;
@ -737,9 +659,11 @@ TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_c
// Animation Blocks // Animation Blocks
b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState); b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState);
handle DragBlockHandle = {0}; handle DragBlockHandle = {0};
for (u32 i = 0; i < ActiveAnim.Blocks_.Count; i++) if (ActiveAnim)
{ {
animation_block* AnimationBlockAt = ActiveAnim.Blocks_.Values + i; for (u32 i = 0; i < ActiveAnim->Blocks_.Count; i++)
{
animation_block* AnimationBlockAt = ActiveAnim->Blocks_.Values + i;
// 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(ViewRange, AnimationBlockAt->Range.Min) || b32 RangeIsVisible = (FrameIsInRange(ViewRange, AnimationBlockAt->Range.Min) ||
@ -751,7 +675,7 @@ TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_c
if (RangeIsVisible) if (RangeIsVisible)
{ {
v4 BlockColor = BlackV4; v4 BlockColor = BlackV4;
if (SelectedBlockHandle.Index == i && SelectedBlockHandle.Generation == ActiveAnim.Blocks_.Generations[i]) if (SelectedBlockHandle.Index == i && SelectedBlockHandle.Generation == ActiveAnim->Blocks_.Generations[i])
{ {
BlockColor = PinkV4; BlockColor = PinkV4;
} }
@ -760,7 +684,8 @@ TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_c
if (PointIsInRect(BlockBounds, Interface->Mouse.Pos)) if (PointIsInRect(BlockBounds, Interface->Mouse.Pos))
{ {
DragBlockHandle.Index = i; DragBlockHandle.Index = i;
DragBlockHandle.Generation = ActiveAnim.Blocks_.Generations[i]; DragBlockHandle.Generation = ActiveAnim->Blocks_.Generations[i];
}
} }
} }
} }
@ -795,10 +720,10 @@ TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_c
internal void internal void
AnimInfoView_SaveAnimFile (gs_const_string Path, app_state* State, context Context) AnimInfoView_SaveAnimFile (gs_const_string Path, app_state* State, context Context)
{ {
u32 ActiveAnimIndex = State->AnimationSystem.ActiveAnimationIndex; animation_handle ActiveAnimHandle = State->AnimationSystem.ActiveFadeGroup.From;
animation ActiveAnimation = State->AnimationSystem.Animations.Values[ActiveAnimIndex]; animation ActiveAnim = *AnimationArray_GetSafe(State->AnimationSystem.Animations, ActiveAnimHandle);
gs_string FileText = AnimSerializer_Serialize(ActiveAnimation, State->Patterns, State->Transient); gs_string FileText = AnimSerializer_Serialize(ActiveAnim, State->Patterns, State->Transient);
if (!WriteEntireFile(Context.ThreadContext.FileHandler, Path, StringToData(FileText))) if (!WriteEntireFile(Context.ThreadContext.FileHandler, Path, StringToData(FileText)))
{ {
@ -816,24 +741,41 @@ PANEL_MODAL_OVERRIDE_CALLBACK(AnimInfoView_SaveAnimFileCallback)
} }
internal void internal void
AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context) AnimationTimeline_SetActiveAnimation (animation_handle Handle, animation_timeline_state* TimelineState,
animation_system* System)
{
System->ActiveFadeGroup.From = Handle;
TimelineState->EditingAnimationHandle = Handle;
}
internal void
AnimInfoView_Render(animation_timeline_state* TimelineState, animation* ActiveAnim, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context)
{ {
animation_system* AnimSystem = &State->AnimationSystem; animation_system* AnimSystem = &State->AnimationSystem;
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(AnimSystem);
ui_interface* Interface = &State->Interface; ui_interface* Interface = &State->Interface;
ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("AnimInfo Layout")); ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("AnimInfo Layout"));
ui_FillRect(&State->Interface, Bounds, Interface->Style.PanelBG); ui_FillRect(&State->Interface, Bounds, Interface->Style.PanelBG);
if (ui_BeginLabeledDropdown(Interface, MakeString("Active Animation"), ActiveAnim->Name)) gs_string AnimName = {};
if (ActiveAnim)
{
AnimName = ActiveAnim->Name;
} else {
AnimName = MakeString("[None]");
}
if (ui_BeginLabeledDropdown(Interface, MakeString("Active Animation"), AnimName))
{ {
for (u32 i = 0; i < AnimSystem->Animations.Count; i++) for (u32 i = 0; i < AnimSystem->Animations.Count; i++)
{ {
animation Animation = AnimSystem->Animations.Values[i]; animation Animation = AnimSystem->Animations.Values[i];
if (ui_Button(Interface, Animation.Name)) if (ui_Button(Interface, Animation.Name))
{ {
AnimSystem->ActiveAnimationIndex = i; animation_handle NewHandle = {};
NewHandle.Index = i;
AnimationTimeline_SetActiveAnimation(NewHandle, TimelineState, AnimSystem);
} }
} }
} }
@ -846,16 +788,16 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel
animation NewAnim = {}; animation NewAnim = {};
NewAnim.Name = PushString(State->AnimationSystem.Storage, 256); NewAnim.Name = PushString(State->AnimationSystem.Storage, 256);
u32 NewAnimIndex = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim); animation_handle NewAnimHandle = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim);
State->AnimationSystem.ActiveAnimationIndex = NewAnimIndex; State->AnimationSystem.ActiveFadeGroup.From = NewAnimHandle;
} }
if (ui_Button(Interface, MakeString("Save"))) if (ActiveAnim && ui_Button(Interface, MakeString("Save")))
{ {
// Save Animation File // Save Animation File
// TODO(pjs): If you created the animation via the "new" button, there won't be a file attached. // TODO(pjs): If you created the animation via the "new" button, there won't be a file attached.
// need to use the file browser to create a file // need to use the file browser to create a file
u32 ActiveAnimIndex = State->AnimationSystem.ActiveAnimationIndex; animation_handle ActiveAnimHandle = State->AnimationSystem.ActiveFadeGroup.From;
animation ActiveAnimation = State->AnimationSystem.Animations.Values[ActiveAnimIndex]; animation ActiveAnimation = *AnimationArray_GetSafe(State->AnimationSystem.Animations, ActiveAnimHandle);
if (!ActiveAnimation.FileInfo.Path.Str) if (!ActiveAnimation.FileInfo.Path.Str)
{ {
@ -875,6 +817,8 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel
} }
ui_EndRow(Interface); ui_EndRow(Interface);
if (ActiveAnim)
{
ui_TextEntry(Interface, MakeString("Anim Name"), &ActiveAnim->Name); ui_TextEntry(Interface, MakeString("Anim Name"), &ActiveAnim->Name);
ui_Label(Interface, MakeString("Frame Range")); ui_Label(Interface, MakeString("Frame Range"));
@ -908,6 +852,11 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel
ui_EndLabeledDropdown(Interface); ui_EndLabeledDropdown(Interface);
} }
if (ui_Button(Interface, MakeString("+ Add Block")))
{
AnimationTimeline_AddAnimationBlockCommand(TimelineState, State, Context);
}
}
ui_PopLayout(Interface, MakeString("AnimInfo Layout")); ui_PopLayout(Interface, MakeString("AnimInfo Layout"));
} }
@ -924,6 +873,15 @@ AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer*
{ {
animation_timeline_state* TimelineState = Panel_GetStateStruct(Panel, animation_timeline_state); animation_timeline_state* TimelineState = Panel_GetStateStruct(Panel, animation_timeline_state);
animation* ActiveAnim = 0;
animation_handle Handle = State->AnimationSystem.ActiveFadeGroup.From;
TimelineState->EditingAnimationHandle = Handle;
if (IsValid(Handle))
{
animation_array Animations = State->AnimationSystem.Animations;
ActiveAnim = AnimationArray_GetSafe(Animations, Handle);
}
ui_FillRect(&State->Interface, PanelBounds, v4{.1f,.1f,.1f,1.f}); ui_FillRect(&State->Interface, PanelBounds, v4{.1f,.1f,.1f,1.f});
rect2 TimelineBounds, InfoBounds; rect2 TimelineBounds, InfoBounds;
@ -940,10 +898,10 @@ AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer*
RectHSplitAtDistanceFromTop(TimeRangePanelBounds, TitleBarHeight, &FrameCountBounds, &TimeRangeBounds); RectHSplitAtDistanceFromTop(TimeRangePanelBounds, TitleBarHeight, &FrameCountBounds, &TimeRangeBounds);
PlayBar_Render(TimelineState, PlayBarBounds, Panel, RenderBuffer, State, Context); PlayBar_Render(TimelineState, PlayBarBounds, Panel, RenderBuffer, State, Context);
FrameCount_Render(TimelineState, FrameCountBounds, RenderBuffer, State, Context); FrameCount_Render(TimelineState, ActiveAnim, FrameCountBounds, RenderBuffer, State, Context);
LayerList_Render(TimelineState, LayersBounds, Panel, RenderBuffer, State, Context); LayerList_Render(TimelineState, ActiveAnim, LayersBounds, Panel, RenderBuffer, State, Context);
TimeRange_Render(TimelineState, TimeRangeBounds, RenderBuffer, State, Context); TimeRange_Render(TimelineState, ActiveAnim, TimeRangeBounds, RenderBuffer, State, Context);
AnimInfoView_Render(TimelineState, InfoBounds, Panel, RenderBuffer, State, Context); AnimInfoView_Render(TimelineState, ActiveAnim, InfoBounds, Panel, RenderBuffer, State, Context);
} }
#define FOLDHAUS_PANEL_ANIMATION_TIMELINE_H #define FOLDHAUS_PANEL_ANIMATION_TIMELINE_H

View File

@ -16,6 +16,8 @@ struct file_view_state
file_view_mode Mode; file_view_mode Mode;
gs_string WorkingDirectory; gs_string WorkingDirectory;
gs_string DisplayDirectory;
gs_memory_arena FileNamesArena; gs_memory_arena FileNamesArena;
gs_file_info_array FileNames; gs_file_info_array FileNames;
@ -55,6 +57,7 @@ FileView_UpdateWorkingDirectory(gs_const_string WorkingDirectory, file_view_stat
Assert(LastChar == '\\' || LastChar == '/'); Assert(LastChar == '\\' || LastChar == '/');
ClearArena(&State->FileNamesArena); ClearArena(&State->FileNamesArena);
gs_string SanitizedDir = PushString(Context.ThreadContext.Transient, WorkingDirectory.Length + 2); gs_string SanitizedDir = PushString(Context.ThreadContext.Transient, WorkingDirectory.Length + 2);
SanitizePath(WorkingDirectory, &SanitizedDir, Context.ThreadContext.Transient); SanitizePath(WorkingDirectory, &SanitizedDir, Context.ThreadContext.Transient);
if (SanitizedDir.Str[SanitizedDir.Length - 1] != '\\') if (SanitizedDir.Str[SanitizedDir.Length - 1] != '\\')
@ -62,6 +65,8 @@ FileView_UpdateWorkingDirectory(gs_const_string WorkingDirectory, file_view_stat
AppendPrintF(&SanitizedDir, "\\"); AppendPrintF(&SanitizedDir, "\\");
} }
gs_const_string SanitizedDisplayDir = SanitizedDir.ConstString;
gs_file_info NewWorkingDir = GetFileInfo(Context.ThreadContext.FileHandler, SanitizedDir.ConstString); gs_file_info NewWorkingDir = GetFileInfo(Context.ThreadContext.FileHandler, SanitizedDir.ConstString);
if (NewWorkingDir.IsDirectory) if (NewWorkingDir.IsDirectory)
{ {
@ -73,7 +78,7 @@ FileView_UpdateWorkingDirectory(gs_const_string WorkingDirectory, file_view_stat
// NOTE(pjs): we might be printing from State->WorkingDirectory to State->WorkingDirectory // NOTE(pjs): we might be printing from State->WorkingDirectory to State->WorkingDirectory
// in some cases. Shouldn't be a problem but it is unnecessary // in some cases. Shouldn't be a problem but it is unnecessary
PrintF(&State->WorkingDirectory, "%S", SanitizedDir.ConstString); PrintF(&State->WorkingDirectory, "%S", SanitizedDir.ConstString);
PrintF(&State->DisplayDirectory, "%S", SanitizedDisplayDir);
} }
} }
@ -88,7 +93,9 @@ FileView_Init(panel* Panel, app_state* State, context Context)
FileViewState->FileNamesArena = CreateMemoryArena(Context.ThreadContext.Allocator); FileViewState->FileNamesArena = CreateMemoryArena(Context.ThreadContext.Allocator);
// TODO(pjs): this shouldn't be stored in permanent // TODO(pjs): this shouldn't be stored in permanent
FileViewState->WorkingDirectory = PushString(&State->Permanent, 256); FileViewState->DisplayDirectory = PushString(&State->Permanent, 1024);
FileViewState->WorkingDirectory = PushString(&State->Permanent, 1024);
FileView_UpdateWorkingDirectory(ConstString(".\\"), FileViewState, Context); FileView_UpdateWorkingDirectory(ConstString(".\\"), FileViewState, Context);
} }
@ -109,13 +116,17 @@ FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBu
ui_PushLayout(&State->Interface, PanelBounds, LayoutDirection_TopDown, MakeString("FileView Layout")); ui_PushLayout(&State->Interface, PanelBounds, LayoutDirection_TopDown, MakeString("FileView Layout"));
{ {
ui_BeginRow(&State->Interface, 3); ui_BeginRow(&State->Interface, 3);
{ {
if (FileViewState->Mode == FileViewMode_Save) if (FileViewState->Mode == FileViewMode_Save)
{ {
if (ui_Button(&State->Interface, MakeString("Save"))) if (ui_Button(&State->Interface, MakeString("Save")))
{ {
if (!FileViewState->SelectedFile.Path.Str)
{
FileViewState->SelectedFile.Path = FileViewState->DisplayDirectory.ConstString;
}
FileView_Exit_(Panel, State, Context); FileView_Exit_(Panel, State, Context);
} }
} }
@ -128,11 +139,11 @@ FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBu
ui_EndRow(&State->Interface); ui_EndRow(&State->Interface);
// Header // Header
if (ui_TextEntry(&State->Interface, MakeString("pwd"), &FileViewState->WorkingDirectory)) if (ui_TextEntry(&State->Interface, MakeString("pwd"), &FileViewState->DisplayDirectory))
{ {
// if last character is a slash, update pwd, and clear the filter string // if last character is a slash, update pwd, and clear the filter string
// otherwise update the filter string // otherwise update the filter string
gs_string Pwd = FileViewState->WorkingDirectory; gs_string Pwd = FileViewState->DisplayDirectory;
char LastChar = Pwd.Str[Pwd.Length - 1]; char LastChar = Pwd.Str[Pwd.Length - 1];
if (LastChar == '\\' || LastChar == '/') if (LastChar == '\\' || LastChar == '/')
{ {
@ -163,9 +174,9 @@ FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBu
} }
else else
{ {
FileViewState->SelectedFile = File;
switch (FileViewState->Mode) switch (FileViewState->Mode)
{ {
FileViewState->SelectedFile = File;
case FileViewMode_Load: case FileViewMode_Load:
{ {
FileView_Exit_(Panel, State, Context); FileView_Exit_(Panel, State, Context);

View File

@ -46,9 +46,7 @@ enum blend_mode
BlendMode_Count, BlendMode_Count,
}; };
// TODO(pjs): Add Opacity to this // TODO(pjs): This really doesn't belong here
typedef pixel led_blend_proc(pixel PixelA, pixel PixelB);
global gs_const_string BlendModeStrings[] = { global gs_const_string BlendModeStrings[] = {
ConstString("Overwrite"), ConstString("Overwrite"),
ConstString("Add"), ConstString("Add"),
@ -86,6 +84,18 @@ struct animation
gs_file_info FileInfo; gs_file_info FileInfo;
}; };
struct animation_handle
{
s32 Index;
};
internal bool IsValid (animation_handle H) { return H.Index >= 0; }
internal void Clear (animation_handle* H) { H->Index = -1; }
internal bool AnimHandlesAreEqual (animation_handle A, animation_handle B)
{
return A.Index == B.Index;
}
struct animation_array struct animation_array
{ {
animation* Values; animation* Values;
@ -101,7 +111,9 @@ struct animation_layer_frame
animation_block NextHot; animation_block NextHot;
bool HasNextHot; bool HasNextHot;
r32 HotOpacity; r32 NextHotOpacity;
blend_mode BlendMode;
}; };
// NOTE(pjs): This is an evaluated frame - across all layers in an // NOTE(pjs): This is an evaluated frame - across all layers in an
@ -112,6 +124,27 @@ struct animation_frame
u32 LayersCount; u32 LayersCount;
}; };
enum animation_repeat_mode
{
AnimationRepeat_Single,
AnimationRepeat_Loop,
AnimationRepeat_Invalid,
};
global gs_const_string AnimationRepeatModeStrings[] = {
ConstString("Repeat Single"),
ConstString("Loop"),
ConstString("Invalid"),
};
struct animation_fade_group
{
animation_handle From;
animation_handle To;
r32 FadeElapsed;
r32 FadeDuration;
};
#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
@ -119,10 +152,14 @@ struct animation_system
gs_memory_arena* Storage; gs_memory_arena* Storage;
animation_array Animations; animation_array Animations;
animation_repeat_mode RepeatMode;
// 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
// panel // panel
u32 ActiveAnimationIndex; animation_handle ActiveAnimationHandle_;
animation_fade_group ActiveFadeGroup;
s32 CurrentFrame; s32 CurrentFrame;
s32 LastUpdatedFrame; s32 LastUpdatedFrame;
r32 SecondsPerFrame; r32 SecondsPerFrame;
@ -214,7 +251,7 @@ Patterns_Create(gs_memory_arena* Arena, s32 CountMax)
return Result; return Result;
} }
#define Patterns_PushPattern(array, proc) Patterns_PushPattern_((array), (proc), Stringify(proc), sizeof(Stringify(proc))) #define Patterns_PushPattern(array, proc) Patterns_PushPattern_((array), (proc), Stringify(proc), sizeof(Stringify(proc)) - 1)
internal void internal void
Patterns_PushPattern_(animation_pattern_array* Array, animation_proc* Proc, char* Name, u32 NameLength) Patterns_PushPattern_(animation_pattern_array* Array, animation_proc* Proc, char* Name, u32 NameLength)
{ {
@ -341,13 +378,33 @@ AnimationArray_Create(gs_memory_arena* Storage, u32 CountMax)
return Result; return Result;
} }
internal u32 internal animation_handle
AnimationArray_Push(animation_array* Array, animation Value) AnimationArray_Push(animation_array* Array, animation Value)
{ {
Assert(Array->Count < Array->CountMax); Assert(Array->Count < Array->CountMax);
u32 Index = Array->Count++; animation_handle Result = {0};
Array->Values[Index] = Value; Result.Index = Array->Count++;
return Index; Array->Values[Result.Index] = Value;
return Result;
}
internal animation*
AnimationArray_Get(animation_array Array, animation_handle Handle)
{
animation* Result = 0;
if (IsValid(Handle) && Handle.Index < (s32)Array.Count)
{
Result = Array.Values + Handle.Index;
}
return Result;
}
internal animation*
AnimationArray_GetSafe(animation_array Array, animation_handle Handle)
{
Assert(IsValid(Handle));
Assert(Handle.Index < (s32)Array.Count);
return AnimationArray_Get(Array, Handle);
} }
////////////////////////// //////////////////////////
@ -482,6 +539,49 @@ ClampFrameToRange(s32 Frame, frame_range Range)
// Layers // Layers
// Fade Group
internal bool
AnimationFadeGroup_ShouldRender (animation_fade_group FadeGroup)
{
return IsValid(FadeGroup.From);
}
internal void
AnimationFadeGroup_Advance(animation_fade_group* Group)
{
Group->From = Group->To;
Clear(&Group->To);
Group->FadeElapsed = 0;
Group->FadeDuration = 0;
}
internal void
AnimationFadeGroup_Update(animation_fade_group* Group, r32 DeltaTime)
{
if (IsValid(Group->To))
{
Group->FadeElapsed += DeltaTime;
if (Group->FadeElapsed >= Group->FadeDuration)
{
AnimationFadeGroup_Advance(Group);
}
}
}
internal void
AnimationFadeGroup_FadeTo(animation_fade_group* Group, animation_handle To, r32 Duration)
{
// complete current fade if there is one in progress
if (IsValid(Group->To))
{
AnimationFadeGroup_Advance(Group);
}
Group->To = To;
Group->FadeDuration = Duration;
}
// System // System
struct animation_system_desc struct animation_system_desc
@ -499,29 +599,38 @@ AnimationSystem_Init(animation_system_desc Desc)
Result.Animations = AnimationArray_Create(Result.Storage, Desc.AnimArrayCount); Result.Animations = AnimationArray_Create(Result.Storage, Desc.AnimArrayCount);
Result.SecondsPerFrame = Desc.SecondsPerFrame; Result.SecondsPerFrame = Desc.SecondsPerFrame;
Clear(&Result.ActiveFadeGroup.From);
Clear(&Result.ActiveFadeGroup.To);
Result.ActiveFadeGroup.FadeElapsed = 0;
return Result; return Result;
} }
internal animation* internal animation*
AnimationSystem_GetActiveAnimation(animation_system* System) AnimationSystem_GetActiveAnimation(animation_system* System)
{ {
// TODO(pjs): need a way to specify the active animation return AnimationArray_Get(System->Animations, System->ActiveFadeGroup.From);
return System->Animations.Values + System->ActiveAnimationIndex;
} }
internal animation_frame internal animation_frame
AnimationSystem_CalculateAnimationFrame(animation_system* System, gs_memory_arena* Arena) AnimationSystem_CalculateAnimationFrame(animation_system* System,
animation* Animation,
gs_memory_arena* Arena)
{ {
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System);
animation_frame Result = {0}; animation_frame Result = {0};
Result.LayersCount = ActiveAnim->Layers.Count; Result.LayersCount = Animation->Layers.Count;
Result.Layers = PushArray(Arena, animation_layer_frame, Result.LayersCount); Result.Layers = PushArray(Arena, animation_layer_frame, Result.LayersCount);
ZeroArray(Result.Layers, animation_layer_frame, Result.LayersCount); ZeroArray(Result.Layers, animation_layer_frame, Result.LayersCount);
for (u32 i = 0; i < ActiveAnim->Blocks_.Count; i++) for (u32 l = 0; l < Animation->Layers.Count; l++)
{ {
animation_block Block = ActiveAnim->Blocks_.Values[i]; animation_layer_frame* Layer = Result.Layers + l;
Layer->BlendMode = Animation->Layers.Values[l].BlendMode;
}
for (u32 i = 0; i < Animation->Blocks_.Count; i++)
{
animation_block Block = Animation->Blocks_.Values[i];
if (FrameIsInRange(Block.Range, System->CurrentFrame)) if (FrameIsInRange(Block.Range, System->CurrentFrame))
{ {
@ -548,12 +657,12 @@ AnimationSystem_CalculateAnimationFrame(animation_system* System, gs_memory_aren
frame_range BlendRange = {}; frame_range BlendRange = {};
BlendRange.Min = Layer->NextHot.Range.Min; BlendRange.Min = Layer->NextHot.Range.Min;
BlendRange.Max = Layer->Hot.Range.Max; BlendRange.Max = Layer->Hot.Range.Max;
Layer->HotOpacity = 1.0f - FrameToPercentRange(System->CurrentFrame, BlendRange); Layer->NextHotOpacity = FrameToPercentRange(System->CurrentFrame, BlendRange);
} }
else else
{ {
Layer->Hot = Block; Layer->Hot = Block;
Layer->HotOpacity = 1.0f; Layer->NextHotOpacity = 0.0f;
Layer->HasHot = true; Layer->HasHot = true;
} }
} }
@ -563,18 +672,34 @@ AnimationSystem_CalculateAnimationFrame(animation_system* System, gs_memory_aren
} }
internal void internal void
AnimationSystem_Update(animation_system* System) AnimationSystem_Update(animation_system* System, r32 DeltaTime)
{ {
if (!System->TimelineShouldAdvance) { return; }
if (!AnimationFadeGroup_ShouldRender(System->ActiveFadeGroup)) { return; }
AnimationFadeGroup_Update(&System->ActiveFadeGroup, DeltaTime);
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System); animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System);
if (System->TimelineShouldAdvance) {
// TODO(Peter): Revisit this. This implies that the framerate of the animation system // TODO(Peter): Revisit this. This implies that the framerate of the animation system
// is tied to the framerate of the simulation. That seems correct to me, but I'm not sure // is tied to the framerate of the simulation. That seems correct to me, but I'm not sure
System->CurrentFrame += 1; System->CurrentFrame += 1;
// Loop back to the beginning // Loop back to the beginning
if (System->CurrentFrame > ActiveAnim->PlayableRange.Max) if (System->CurrentFrame > ActiveAnim->PlayableRange.Max)
{
switch (System->RepeatMode)
{
case AnimationRepeat_Single:
{ {
System->CurrentFrame = 0; System->CurrentFrame = 0;
}break;
case AnimationRepeat_Loop:
{
// TODO(pjs):
}break;
InvalidDefaultCase;
} }
} }
} }

View File

@ -6,25 +6,27 @@
#ifndef FOLDHAUS_ANIMATION_RENDERER_CPP #ifndef FOLDHAUS_ANIMATION_RENDERER_CPP
internal pixel internal pixel
LedBlend_Overwrite(pixel PixelA, pixel PixelB) LedBlend_Overwrite(pixel PixelA, pixel PixelB, u8* UserData)
{ {
return PixelB; return PixelB;
} }
internal pixel internal pixel
LedBlend_Overwrite(pixel PixelA, pixel PixelB, r32 Opacity) LedBlend_Lerp(pixel PixelA, pixel PixelB, u8* UserData)
{ {
r32 BOpacity = *(r32*)UserData;
pixel Result = {}; pixel Result = {};
r32 BOpacity = 1.0f - Opacity; r32 AOpacity = 1.0f - BOpacity;
Result.R = (u8)((PixelA.R * Opacity) + (PixelB.R * BOpacity)); Result.R = (u8)((PixelA.R * AOpacity) + (PixelB.R * BOpacity));
Result.G = (u8)((PixelA.G * Opacity) + (PixelB.G * BOpacity)); Result.G = (u8)((PixelA.G * AOpacity) + (PixelB.G * BOpacity));
Result.B = (u8)((PixelA.B * Opacity) + (PixelB.B * BOpacity)); Result.B = (u8)((PixelA.B * AOpacity) + (PixelB.B * BOpacity));
return Result; return Result;
} }
internal pixel internal pixel
LedBlend_Add(pixel PixelA, pixel PixelB) LedBlend_Add(pixel PixelA, pixel PixelB, u8* UserData)
{ {
pixel Result = {}; pixel Result = {};
@ -40,7 +42,7 @@ LedBlend_Add(pixel PixelA, pixel PixelB)
} }
internal pixel internal pixel
LedBlend_Multiply(pixel PixelA, pixel PixelB) LedBlend_Multiply(pixel PixelA, pixel PixelB, u8* UserData)
{ {
pixel Result = {}; pixel Result = {};
@ -60,7 +62,7 @@ LedBlend_Multiply(pixel PixelA, pixel PixelB)
} }
internal pixel internal pixel
LedBlend_Overlay(pixel PixelA, pixel PixelB) LedBlend_Overlay(pixel PixelA, pixel PixelB, u8* UserData)
{ {
pixel Result = {}; pixel Result = {};
return Result; return Result;
@ -80,30 +82,23 @@ LedBlend_GetProc(blend_mode BlendMode)
return Result; return Result;
} }
struct pattern_args
{
assembly Assembly;
gs_memory_arena* Transient;
u8* UserData;
};
internal void internal void
AnimationSystem_RenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, assembly Assembly, animation_pattern_array Patterns, gs_memory_arena* Transient, AnimationSystem_RenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, animation_pattern_array Patterns, pattern_args PatternArgs)
u8* UserData)
{ {
u32 FramesIntoBlock = System->CurrentFrame - Block.Range.Min; u32 FramesIntoBlock = System->CurrentFrame - Block.Range.Min;
r32 SecondsIntoBlock = FramesIntoBlock * System->SecondsPerFrame; r32 SecondsIntoBlock = FramesIntoBlock * System->SecondsPerFrame;
animation_pattern Pattern = Patterns_GetPattern(Patterns, Block.AnimationProcHandle); animation_pattern Pattern = Patterns_GetPattern(Patterns, Block.AnimationProcHandle);
Pattern.Proc(Buffer, Assembly, SecondsIntoBlock, Transient, UserData); Pattern.Proc(Buffer, PatternArgs.Assembly, SecondsIntoBlock, PatternArgs.Transient, PatternArgs.UserData);
} }
internal void
AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Assemblies,
led_system* LedSystem,
animation_pattern_array Patterns,
gs_memory_arena* Transient,
u8* UserData)
{
s32 CurrentFrame = System->CurrentFrame;
r32 FrameTime = CurrentFrame * System->SecondsPerFrame;
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System);
animation_frame CurrFrame = AnimationSystem_CalculateAnimationFrame(System, Transient);
// NOTE(pjs): This mirrors animation_layer_frame to account // NOTE(pjs): This mirrors animation_layer_frame to account
// for overlapping // for overlapping
struct layer_led_buffer struct layer_led_buffer
@ -112,31 +107,30 @@ AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Asse
led_buffer NextHotBuffer; led_buffer NextHotBuffer;
}; };
layer_led_buffer* LayerBuffers = PushArray(Transient, layer_led_buffer, CurrFrame.LayersCount); internal led_buffer
RenderAnimationToLedBuffer (animation_system* System,
for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++) pattern_args PatternArgs,
animation_frame CurrFrame,
layer_led_buffer* LayerBuffers,
led_buffer* AssemblyLedBuffer,
animation_pattern_array Patterns,
gs_memory_arena* Transient)
{ {
assembly* Assembly = &Assemblies.Values[AssemblyIndex]; led_buffer AccBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient);
led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly->LedBufferIndex);
// Create the LayerLEDBuffers // Create the LayerLEDBuffers
for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++) for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
{ {
layer_led_buffer TempBuffer = {}; layer_led_buffer TempBuffer = {};
if (CurrFrame.Layers[Layer].HasHot) if (CurrFrame.Layers[Layer].HasHot)
{ {
TempBuffer.HotBuffer.LedCount = AssemblyLedBuffer->LedCount; TempBuffer.HotBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient);
TempBuffer.HotBuffer.Positions = AssemblyLedBuffer->Positions;
TempBuffer.HotBuffer.Colors = PushArray(Transient, pixel, TempBuffer.HotBuffer.LedCount);
LedBuffer_ClearToBlack(&TempBuffer.HotBuffer);
}
if (CurrFrame.Layers[Layer].HasNextHot) if (CurrFrame.Layers[Layer].HasNextHot)
{ {
TempBuffer.NextHotBuffer.LedCount = AssemblyLedBuffer->LedCount; TempBuffer.NextHotBuffer = LedBuffer_CreateCopyCleared(*AssemblyLedBuffer, Transient);
TempBuffer.NextHotBuffer.Positions = AssemblyLedBuffer->Positions; }
TempBuffer.NextHotBuffer.Colors = PushArray(Transient, pixel, TempBuffer.HotBuffer.LedCount);
LedBuffer_ClearToBlack(&TempBuffer.NextHotBuffer);
} }
LayerBuffers[Layer] = TempBuffer; LayerBuffers[Layer] = TempBuffer;
@ -150,14 +144,14 @@ AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Asse
{ {
led_buffer TempBuffer = LayerBuffers[Layer].HotBuffer; led_buffer TempBuffer = LayerBuffers[Layer].HotBuffer;
animation_block Block = LayerFrame.Hot; animation_block Block = LayerFrame.Hot;
AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, *Assembly, Patterns, Transient, UserData); AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs);
} }
if (LayerFrame.HasNextHot) if (LayerFrame.HasNextHot)
{ {
led_buffer TempBuffer = LayerBuffers[Layer].NextHotBuffer; led_buffer TempBuffer = LayerBuffers[Layer].NextHotBuffer;
animation_block Block = LayerFrame.NextHot; animation_block Block = LayerFrame.NextHot;
AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, *Assembly, Patterns, Transient, UserData); AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, Patterns, PatternArgs);
} }
} }
@ -168,33 +162,121 @@ AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Asse
layer_led_buffer LayerBuffer = LayerBuffers[Layer]; layer_led_buffer LayerBuffer = LayerBuffers[Layer];
if (LayerFrame.HasNextHot) if (LayerFrame.HasNextHot)
{ {
for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++) LedBuffer_Blend(LayerBuffer.HotBuffer,
{ LayerBuffer.NextHotBuffer,
pixel A = LayerBuffer.HotBuffer.Colors[LED]; &LayerBuffer.HotBuffer,
pixel B = LayerBuffer.NextHotBuffer.Colors[LED]; LedBlend_Lerp,
LayerBuffer.HotBuffer.Colors[LED] = LedBlend_Overwrite(A, B, LayerFrame.HotOpacity); (u8*)&LayerFrame.NextHotOpacity);
}
} }
} }
// Consolidate Temp Buffers // Consolidate Temp Buffers back into AssemblyLedBuffer
// 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 < CurrFrame.LayersCount; Layer++) for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
{ {
if (CurrFrame.Layers[Layer].HasHot) if (CurrFrame.Layers[Layer].HasHot)
{ {
led_blend_proc* Blend = LedBlend_GetProc(ActiveAnim->Layers.Values[Layer].BlendMode); led_blend_proc* Blend = LedBlend_GetProc(CurrFrame.Layers[Layer].BlendMode);
Assert(Blend != 0); LedBuffer_Blend(AccBuffer,
for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++) LayerBuffers[Layer].HotBuffer,
&AccBuffer,
Blend,
0);
}
}
return AccBuffer;
}
internal void
AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Assemblies,
led_system* LedSystem,
animation_pattern_array Patterns,
gs_memory_arena* Transient,
u8* UserData)
{ {
pixel A = AssemblyLedBuffer->Colors[LED]; s32 CurrentFrame = System->CurrentFrame;
pixel B = LayerBuffers[Layer].HotBuffer.Colors[LED]; r32 FrameTime = CurrentFrame * System->SecondsPerFrame;
AssemblyLedBuffer->Colors[LED] = Blend(A, B);
#if 1
animation_array Animations = System->Animations;
animation_fade_group FadeGroup = System->ActiveFadeGroup;
animation* FromAnim = AnimationArray_Get(Animations, FadeGroup.From);
animation_frame FromFrame = AnimationSystem_CalculateAnimationFrame(System, FromAnim, Transient);
layer_led_buffer* FromLayerBuffers = PushArray(Transient, layer_led_buffer, FromFrame.LayersCount);
animation* ToAnim = AnimationArray_Get(Animations, FadeGroup.To);
animation_frame ToFrame = {0};
layer_led_buffer* ToLayerBuffers = 0;
if (ToAnim)
{
ToFrame = AnimationSystem_CalculateAnimationFrame(System, ToAnim, Transient);
ToLayerBuffers = PushArray(Transient, layer_led_buffer, ToFrame.LayersCount);
} }
for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++)
{
assembly Assembly = Assemblies.Values[AssemblyIndex];
led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex);
pattern_args PatternArgs = {};
PatternArgs.Assembly = Assembly;
PatternArgs.Transient = Transient;
PatternArgs.UserData = UserData;
led_buffer FromBuffer = RenderAnimationToLedBuffer(System,
PatternArgs,
FromFrame,
FromLayerBuffers,
AssemblyLedBuffer,
Patterns,
Transient);
led_buffer ConsolidatedBuffer = FromBuffer;
if (ToAnim) {
led_buffer ToBuffer = RenderAnimationToLedBuffer(System,
PatternArgs,
ToFrame,
ToLayerBuffers,
AssemblyLedBuffer,
Patterns,
Transient);
r32 BlendPercent = FadeGroup.FadeElapsed / FadeGroup.FadeDuration;
LedBuffer_Blend(FromBuffer, ToBuffer, &ConsolidatedBuffer, LedBlend_Lerp, (u8*)&BlendPercent);
} }
LedBuffer_Copy(ConsolidatedBuffer, AssemblyLedBuffer);
} }
#else
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System);
animation_frame CurrFrame = AnimationSystem_CalculateAnimationFrame(System, ActiveAnim, Transient);
for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++)
{
assembly Assembly = Assemblies.Values[AssemblyIndex];
led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly.LedBufferIndex);
pattern_args PatternArgs = {};
PatternArgs.Assembly = Assembly;
PatternArgs.Transient = Transient;
PatternArgs.UserData = UserData;
led_buffer AccBuffer = RenderAnimationToLedBuffer(System,
PatternArgs,
CurrFrame,
LayerBuffers,
AssemblyLedBuffer,
Patterns,
Transient);
LedBuffer_Copy(AccBuffer, AssemblyLedBuffer);
} }
#endif
System->LastUpdatedFrame = System->CurrentFrame; System->LastUpdatedFrame = System->CurrentFrame;
} }

View File

@ -160,6 +160,8 @@ struct assembly_array
assembly* Values; assembly* Values;
}; };
typedef pixel led_blend_proc(pixel A, pixel B, u8* UserData);
internal led_buffer* internal led_buffer*
LedSystemGetBuffer(led_system* System, u32 Index) LedSystemGetBuffer(led_system* System, u32 Index)
{ {
@ -178,6 +180,44 @@ LedBuffer_ClearToBlack(led_buffer* Buffer)
} }
} }
internal void
LedBuffer_Copy(led_buffer From, led_buffer* To)
{
Assert(From.LedCount == To->LedCount);
u32 LedCount = To->LedCount;
for (u32 i = 0; i < LedCount; i++)
{
To->Colors[i] = From.Colors[i];
}
}
internal void
LedBuffer_Blend(led_buffer A, led_buffer B, led_buffer* Dest, led_blend_proc* BlendProc, u8* UserData)
{
Assert(A.LedCount == B.LedCount);
Assert(Dest->LedCount == A.LedCount);
Assert(BlendProc);
u32 LedCount = Dest->LedCount;
for (u32 i = 0; i < LedCount; i++)
{
pixel PA = A.Colors[i];
pixel PB = B.Colors[i];
Dest->Colors[i] = BlendProc(PA, PB, UserData);
}
}
internal led_buffer
LedBuffer_CreateCopyCleared (led_buffer Buffer, gs_memory_arena* Arena)
{
led_buffer Result = {};
Result.LedCount = Buffer.LedCount;
Result.Positions = Buffer.Positions;
Result.Colors = PushArray(Arena, pixel, Buffer.LedCount);
LedBuffer_ClearToBlack(&Result);
return Result;
}
internal u32 internal u32
StripGenData_CountLeds(strip_gen_data Data) StripGenData_CountLeds(strip_gen_data Data)
{ {

View File

@ -62,6 +62,14 @@ INITIALIZE_APPLICATION(InitializeApplication)
GlobalDebugServices->Interface.RenderSculpture = true; GlobalDebugServices->Interface.RenderSculpture = true;
PanelSystem_Init(&State->PanelSystem, GlobalPanelDefs, GlobalPanelDefsCount, &State->Permanent); PanelSystem_Init(&State->PanelSystem, GlobalPanelDefs, GlobalPanelDefsCount, &State->Permanent);
State->Modes = OperationModeSystemInit(&State->Permanent, Context.ThreadContext);
State->UserSpaceDesc = BlumenLumen_UserSpaceCreate();
ReloadStaticData(Context, GlobalDebugServices);
US_CustomInit(&State->UserSpaceDesc, State, Context);
{ {
// NOTE(pjs): This just sets up the default panel layout // NOTE(pjs): This just sets up the default panel layout
panel* RootPanel = PanelSystem_PushPanel(&State->PanelSystem, PanelType_SculptureView, State, Context); panel* RootPanel = PanelSystem_PushPanel(&State->PanelSystem, PanelType_SculptureView, State, Context);
@ -83,13 +91,6 @@ INITIALIZE_APPLICATION(InitializeApplication)
Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_AssemblyDebug, State, Context); Panel_SetType(Hierarchy, &State->PanelSystem, PanelType_AssemblyDebug, State, Context);
} }
State->Modes = OperationModeSystemInit(&State->Permanent, Context.ThreadContext);
State->UserSpaceDesc = BlumenLumen_UserSpaceCreate();
ReloadStaticData(Context, GlobalDebugServices);
US_CustomInit(&State->UserSpaceDesc, State, Context);
} }
UPDATE_AND_RENDER(UpdateAndRender) UPDATE_AND_RENDER(UpdateAndRender)
@ -105,7 +106,7 @@ UPDATE_AND_RENDER(UpdateAndRender)
Editor_Update(State, Context, InputQueue); Editor_Update(State, Context, InputQueue);
AnimationSystem_Update(&State->AnimationSystem); AnimationSystem_Update(&State->AnimationSystem, Context->DeltaTime);
if (AnimationSystem_NeedsRender(State->AnimationSystem)) if (AnimationSystem_NeedsRender(State->AnimationSystem))
{ {
AnimationSystem_RenderToLedBuffers(&State->AnimationSystem, AnimationSystem_RenderToLedBuffers(&State->AnimationSystem,

View File

@ -260,6 +260,10 @@ Win32SocketPeek(platform_socket* Socket)
{ {
}break; }break;
case WSAECONNRESET:
{
}break;
InvalidDefaultCase; InvalidDefaultCase;
} }
} }