Merge branch 'dev'
This commit is contained in:
commit
fea1863166
|
@ -5,7 +5,7 @@
|
|||
//
|
||||
#ifndef FOLDHAUS_COMMAND_DISPATCH_H
|
||||
|
||||
#define FOLDHAUS_INPUT_COMMAND_PROC(name) void name(app_state* State, input_entry Event, mouse_state Mouse, context Context)
|
||||
#define FOLDHAUS_INPUT_COMMAND_PROC(name) void name(app_state* State, input_entry Event, mouse_state Mouse, context Context, panel* Panel)
|
||||
typedef FOLDHAUS_INPUT_COMMAND_PROC(input_command_proc);
|
||||
|
||||
// NOTE(Peter): Helper function so I don't have to remember the parameters to this define
|
||||
|
@ -86,12 +86,12 @@ GetCommandIndexInQueue(input_command_queue* Queue, input_command Command, input_
|
|||
}
|
||||
|
||||
internal input_command_queue
|
||||
InitializeCommandQueue(command_queue_entry* Memory, s32 MemorySize)
|
||||
CommandQueue_Create(gs_memory_arena* Storage, u64 CommandMaxCount)
|
||||
{
|
||||
input_command_queue Result = {};
|
||||
Result.Size = MemorySize;
|
||||
Result.Size = CommandMaxCount;
|
||||
Result.Used = 0;
|
||||
Result.Commands = Memory;
|
||||
Result.Commands = PushArray(Storage, command_queue_entry, CommandMaxCount);
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
//
|
||||
// File: foldhaus_editor.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-10-24
|
||||
//
|
||||
#ifndef FOLDHAUS_EDITOR_CPP
|
||||
|
||||
internal void
|
||||
Editor_HandleInput (app_state* State, rect2 WindowBounds, input_queue InputQueue, mouse_state Mouse, context Context)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
b32 MouseInputHandled = HandleMousePanelInteraction(&State->PanelSystem, State->WindowBounds, Mouse, State);
|
||||
|
||||
panel* ActivePanel = PanelSystem_GetPanelContainingPoint(&State->PanelSystem, Mouse.Pos);
|
||||
if (ActivePanel)
|
||||
{
|
||||
panel_definition ActiveDef = State->PanelSystem.PanelDefs[ActivePanel->TypeIndex];
|
||||
|
||||
input_command_registry ActiveCommands = {};
|
||||
if (State->Modes.ActiveModesCount > 0)
|
||||
{
|
||||
ActiveCommands = State->Modes.ActiveModes[State->Modes.ActiveModesCount - 1].Commands;
|
||||
}
|
||||
else if (ActiveDef.InputCommands)
|
||||
{
|
||||
ActiveCommands.Commands = ActiveDef.InputCommands;
|
||||
ActiveCommands.Size = sizeof(*ActiveDef.InputCommands) / sizeof(ActiveDef.InputCommands[0]);
|
||||
ActiveCommands.Used = ActiveCommands.Size;
|
||||
}
|
||||
|
||||
// Fill up the command queue
|
||||
for (s32 EventIdx = 0; EventIdx < InputQueue.QueueUsed; EventIdx++)
|
||||
{
|
||||
input_entry Event = InputQueue.Entries[EventIdx];
|
||||
|
||||
bool IsMouseEvent = (Event.Key == KeyCode_MouseLeftButton ||
|
||||
Event.Key == KeyCode_MouseMiddleButton ||
|
||||
Event.Key == KeyCode_MouseRightButton);
|
||||
if (IsMouseEvent && MouseInputHandled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE(Peter): These are in the order Down, Up, Held because we want to privalege
|
||||
// Down and Up over Held. In other words, we don't want to call a Held command on the
|
||||
// frame when the button was released, even if the command is registered to both events
|
||||
if (KeyTransitionedDown(Event))
|
||||
{
|
||||
FindAndPushExistingCommand(ActiveCommands, Event, Command_Began, &State->CommandQueue);
|
||||
}
|
||||
else if (KeyTransitionedUp(Event))
|
||||
{
|
||||
FindAndPushExistingCommand(ActiveCommands, Event, Command_Ended, &State->CommandQueue);
|
||||
}
|
||||
else if (KeyHeldDown(Event))
|
||||
{
|
||||
FindAndPushExistingCommand(ActiveCommands, Event, Command_Held, &State->CommandQueue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute all commands in CommandQueue
|
||||
for (s32 CommandIdx = State->CommandQueue.Used - 1; CommandIdx >= 0; CommandIdx--)
|
||||
{
|
||||
command_queue_entry* Entry = &State->CommandQueue.Commands[CommandIdx];
|
||||
if (Entry->Command.Proc)
|
||||
{
|
||||
Entry->Command.Proc(State, Entry->Event, Mouse, Context, ActivePanel);
|
||||
}
|
||||
else
|
||||
{
|
||||
EndCurrentOperationMode(State);
|
||||
}
|
||||
}
|
||||
|
||||
ClearCommandQueue(&State->CommandQueue);
|
||||
}
|
||||
|
||||
internal void
|
||||
Editor_Update(app_state* State, context* Context, input_queue InputQueue)
|
||||
{
|
||||
Context->Mouse.CursorType = CursorType_Arrow;
|
||||
State->WindowBounds = Context->WindowBounds;
|
||||
State->Interface.Mouse = Context->Mouse;
|
||||
|
||||
PanelSystem_UpdateLayout(&State->PanelSystem, State->WindowBounds);
|
||||
Editor_HandleInput(State, State->WindowBounds, InputQueue, Context->Mouse, *Context);
|
||||
}
|
||||
|
||||
internal void
|
||||
Editor_Render(app_state* State, context* Context, render_command_buffer* RenderBuffer)
|
||||
{
|
||||
PushRenderOrthographic(RenderBuffer, State->WindowBounds);
|
||||
PushRenderClearScreen(RenderBuffer);
|
||||
|
||||
ui_InterfaceReset(&State->Interface);
|
||||
State->Interface.RenderBuffer = RenderBuffer;
|
||||
|
||||
ui_layout Layout = ui_CreateLayout(&State->Interface, Context->WindowBounds);
|
||||
ui_PushLayout(&State->Interface, Layout);
|
||||
|
||||
DrawAllPanels(State->PanelSystem, RenderBuffer, &Context->Mouse, State, *Context);
|
||||
|
||||
for (s32 m = 0; m < State->Modes.ActiveModesCount; m++)
|
||||
{
|
||||
operation_mode OperationMode = State->Modes.ActiveModes[m];
|
||||
if (OperationMode.Render != 0)
|
||||
{
|
||||
OperationMode.Render(State, RenderBuffer, OperationMode, Context->Mouse, *Context);
|
||||
}
|
||||
}
|
||||
|
||||
ui_PopLayout(&State->Interface);
|
||||
|
||||
// Draw the Interface
|
||||
for (u32 i = 0; i < State->Interface.WidgetsCount; i++)
|
||||
{
|
||||
ui_widget Widget = State->Interface.Widgets[i];
|
||||
|
||||
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawBackground))
|
||||
{
|
||||
v4 Color = State->Interface.Style.ButtonColor_Inactive;
|
||||
if (ui_WidgetIdsEqual(Widget.Id, State->Interface.HotWidget))
|
||||
{
|
||||
Color = State->Interface.Style.ButtonColor_Active;
|
||||
}
|
||||
if (ui_WidgetIdsEqual(Widget.Id, State->Interface.ActiveWidget))
|
||||
{
|
||||
Color = State->Interface.Style.ButtonColor_Selected;
|
||||
}
|
||||
PushRenderQuad2D(RenderBuffer, Widget.Bounds.Min, Widget.Bounds.Max, Color);
|
||||
}
|
||||
|
||||
if (Widget.String.Length > 0)
|
||||
{
|
||||
v4 Color = State->Interface.Style.TextColor;
|
||||
render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer,
|
||||
Widget.String.Length,
|
||||
State->Interface.Style.Font->BitmapMemory,
|
||||
State->Interface.Style.Font->BitmapTextureHandle,
|
||||
State->Interface.Style.Font->BitmapWidth,
|
||||
State->Interface.Style.Font->BitmapHeight,
|
||||
State->Interface.Style.Font->BitmapBytesPerPixel,
|
||||
State->Interface.Style.Font->BitmapStride);
|
||||
|
||||
v2 RegisterPosition = Widget.Bounds.Min + State->Interface.Style.Margin;
|
||||
|
||||
switch (Widget.Alignment)
|
||||
{
|
||||
case Align_Left:
|
||||
{
|
||||
RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, Color);
|
||||
}break;
|
||||
|
||||
case Align_Right:
|
||||
{
|
||||
RegisterPosition = DrawStringRightAligned(&BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, Color);
|
||||
}break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
}
|
||||
|
||||
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawOutline))
|
||||
{
|
||||
// TODO(pjs): replace these with values from the style
|
||||
r32 Thickness = 1.0f;
|
||||
v4 Color = WhiteV4;
|
||||
PushRenderBoundingBox2D(RenderBuffer, Widget.Bounds.Min, Widget.Bounds.Max, Thickness, Color);
|
||||
}
|
||||
}
|
||||
|
||||
Context->GeneralWorkQueue->CompleteQueueWork(Context->GeneralWorkQueue, Context->ThreadContext);
|
||||
Context->GeneralWorkQueue->ResetWorkQueue(Context->GeneralWorkQueue);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#define FOLDHAUS_EDITOR_CPP
|
||||
#endif // FOLDHAUS_EDITOR_CPP
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
enum panel_edit_mode
|
||||
{
|
||||
PanelEdit_Invalid,
|
||||
|
||||
PanelEdit_Modify,
|
||||
PanelEdit_Destroy,
|
||||
|
||||
|
@ -92,7 +94,6 @@ OPERATION_RENDER_PROC(UpdateAndRenderDragPanelBorder)
|
|||
FOLDHAUS_INPUT_COMMAND_PROC(EndDragPanelBorderOperation)
|
||||
{
|
||||
drag_panel_border_operation_state* OpState = GetCurrentOperationState(State->Modes, drag_panel_border_operation_state);
|
||||
panel* Panel = OpState->Panel;
|
||||
rect2 PanelBounds = OpState->InitialPanelBounds;
|
||||
|
||||
if (OpState->PanelEditMode == PanelEdit_Modify)
|
||||
|
@ -111,6 +112,7 @@ FOLDHAUS_INPUT_COMMAND_PROC(EndDragPanelBorderOperation)
|
|||
else
|
||||
{
|
||||
Panel->SplitPercent = (NewSplitY - PanelBounds.Min.y) / Rect2Height(PanelBounds);
|
||||
Panel_UpdateLayout(Panel, PanelBounds);
|
||||
}
|
||||
}
|
||||
else if (Panel->SplitDirection == PanelSplit_Vertical)
|
||||
|
@ -127,6 +129,7 @@ FOLDHAUS_INPUT_COMMAND_PROC(EndDragPanelBorderOperation)
|
|||
else
|
||||
{
|
||||
Panel->SplitPercent = (NewSplitX - PanelBounds.Min.x) / Rect2Width(PanelBounds);
|
||||
Panel_UpdateLayout(Panel, PanelBounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -220,7 +223,6 @@ OPERATION_RENDER_PROC(UpdateAndRenderSplitPanel)
|
|||
FOLDHAUS_INPUT_COMMAND_PROC(EndSplitPanelOperation)
|
||||
{
|
||||
split_panel_operation_state* OpState = GetCurrentOperationState(State->Modes, split_panel_operation_state);
|
||||
panel* Panel = OpState->Panel;
|
||||
rect2 PanelBounds = OpState->InitialPanelBounds;
|
||||
|
||||
r32 XDistance = Abs(Mouse.Pos.x - Mouse.DownPos.x);
|
||||
|
@ -239,9 +241,9 @@ FOLDHAUS_INPUT_COMMAND_PROC(EndSplitPanelOperation)
|
|||
|
||||
s32 PanelTypeIndex = Panel->TypeIndex;
|
||||
gs_data PanelStateMemory = Panel->StateMemory;
|
||||
Panel_SetCurrentType(&Panel->Left->Panel, &State->PanelSystem, PanelTypeIndex, PanelStateMemory, State, Context);
|
||||
Panel_SetCurrentType(Panel->Left, &State->PanelSystem, PanelTypeIndex, PanelStateMemory, State, Context);
|
||||
|
||||
SetAndInitPanelType(&Panel->Right->Panel, &State->PanelSystem, PanelTypeIndex, State, Context);
|
||||
Panel_SetType(Panel->Right, &State->PanelSystem, PanelTypeIndex, State, Context);
|
||||
|
||||
DeactivateCurrentOperationMode(&State->Modes);
|
||||
}
|
||||
|
@ -251,12 +253,12 @@ input_command SplitPanelCommands[] = {
|
|||
};
|
||||
|
||||
internal void
|
||||
BeginSplitPanelOperation(panel* Panel, rect2 PanelBounds, mouse_state Mouse, app_state* State)
|
||||
BeginSplitPanelOperation(panel* Panel, mouse_state Mouse, app_state* State)
|
||||
{
|
||||
operation_mode* SplitPanel = ActivateOperationModeWithCommands(&State->Modes, SplitPanelCommands, UpdateAndRenderSplitPanel);
|
||||
split_panel_operation_state* OpState = CreateOperationState(SplitPanel, &State->Modes, split_panel_operation_state);
|
||||
OpState->Panel = Panel;
|
||||
OpState->InitialPanelBounds = PanelBounds;
|
||||
OpState->InitialPanelBounds = Panel->Bounds;
|
||||
}
|
||||
|
||||
|
||||
|
@ -265,61 +267,44 @@ BeginSplitPanelOperation(panel* Panel, rect2 PanelBounds, mouse_state Mouse, app
|
|||
#define PANEL_EDGE_CLICK_MAX_DISTANCE 6
|
||||
|
||||
internal b32
|
||||
HandleMouseDownPanelInteractionOrRecurse(panel* Panel, panel_edit_mode PanelEditMode, rect2 PanelBounds, mouse_state Mouse, app_state* State)
|
||||
HandleMouseDownPanelInteractionOrRecurse(panel* Panel, panel_edit_mode PanelEditMode, mouse_state Mouse, app_state* State)
|
||||
{
|
||||
b32 HandledMouseInput = false;
|
||||
|
||||
rect2 PanelSplitButtonBounds = rect2{ PanelBounds.Min, PanelBounds.Min + v2{25, 25} };
|
||||
// TODO(pjs): this can probably live in panel_with_layout
|
||||
rect2 PanelSplitButtonBounds = rect2{ Panel->Bounds.Min, Panel->Bounds.Min + v2{25, 25} };
|
||||
|
||||
if (Panel->SplitDirection == PanelSplit_NoSplit
|
||||
&& PointIsInRect(PanelSplitButtonBounds, Mouse.DownPos))
|
||||
{
|
||||
BeginSplitPanelOperation(Panel, PanelBounds, Mouse, State);
|
||||
BeginSplitPanelOperation(Panel, Mouse, State);
|
||||
HandledMouseInput = true;
|
||||
}
|
||||
else if (Panel->SplitDirection == PanelSplit_Horizontal)
|
||||
else if (Panel->SplitDirection != PanelSplit_NoSplit)
|
||||
{
|
||||
r32 SplitY = LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y);
|
||||
r32 ClickDistanceFromSplit = Abs(Mouse.DownPos.y - SplitY);
|
||||
u32 ElementIndex = 0;
|
||||
switch(Panel->SplitDirection)
|
||||
{
|
||||
case PanelSplit_Vertical: { ElementIndex = 0; } break;
|
||||
case PanelSplit_Horizontal: { ElementIndex = 1; } break;
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
|
||||
r32 SplitPosition = LerpR32(Panel->SplitPercent, Panel->Bounds.Min.E[ElementIndex], Panel->Bounds.Max.E[ElementIndex]);
|
||||
r32 ClickDistanceFromSplit = Abs(Mouse.DownPos.E[ElementIndex] - SplitPosition);
|
||||
if (ClickDistanceFromSplit < PANEL_EDGE_CLICK_MAX_DISTANCE)
|
||||
{
|
||||
BeginDragPanelBorder(Panel, PanelEditMode, PanelBounds, PanelSplit_Horizontal, Mouse, State);
|
||||
HandledMouseInput = true;
|
||||
BeginDragPanelBorder(Panel, PanelEditMode, Panel->Bounds, Panel->SplitDirection, Mouse, State);
|
||||
}
|
||||
else
|
||||
{
|
||||
rect2 TopPanelBounds = GetTopPanelBounds(Panel, PanelBounds);
|
||||
rect2 BottomPanelBounds = GetBottomPanelBounds(Panel, PanelBounds);
|
||||
if (PointIsInRect(BottomPanelBounds, Mouse.DownPos))
|
||||
if (PointIsInRect(Panel->Bottom->Bounds, Mouse.DownPos))
|
||||
{
|
||||
HandleMouseDownPanelInteractionOrRecurse(&Panel->Bottom->Panel, PanelEditMode, BottomPanelBounds, Mouse, State);
|
||||
HandleMouseDownPanelInteractionOrRecurse(Panel->Bottom, PanelEditMode, Mouse, State);
|
||||
}
|
||||
if (PointIsInRect(TopPanelBounds, Mouse.DownPos))
|
||||
else if (PointIsInRect(Panel->Top->Bounds, Mouse.DownPos))
|
||||
{
|
||||
HandleMouseDownPanelInteractionOrRecurse(&Panel->Top->Panel, PanelEditMode, TopPanelBounds, Mouse, State);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Panel->SplitDirection == PanelSplit_Vertical)
|
||||
{
|
||||
r32 SplitX = LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x);
|
||||
r32 ClickDistanceFromSplit = Abs(Mouse.DownPos.x - SplitX);
|
||||
if (ClickDistanceFromSplit < PANEL_EDGE_CLICK_MAX_DISTANCE)
|
||||
{
|
||||
BeginDragPanelBorder(Panel, PanelEditMode, PanelBounds, PanelSplit_Vertical, Mouse, State);
|
||||
HandledMouseInput = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
rect2 LeftPanelBounds = GetLeftPanelBounds(Panel, PanelBounds);
|
||||
rect2 RightPanelBounds = GetRightPanelBounds(Panel, PanelBounds);
|
||||
if (PointIsInRect(LeftPanelBounds, Mouse.DownPos))
|
||||
{
|
||||
HandleMouseDownPanelInteractionOrRecurse(&Panel->Left->Panel, PanelEditMode, LeftPanelBounds, Mouse, State);
|
||||
}
|
||||
if (PointIsInRect(RightPanelBounds, Mouse.DownPos))
|
||||
{
|
||||
HandleMouseDownPanelInteractionOrRecurse(&Panel->Right->Panel, PanelEditMode, RightPanelBounds, Mouse, State);
|
||||
HandleMouseDownPanelInteractionOrRecurse(Panel->Top, PanelEditMode, Mouse, State);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -332,14 +317,21 @@ HandleMousePanelInteraction(panel_system* PanelSystem, rect2 WindowBounds, mouse
|
|||
{
|
||||
b32 HandledMouseInput = false;
|
||||
|
||||
panel* FirstPanel = &PanelSystem->Panels[0].Panel;
|
||||
panel* FirstPanel = PanelSystem->Panels + 0;
|
||||
panel_edit_mode EditMode = PanelEdit_Invalid;
|
||||
|
||||
if (MouseButtonTransitionedDown(Mouse.LeftButtonState))
|
||||
{
|
||||
HandledMouseInput = HandleMouseDownPanelInteractionOrRecurse(FirstPanel, PanelEdit_Modify, WindowBounds, Mouse, State);
|
||||
EditMode = PanelEdit_Modify;
|
||||
}
|
||||
else if (MouseButtonTransitionedDown(Mouse.RightButtonState))
|
||||
{
|
||||
HandledMouseInput = HandleMouseDownPanelInteractionOrRecurse(FirstPanel, PanelEdit_Destroy, WindowBounds, Mouse, State);
|
||||
EditMode = PanelEdit_Destroy;
|
||||
}
|
||||
|
||||
if (EditMode != PanelEdit_Invalid)
|
||||
{
|
||||
HandledMouseInput = HandleMouseDownPanelInteractionOrRecurse(FirstPanel, EditMode, Mouse, State);
|
||||
}
|
||||
|
||||
return HandledMouseInput;
|
||||
|
@ -394,50 +386,27 @@ DrawPanelFooter(panel* Panel, render_command_buffer* RenderBuffer, rect2 FooterB
|
|||
|
||||
rect2 PanelSelectBtnBounds = MakeRect2MinDim(FooterBounds.Min + v2{30, 1}, v2{100, 23});
|
||||
|
||||
if (Panel->PanelSelectionMenuOpen)
|
||||
if (ui_BeginDropdown(&State->Interface, MakeString("Select"), PanelSelectBtnBounds))
|
||||
{
|
||||
rect2 ButtonBounds = MakeRect2MinDim(v2{ PanelSelectBtnBounds.Min.x, FooterBounds.Max.y }, v2{ 100, 25 });
|
||||
|
||||
rect2 MenuBounds = rect2
|
||||
{
|
||||
ButtonBounds.Min,
|
||||
v2{
|
||||
ButtonBounds.Min.x + Rect2Width(ButtonBounds), ButtonBounds.Min.y + (Rect2Height(ButtonBounds) * GlobalPanelDefsCount)
|
||||
},
|
||||
};
|
||||
|
||||
if (MouseButtonTransitionedDown(Mouse.LeftButtonState)
|
||||
&& !PointIsInRect(MenuBounds, Mouse.DownPos))
|
||||
{
|
||||
Panel->PanelSelectionMenuOpen = false;
|
||||
}
|
||||
|
||||
for (s32 i = 0; i < GlobalPanelDefsCount; i++)
|
||||
{
|
||||
panel_definition Def = GlobalPanelDefs[i];
|
||||
panel_definition Def = State->PanelSystem.PanelDefs[i];
|
||||
gs_string DefName = MakeString(Def.PanelName, Def.PanelNameLength);
|
||||
if (ui_Button(&State->Interface, DefName, ButtonBounds))
|
||||
if (ui_Button(&State->Interface, DefName))
|
||||
{
|
||||
SetAndInitPanelType(Panel, &State->PanelSystem, i, State, Context);
|
||||
Panel->PanelSelectionMenuOpen = false;
|
||||
}
|
||||
|
||||
ButtonBounds = Rect2TranslateY(ButtonBounds, Rect2Height(ButtonBounds));
|
||||
Panel_SetType(Panel, &State->PanelSystem, i, State, Context);
|
||||
}
|
||||
}
|
||||
|
||||
if (ui_Button(&State->Interface, MakeString("Select"), PanelSelectBtnBounds))
|
||||
{
|
||||
Panel->PanelSelectionMenuOpen = !Panel->PanelSelectionMenuOpen;
|
||||
}
|
||||
|
||||
ui_EndDropdown(&State->Interface);
|
||||
}
|
||||
|
||||
internal void
|
||||
RenderPanel(panel* Panel, rect2 PanelBounds, rect2 WindowBounds, render_command_buffer* RenderBuffer, app_state* State, context Context, mouse_state Mouse)
|
||||
{
|
||||
s32 PanelType = Panel->TypeIndex;
|
||||
u32 PanelType = Panel->TypeIndex;
|
||||
Assert(PanelType >= 0);
|
||||
Assert(PanelType < State->PanelSystem.PanelDefsCount);
|
||||
|
||||
rect2 FooterBounds = rect2{
|
||||
PanelBounds.Min,
|
||||
|
@ -448,7 +417,7 @@ RenderPanel(panel* Panel, rect2 PanelBounds, rect2 WindowBounds, render_command_
|
|||
PanelBounds.Max,
|
||||
};
|
||||
|
||||
panel_definition Definition = GlobalPanelDefs[PanelType];
|
||||
panel_definition Definition = State->PanelSystem.PanelDefs[PanelType];
|
||||
Definition.Render(Panel, PanelViewBounds, RenderBuffer, State, Context);
|
||||
|
||||
PushRenderOrthographic(RenderBuffer, WindowBounds);
|
||||
|
@ -456,21 +425,37 @@ RenderPanel(panel* Panel, rect2 PanelBounds, rect2 WindowBounds, render_command_
|
|||
}
|
||||
|
||||
internal void
|
||||
DrawAllPanels(panel_layout PanelLayout, render_command_buffer* RenderBuffer, mouse_state* Mouse, app_state* State, context Context)
|
||||
DrawPanelRecursive(panel* Panel, render_command_buffer* RenderBuffer, mouse_state* Mouse, app_state* State, context Context)
|
||||
{
|
||||
for (u32 i = 0; i < PanelLayout.PanelsCount; i++)
|
||||
switch (Panel->SplitDirection)
|
||||
{
|
||||
panel_with_layout PanelWithLayout = PanelLayout.Panels[i];
|
||||
panel* Panel = PanelWithLayout.Panel;
|
||||
rect2 PanelBounds = PanelWithLayout.Bounds;
|
||||
case PanelSplit_Horizontal:
|
||||
case PanelSplit_Vertical:
|
||||
{
|
||||
DrawPanelRecursive(Panel->Left, RenderBuffer, Mouse, State, Context);
|
||||
DrawPanelRecursive(Panel->Right, RenderBuffer, Mouse, State, Context);
|
||||
}break;
|
||||
|
||||
RenderPanel(Panel, PanelBounds, State->WindowBounds, RenderBuffer, State, Context, *Mouse);
|
||||
case PanelSplit_NoSplit:
|
||||
{
|
||||
panel* OverridePanel = Panel_GetModalOverride(Panel);
|
||||
RenderPanel(OverridePanel, OverridePanel->Bounds, State->WindowBounds, RenderBuffer, State, Context, *Mouse);
|
||||
v4 BorderColor = v4{0, 0, 0, 1};
|
||||
|
||||
PushRenderOrthographic(RenderBuffer, State->WindowBounds);
|
||||
DrawPanelBorder(*Panel, PanelBounds.Min, PanelBounds.Max, BorderColor, Mouse, RenderBuffer);
|
||||
DrawPanelBorder(*OverridePanel, OverridePanel->Bounds.Min, OverridePanel->Bounds.Max, BorderColor, Mouse, RenderBuffer);
|
||||
}break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
DrawAllPanels(panel_system System, render_command_buffer* RenderBuffer, mouse_state* Mouse, app_state* State, context Context)
|
||||
{
|
||||
panel* PanelAt = System.Panels + 0;
|
||||
DrawPanelRecursive(PanelAt, RenderBuffer, Mouse, State, Context);
|
||||
}
|
||||
|
||||
#define FOLDHAUS_INTERFACE_CPP
|
||||
#endif // FOLDHAUS_INTERFACE_CPP
|
|
@ -18,7 +18,6 @@ enum panel_split_direction
|
|||
PanelSplit_Count,
|
||||
};
|
||||
|
||||
typedef struct panel_entry panel_entry;
|
||||
typedef struct panel panel;
|
||||
|
||||
#define PANEL_MODAL_OVERRIDE_CALLBACK(name) void name(panel* ReturningFrom, app_state* State, context Context)
|
||||
|
@ -29,36 +28,29 @@ struct panel
|
|||
s32 TypeIndex;
|
||||
gs_data StateMemory;
|
||||
|
||||
panel_entry* ModalOverride;
|
||||
panel* IsModalOverrideFor; // TODO(pjs): I don't like that this is panel* but ModalOverride is panel_entry*
|
||||
panel* ModalOverride;
|
||||
panel* IsModalOverrideFor;
|
||||
panel_modal_override_callback* ModalOverrideCB;
|
||||
|
||||
rect2 Bounds;
|
||||
panel_split_direction SplitDirection;
|
||||
r32 SplitPercent;
|
||||
|
||||
// TODO(Peter): This REALLY doesn't want to live here
|
||||
// Probably belongs in a more generalized PanelInterfaceState or something
|
||||
b32 PanelSelectionMenuOpen;
|
||||
panel* Parent;
|
||||
|
||||
union{
|
||||
panel_entry* Left;
|
||||
panel_entry* Top;
|
||||
panel* Left;
|
||||
panel* Top;
|
||||
};
|
||||
union{
|
||||
panel_entry* Right;
|
||||
panel_entry* Bottom;
|
||||
panel* Right;
|
||||
panel* Bottom;
|
||||
};
|
||||
};
|
||||
|
||||
struct free_panel
|
||||
{
|
||||
panel_entry* Next;
|
||||
};
|
||||
|
||||
struct panel_entry
|
||||
{
|
||||
panel Panel;
|
||||
free_panel Free;
|
||||
free_panel* Next;
|
||||
};
|
||||
|
||||
#define PANEL_INIT_PROC(name) void name(panel* Panel, app_state* State, context Context)
|
||||
|
@ -88,26 +80,10 @@ struct panel_system
|
|||
panel_definition* PanelDefs;
|
||||
u32 PanelDefsCount;
|
||||
|
||||
panel_entry Panels[PANELS_MAX];
|
||||
panel* Panels;
|
||||
u32 PanelsUsed;
|
||||
|
||||
panel_entry FreeList;
|
||||
};
|
||||
|
||||
// NOTE(Peter): This representation is used to let external code render and interact
|
||||
// with panels. It shouldn't be stored across frame boundaries as the pointers to
|
||||
// Panel's are liable to change.
|
||||
struct panel_with_layout
|
||||
{
|
||||
panel* Panel;
|
||||
rect2 Bounds;
|
||||
};
|
||||
|
||||
struct panel_layout
|
||||
{
|
||||
panel_with_layout* Panels;
|
||||
u32 PanelsCount;
|
||||
u32 PanelsMax;
|
||||
free_panel* FreeList;
|
||||
};
|
||||
|
||||
/////////////////////////////////
|
||||
|
@ -117,21 +93,24 @@ struct panel_layout
|
|||
/////////////////////////////////
|
||||
|
||||
internal void
|
||||
InitializePanelSystem(panel_system* PanelSystem, panel_definition* PanelDefs, u32 PanelDefsCount)
|
||||
PanelSystem_Init(panel_system* PanelSystem, panel_definition* PanelDefs, u32 PanelDefsCount, gs_memory_arena* Storage)
|
||||
{
|
||||
PanelSystem->FreeList.Free.Next = &PanelSystem->FreeList;
|
||||
PanelSystem->FreeList = 0;
|
||||
PanelSystem->PanelDefs = PanelDefs;
|
||||
PanelSystem->PanelDefsCount = PanelDefsCount;
|
||||
|
||||
PanelSystem->Panels = PushArray(Storage, panel, PANELS_MAX);
|
||||
}
|
||||
|
||||
internal panel_entry*
|
||||
TakeNewPanelEntry(panel_system* PanelSystem)
|
||||
internal panel*
|
||||
PanelSystem_TakePanel(panel_system* PanelSystem)
|
||||
{
|
||||
panel_entry* FreeEntry = 0;
|
||||
if (PanelSystem->FreeList.Free.Next != &PanelSystem->FreeList)
|
||||
panel* FreeEntry = 0;
|
||||
if (PanelSystem->FreeList != 0)
|
||||
{
|
||||
FreeEntry = PanelSystem->FreeList.Free.Next;
|
||||
PanelSystem->FreeList.Free.Next = FreeEntry->Free.Next;
|
||||
free_panel* FreePanel = PanelSystem->FreeList;
|
||||
PanelSystem->FreeList = FreePanel->Next;
|
||||
FreeEntry = (panel*)FreePanel;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -142,43 +121,24 @@ TakeNewPanelEntry(panel_system* PanelSystem)
|
|||
}
|
||||
|
||||
internal void
|
||||
FreePanelEntry(panel_entry* Entry, panel_system* PanelSystem)
|
||||
PanelSystem_FreePanel(panel* Panel, panel_system* PanelSystem)
|
||||
{
|
||||
Assert(Entry >= PanelSystem->Panels && Entry <= PanelSystem->Panels + PANELS_MAX);
|
||||
Entry->Panel = {0};
|
||||
Entry->Free.Next = PanelSystem->FreeList.Free.Next;
|
||||
PanelSystem->FreeList.Free.Next = Entry;
|
||||
Assert(Panel >= PanelSystem->Panels && Panel <= PanelSystem->Panels + PANELS_MAX);
|
||||
|
||||
free_panel* FreeEntry = (free_panel*)Panel;
|
||||
FreeEntry->Next = PanelSystem->FreeList;
|
||||
PanelSystem->FreeList = FreeEntry;
|
||||
}
|
||||
|
||||
internal void
|
||||
FreePanelEntryRecursive(panel_entry* Entry, panel_system* PanelSystem)
|
||||
PanelSystem_FreePanelRecursive(panel* Panel, panel_system* PanelSystem)
|
||||
{
|
||||
if (Entry->Panel.SplitDirection != PanelSplit_NoSplit)
|
||||
if (Panel->SplitDirection != PanelSplit_NoSplit)
|
||||
{
|
||||
FreePanelEntryRecursive(Entry->Panel.Left, PanelSystem);
|
||||
FreePanelEntryRecursive(Entry->Panel.Right, PanelSystem);
|
||||
PanelSystem_FreePanelRecursive(Panel->Left, PanelSystem);
|
||||
PanelSystem_FreePanelRecursive(Panel->Right, PanelSystem);
|
||||
}
|
||||
FreePanelEntry(Entry, PanelSystem);
|
||||
}
|
||||
|
||||
internal void
|
||||
FreePanelAtIndex(s32 Index, panel_system* PanelSystem)
|
||||
{
|
||||
Assert(Index > 0 && Index < (s32)PanelSystem->PanelsUsed);
|
||||
panel_entry* EntryToFree = PanelSystem->Panels + Index;
|
||||
EntryToFree->Free.Next = PanelSystem->FreeList.Free.Next;
|
||||
PanelSystem->FreeList.Free.Next = EntryToFree;
|
||||
}
|
||||
|
||||
internal panel_entry*
|
||||
Panel_GetModalOverride(panel_entry* PanelEntry)
|
||||
{
|
||||
panel_entry* Result = PanelEntry;
|
||||
if (PanelEntry->Panel.ModalOverride != 0)
|
||||
{
|
||||
Result = Panel_GetModalOverride(PanelEntry->Panel.ModalOverride);
|
||||
}
|
||||
return Result;
|
||||
PanelSystem_FreePanel(Panel, PanelSystem);
|
||||
}
|
||||
|
||||
internal panel*
|
||||
|
@ -187,24 +147,25 @@ Panel_GetModalOverride(panel* Panel)
|
|||
panel* Result = Panel;
|
||||
if (Panel->ModalOverride != 0)
|
||||
{
|
||||
Result = &Panel_GetModalOverride(Panel->ModalOverride)->Panel;
|
||||
Result = Panel_GetModalOverride(Panel->ModalOverride);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
Panel_PushModalOverride(panel* Root, panel_entry* Override, panel_modal_override_callback* Callback)
|
||||
Panel_PushModalOverride(panel* Root, panel* Override, panel_modal_override_callback* Callback)
|
||||
{
|
||||
Root->ModalOverride = Override;
|
||||
Root->ModalOverrideCB = Callback;
|
||||
Override->Panel.IsModalOverrideFor = Root;
|
||||
Override->IsModalOverrideFor = Root;
|
||||
Override->Bounds = Root->Bounds;
|
||||
}
|
||||
|
||||
internal void
|
||||
Panel_PopModalOverride(panel* Parent, panel_system* System)
|
||||
{
|
||||
// TODO(pjs): Free the overrided panel
|
||||
FreePanelEntry(Parent->ModalOverride, System);
|
||||
PanelSystem_FreePanel(Parent->ModalOverride, System);
|
||||
Parent->ModalOverride = 0;
|
||||
}
|
||||
|
||||
|
@ -223,7 +184,7 @@ Panel_SetCurrentType(panel* Panel, panel_system* System, s32 NewPanelType, gs_da
|
|||
}
|
||||
|
||||
internal void
|
||||
SetAndInitPanelType(panel* Panel, panel_system* System, s32 NewPanelTypeIndex, app_state* State, context Context)
|
||||
Panel_SetType(panel* Panel, panel_system* System, s32 NewPanelTypeIndex, app_state* State, context Context)
|
||||
{
|
||||
gs_data EmptyStateData = {0};
|
||||
Panel_SetCurrentType(Panel, System, NewPanelTypeIndex, EmptyStateData, State, Context);
|
||||
|
@ -239,12 +200,12 @@ Panel_GetStateMemory(panel* Panel, u64 Size)
|
|||
return Result;
|
||||
}
|
||||
|
||||
internal panel_entry*
|
||||
internal panel*
|
||||
PanelSystem_PushPanel(panel_system* PanelSystem, s32 PanelTypeIndex, app_state* State, context Context)
|
||||
{
|
||||
panel_entry* PanelEntry = TakeNewPanelEntry(PanelSystem);
|
||||
SetAndInitPanelType(&PanelEntry->Panel, PanelSystem, PanelTypeIndex, State, Context);
|
||||
return PanelEntry;
|
||||
panel* Panel = PanelSystem_TakePanel(PanelSystem);
|
||||
Panel_SetType(Panel, PanelSystem, PanelTypeIndex, State, Context);
|
||||
return Panel;
|
||||
}
|
||||
|
||||
internal void
|
||||
|
@ -257,11 +218,14 @@ SplitPanel(panel* Parent, r32 Percent, panel_split_direction SplitDirection, pan
|
|||
|
||||
s32 ParentTypeIndex = Parent->TypeIndex;
|
||||
gs_data ParentStateMemory = Parent->StateMemory;
|
||||
Parent->Left = TakeNewPanelEntry(PanelSystem);
|
||||
Panel_SetCurrentType(&Parent->Left->Panel, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context);
|
||||
|
||||
Parent->Right = TakeNewPanelEntry(PanelSystem);
|
||||
Panel_SetCurrentType(&Parent->Right->Panel, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context);
|
||||
Parent->Left = PanelSystem_TakePanel(PanelSystem);
|
||||
Panel_SetCurrentType(Parent->Left, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context);
|
||||
Parent->Left->Parent = Parent;
|
||||
|
||||
Parent->Right = PanelSystem_TakePanel(PanelSystem);
|
||||
Panel_SetCurrentType(Parent->Right, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context);
|
||||
Parent->Right->Parent = Parent;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -278,17 +242,17 @@ SplitPanelHorizontally(panel* Parent, r32 Percent, panel_system* PanelSystem, ap
|
|||
}
|
||||
|
||||
internal void
|
||||
ConsolidatePanelsKeepOne(panel* Parent, panel_entry* PanelEntryToKeep, panel_system* PanelSystem)
|
||||
ConsolidatePanelsKeepOne(panel* Parent, panel* PanelToKeep, panel_system* PanelSystem)
|
||||
{
|
||||
panel_entry* LeftChild = Parent->Left;
|
||||
panel_entry* RightChild = Parent->Right;
|
||||
panel* LeftChild = Parent->Left;
|
||||
panel* RightChild = Parent->Right;
|
||||
|
||||
panel_entry* PanelEntryToDestroy = PanelEntryToKeep == LeftChild ? RightChild : LeftChild;
|
||||
panel* PanelToDestroy = PanelToKeep == LeftChild ? RightChild : LeftChild;
|
||||
|
||||
*Parent = PanelEntryToKeep->Panel;
|
||||
*Parent = *PanelToKeep;
|
||||
|
||||
FreePanelEntry(PanelEntryToKeep, PanelSystem);
|
||||
FreePanelEntryRecursive(PanelEntryToDestroy, PanelSystem);
|
||||
PanelSystem_FreePanel(PanelToKeep, PanelSystem);
|
||||
PanelSystem_FreePanelRecursive(PanelToDestroy, PanelSystem);
|
||||
}
|
||||
|
||||
/////////////////////////////////
|
||||
|
@ -297,6 +261,54 @@ ConsolidatePanelsKeepOne(panel* Parent, panel_entry* PanelEntryToKeep, panel_sys
|
|||
//
|
||||
/////////////////////////////////
|
||||
|
||||
internal rect2
|
||||
GetTopPanelBounds(panel* Panel)
|
||||
{
|
||||
rect2 Result = {};
|
||||
Result.Min = v2{
|
||||
Panel->Bounds.Min.x,
|
||||
LerpR32(Panel->SplitPercent, Panel->Bounds.Min.y, Panel->Bounds.Max.y)
|
||||
};
|
||||
Result.Max = Panel->Bounds.Max;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal rect2
|
||||
GetBottomPanelBounds(panel* Panel)
|
||||
{
|
||||
rect2 Result = {};
|
||||
Result.Min = Panel->Bounds.Min;
|
||||
Result.Max = v2{
|
||||
Panel->Bounds.Max.x,
|
||||
LerpR32(Panel->SplitPercent, Panel->Bounds.Min.y, Panel->Bounds.Max.y)
|
||||
};
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal rect2
|
||||
GetRightPanelBounds(panel* Panel)
|
||||
{
|
||||
rect2 Result = {};
|
||||
Result.Min = v2{
|
||||
LerpR32(Panel->SplitPercent, Panel->Bounds.Min.x, Panel->Bounds.Max.x),
|
||||
Panel->Bounds.Min.y
|
||||
};
|
||||
Result.Max = Panel->Bounds.Max;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal rect2
|
||||
GetLeftPanelBounds(panel* Panel)
|
||||
{
|
||||
rect2 Result = {};
|
||||
Result.Min = Panel->Bounds.Min;
|
||||
Result.Max = v2{
|
||||
LerpR32(Panel->SplitPercent, Panel->Bounds.Min.x, Panel->Bounds.Max.x),
|
||||
Panel->Bounds.Max.y
|
||||
};
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal rect2
|
||||
GetTopPanelBounds(panel* Panel, rect2 PanelBounds)
|
||||
{
|
||||
|
@ -346,103 +358,85 @@ GetLeftPanelBounds(panel* Panel, rect2 PanelBounds)
|
|||
}
|
||||
|
||||
internal void
|
||||
LayoutPanel(panel* Panel, rect2 PanelBounds, panel_layout* Layout)
|
||||
Panel_UpdateLayout(panel* Panel, rect2 Bounds)
|
||||
{
|
||||
if (Panel->SplitDirection == PanelSplit_NoSplit)
|
||||
Panel->Bounds = Bounds;
|
||||
|
||||
if (Panel->SplitDirection != PanelSplit_NoSplit)
|
||||
{
|
||||
panel_with_layout* WithLayout = Layout->Panels + Layout->PanelsCount++;
|
||||
WithLayout->Panel = Panel_GetModalOverride(Panel);
|
||||
WithLayout->Bounds = PanelBounds;
|
||||
rect2 LeftOrTopBounds = {};
|
||||
rect2 RightOrBottomBounds = {};
|
||||
switch (Panel->SplitDirection)
|
||||
{
|
||||
case PanelSplit_Horizontal:
|
||||
{
|
||||
LeftOrTopBounds = GetTopPanelBounds(Panel);
|
||||
RightOrBottomBounds = GetBottomPanelBounds(Panel);
|
||||
} break;
|
||||
|
||||
case PanelSplit_Vertical:
|
||||
{
|
||||
LeftOrTopBounds = GetLeftPanelBounds(Panel);
|
||||
RightOrBottomBounds = GetRightPanelBounds(Panel);
|
||||
} break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
else if (Panel->SplitDirection == PanelSplit_Horizontal)
|
||||
{
|
||||
rect2 TopPanelBounds = GetTopPanelBounds(Panel, PanelBounds);
|
||||
rect2 BottomPanelBounds = GetBottomPanelBounds(Panel, PanelBounds);
|
||||
|
||||
panel* TopPanel = Panel_GetModalOverride(&Panel->Top->Panel);
|
||||
panel* BottomPanel = Panel_GetModalOverride(&Panel->Bottom->Panel);
|
||||
|
||||
LayoutPanel(&Panel->Top->Panel, TopPanelBounds, Layout);
|
||||
LayoutPanel(&Panel->Bottom->Panel, BottomPanelBounds, Layout);
|
||||
}
|
||||
else if (Panel->SplitDirection == PanelSplit_Vertical)
|
||||
{
|
||||
rect2 LeftPanelBounds = GetLeftPanelBounds(Panel, PanelBounds);
|
||||
rect2 RightPanelBounds = GetRightPanelBounds(Panel, PanelBounds);
|
||||
|
||||
panel* LeftPanel = Panel_GetModalOverride(&Panel->Top->Panel);
|
||||
panel* RightPanel = Panel_GetModalOverride(&Panel->Bottom->Panel);
|
||||
|
||||
LayoutPanel(&Panel->Left->Panel, LeftPanelBounds, Layout);
|
||||
LayoutPanel(&Panel->Right->Panel, RightPanelBounds, Layout);
|
||||
Panel_UpdateLayout(Panel->Left, LeftOrTopBounds);
|
||||
Panel_UpdateLayout(Panel->Right, RightOrBottomBounds);
|
||||
}
|
||||
}
|
||||
|
||||
internal panel_layout
|
||||
GetPanelLayout(panel_system* System, rect2 WindowBounds, gs_memory_arena* Storage)
|
||||
internal void
|
||||
PanelSystem_UpdateLayout(panel_system* System, rect2 WindowBounds)
|
||||
{
|
||||
panel_layout Result = {};
|
||||
Result.PanelsMax = System->PanelsUsed;
|
||||
Result.Panels = PushArray(Storage, panel_with_layout, Result.PanelsMax);
|
||||
|
||||
LayoutPanel(&System->Panels[0].Panel, WindowBounds, &Result);
|
||||
|
||||
return Result;
|
||||
panel* Root = System->Panels;
|
||||
Panel_UpdateLayout(Root, WindowBounds);
|
||||
}
|
||||
|
||||
internal panel_with_layout
|
||||
GetPanelContainingPoint(v2 Point, panel* Panel, rect2 PanelBounds)
|
||||
internal panel*
|
||||
GetPanelContainingPoint(panel* Panel, v2 Point)
|
||||
{
|
||||
panel_with_layout Result = {0};
|
||||
panel* Result = 0;
|
||||
|
||||
if (Panel->SplitDirection == PanelSplit_NoSplit)
|
||||
if (PointIsInRect(Panel->Bounds, Point))
|
||||
{
|
||||
Result.Panel = Panel;
|
||||
Result.Bounds = PanelBounds;
|
||||
}
|
||||
else if (Panel->SplitDirection == PanelSplit_Horizontal)
|
||||
switch (Panel->SplitDirection)
|
||||
{
|
||||
rect2 TopPanelBounds = GetTopPanelBounds(Panel, PanelBounds);
|
||||
rect2 BottomPanelBounds = GetBottomPanelBounds(Panel, PanelBounds);
|
||||
case PanelSplit_NoSplit:
|
||||
{
|
||||
Result = Panel;
|
||||
}break;
|
||||
|
||||
if (PointIsInRect(TopPanelBounds, Point))
|
||||
case PanelSplit_Vertical:
|
||||
case PanelSplit_Horizontal:
|
||||
{
|
||||
Result = GetPanelContainingPoint(Point, &Panel->Top->Panel, TopPanelBounds);
|
||||
}
|
||||
else if (PointIsInRect(BottomPanelBounds, Point))
|
||||
if (PointIsInRect(Panel->Left->Bounds, Point))
|
||||
{
|
||||
Result = GetPanelContainingPoint(Point, &Panel->Bottom->Panel, BottomPanelBounds);
|
||||
Result = GetPanelContainingPoint(Panel->Left, Point);
|
||||
}
|
||||
}
|
||||
else if (Panel->SplitDirection == PanelSplit_Vertical)
|
||||
else if (PointIsInRect(Panel->Right->Bounds, Point))
|
||||
{
|
||||
rect2 LeftPanelBounds = GetLeftPanelBounds(Panel, PanelBounds);
|
||||
rect2 RightPanelBounds = GetRightPanelBounds(Panel, PanelBounds);
|
||||
Result = GetPanelContainingPoint(Panel->Right, Point);
|
||||
}
|
||||
}break;
|
||||
|
||||
if (PointIsInRect(LeftPanelBounds, Point))
|
||||
{
|
||||
Result = GetPanelContainingPoint(Point, &Panel->Left->Panel, LeftPanelBounds);
|
||||
}
|
||||
else if (PointIsInRect(RightPanelBounds, Point))
|
||||
{
|
||||
Result = GetPanelContainingPoint(Point, &Panel->Right->Panel, RightPanelBounds);
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal panel_with_layout
|
||||
GetPanelContainingPoint(v2 Point, panel_system* PanelSystem, rect2 WindowBounds)
|
||||
internal panel*
|
||||
PanelSystem_GetPanelContainingPoint(panel_system* System, v2 Point)
|
||||
{
|
||||
panel_with_layout Result = {0};
|
||||
if (PanelSystem->PanelsUsed > 0)
|
||||
{
|
||||
Result = GetPanelContainingPoint(Point, &PanelSystem->Panels[0].Panel, WindowBounds);
|
||||
}
|
||||
panel* Result = 0;
|
||||
panel* Root = System->Panels;
|
||||
Result = GetPanelContainingPoint(Root, Point);
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
#define FOLDHAUS_PANEL_H
|
||||
#endif // FOLDHAUS_PANEL_H
|
|
@ -6,13 +6,13 @@
|
|||
#ifndef FOLDHAUS_PANEL_ANIMATION_TIMELINE_H
|
||||
|
||||
// Colors
|
||||
global v4 TimeSliderColor = GreenV4; //v4{.36f, .52f, .78f, 1.f};
|
||||
global v4 TimeSliderColor = v4{.36f, .52f, .78f, 1.f};
|
||||
|
||||
//
|
||||
struct animation_timeline_state
|
||||
{
|
||||
frame_range VisibleRange;
|
||||
handle SelectedAnimationBlockHandle;
|
||||
handle SelectedBlockHandle;
|
||||
u32 SelectedAnimationLayer;
|
||||
};
|
||||
|
||||
|
@ -43,27 +43,17 @@ AddAnimationBlockAtCurrentTime (u32 AnimationProcHandle, u32 LayerHandle, animat
|
|||
return AnimHandle;
|
||||
}
|
||||
|
||||
internal void
|
||||
SelectAnimationBlock(handle BlockHandle, app_state* State)
|
||||
{
|
||||
State->SelectedAnimationBlockHandle = BlockHandle;
|
||||
}
|
||||
|
||||
internal void
|
||||
DeselectCurrentAnimationBlock(app_state* State)
|
||||
{
|
||||
State->SelectedAnimationBlockHandle = {};
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(DeleteAnimationBlockCommand)
|
||||
{
|
||||
handle SelectedAnimHandle = State->SelectedAnimationBlockHandle;
|
||||
animation_timeline_state* PanelState = Panel_GetStateStruct(Panel, animation_timeline_state);
|
||||
|
||||
handle SelectedBlockHandle = PanelState->SelectedBlockHandle;
|
||||
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
|
||||
if(SelectedAnimHandle.Index < ActiveAnim->Blocks_.Count &&
|
||||
ActiveAnim->Blocks_.Generations[SelectedAnimHandle.Index] == SelectedAnimHandle.Generation)
|
||||
if(SelectedBlockHandle.Index < ActiveAnim->Blocks_.Count &&
|
||||
ActiveAnim->Blocks_.Generations[SelectedBlockHandle.Index] == SelectedBlockHandle.Generation)
|
||||
{
|
||||
Animation_RemoveBlock(ActiveAnim, State->SelectedAnimationBlockHandle);
|
||||
State->SelectedAnimationBlockHandle = {0};
|
||||
Animation_RemoveBlock(ActiveAnim, PanelState->SelectedBlockHandle);
|
||||
PanelState->SelectedBlockHandle = {0};
|
||||
// TODO(pjs): Introduce an animation_block_selection in this file
|
||||
// it should have a handle to the animation, block, and a HasSelection flag
|
||||
// as it is now, you kind of always have the first block selected
|
||||
|
@ -119,9 +109,10 @@ StartDragTimeMarker(rect2 TimelineBounds, frame_range VisibleFrames, app_state*
|
|||
|
||||
#define CLICK_ANIMATION_BLOCK_EDGE_MAX_SCREEN_DISTANCE 10
|
||||
|
||||
OPERATION_STATE_DEF(drag_animation_clip_state)
|
||||
OPERATION_STATE_DEF(drag_animation_block_state)
|
||||
{
|
||||
rect2 TimelineBounds;
|
||||
handle BlockHandle;
|
||||
frame_range VisibleRange;
|
||||
frame_range ClipRange;
|
||||
};
|
||||
|
@ -138,9 +129,9 @@ AttemptToSnapPosition(u32 SnappingFrame, u32 SnapToFrame)
|
|||
return Result;
|
||||
}
|
||||
|
||||
OPERATION_RENDER_PROC(UpdateDragAnimationClip)
|
||||
OPERATION_RENDER_PROC(UpdateDragAnimationBlock)
|
||||
{
|
||||
drag_animation_clip_state* OpState = (drag_animation_clip_state*)Operation.OpStateMemory;
|
||||
drag_animation_block_state* OpState = (drag_animation_block_state*)Operation.OpStateMemory;
|
||||
|
||||
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
|
||||
|
||||
|
@ -158,10 +149,10 @@ OPERATION_RENDER_PROC(UpdateDragAnimationClip)
|
|||
u32 FrameAtMouseX = GetFrameFromPointInAnimationPanel(Mouse.Pos, OpState->TimelineBounds, OpState->VisibleRange);
|
||||
s32 FrameOffset = (s32)FrameAtMouseX - (s32)FrameAtMouseDownX;
|
||||
|
||||
animation_block* AnimationBlock = Animation_GetBlockFromHandle(ActiveAnim, State->SelectedAnimationBlockHandle);
|
||||
animation_block* AnimationBlock = Animation_GetBlockFromHandle(ActiveAnim, OpState->BlockHandle);
|
||||
if (!AnimationBlock)
|
||||
{
|
||||
EndCurrentOperationMode(State, {}, Mouse, Context);
|
||||
EndCurrentOperationMode(State);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -237,22 +228,23 @@ OPERATION_RENDER_PROC(UpdateDragAnimationClip)
|
|||
AnimationBlock->Range.Max = (u32)Clamp(PlayableStartFrame, (s32)AnimationBlock->Range.Max, PlayableEndFrame);
|
||||
}
|
||||
|
||||
input_command DragAnimationClipCommands [] = {
|
||||
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndCurrentOperationMode },
|
||||
input_command DragAnimationBlockCommands [] = {
|
||||
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, 0 },
|
||||
};
|
||||
|
||||
internal void
|
||||
SelectAndBeginDragAnimationBlock(handle BlockHandle, frame_range VisibleRange, rect2 TimelineBounds, app_state* State)
|
||||
SelectAndBeginDragAnimationBlock(animation_timeline_state* TimelineState, handle BlockHandle, frame_range VisibleRange, rect2 TimelineBounds, app_state* State)
|
||||
{
|
||||
SelectAnimationBlock(BlockHandle, State);
|
||||
TimelineState->SelectedBlockHandle = BlockHandle;
|
||||
|
||||
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
|
||||
operation_mode* DragAnimationClipMode = ActivateOperationModeWithCommands(&State->Modes, DragAnimationClipCommands, UpdateDragAnimationClip);
|
||||
operation_mode* DragAnimationBlockMode = ActivateOperationModeWithCommands(&State->Modes, DragAnimationBlockCommands, UpdateDragAnimationBlock);
|
||||
|
||||
drag_animation_clip_state* OpState = CreateOperationState(DragAnimationClipMode,
|
||||
drag_animation_block_state* OpState = CreateOperationState(DragAnimationBlockMode,
|
||||
&State->Modes,
|
||||
drag_animation_clip_state);
|
||||
drag_animation_block_state);
|
||||
OpState->TimelineBounds = TimelineBounds;
|
||||
OpState->BlockHandle = BlockHandle;
|
||||
OpState->VisibleRange = VisibleRange;
|
||||
|
||||
animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, BlockHandle);
|
||||
|
@ -262,14 +254,15 @@ SelectAndBeginDragAnimationBlock(handle BlockHandle, frame_range VisibleRange, r
|
|||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(AddAnimationBlockCommand)
|
||||
{
|
||||
animation_timeline_state* TimelineState = Panel_GetStateStruct(Panel, animation_timeline_state);
|
||||
|
||||
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
|
||||
|
||||
panel_with_layout ActivePanel = GetPanelContainingPoint(Mouse.Pos, &State->PanelSystem, State->WindowBounds);
|
||||
frame_range Range = ActiveAnim->PlayableRange;
|
||||
u32 MouseDownFrame = GetFrameFromPointInAnimationPanel(Mouse.Pos, ActivePanel.Bounds, Range);
|
||||
u32 MouseDownFrame = GetFrameFromPointInAnimationPanel(Mouse.Pos, Panel->Bounds, Range);
|
||||
|
||||
handle NewBlockHandle = Animation_AddBlock(ActiveAnim, MouseDownFrame, MouseDownFrame + SecondsToFrames(3, State->AnimationSystem), 4, State->SelectedAnimationLayer);
|
||||
SelectAnimationBlock(NewBlockHandle, State);
|
||||
handle NewBlockHandle = Animation_AddBlock(ActiveAnim, MouseDownFrame, MouseDownFrame + SecondsToFrames(3, State->AnimationSystem), 4, TimelineState->SelectedAnimationLayer);
|
||||
TimelineState->SelectedBlockHandle = NewBlockHandle;
|
||||
}
|
||||
|
||||
input_command AnimationTimeline_Commands[] = {
|
||||
|
@ -310,6 +303,8 @@ DrawFrameBar (animation_system* AnimationSystem, ui_interface Interface, frame_r
|
|||
r32 BarWidth = Rect2Width(BarBounds);
|
||||
|
||||
// Mouse clicked inside frame nubmer bar -> change current frame on timeline
|
||||
// TODO(pjs): both of these functions can get wrapped in a MouseClickedRect
|
||||
// and an alternate MouseIsDraggingRect
|
||||
if (MouseButtonTransitionedDown(Interface.Mouse.LeftButtonState) &&
|
||||
PointIsInRect(BarBounds, Interface.Mouse.DownPos))
|
||||
{
|
||||
|
@ -484,6 +479,8 @@ DrawAnimationBlock (animation_block AnimationBlock, v4 BlockColor, frame_range V
|
|||
PushRenderQuad2D(RenderBuffer, BlockBounds.Min, BlockBounds.Max, BlockColor);
|
||||
PushRenderBoundingBox2D(RenderBuffer, BlockBounds.Min, BlockBounds.Max, 1, WhiteV4);
|
||||
|
||||
// TODO(pjs): If mouse is on one of the border hot spots, render an off colored square to signal the region is hot
|
||||
|
||||
return BlockBounds;
|
||||
}
|
||||
|
||||
|
@ -505,7 +502,7 @@ DrawAnimationTimeline (animation_system* AnimationSystem, animation_timeline_sta
|
|||
RectHSplitAtDistanceFromTop(TimelineBounds, 32, &TimelineFrameBarBounds, &TimelineBounds);
|
||||
RectHSplitAtDistanceFromBottom(TimelineBounds, 24, &TimelineBlockDisplayBounds, &TimelineRangeBarBounds);
|
||||
|
||||
DrawLayerMenu(AnimationSystem, CurrAnimation, *Interface, LayerMenuBounds, &State->SelectedAnimationLayer);
|
||||
DrawLayerMenu(AnimationSystem, CurrAnimation, *Interface, LayerMenuBounds, &TimelineState->SelectedAnimationLayer);
|
||||
|
||||
frame_range AdjustedViewRange = DrawTimelineRangeBar(AnimationSystem, CurrAnimation, TimelineState, *Interface, TimelineRangeBarBounds);
|
||||
|
||||
|
@ -546,7 +543,7 @@ DrawAnimationTimeline (animation_system* AnimationSystem, animation_timeline_sta
|
|||
if (MouseDownAndNotHandled && Handle_IsValid(DragBlockHandle))
|
||||
{
|
||||
MouseDownAndNotHandled = false;
|
||||
SelectAndBeginDragAnimationBlock(DragBlockHandle, AdjustedViewRange, TimelineBounds, State);
|
||||
SelectAndBeginDragAnimationBlock(TimelineState, DragBlockHandle, AdjustedViewRange, TimelineBounds, State);
|
||||
}
|
||||
|
||||
// Time Slider
|
||||
|
@ -567,7 +564,7 @@ DrawAnimationTimeline (animation_system* AnimationSystem, animation_timeline_sta
|
|||
|
||||
if (MouseDownAndNotHandled && PointIsInRect(TimelineBounds, Interface->Mouse.Pos))
|
||||
{
|
||||
DeselectCurrentAnimationBlock(State);
|
||||
TimelineState->SelectedBlockHandle = {0};
|
||||
}
|
||||
|
||||
return Result;
|
||||
|
@ -579,27 +576,256 @@ PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback)
|
|||
file_view_state* FileViewState = Panel_GetStateStruct(ReturningFrom, file_view_state);
|
||||
gs_file_info FileInfo = FileViewState->SelectedFile;
|
||||
|
||||
if (FileInfo.Path.Length > 0)
|
||||
{
|
||||
gs_file AnimFile = ReadEntireFile(Context.ThreadContext.FileHandler, FileInfo.Path);
|
||||
gs_string AnimFileString = MakeString((char*)AnimFile.Data.Memory, AnimFile.Data.Size);
|
||||
animation NewAnim = AnimParser_Parse(AnimFileString, State->AnimationSystem.Storage, GlobalAnimationClipsCount, GlobalAnimationClips);
|
||||
animation NewAnim = AnimParser_Parse(AnimFileString, State->AnimationSystem.Storage, GlobalAnimationPatternsCount, GlobalAnimationPatterns);
|
||||
|
||||
u32 NewAnimIndex = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim);
|
||||
State->AnimationSystem.ActiveAnimationIndex = NewAnimIndex;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
DrawAnimationClipsList(rect2 PanelBounds, ui_interface* Interface, u32 SelectedAnimationLayerHandle, animation_system* AnimationSystem)
|
||||
DrawAnimationPatternList(rect2 PanelBounds, ui_interface* Interface, u32 SelectedAnimationLayerHandle, animation_system* AnimationSystem)
|
||||
{
|
||||
ui_layout Layout = ui_CreateLayout(*Interface, PanelBounds);
|
||||
for (s32 i = 0; i < GlobalAnimationClipsCount; i++)
|
||||
ui_layout Layout = ui_CreateLayout(Interface, PanelBounds);
|
||||
ui_PushLayout(Interface, Layout);
|
||||
for (s32 i = 0; i < GlobalAnimationPatternsCount; i++)
|
||||
{
|
||||
animation_clip Clip = GlobalAnimationClips[i];
|
||||
gs_string ClipName = MakeString(Clip.Name, Clip.NameLength);
|
||||
if (ui_LayoutListEntry(Interface, &Layout, ClipName, i))
|
||||
animation_pattern Pattern = GlobalAnimationPatterns[i];
|
||||
gs_string PatternName = MakeString(Pattern.Name, Pattern.NameLength);
|
||||
if (ui_LayoutListEntry(Interface, &Layout, PatternName, i))
|
||||
{
|
||||
AddAnimationBlockAtCurrentTime(i + 1, SelectedAnimationLayerHandle, AnimationSystem);
|
||||
}
|
||||
}
|
||||
ui_PopLayout(Interface);
|
||||
}
|
||||
|
||||
internal void
|
||||
PlayBar_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
animation_system* AnimSystem = &State->AnimationSystem;
|
||||
ui_interface* Interface = &State->Interface;
|
||||
ui_layout Layout = ui_CreateLayout(Interface, Bounds);
|
||||
ui_PushLayout(Interface, Layout);
|
||||
|
||||
ui_FillRect(Interface, Bounds, Interface->Style.PanelBGColors[0]);
|
||||
ui_StartRow(&State->Interface, 4);
|
||||
{
|
||||
if (ui_Button(Interface, MakeString("Pause")))
|
||||
{
|
||||
AnimSystem->TimelineShouldAdvance = false;
|
||||
}
|
||||
|
||||
if (ui_Button(Interface, MakeString("Play")))
|
||||
{
|
||||
AnimSystem->TimelineShouldAdvance = true;
|
||||
}
|
||||
|
||||
if (ui_Button(Interface, MakeString("Stop")))
|
||||
{
|
||||
AnimSystem->TimelineShouldAdvance = false;
|
||||
AnimSystem->CurrentFrame = 0;
|
||||
}
|
||||
|
||||
if (ui_Button(Interface, MakeString("Load")))
|
||||
{
|
||||
panel* FileBrowser = PanelSystem_PushPanel(&State->PanelSystem, PanelType_FileView, State, Context);
|
||||
Panel_PushModalOverride(Panel, FileBrowser, LoadAnimationFileCallback);
|
||||
}
|
||||
}
|
||||
ui_EndRow(&State->Interface);
|
||||
ui_PopLayout(&State->Interface);
|
||||
}
|
||||
|
||||
internal void
|
||||
FrameCount_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
ui_interface* Interface = &State->Interface;
|
||||
gs_string TempString = PushString(State->Transient, 256);
|
||||
frame_range VisibleFrames = TimelineState->VisibleRange;
|
||||
s32 VisibleFrameCount = VisibleFrames.Max - VisibleFrames.Min;
|
||||
|
||||
ui_FillRect(Interface, Bounds, Interface->Style.PanelBGColors[0]);
|
||||
|
||||
// Frame Ticks
|
||||
u32 TickCount = 10;
|
||||
for (u32 Tick = 0; Tick < TickCount; Tick++)
|
||||
{
|
||||
r32 Percent = (r32)Tick / (r32)TickCount;
|
||||
u32 Frame = PercentToFrameInRange(Percent, VisibleFrames);
|
||||
PrintF(&TempString, "%d", Frame);
|
||||
r32 FramePercent = FrameToPercentRange(Frame, VisibleFrames);
|
||||
r32 FrameX = LerpR32(FramePercent, Bounds.Min.x, Bounds.Max.x);
|
||||
v2 FrameTextPos = v2{FrameX, Bounds.Min.y + 2};
|
||||
DrawString(Interface->RenderBuffer, TempString, Interface->Style.Font, FrameTextPos, WhiteV4);
|
||||
}
|
||||
|
||||
// Time Slider
|
||||
s32 CurrentFrame = State->AnimationSystem.CurrentFrame;
|
||||
if (FrameIsInRange(VisibleFrames, CurrentFrame))
|
||||
{
|
||||
r32 FrameAtPercentVisibleRange = FrameToPercentRange(CurrentFrame, VisibleFrames);
|
||||
r32 SliderX = LerpR32(FrameAtPercentVisibleRange, Bounds.Min.x, Bounds.Max.x);
|
||||
|
||||
PrintF(&TempString, "%d", CurrentFrame);
|
||||
|
||||
// space for each character + a margin on either side
|
||||
r32 SliderWidth = (8 * TempString.Length) + 8;
|
||||
r32 SliderHalfWidth = SliderWidth / 2.f;
|
||||
v2 HeadMin = v2{SliderX - SliderHalfWidth, Bounds.Min.y};
|
||||
v2 HeadMax = v2{SliderX + SliderHalfWidth, Bounds.Max.y};
|
||||
PushRenderQuad2D(Interface->RenderBuffer, HeadMin, HeadMax, TimeSliderColor);
|
||||
DrawString(Interface->RenderBuffer, TempString, Interface->Style.Font, HeadMin + v2{6, 4}, WhiteV4);
|
||||
}
|
||||
|
||||
// Interaction
|
||||
// Mouse clicked inside frame nubmer bar -> change current frame on timeline
|
||||
if (ui_MouseClickedRect(*Interface, Bounds))
|
||||
{
|
||||
StartDragTimeMarker(Bounds, VisibleFrames, State);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
LayerList_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
ui_interface* Interface = &State->Interface;
|
||||
animation ActiveAnim = *AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
|
||||
|
||||
ui_FillRect(Interface, Bounds, Interface->Style.PanelBGColors[0]);
|
||||
|
||||
v2 LayerDim = { Rect2Width(Bounds), LAYER_HEIGHT };
|
||||
rect2 LayerBounds = {0};
|
||||
LayerBounds.Min = Bounds.Min;
|
||||
LayerBounds.Max = LayerBounds.Min + LayerDim;
|
||||
for (u32 i = 0; i < ActiveAnim.Layers.Count; i++)
|
||||
{
|
||||
anim_layer* Layer = ActiveAnim.Layers.Values + i;
|
||||
|
||||
if (ui_MouseClickedRect(*Interface, LayerBounds))
|
||||
{
|
||||
TimelineState->SelectedAnimationLayer = i;
|
||||
}
|
||||
|
||||
v2 LayerTextPos = { LayerBounds.Min.x + 6, LayerBounds.Max.y - 16};
|
||||
if (TimelineState->SelectedAnimationLayer == i)
|
||||
{
|
||||
PushRenderBoundingBox2D(Interface->RenderBuffer, LayerBounds.Min, LayerBounds.Max, 1, WhiteV4);
|
||||
}
|
||||
DrawString(Interface->RenderBuffer, Layer->Name, Interface->Style.Font, LayerTextPos, WhiteV4);
|
||||
|
||||
LayerBounds = Rect2TranslateY(LayerBounds, LayerDim.y);
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
ui_interface* Interface = &State->Interface;
|
||||
frame_range ViewRange = TimelineState->VisibleRange;
|
||||
|
||||
animation ActiveAnim = *AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
|
||||
handle SelectedBlockHandle = TimelineState->SelectedBlockHandle;
|
||||
s32 CurrentFrame = State->AnimationSystem.CurrentFrame;
|
||||
|
||||
// Animation Blocks
|
||||
b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState);
|
||||
handle DragBlockHandle = {0};
|
||||
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
|
||||
b32 RangeIsVisible = (FrameIsInRange(ViewRange, AnimationBlockAt->Range.Min) ||
|
||||
FrameIsInRange(ViewRange, 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 <= ViewRange.Min &&
|
||||
AnimationBlockAt->Range.Max>= ViewRange.Max);
|
||||
if (RangeIsVisible)
|
||||
{
|
||||
v4 BlockColor = BlackV4;
|
||||
if (SelectedBlockHandle.Index == i && SelectedBlockHandle.Generation == ActiveAnim.Blocks_.Generations[i])
|
||||
{
|
||||
BlockColor = PinkV4;
|
||||
}
|
||||
rect2 BlockBounds = DrawAnimationBlock(*AnimationBlockAt, BlockColor, ViewRange, Bounds, Interface->RenderBuffer);
|
||||
|
||||
if (PointIsInRect(BlockBounds, Interface->Mouse.Pos))
|
||||
{
|
||||
DragBlockHandle.Index = i;
|
||||
DragBlockHandle.Generation = ActiveAnim.Blocks_.Generations[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Time Slider
|
||||
if (FrameIsInRange(ViewRange, CurrentFrame))
|
||||
{
|
||||
r32 FrameAtPercentVisibleRange = FrameToPercentRange(CurrentFrame, ViewRange);
|
||||
r32 SliderX = LerpR32(FrameAtPercentVisibleRange, Bounds.Min.x, Bounds.Max.x);
|
||||
rect2 SliderBounds = {
|
||||
v2{ SliderX, Bounds.Min.y },
|
||||
v2{ SliderX + 1, Bounds.Max.y }
|
||||
};
|
||||
ui_FillRect(Interface, SliderBounds, TimeSliderColor);
|
||||
}
|
||||
|
||||
// Interaction
|
||||
if (MouseDownAndNotHandled)
|
||||
{
|
||||
if (Handle_IsValid(DragBlockHandle))
|
||||
{
|
||||
MouseDownAndNotHandled = false;
|
||||
SelectAndBeginDragAnimationBlock(TimelineState, DragBlockHandle, ViewRange, Bounds, State);
|
||||
}
|
||||
else if (PointIsInRect(Bounds, Interface->Mouse.Pos))
|
||||
{
|
||||
TimelineState->SelectedBlockHandle = {0};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
animation_system* AnimSystem = &State->AnimationSystem;
|
||||
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(AnimSystem);
|
||||
|
||||
ui_interface* Interface = &State->Interface;
|
||||
ui_layout Layout = ui_CreateLayout(Interface, Bounds);
|
||||
ui_PushLayout(Interface, Layout);
|
||||
|
||||
ui_FillRect(&State->Interface, Bounds, Interface->Style.PanelBGColors[0]);
|
||||
|
||||
ui_StartRow(&State->Interface, 2);
|
||||
{
|
||||
ui_DrawString(Interface, MakeString("Active Animation"));
|
||||
if (ui_BeginDropdown(Interface, ActiveAnim->Name))
|
||||
{
|
||||
for (u32 i = 0; i < AnimSystem->Animations.Count; i++)
|
||||
{
|
||||
animation Animation = AnimSystem->Animations.Values[i];
|
||||
if (ui_Button(Interface, Animation.Name))
|
||||
{
|
||||
AnimSystem->ActiveAnimationIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
ui_EndDropdown(Interface);
|
||||
}
|
||||
ui_EndRow(&State->Interface);
|
||||
ui_PopLayout(Interface);
|
||||
}
|
||||
|
||||
internal void
|
||||
SelectionInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
ui_FillRect(&State->Interface, Bounds, YellowV4);
|
||||
}
|
||||
|
||||
GSMetaTag(panel_render);
|
||||
|
@ -608,50 +834,32 @@ internal void
|
|||
AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
animation_timeline_state* TimelineState = Panel_GetStateStruct(Panel, animation_timeline_state);
|
||||
// TODO(pjs): SelectedAnimationBlockHandle should be a property of animation_timeline_state
|
||||
// unless its used elsewhere. Audit later
|
||||
handle SelectedBlockHandle = State->SelectedAnimationBlockHandle;
|
||||
ui_interface* Interface = &State->Interface;
|
||||
animation_system* AnimationSystem = &State->AnimationSystem;
|
||||
|
||||
rect2 TitleBarBounds, PanelContentsBounds;
|
||||
rect2 AnimationListBounds, TimelineBounds;
|
||||
RectHSplitAtDistanceFromTop(PanelBounds, Interface->Style.RowHeight, &TitleBarBounds, &PanelContentsBounds);
|
||||
RectVSplitAtDistanceFromLeft(PanelContentsBounds, 300, &AnimationListBounds, &TimelineBounds);
|
||||
rect2 TimelineBounds, InfoBounds;
|
||||
RectVSplit(PanelBounds, 300, &InfoBounds, &TimelineBounds);
|
||||
|
||||
ui_FillRect(Interface, TitleBarBounds, Interface->Style.PanelBGColors[0]);
|
||||
ui_layout TitleBarLayout = ui_CreateLayout(*Interface, TitleBarBounds);
|
||||
ui_StartRow(&TitleBarLayout, 4);
|
||||
{
|
||||
if (ui_LayoutButton(Interface, &TitleBarLayout, MakeString("Pause")))
|
||||
{
|
||||
State->AnimationSystem.TimelineShouldAdvance = false;
|
||||
rect2 AnimInfoBounds, SelectionInfoBounds;
|
||||
RectHSplitAtPercent(InfoBounds, .65f, &AnimInfoBounds, &SelectionInfoBounds);
|
||||
|
||||
{ // Timeline
|
||||
rect2 LayersPanelBounds, TimeRangePanelBounds;
|
||||
RectVSplitAtDistanceFromLeft(TimelineBounds, 200, &LayersPanelBounds, &TimeRangePanelBounds);
|
||||
|
||||
r32 TitleBarHeight = State->Interface.Style.RowHeight;
|
||||
// These are the actual rects we will draw in
|
||||
rect2 PlayBarBounds, FrameCountBounds;
|
||||
rect2 LayersBounds, TimeRangeBounds;
|
||||
RectHSplitAtDistanceFromTop(LayersPanelBounds, TitleBarHeight, &PlayBarBounds, &LayersBounds);
|
||||
RectHSplitAtDistanceFromTop(TimeRangePanelBounds, TitleBarHeight, &FrameCountBounds, &TimeRangeBounds);
|
||||
|
||||
PlayBar_Render(TimelineState, PlayBarBounds, Panel, RenderBuffer, State, Context);
|
||||
FrameCount_Render(TimelineState, FrameCountBounds, RenderBuffer, State, Context);
|
||||
LayerList_Render(TimelineState, LayersBounds, RenderBuffer, State, Context);
|
||||
TimeRange_Render(TimelineState, TimeRangeBounds, RenderBuffer, State, Context);
|
||||
}
|
||||
|
||||
if (ui_LayoutButton(Interface, &TitleBarLayout, MakeString("Play"), (State->AnimationSystem.TimelineShouldAdvance ? PinkV4 : BlackV4), v4{.3f, .3f, .3f, 1.0f}, TealV4))
|
||||
{
|
||||
State->AnimationSystem.TimelineShouldAdvance = true;
|
||||
}
|
||||
|
||||
if (ui_LayoutButton(Interface, &TitleBarLayout, MakeString("Stop")))
|
||||
{
|
||||
State->AnimationSystem.TimelineShouldAdvance = false;
|
||||
State->AnimationSystem.CurrentFrame = 0;
|
||||
}
|
||||
|
||||
if (ui_LayoutButton(Interface, &TitleBarLayout, MakeString("Load")))
|
||||
{
|
||||
panel_entry* FileBrowser = PanelSystem_PushPanel(&State->PanelSystem, PanelType_FileView, State, Context);
|
||||
Panel_PushModalOverride(Panel, FileBrowser, LoadAnimationFileCallback);
|
||||
}
|
||||
}
|
||||
ui_EndRow(&TitleBarLayout);
|
||||
|
||||
if (Rect2Height(TimelineBounds) > 0)
|
||||
{
|
||||
SelectedBlockHandle = DrawAnimationTimeline(AnimationSystem, TimelineState, TimelineBounds, SelectedBlockHandle, Interface, State);
|
||||
DrawAnimationClipsList(AnimationListBounds, Interface, State->SelectedAnimationLayer, &State->AnimationSystem);
|
||||
}
|
||||
AnimInfoView_Render(TimelineState, AnimInfoBounds, RenderBuffer, State, Context);
|
||||
SelectionInfoView_Render(TimelineState, SelectionInfoBounds, RenderBuffer, State, Context);
|
||||
}
|
||||
|
||||
#define FOLDHAUS_PANEL_ANIMATION_TIMELINE_H
|
||||
|
|
|
@ -88,15 +88,16 @@ internal void
|
|||
FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
file_view_state* FileViewState = Panel_GetStateStruct(Panel, file_view_state);
|
||||
ui_layout Layout = ui_CreateLayout(State->Interface, PanelBounds);
|
||||
ui_layout Layout = ui_CreateLayout(&State->Interface, PanelBounds);
|
||||
ui_PushLayout(&State->Interface, Layout);
|
||||
|
||||
if (ui_LayoutButton(&State->Interface, &Layout, MakeString("Exit")))
|
||||
if (ui_Button(&State->Interface, MakeString("Exit")))
|
||||
{
|
||||
FileView_Exit_(Panel, State, Context);
|
||||
}
|
||||
|
||||
// Header
|
||||
ui_LayoutDrawString(&State->Interface, &Layout, FileViewState->WorkingDirectory, v4{0, 1, 0, 1});
|
||||
ui_DrawString(&State->Interface, FileViewState->WorkingDirectory);
|
||||
|
||||
// File Display
|
||||
for (u32 i = 0; i < FileViewState->FileNames.Count; i++)
|
||||
|
@ -121,6 +122,7 @@ FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBu
|
|||
}
|
||||
}
|
||||
|
||||
ui_PopLayout(&State->Interface);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -38,7 +38,9 @@ GSMetaTag(panel_type_hierarchy);
|
|||
internal void
|
||||
HierarchyView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
ui_layout Layout = ui_CreateLayout(State->Interface, PanelBounds);
|
||||
ui_layout Layout = ui_CreateLayout(&State->Interface, PanelBounds);
|
||||
ui_PushLayout(&State->Interface, Layout);
|
||||
|
||||
gs_string TempString = PushString(State->Transient, 256);
|
||||
u32 LineCount = (u32)(Rect2Height(PanelBounds) / Layout.RowHeight) + 1;
|
||||
u32 AssembliesToDraw = Min(LineCount, State->Assemblies.Count);
|
||||
|
@ -57,16 +59,15 @@ HierarchyView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren
|
|||
assembly Assembly = State->Assemblies.Values[AssemblyIndex];
|
||||
PrintF(&TempString, "%S", Assembly.Name);
|
||||
|
||||
ui_layout ItemLayout = ui_CreateLayout(State->Interface, LineBounds[AssemblyIndex]);
|
||||
ui_StartRow(&ItemLayout, 2);
|
||||
ui_StartRow(&State->Interface, 2);
|
||||
{
|
||||
ui_LayoutDrawString(&State->Interface, &ItemLayout, TempString, State->Interface.Style.TextColor);
|
||||
if (ui_LayoutListButton(&State->Interface, &ItemLayout, MakeString("X"), AssemblyIndex))
|
||||
ui_DrawString(&State->Interface, TempString);
|
||||
if (ui_LayoutListButton(&State->Interface, &Layout, MakeString("X"), AssemblyIndex))
|
||||
{
|
||||
UnloadAssembly(AssemblyIndex, State, Context);
|
||||
}
|
||||
}
|
||||
ui_EndRow(&ItemLayout);
|
||||
ui_EndRow(&State->Interface);
|
||||
}
|
||||
|
||||
if (AssembliesToDraw < LineCount)
|
||||
|
@ -75,10 +76,12 @@ HierarchyView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren
|
|||
PrintF(&TempString, "+ Add Assembly");
|
||||
if (ui_ListButton(&State->Interface, TempString, LineBounds[AssembliesToDraw], AssembliesToDraw))
|
||||
{
|
||||
panel_entry* FileBrowser = PanelSystem_PushPanel(&State->PanelSystem, PanelType_FileView, State, Context);
|
||||
panel* FileBrowser = PanelSystem_PushPanel(&State->PanelSystem, PanelType_FileView, State, Context);
|
||||
Panel_PushModalOverride(Panel, FileBrowser, LoadAssemblyCallback);
|
||||
}
|
||||
}
|
||||
|
||||
ui_PopLayout(&State->Interface);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ RenderProfiler_ScopeVisualization(ui_interface* Interface, ui_layout Layout, deb
|
|||
PrintF(&String, "%S : %d - %d", HotRecordName->Name, HotRecord->StartCycles, HotRecord->EndCycles);
|
||||
|
||||
rect2 TextBounds = MakeRect2MinDim(Interface->Mouse.Pos, v2{256, 32});
|
||||
ui_TextBox(Interface, TextBounds, String, BlackV4, WhiteV4);
|
||||
ui_DrawString(Interface, String, TextBounds);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,15 +95,15 @@ RenderProfiler_ListVisualization(ui_interface* Interface, ui_layout Layout, debu
|
|||
gs_string String = MakeString(Backbuffer, 0, 256);
|
||||
|
||||
r32 ColumnWidths[] = {256, 128, 128, 128, 128};
|
||||
ui_StartRow(&Layout, 5, &ColumnWidths[0]);
|
||||
ui_StartRow(Interface, 5, &ColumnWidths[0]);
|
||||
{
|
||||
ui_LayoutDrawString(Interface, &Layout, MakeString("Procedure"), Interface->Style.TextColor);
|
||||
ui_LayoutDrawString(Interface, &Layout, MakeString("% Frame"), Interface->Style.TextColor);
|
||||
ui_LayoutDrawString(Interface, &Layout, MakeString("Seconds"), Interface->Style.TextColor);
|
||||
ui_LayoutDrawString(Interface, &Layout, MakeString("Cycles"), Interface->Style.TextColor);
|
||||
ui_LayoutDrawString(Interface, &Layout, MakeString("Calls"), Interface->Style.TextColor);
|
||||
ui_DrawString(Interface, MakeString("Procedure"));
|
||||
ui_DrawString(Interface, MakeString("% Frame"));
|
||||
ui_DrawString(Interface, MakeString("Seconds"));
|
||||
ui_DrawString(Interface, MakeString("Cycles"));
|
||||
ui_DrawString(Interface, MakeString("Calls"));
|
||||
}
|
||||
ui_EndRow(&Layout);
|
||||
ui_EndRow(Interface);
|
||||
|
||||
for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++)
|
||||
{
|
||||
|
@ -112,24 +112,24 @@ RenderProfiler_ListVisualization(ui_interface* Interface, ui_layout Layout, debu
|
|||
{
|
||||
collated_scope_record* CollatedRecord = VisibleFrame->CollatedScopes + n;
|
||||
|
||||
ui_StartRow(&Layout, 5, &ColumnWidths[0]);
|
||||
ui_StartRow(Interface, 5, &ColumnWidths[0]);
|
||||
{
|
||||
PrintF(&String, "%S", NameEntry.Name);
|
||||
ui_LayoutDrawString(Interface, &Layout, String, Interface->Style.TextColor);
|
||||
ui_DrawString(Interface, String);
|
||||
|
||||
PrintF(&String, "%f%%", CollatedRecord->PercentFrameTime);
|
||||
ui_LayoutDrawString(Interface, &Layout, String, Interface->Style.TextColor);
|
||||
ui_DrawString(Interface, String);
|
||||
|
||||
PrintF(&String, "%fs", CollatedRecord->TotalSeconds);
|
||||
ui_LayoutDrawString(Interface, &Layout, String, Interface->Style.TextColor);
|
||||
ui_DrawString(Interface, String);
|
||||
|
||||
PrintF(&String, "%dcy", CollatedRecord->TotalCycles);
|
||||
ui_LayoutDrawString(Interface, &Layout, String, Interface->Style.TextColor);
|
||||
ui_DrawString(Interface, String);
|
||||
|
||||
PrintF(&String, "%d", CollatedRecord->CallCount);
|
||||
ui_LayoutDrawString(Interface, &Layout, String, Interface->Style.TextColor);
|
||||
ui_DrawString(Interface, String);
|
||||
}
|
||||
ui_EndRow(&Layout);
|
||||
ui_EndRow(Interface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -179,41 +179,43 @@ ProfilerView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Rend
|
|||
|
||||
debug_frame* VisibleFrame = GetLastDebugFrame(GlobalDebugServices);
|
||||
|
||||
ui_layout Layout = ui_CreateLayout(State->Interface, ProcListBounds);
|
||||
ui_StartRow(&Layout, 4);
|
||||
ui_layout Layout = ui_CreateLayout(&State->Interface, ProcListBounds);
|
||||
ui_PushLayout(&State->Interface, Layout);
|
||||
|
||||
ui_StartRow(&State->Interface, 4);
|
||||
{
|
||||
s64 FrameStartCycles = VisibleFrame->FrameStartCycles;
|
||||
s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles;
|
||||
u32 CurrentDebugFrame = GlobalDebugServices->CurrentDebugFrame - 1;
|
||||
PrintF(&String, "Frame %d", CurrentDebugFrame);
|
||||
ui_LayoutDrawString(&State->Interface, &Layout, String, WhiteV4);
|
||||
ui_DrawString(&State->Interface, String);
|
||||
|
||||
PrintF(&String, "Total Cycles: %lld", FrameTotalCycles);
|
||||
ui_LayoutDrawString(&State->Interface, &Layout, String, WhiteV4);
|
||||
ui_DrawString(&State->Interface, String);
|
||||
|
||||
// NOTE(NAME): Skipping a space for aesthetic reasons, not functional, and could
|
||||
// be removed, or used for something else
|
||||
ui_ReserveElementBounds(&Layout);
|
||||
|
||||
if (ui_LayoutButton(&State->Interface, &Layout, MakeString("Resume Recording")))
|
||||
if (ui_Button(&State->Interface, MakeString("Resume Recording")))
|
||||
{
|
||||
GlobalDebugServices->RecordFrames = true;
|
||||
}
|
||||
}
|
||||
ui_EndRow(&Layout);
|
||||
ui_EndRow(&State->Interface);
|
||||
|
||||
ui_StartRow(&Layout, 8);
|
||||
ui_StartRow(&State->Interface, 8);
|
||||
{
|
||||
if (ui_LayoutButton(&State->Interface, &Layout, MakeString("Scope View")))
|
||||
if (ui_Button(&State->Interface, MakeString("Scope View")))
|
||||
{
|
||||
GlobalDebugServices->Interface.FrameView = FRAME_VIEW_PROFILER;
|
||||
}
|
||||
if (ui_LayoutButton(&State->Interface, &Layout, MakeString("List View")))
|
||||
if (ui_Button(&State->Interface, MakeString("List View")))
|
||||
{
|
||||
GlobalDebugServices->Interface.FrameView = FRAME_VIEW_SCOPE_LIST;
|
||||
}
|
||||
}
|
||||
ui_EndRow(&Layout);
|
||||
ui_EndRow(&State->Interface);
|
||||
|
||||
if (GlobalDebugServices->Interface.FrameView == FRAME_VIEW_PROFILER)
|
||||
{
|
||||
|
@ -223,6 +225,8 @@ ProfilerView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Rend
|
|||
{
|
||||
RenderProfiler_ListVisualization(&State->Interface, Layout, VisibleFrame, Memory);
|
||||
}
|
||||
|
||||
ui_PopLayout(&State->Interface);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,11 +5,17 @@
|
|||
//
|
||||
#ifndef FOLDHAUS_PANEL_SCULPTURE_VIEW_H
|
||||
|
||||
struct sculpture_view_panel_state
|
||||
{
|
||||
camera Camera;
|
||||
};
|
||||
|
||||
// 3D Mouse View
|
||||
|
||||
OPERATION_STATE_DEF(mouse_rotate_view_operation_state)
|
||||
{
|
||||
v4 CameraStartPos;
|
||||
camera* Camera;
|
||||
};
|
||||
|
||||
OPERATION_RENDER_PROC(Update3DViewMouseRotate)
|
||||
|
@ -22,7 +28,7 @@ OPERATION_RENDER_PROC(Update3DViewMouseRotate)
|
|||
m44 YRotation = M44RotationY(TotalDeltaPos.x * State->PixelsToWorldScale);
|
||||
m44 Combined = XRotation * YRotation;
|
||||
|
||||
State->Camera.Position = (Combined * OpState->CameraStartPos).xyz;
|
||||
OpState->Camera->Position = (Combined * OpState->CameraStartPos).xyz;
|
||||
}
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(End3DViewMouseRotate)
|
||||
|
@ -36,11 +42,14 @@ input_command MouseRotateViewCommands [] = {
|
|||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(Begin3DViewMouseRotate)
|
||||
{
|
||||
sculpture_view_panel_state* PanelState = Panel_GetStateStruct(Panel, sculpture_view_panel_state);
|
||||
|
||||
operation_mode* RotateViewMode = ActivateOperationModeWithCommands(&State->Modes, MouseRotateViewCommands, Update3DViewMouseRotate);
|
||||
mouse_rotate_view_operation_state* OpState = CreateOperationState(RotateViewMode,
|
||||
&State->Modes,
|
||||
mouse_rotate_view_operation_state);
|
||||
OpState->CameraStartPos = ToV4Point(State->Camera.Position);
|
||||
OpState->CameraStartPos = ToV4Point(PanelState->Camera.Position);
|
||||
OpState->Camera = &PanelState->Camera;
|
||||
}
|
||||
|
||||
// ----------------
|
||||
|
@ -57,7 +66,16 @@ GSMetaTag(panel_type_sculpture_view);
|
|||
internal void
|
||||
SculptureView_Init(panel* Panel, app_state* State, context Context)
|
||||
{
|
||||
sculpture_view_panel_state* PanelState = PushStruct(&State->Permanent, sculpture_view_panel_state);
|
||||
|
||||
PanelState->Camera.FieldOfView = 45.0f;
|
||||
PanelState->Camera.AspectRatio = RectAspectRatio(State->WindowBounds);
|
||||
PanelState->Camera.Near = .1f;
|
||||
PanelState->Camera.Far = 800.0f;
|
||||
PanelState->Camera.Position = v3{0, 0, 400};
|
||||
PanelState->Camera.LookAt = v3{0, 0, 0};
|
||||
|
||||
Panel->StateMemory = StructToData(PanelState, sculpture_view_panel_state);
|
||||
}
|
||||
|
||||
GSMetaTag(panel_cleanup);
|
||||
|
@ -159,9 +177,10 @@ internal void
|
|||
SculptureView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
|
||||
{
|
||||
DEBUG_TRACK_SCOPE(RenderSculpture);
|
||||
State->Camera.AspectRatio = RectAspectRatio(PanelBounds);
|
||||
sculpture_view_panel_state* PanelState = Panel_GetStateStruct(Panel, sculpture_view_panel_state);
|
||||
PanelState->Camera.AspectRatio = RectAspectRatio(PanelBounds);
|
||||
|
||||
PushRenderPerspective(RenderBuffer, PanelBounds, State->Camera);
|
||||
PushRenderPerspective(RenderBuffer, PanelBounds, PanelState->Camera);
|
||||
|
||||
u32 MaxLEDsPerJob = 2048;
|
||||
render_quad_batch_constructor RenderLEDsBatch = PushRenderQuad3DBatch(RenderBuffer, State->LedSystem.LedsCountTotal);
|
||||
|
@ -184,7 +203,7 @@ SculptureView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren
|
|||
JobData->Batch = &RenderLEDsBatch;
|
||||
JobData->BatchReservedRange = ReserveRangeInQuadConstructor(JobData->Batch, JobLedCount * 2);
|
||||
JobData->LEDHalfWidth = .5f;
|
||||
JobData->CameraPosition = ToV4Point(State->Camera.Position);
|
||||
JobData->CameraPosition = ToV4Point(PanelState->Camera.Position);
|
||||
#if 1
|
||||
Context.GeneralWorkQueue->PushWorkOnQueue(Context.GeneralWorkQueue, (thread_proc*)DrawLEDsInBufferRangeJob, Data, ConstString("Sculpture Draw LEDS"));
|
||||
#else
|
||||
|
@ -204,7 +223,7 @@ SculptureView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren
|
|||
led_buffer* LedBuffer = LedSystemGetBuffer(&State->LedSystem, Assembly.LedBufferIndex);
|
||||
|
||||
v4 LedPosition = LedBuffer->Positions[FocusPixel];
|
||||
v2 LedOnScreenPosition = SculptureView_WorldToScreenPosition(LedPosition, State->Camera, PanelBounds);
|
||||
v2 LedOnScreenPosition = SculptureView_WorldToScreenPosition(LedPosition, PanelState->Camera, PanelBounds);
|
||||
|
||||
gs_string Tempgs_string = PushString(State->Transient, 256);
|
||||
PrintF(&Tempgs_string, "%f %f", LedOnScreenPosition.x, LedOnScreenPosition.y);
|
||||
|
|
|
@ -14,6 +14,10 @@ struct frame_range
|
|||
s32 Max;
|
||||
};
|
||||
|
||||
// NOTE(pjs): An animation block is a time range paired with an
|
||||
// animation_pattern (see below). While a timeline's current time
|
||||
// is within the range of a block, that particular block's animation
|
||||
// will run
|
||||
struct animation_block
|
||||
{
|
||||
frame_range Range;
|
||||
|
@ -37,6 +41,9 @@ enum blend_mode
|
|||
BlendMode_Count,
|
||||
};
|
||||
|
||||
// TODO(pjs): Add Opacity to this
|
||||
typedef pixel led_blend_proc(pixel PixelA, pixel PixelB);
|
||||
|
||||
global gs_const_string BlendModeStrings[] = {
|
||||
ConstString("Overwrite"),
|
||||
ConstString("Add"),
|
||||
|
@ -57,11 +64,15 @@ struct anim_layer_array
|
|||
u32 CountMax;
|
||||
};
|
||||
|
||||
// NOTE(pjs): An animation is a stack of layers, each of which
|
||||
// is a timeline of animation blocks.
|
||||
struct animation
|
||||
{
|
||||
gs_string Name;
|
||||
|
||||
anim_layer_array Layers;
|
||||
// TODO(pjs): Pretty sure Blocks_ should be obsolete and
|
||||
// Layers should contain their own blocks
|
||||
animation_block_array Blocks_;
|
||||
|
||||
frame_range PlayableRange;
|
||||
|
@ -74,14 +85,23 @@ struct animation_array
|
|||
u32 CountMax;
|
||||
};
|
||||
|
||||
struct animation_layer_frame
|
||||
{
|
||||
animation_block Hot;
|
||||
bool HasHot;
|
||||
|
||||
animation_block NextHot;
|
||||
bool HasNextHot;
|
||||
|
||||
r32 HotOpacity;
|
||||
};
|
||||
|
||||
// NOTE(pjs): This is an evaluated frame - across all layers in an
|
||||
// animation, these are the blocks that need to be run
|
||||
struct animation_frame
|
||||
{
|
||||
// NOTE(pjs): These are all parallel arrays of equal length
|
||||
animation_block* Blocks;
|
||||
b8* BlocksFilled;
|
||||
|
||||
u32 BlocksCountMax;
|
||||
u32 BlocksCount;
|
||||
animation_layer_frame* Layers;
|
||||
u32 LayersCount;
|
||||
};
|
||||
|
||||
#define ANIMATION_SYSTEM_LAYERS_MAX 128
|
||||
|
@ -102,8 +122,10 @@ struct animation_system
|
|||
|
||||
};
|
||||
|
||||
// TODO(pjs): Better name - something like animation_prototype
|
||||
struct animation_clip
|
||||
// NOTE(pjs): A Pattern is a named procedure which can be used as
|
||||
// an element of an animation. Patterns are sequenced on a timeline
|
||||
// and blended via layers to create an animation
|
||||
struct animation_pattern
|
||||
{
|
||||
char* Name;
|
||||
s32 NameLength;
|
||||
|
@ -411,10 +433,9 @@ AnimationSystem_CalculateAnimationFrame(animation_system* System, gs_memory_aren
|
|||
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System);
|
||||
|
||||
animation_frame Result = {0};
|
||||
Result.BlocksCountMax = ActiveAnim->Layers.Count;
|
||||
Result.Blocks = PushArray(Arena, animation_block, Result.BlocksCountMax);
|
||||
Result.BlocksFilled = PushArray(Arena, b8, Result.BlocksCountMax);
|
||||
ZeroArray(Result.BlocksFilled, b8, Result.BlocksCountMax);
|
||||
Result.LayersCount = ActiveAnim->Layers.Count;
|
||||
Result.Layers = PushArray(Arena, animation_layer_frame, Result.LayersCount);
|
||||
ZeroArray(Result.Layers, animation_layer_frame, Result.LayersCount);
|
||||
|
||||
for (u32 i = 0; i < ActiveAnim->Blocks_.Count; i++)
|
||||
{
|
||||
|
@ -422,14 +443,66 @@ AnimationSystem_CalculateAnimationFrame(animation_system* System, gs_memory_aren
|
|||
|
||||
if (FrameIsInRange(Block.Range, System->CurrentFrame))
|
||||
{
|
||||
Result.BlocksFilled[Block.Layer] = true;
|
||||
Result.Blocks[Block.Layer] = Block;
|
||||
Result.BlocksCount++;
|
||||
animation_layer_frame* Layer = Result.Layers + Block.Layer;
|
||||
if (Layer->HasHot)
|
||||
{
|
||||
// NOTE(pjs): With current implementation, we don't allow
|
||||
// animations to hvae more than 2 concurrent blocks in the
|
||||
// timeline
|
||||
Assert(!Layer->HasNextHot);
|
||||
|
||||
// NOTE(pjs): Make sure that Hot comes before NextHot
|
||||
if (Layer->Hot.Range.Min < Block.Range.Min)
|
||||
{
|
||||
Layer->NextHot = Block;
|
||||
}
|
||||
else
|
||||
{
|
||||
Layer->NextHot = Layer->Hot;
|
||||
Layer->Hot = Block;
|
||||
}
|
||||
Layer->HasNextHot = true;
|
||||
|
||||
frame_range BlendRange = {};
|
||||
BlendRange.Min = Layer->NextHot.Range.Min;
|
||||
BlendRange.Max = Layer->Hot.Range.Max;
|
||||
Layer->HotOpacity = 1.0f - FrameToPercentRange(System->CurrentFrame, BlendRange);
|
||||
}
|
||||
else
|
||||
{
|
||||
Layer->Hot = Block;
|
||||
Layer->HotOpacity = 1.0f;
|
||||
Layer->HasHot = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
AnimationSystem_Update(animation_system* System)
|
||||
{
|
||||
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(System);
|
||||
if (System->TimelineShouldAdvance) {
|
||||
// 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
|
||||
System->CurrentFrame += 1;
|
||||
|
||||
// Loop back to the beginning
|
||||
if (System->CurrentFrame > ActiveAnim->PlayableRange.Max)
|
||||
{
|
||||
System->CurrentFrame = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool
|
||||
AnimationSystem_NeedsRender(animation_system System)
|
||||
{
|
||||
bool Result = (System.CurrentFrame != System.LastUpdatedFrame);
|
||||
return Result;
|
||||
}
|
||||
|
||||
#define FOLDHAUS_ANIMATION
|
||||
#endif // FOLDHAUS_ANIMATION
|
|
@ -0,0 +1,200 @@
|
|||
//
|
||||
// File: foldhaus_animation_renderer.cpp
|
||||
// Author: Peter Slattery
|
||||
// Creation Date: 2020-11-14
|
||||
//
|
||||
#ifndef FOLDHAUS_ANIMATION_RENDERER_CPP
|
||||
|
||||
internal pixel
|
||||
LedBlend_Overwrite(pixel PixelA, pixel PixelB)
|
||||
{
|
||||
return PixelB;
|
||||
}
|
||||
|
||||
internal pixel
|
||||
LedBlend_Overwrite(pixel PixelA, pixel PixelB, r32 Opacity)
|
||||
{
|
||||
pixel Result = {};
|
||||
|
||||
r32 BOpacity = 1.0f - Opacity;
|
||||
Result.R = (u8)((PixelA.R * Opacity) + (PixelB.R * BOpacity));
|
||||
Result.G = (u8)((PixelA.G * Opacity) + (PixelB.G * BOpacity));
|
||||
Result.B = (u8)((PixelA.B * Opacity) + (PixelB.B * BOpacity));
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal pixel
|
||||
LedBlend_Add(pixel PixelA, pixel PixelB)
|
||||
{
|
||||
pixel Result = {};
|
||||
|
||||
u32 R = (u32)PixelA.R + (u32)PixelB.R;
|
||||
u32 G = (u32)PixelA.G + (u32)PixelB.G;
|
||||
u32 B = (u32)PixelA.B + (u32)PixelB.B;
|
||||
|
||||
Result.R = (u8)Min(R, (u32)255);
|
||||
Result.G = (u8)Min(G, (u32)255);
|
||||
Result.B = (u8)Min(B, (u32)255);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal pixel
|
||||
LedBlend_Multiply(pixel PixelA, pixel PixelB)
|
||||
{
|
||||
pixel Result = {};
|
||||
|
||||
r32 DR = (r32)PixelA.R / 255.f;
|
||||
r32 DG = (r32)PixelA.G / 255.f;
|
||||
r32 DB = (r32)PixelA.B / 255.f;
|
||||
|
||||
r32 SR = (r32)PixelB.R / 255.f;
|
||||
r32 SG = (r32)PixelB.G / 255.f;
|
||||
r32 SB = (r32)PixelB.B / 255.f;
|
||||
|
||||
Result.R = (u8)((DR * SR) * 255.f);
|
||||
Result.G = (u8)((DG * SG) * 255.f);
|
||||
Result.B = (u8)((DB * SB) * 255.f);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal pixel
|
||||
LedBlend_Overlay(pixel PixelA, pixel PixelB)
|
||||
{
|
||||
pixel Result = {};
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal led_blend_proc*
|
||||
LedBlend_GetProc(blend_mode BlendMode)
|
||||
{
|
||||
led_blend_proc* Result = 0;
|
||||
switch (BlendMode)
|
||||
{
|
||||
case BlendMode_Overwrite: { Result = LedBlend_Overwrite; }break;
|
||||
case BlendMode_Add: { Result = LedBlend_Add; }break;
|
||||
case BlendMode_Multiply: { Result = LedBlend_Multiply; }break;
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
AnimationSystem_RenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, assembly Assembly, animation_pattern* Patterns, gs_memory_arena* Transient)
|
||||
{
|
||||
u32 FramesIntoBlock = System->CurrentFrame - Block.Range.Min;
|
||||
r32 SecondsIntoBlock = FramesIntoBlock * System->SecondsPerFrame;
|
||||
|
||||
// :AnimProcHandle
|
||||
u32 AnimationProcIndex = Block.AnimationProcHandle - 1;
|
||||
animation_proc* AnimationProc = Patterns[AnimationProcIndex].Proc;
|
||||
AnimationProc(Buffer, Assembly, SecondsIntoBlock, Transient);
|
||||
}
|
||||
|
||||
internal void
|
||||
AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Assemblies,
|
||||
led_system* LedSystem,
|
||||
animation_pattern* Patterns,
|
||||
gs_memory_arena* Transient)
|
||||
{
|
||||
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
|
||||
// for overlapping
|
||||
struct layer_led_buffer
|
||||
{
|
||||
led_buffer HotBuffer;
|
||||
led_buffer NextHotBuffer;
|
||||
};
|
||||
|
||||
layer_led_buffer* LayerBuffers = PushArray(Transient, layer_led_buffer, CurrFrame.LayersCount);
|
||||
|
||||
for (u32 AssemblyIndex = 0; AssemblyIndex < Assemblies.Count; AssemblyIndex++)
|
||||
{
|
||||
assembly* Assembly = &Assemblies.Values[AssemblyIndex];
|
||||
led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(LedSystem, Assembly->LedBufferIndex);
|
||||
|
||||
// Create the LayerLEDBuffers
|
||||
for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
|
||||
{
|
||||
layer_led_buffer TempBuffer = {};
|
||||
if (CurrFrame.Layers[Layer].HasHot)
|
||||
{
|
||||
TempBuffer.HotBuffer.LedCount = AssemblyLedBuffer->LedCount;
|
||||
TempBuffer.HotBuffer.Positions = AssemblyLedBuffer->Positions;
|
||||
TempBuffer.HotBuffer.Colors = PushArray(Transient, pixel, TempBuffer.HotBuffer.LedCount);
|
||||
LedBuffer_ClearToBlack(&TempBuffer.HotBuffer);
|
||||
}
|
||||
|
||||
if (CurrFrame.Layers[Layer].HasNextHot)
|
||||
{
|
||||
TempBuffer.NextHotBuffer.LedCount = AssemblyLedBuffer->LedCount;
|
||||
TempBuffer.NextHotBuffer.Positions = AssemblyLedBuffer->Positions;
|
||||
TempBuffer.NextHotBuffer.Colors = PushArray(Transient, pixel, TempBuffer.HotBuffer.LedCount);
|
||||
LedBuffer_ClearToBlack(&TempBuffer.NextHotBuffer);
|
||||
}
|
||||
|
||||
LayerBuffers[Layer] = TempBuffer;
|
||||
}
|
||||
|
||||
// Render Each layer's block to the appropriate temp buffer
|
||||
for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
|
||||
{
|
||||
animation_layer_frame LayerFrame = CurrFrame.Layers[Layer];
|
||||
if (LayerFrame.HasHot)
|
||||
{
|
||||
led_buffer TempBuffer = LayerBuffers[Layer].HotBuffer;
|
||||
animation_block Block = LayerFrame.Hot;
|
||||
AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, *Assembly, Patterns, Transient);
|
||||
}
|
||||
|
||||
if (LayerFrame.HasNextHot)
|
||||
{
|
||||
led_buffer TempBuffer = LayerBuffers[Layer].NextHotBuffer;
|
||||
animation_block Block = LayerFrame.NextHot;
|
||||
AnimationSystem_RenderBlockToLedBuffer(System, Block, &TempBuffer, *Assembly, Patterns, Transient);
|
||||
}
|
||||
}
|
||||
|
||||
// Blend together any layers that have a hot and next hot buffer
|
||||
for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
|
||||
{
|
||||
animation_layer_frame LayerFrame = CurrFrame.Layers[Layer];
|
||||
layer_led_buffer LayerBuffer = LayerBuffers[Layer];
|
||||
if (LayerFrame.HasNextHot)
|
||||
{
|
||||
for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++)
|
||||
{
|
||||
pixel A = LayerBuffer.HotBuffer.Colors[LED];
|
||||
pixel B = LayerBuffer.NextHotBuffer.Colors[LED];
|
||||
LayerBuffer.HotBuffer.Colors[LED] = LedBlend_Overwrite(A, B, LayerFrame.HotOpacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Consolidate Temp Buffers
|
||||
// We do this in reverse order so that they go from top to bottom
|
||||
for (u32 Layer = 0; Layer < CurrFrame.LayersCount; Layer++)
|
||||
{
|
||||
if (CurrFrame.Layers[Layer].HasHot)
|
||||
{
|
||||
led_blend_proc* Blend = LedBlend_GetProc(ActiveAnim->Layers.Values[Layer].BlendMode);
|
||||
Assert(Blend != 0);
|
||||
for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++)
|
||||
{
|
||||
pixel A = AssemblyLedBuffer->Colors[LED];
|
||||
pixel B = LayerBuffers[Layer].HotBuffer.Colors[LED];
|
||||
AssemblyLedBuffer->Colors[LED] = Blend(A, B);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define FOLDHAUS_ANIMATION_RENDERER_CPP
|
||||
#endif // FOLDHAUS_ANIMATION_RENDERER_CPP
|
|
@ -6,7 +6,7 @@
|
|||
#ifndef FOLDHAUS_ANIMATION_SERIALIZER_CPP
|
||||
|
||||
internal gs_string
|
||||
AnimSerializer_Serialize(animation Anim, animation_clip* GlobalClips, gs_memory_arena* Arena)
|
||||
AnimSerializer_Serialize(animation Anim, animation_pattern* GlobalClips, gs_memory_arena* Arena)
|
||||
{
|
||||
serializer Serializer = {0};
|
||||
Serializer.String = PushString(Arena, 4096);
|
||||
|
@ -48,7 +48,7 @@ AnimSerializer_Serialize(animation Anim, animation_clip* GlobalClips, gs_memory_
|
|||
// TODO(pjs): Systematize the AnimationProcHandle
|
||||
// :AnimProcHandle
|
||||
u32 AnimationProcIndex = AnimationBlockAt.AnimationProcHandle - 1;
|
||||
animation_clip Animation = GlobalClips[AnimationProcIndex];
|
||||
animation_pattern Animation = GlobalClips[AnimationProcIndex];
|
||||
|
||||
Serializer_OpenStruct(&Serializer, AnimField_Block);
|
||||
{
|
||||
|
@ -70,7 +70,7 @@ AnimSerializer_Serialize(animation Anim, animation_clip* GlobalClips, gs_memory_
|
|||
}
|
||||
|
||||
internal animation
|
||||
AnimParser_Parse(gs_string File, gs_memory_arena* Arena, u32 AnimClipsCount, animation_clip* AnimClips)
|
||||
AnimParser_Parse(gs_string File, gs_memory_arena* Arena, u32 AnimClipsCount, animation_pattern* AnimClips)
|
||||
{
|
||||
animation Result = {0};
|
||||
|
||||
|
|
|
@ -168,6 +168,17 @@ LedSystemGetBuffer(led_system* System, u32 Index)
|
|||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
LedBuffer_ClearToBlack(led_buffer* Buffer)
|
||||
{
|
||||
for (u32 i = 0; i < Buffer->LedCount; i++)
|
||||
{
|
||||
Buffer->Colors[i].R = 0;
|
||||
Buffer->Colors[i].G = 0;
|
||||
Buffer->Colors[i].B = 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal u32
|
||||
StripGenData_CountLeds(strip_gen_data Data)
|
||||
{
|
||||
|
|
|
@ -15,6 +15,8 @@ RELOAD_STATIC_DATA(ReloadStaticData)
|
|||
app_state* State = (app_state*)Context.MemoryBase;
|
||||
|
||||
GlobalDebugServices = DebugServices;
|
||||
State->PanelSystem.PanelDefs = GlobalPanelDefs;
|
||||
State->PanelSystem.PanelDefsCount = GlobalPanelDefsCount;
|
||||
}
|
||||
|
||||
INITIALIZE_APPLICATION(InitializeApplication)
|
||||
|
@ -30,11 +32,7 @@ INITIALIZE_APPLICATION(InitializeApplication)
|
|||
State->GlobalLog = PushStruct(State->Transient, event_log);
|
||||
*State->GlobalLog = {0};
|
||||
|
||||
s32 CommandQueueSize = 32;
|
||||
command_queue_entry* CommandQueueMemory = PushArray(&State->Permanent,
|
||||
command_queue_entry,
|
||||
CommandQueueSize);
|
||||
State->CommandQueue = InitializeCommandQueue(CommandQueueMemory, CommandQueueSize);
|
||||
State->CommandQueue = CommandQueue_Create(&State->Permanent, 32);
|
||||
|
||||
// TODO(Peter): put in InitializeInterface?
|
||||
r32 FontSize = 14;
|
||||
|
@ -101,7 +99,7 @@ INITIALIZE_APPLICATION(InitializeApplication)
|
|||
State->Interface.Style.PanelBGColors[3] = v4{.6f, .6f, .6f, 1};
|
||||
State->Interface.Style.ButtonColor_Inactive = BlackV4;
|
||||
State->Interface.Style.ButtonColor_Active = v4{.1f, .1f, .1f, 1};
|
||||
State->Interface.Style.ButtonColor_Selected = v4{.1f, .1f, .3f, 1};
|
||||
State->Interface.Style.ButtonColor_Selected = v4{.3f, .3f, .3f, 1};
|
||||
State->Interface.Style.TextColor = WhiteV4;
|
||||
State->Interface.Style.ListBGColors[0] = v4{ .16f, .16f, .16f, 1.f };
|
||||
State->Interface.Style.ListBGColors[1] = v4{ .18f, .18f, .18f, 1.f };
|
||||
|
@ -110,15 +108,10 @@ INITIALIZE_APPLICATION(InitializeApplication)
|
|||
State->Interface.Style.Margin = v2{5, 5};
|
||||
State->Interface.Style.RowHeight = ui_GetTextLineHeight(State->Interface);
|
||||
|
||||
State->SACN = SACN_Initialize(Context);
|
||||
State->Interface.WidgetsCountMax = 4096;
|
||||
State->Interface.Widgets = PushArray(&State->Permanent, ui_widget, State->Interface.WidgetsCountMax);
|
||||
|
||||
State->Camera.FieldOfView = 45.0f;
|
||||
State->Camera.AspectRatio = RectAspectRatio(State->WindowBounds);
|
||||
State->Camera.Near = .1f;
|
||||
State->Camera.Far = 800.0f;
|
||||
State->Camera.Position = v3{0, 0, 400};
|
||||
State->Camera.LookAt = v3{0, 0, 0
|
||||
};
|
||||
State->SACN = SACN_Initialize(Context);
|
||||
|
||||
State->LedSystem = LedSystemInitialize(Context.ThreadContext.Allocator, 128);
|
||||
|
||||
|
@ -158,70 +151,10 @@ INITIALIZE_APPLICATION(InitializeApplication)
|
|||
} // End Animation Playground
|
||||
|
||||
|
||||
InitializePanelSystem(&State->PanelSystem, GlobalPanelDefs, GlobalPanelDefsCount);
|
||||
PanelSystem_Init(&State->PanelSystem, GlobalPanelDefs, GlobalPanelDefsCount, &State->Permanent);
|
||||
PanelSystem_PushPanel(&State->PanelSystem, PanelType_SculptureView, State, Context);
|
||||
}
|
||||
|
||||
internal void
|
||||
HandleInput (app_state* State, rect2 WindowBounds, input_queue InputQueue, mouse_state Mouse, context Context)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
||||
b32 PanelSystemHandledInput = HandleMousePanelInteraction(&State->PanelSystem, State->WindowBounds, Mouse, State);
|
||||
if (!PanelSystemHandledInput)
|
||||
{
|
||||
input_command_registry ActiveCommands = {};
|
||||
if (State->Modes.ActiveModesCount > 0)
|
||||
{
|
||||
ActiveCommands = State->Modes.ActiveModes[State->Modes.ActiveModesCount - 1].Commands;
|
||||
}
|
||||
else
|
||||
{
|
||||
panel_with_layout PanelWithMouseOverIt = GetPanelContainingPoint(Mouse.Pos, &State->PanelSystem, WindowBounds);
|
||||
if (!PanelWithMouseOverIt.Panel) { return; }
|
||||
State->HotPanel = PanelWithMouseOverIt.Panel;
|
||||
|
||||
s32 PanelTypeIndex = PanelWithMouseOverIt.Panel->TypeIndex;
|
||||
panel_definition PanelDefinition = GlobalPanelDefs[PanelTypeIndex];
|
||||
if (!PanelDefinition.InputCommands) { return; }
|
||||
|
||||
ActiveCommands.Commands = PanelDefinition.InputCommands;
|
||||
ActiveCommands.Size = sizeof(*PanelDefinition.InputCommands) / sizeof(PanelDefinition.InputCommands[0]);
|
||||
ActiveCommands.Used = ActiveCommands.Size;
|
||||
}
|
||||
|
||||
for (s32 EventIdx = 0; EventIdx < InputQueue.QueueUsed; EventIdx++)
|
||||
{
|
||||
input_entry Event = InputQueue.Entries[EventIdx];
|
||||
|
||||
// NOTE(Peter): These are in the order Down, Up, Held because we want to privalege
|
||||
// Down and Up over Held. In other words, we don't want to call a Held command on the
|
||||
// frame when the button was released, even if the command is registered to both events
|
||||
if (KeyTransitionedDown(Event))
|
||||
{
|
||||
FindAndPushExistingCommand(ActiveCommands, Event, Command_Began, &State->CommandQueue);
|
||||
}
|
||||
else if (KeyTransitionedUp(Event))
|
||||
{
|
||||
FindAndPushExistingCommand(ActiveCommands, Event, Command_Ended, &State->CommandQueue);
|
||||
}
|
||||
else if (KeyHeldDown(Event))
|
||||
{
|
||||
FindAndPushExistingCommand(ActiveCommands, Event, Command_Held, &State->CommandQueue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute all commands in CommandQueue
|
||||
for (s32 CommandIdx = State->CommandQueue.Used - 1; CommandIdx >= 0; CommandIdx--)
|
||||
{
|
||||
command_queue_entry* Entry = &State->CommandQueue.Commands[CommandIdx];
|
||||
Entry->Command.Proc(State, Entry->Event, Mouse, Context);
|
||||
}
|
||||
|
||||
ClearCommandQueue(&State->CommandQueue);
|
||||
}
|
||||
|
||||
UPDATE_AND_RENDER(UpdateAndRender)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
|
@ -232,111 +165,18 @@ UPDATE_AND_RENDER(UpdateAndRender)
|
|||
// zero the Transient arena when we clear it so it wouldn't be a problem, but it is technically
|
||||
// incorrect to clear the arena, and then access the memory later.
|
||||
ClearArena(State->Transient);
|
||||
Context->Mouse.CursorType = CursorType_Arrow;
|
||||
|
||||
PushRenderClearScreen(RenderBuffer);
|
||||
State->Camera.AspectRatio = RectAspectRatio(Context->WindowBounds);
|
||||
|
||||
HandleInput(State, State->WindowBounds, InputQueue, Context->Mouse, *Context);
|
||||
Editor_Update(State, Context, InputQueue);
|
||||
|
||||
AnimationSystem_Update(&State->AnimationSystem);
|
||||
if (AnimationSystem_NeedsRender(State->AnimationSystem))
|
||||
{
|
||||
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
|
||||
if (State->AnimationSystem.TimelineShouldAdvance) {
|
||||
// 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
|
||||
State->AnimationSystem.CurrentFrame += 1;
|
||||
|
||||
// Loop back to the beginning
|
||||
if (State->AnimationSystem.CurrentFrame > ActiveAnim->PlayableRange.Max)
|
||||
{
|
||||
State->AnimationSystem.CurrentFrame = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s32 CurrentFrame = State->AnimationSystem.CurrentFrame;
|
||||
if (CurrentFrame != State->AnimationSystem.LastUpdatedFrame)
|
||||
{
|
||||
State->AnimationSystem.LastUpdatedFrame = CurrentFrame;
|
||||
r32 FrameTime = CurrentFrame * State->AnimationSystem.SecondsPerFrame;
|
||||
|
||||
animation_frame CurrFrame = AnimationSystem_CalculateAnimationFrame(&State->AnimationSystem, State->Transient);
|
||||
|
||||
led_buffer* LayerLEDBuffers = PushArray(State->Transient, led_buffer, CurrFrame.BlocksCountMax);
|
||||
for (u32 AssemblyIndex = 0; AssemblyIndex < State->Assemblies.Count; AssemblyIndex++)
|
||||
{
|
||||
assembly* Assembly = &State->Assemblies.Values[AssemblyIndex];
|
||||
led_buffer* AssemblyLedBuffer = LedSystemGetBuffer(&State->LedSystem, Assembly->LedBufferIndex);
|
||||
|
||||
for (u32 Layer = 0; Layer < CurrFrame.BlocksCountMax; Layer++)
|
||||
{
|
||||
if (!CurrFrame.BlocksFilled[Layer]) { continue; }
|
||||
animation_block Block = CurrFrame.Blocks[Layer];
|
||||
|
||||
// Prep Temp Buffer
|
||||
LayerLEDBuffers[Layer] = *AssemblyLedBuffer;
|
||||
LayerLEDBuffers[Layer].Colors = PushArray(State->Transient, pixel, AssemblyLedBuffer->LedCount);
|
||||
|
||||
u32 FramesIntoBlock = CurrentFrame - Block.Range.Min;
|
||||
r32 SecondsIntoBlock = FramesIntoBlock * State->AnimationSystem.SecondsPerFrame;
|
||||
|
||||
// :AnimProcHandle
|
||||
u32 AnimationProcIndex = Block.AnimationProcHandle - 1;
|
||||
animation_proc* AnimationProc = GlobalAnimationClips[AnimationProcIndex].Proc;
|
||||
AnimationProc(&LayerLEDBuffers[Layer], *Assembly, SecondsIntoBlock, State->Transient);
|
||||
}
|
||||
|
||||
// Consolidate Temp Buffers
|
||||
// We do this in reverse order so that they go from top to bottom
|
||||
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
|
||||
for (u32 Layer = 0; Layer < CurrFrame.BlocksCountMax; Layer++)
|
||||
{
|
||||
if (!CurrFrame.BlocksFilled[Layer]) { continue; }
|
||||
|
||||
switch (ActiveAnim->Layers.Values[Layer].BlendMode)
|
||||
{
|
||||
case BlendMode_Overwrite:
|
||||
{
|
||||
for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++)
|
||||
{
|
||||
AssemblyLedBuffer->Colors[LED] = LayerLEDBuffers[Layer].Colors[LED];
|
||||
}
|
||||
}break;
|
||||
|
||||
case BlendMode_Add:
|
||||
{
|
||||
for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++)
|
||||
{
|
||||
u32 R = (u32)AssemblyLedBuffer->Colors[LED].R + (u32)LayerLEDBuffers[Layer].Colors[LED].R;
|
||||
u32 G = (u32)AssemblyLedBuffer->Colors[LED].G + (u32)LayerLEDBuffers[Layer].Colors[LED].G;
|
||||
u32 B = (u32)AssemblyLedBuffer->Colors[LED].B + (u32)LayerLEDBuffers[Layer].Colors[LED].B;
|
||||
|
||||
AssemblyLedBuffer->Colors[LED].R = (u8)Min(R, (u32)255);
|
||||
AssemblyLedBuffer->Colors[LED].G = (u8)Min(G, (u32)255);
|
||||
AssemblyLedBuffer->Colors[LED].B = (u8)Min(B, (u32)255);
|
||||
}
|
||||
}break;
|
||||
|
||||
case BlendMode_Multiply:
|
||||
{
|
||||
for (u32 LED = 0; LED < AssemblyLedBuffer->LedCount; LED++)
|
||||
{
|
||||
r32 DR = (r32)AssemblyLedBuffer->Colors[LED].R / 255.f;
|
||||
r32 DG = (r32)AssemblyLedBuffer->Colors[LED].G / 255.f;
|
||||
r32 DB = (r32)AssemblyLedBuffer->Colors[LED].B / 255.f;
|
||||
|
||||
r32 SR = (r32)LayerLEDBuffers[Layer].Colors[LED].R / 255.f;
|
||||
r32 SG = (r32)LayerLEDBuffers[Layer].Colors[LED].G / 255.f;
|
||||
r32 SB = (r32)LayerLEDBuffers[Layer].Colors[LED].B / 255.f;
|
||||
|
||||
AssemblyLedBuffer->Colors[LED].R = (u8)((DR * SR) * 255.f);
|
||||
AssemblyLedBuffer->Colors[LED].G = (u8)((DG * SG) * 255.f);
|
||||
AssemblyLedBuffer->Colors[LED].B = (u8)((DB * SB) * 255.f);
|
||||
}
|
||||
}break;
|
||||
}
|
||||
}
|
||||
}
|
||||
State->AnimationSystem.LastUpdatedFrame = State->AnimationSystem.CurrentFrame;
|
||||
AnimationSystem_RenderToLedBuffers(&State->AnimationSystem,
|
||||
State->Assemblies,
|
||||
&State->LedSystem,
|
||||
GlobalAnimationPatterns,
|
||||
State->Transient);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -348,27 +188,7 @@ UPDATE_AND_RENDER(UpdateAndRender)
|
|||
UART_BuildOutputData(OutputData, UARTAssemblies, &State->LedSystem);
|
||||
}
|
||||
|
||||
PushRenderOrthographic(RenderBuffer, State->WindowBounds);
|
||||
PushRenderClearScreen(RenderBuffer);
|
||||
|
||||
State->WindowBounds = Context->WindowBounds;
|
||||
State->Interface.RenderBuffer = RenderBuffer;
|
||||
State->Interface.Mouse = Context->Mouse;
|
||||
|
||||
panel_layout PanelsToRender = GetPanelLayout(&State->PanelSystem, State->WindowBounds, State->Transient);
|
||||
DrawAllPanels(PanelsToRender, RenderBuffer, &Context->Mouse, State, *Context);
|
||||
|
||||
for (s32 m = 0; m < State->Modes.ActiveModesCount; m++)
|
||||
{
|
||||
operation_mode OperationMode = State->Modes.ActiveModes[m];
|
||||
if (OperationMode.Render != 0)
|
||||
{
|
||||
OperationMode.Render(State, RenderBuffer, OperationMode, Context->Mouse, *Context);
|
||||
}
|
||||
}
|
||||
|
||||
Context->GeneralWorkQueue->CompleteQueueWork(Context->GeneralWorkQueue, Context->ThreadContext);
|
||||
Context->GeneralWorkQueue->ResetWorkQueue(Context->GeneralWorkQueue);
|
||||
Editor_Render(State, Context, RenderBuffer);
|
||||
|
||||
// Checking for overflows
|
||||
#if 0
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
typedef struct app_state app_state;
|
||||
|
||||
typedef struct panel panel;
|
||||
|
||||
#include "editor/foldhaus_command_dispatch.h"
|
||||
#include "editor/foldhaus_operation_mode.h"
|
||||
|
||||
|
@ -36,6 +38,7 @@ typedef struct app_state app_state;
|
|||
|
||||
#include "engine/animation/foldhaus_animation.h"
|
||||
#include "engine/animation/foldhaus_animation_serializer.cpp"
|
||||
#include "engine/animation/foldhaus_animation_renderer.cpp"
|
||||
|
||||
struct app_state
|
||||
{
|
||||
|
@ -62,10 +65,7 @@ struct app_state
|
|||
panel_system PanelSystem;
|
||||
panel* HotPanel;
|
||||
|
||||
camera Camera; // TODO(Peter): move into the sculpture view
|
||||
r32 PixelsToWorldScale;
|
||||
handle SelectedAnimationBlockHandle; // TODO(Peter): move into animation panel
|
||||
u32 SelectedAnimationLayer; // TODO(Peter): move into animation panel
|
||||
};
|
||||
|
||||
internal void OpenColorPicker(app_state* State, v4* Address);
|
||||
|
@ -202,18 +202,31 @@ TestPatternThree(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena*
|
|||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
Pattern_AllGreen(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient)
|
||||
{
|
||||
for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++)
|
||||
{
|
||||
Leds->Colors[LedIndex].R = 0;
|
||||
Leds->Colors[LedIndex].B = 255;
|
||||
Leds->Colors[LedIndex].G = 255;
|
||||
}
|
||||
}
|
||||
|
||||
// END TEMPORARY PATTERNS
|
||||
|
||||
FOLDHAUS_INPUT_COMMAND_PROC(EndCurrentOperationMode)
|
||||
internal void
|
||||
EndCurrentOperationMode(app_state* State)
|
||||
{
|
||||
DeactivateCurrentOperationMode(&State->Modes);
|
||||
}
|
||||
|
||||
s32 GlobalAnimationClipsCount = 3;
|
||||
animation_clip GlobalAnimationClips[] = {
|
||||
s32 GlobalAnimationPatternsCount = 4;
|
||||
animation_pattern GlobalAnimationPatterns[] = {
|
||||
{ "Test Pattern One", 16, TestPatternOne },
|
||||
{ "Test Pattern Two", 16, TestPatternTwo },
|
||||
{ "Test Pattern Three", 18, TestPatternThree },
|
||||
{ "Pattern_AllGreen", 16, Pattern_AllGreen },
|
||||
};
|
||||
|
||||
#include "editor/panels/foldhaus_panel_types.h"
|
||||
|
@ -233,5 +246,7 @@ animation_clip GlobalAnimationClips[] = {
|
|||
|
||||
#include "../meta/gs_meta_include.cpp"
|
||||
|
||||
#include "editor/foldhaus_editor.cpp"
|
||||
|
||||
#define FOLDHAUS_APP_H
|
||||
#endif // FOLDHAUS_APP_H
|
|
@ -148,7 +148,7 @@ typedef DRAW_FONT_CODEPOINT(platform_draw_font_codepoint);
|
|||
|
||||
// Worker Threads
|
||||
|
||||
#define PLATFORM_THREAD_COUNT 4
|
||||
#define PLATFORM_THREAD_COUNT 0
|
||||
|
||||
RESET_WORK_QUEUE(ResetWorkQueue)
|
||||
{
|
||||
|
|
|
@ -5,6 +5,14 @@
|
|||
//
|
||||
#ifndef INTERFACE_H
|
||||
|
||||
// Widget Capabilities
|
||||
// - string
|
||||
// - background
|
||||
// - outline
|
||||
// - active (mouse is interacting)
|
||||
// - hot (mouse could be about to interact)
|
||||
// - retained state - if a toggle is active, or a drop down is open
|
||||
|
||||
enum gs_string_alignment
|
||||
{
|
||||
Align_Left,
|
||||
|
@ -129,7 +137,7 @@ DrawCursor (render_quad_batch_constructor* BatchConstructor, v2 Position, v4 Col
|
|||
}
|
||||
|
||||
internal v2
|
||||
Drawgs_stringWithCursor (render_command_buffer* RenderBuffer, gs_string String, s32 CursorPosition, bitmap_font* Font, v2 Position, v4 Color, v4 CursorColor, gs_string_alignment Alignment = Align_Left)
|
||||
DrawStringWithCursor (render_command_buffer* RenderBuffer, gs_string String, s32 CursorPosition, bitmap_font* Font, v2 Position, v4 Color, v4 CursorColor, gs_string_alignment Alignment = Align_Left)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
v2 LowerRight = Position;
|
||||
|
@ -180,10 +188,44 @@ Drawgs_stringWithCursor (render_command_buffer* RenderBuffer, gs_string String,
|
|||
return LowerRight;
|
||||
}
|
||||
|
||||
// TODO(pjs): remove the need for htis (go thru and remove code that's in the #else block of #ifdef EXTERNAL_RENDERER s
|
||||
#define EXTERNAL_RENDERER
|
||||
|
||||
enum ui_widget_flag
|
||||
{
|
||||
UIWidgetFlag_DrawBackground,
|
||||
UIWidgetFlag_DrawOutline,
|
||||
UIWidgetFlag_Clickable,
|
||||
};
|
||||
|
||||
struct ui_widget_id
|
||||
{
|
||||
u64 Index;
|
||||
u64 LayoutId;
|
||||
};
|
||||
|
||||
struct ui_widget
|
||||
{
|
||||
ui_widget_id Id;
|
||||
|
||||
gs_string String;
|
||||
gs_string_alignment Alignment;
|
||||
|
||||
rect2 Bounds;
|
||||
u64 Flags;
|
||||
bool RetainedState;
|
||||
};
|
||||
|
||||
struct ui_eval_result
|
||||
{
|
||||
bool Clicked;
|
||||
};
|
||||
|
||||
struct interface_config
|
||||
{
|
||||
v4 PanelBGColors[4];
|
||||
|
||||
// TODO(pjs): Turn these into _Default, _Hot, _Active
|
||||
v4 ButtonColor_Inactive, ButtonColor_Active, ButtonColor_Selected;
|
||||
|
||||
v4 TextColor;
|
||||
|
@ -199,17 +241,38 @@ struct interface_config
|
|||
r32 RowHeight;
|
||||
};
|
||||
|
||||
enum ui_layout_direction
|
||||
{
|
||||
LayoutDirection_TopDown,
|
||||
LayoutDirection_BottomUp,
|
||||
};
|
||||
|
||||
struct ui_layout
|
||||
{
|
||||
u64 Id;
|
||||
|
||||
rect2 Bounds;
|
||||
v2 Margin;
|
||||
r32 RowHeight;
|
||||
r32 RowYAt;
|
||||
|
||||
ui_layout_direction FillDirection;
|
||||
|
||||
b32 DrawHorizontal;
|
||||
u32 ColumnsMax;
|
||||
r32* ColumnWidths;
|
||||
u32 ColumnsCount;
|
||||
|
||||
// NOTE(pjs): I'm not sure this will stay but
|
||||
// its here so that when we end things like a dropdown,
|
||||
// we can check the retained state of that dropdown
|
||||
ui_widget_id WidgetReference;
|
||||
};
|
||||
|
||||
struct ui_widget_retained_state
|
||||
{
|
||||
ui_widget_id Id;
|
||||
bool Value;
|
||||
};
|
||||
|
||||
struct ui_interface
|
||||
|
@ -217,49 +280,144 @@ struct ui_interface
|
|||
interface_config Style;
|
||||
mouse_state Mouse;
|
||||
render_command_buffer* RenderBuffer;
|
||||
|
||||
ui_widget* Widgets;
|
||||
u64 WidgetsCount;
|
||||
u64 WidgetsCountMax;
|
||||
|
||||
ui_widget_id HotWidget;
|
||||
ui_widget_id ActiveWidget;
|
||||
|
||||
#define LAYOUT_COUNT_MAX 8
|
||||
ui_layout LayoutStack[LAYOUT_COUNT_MAX];
|
||||
u64 LayoutStackCount;
|
||||
u64 LayoutIdAcc;
|
||||
|
||||
#define RETAINED_STATE_MAX 128
|
||||
ui_widget_retained_state RetainedState[RETAINED_STATE_MAX];
|
||||
u64 RetainedStateCount;
|
||||
};
|
||||
|
||||
internal void
|
||||
ui_InterfaceReset(ui_interface* Interface)
|
||||
{
|
||||
Interface->WidgetsCount = 0;
|
||||
Interface->LayoutStackCount = 0;
|
||||
Interface->LayoutIdAcc = 0;
|
||||
}
|
||||
|
||||
internal bool
|
||||
ui_WidgetIdsEqual(ui_widget_id A, ui_widget_id B)
|
||||
{
|
||||
bool Result = (A.Index == B.Index) && (A.LayoutId == B.LayoutId);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal ui_widget_retained_state*
|
||||
ui_GetRetainedState(ui_interface* Interface, ui_widget_id Id)
|
||||
{
|
||||
ui_widget_retained_state* Result = 0;
|
||||
for (u64 i = 0; i < Interface->RetainedStateCount; i++)
|
||||
{
|
||||
if (ui_WidgetIdsEqual(Interface->RetainedState[i].Id, Id))
|
||||
{
|
||||
Result = Interface->RetainedState + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal ui_widget_retained_state*
|
||||
ui_CreateRetainedState(ui_interface* Interface, ui_widget_id Id)
|
||||
{
|
||||
u64 Index = Interface->RetainedStateCount++;
|
||||
ui_widget_retained_state* Result = Interface->RetainedState + Index;
|
||||
Result->Id = Id;
|
||||
return Result;
|
||||
}
|
||||
|
||||
//
|
||||
// Interaction
|
||||
//
|
||||
|
||||
internal b32
|
||||
ui_MouseClickedRect(ui_interface Interface, rect2 Rect)
|
||||
{
|
||||
b32 Result = MouseButtonTransitionedDown(Interface.Mouse.LeftButtonState);
|
||||
Result &= PointIsInRect(Rect, Interface.Mouse.Pos);
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Layout
|
||||
|
||||
static ui_layout
|
||||
ui_CreateLayout(ui_interface Interface, rect2 Bounds)
|
||||
ui_CreateLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDirection = LayoutDirection_TopDown)
|
||||
{
|
||||
ui_layout Result = {0};
|
||||
Result.Bounds = Bounds;
|
||||
Result.Margin = Interface.Style.Margin;
|
||||
Result.RowHeight = Interface.Style.RowHeight;
|
||||
Result.Margin = Interface->Style.Margin;
|
||||
Result.RowHeight = Interface->Style.RowHeight;
|
||||
Result.FillDirection = FillDirection;
|
||||
switch(FillDirection)
|
||||
{
|
||||
case LayoutDirection_BottomUp:
|
||||
{
|
||||
Result.RowYAt = Bounds.Min.y;
|
||||
}break;
|
||||
|
||||
case LayoutDirection_TopDown:
|
||||
{
|
||||
Result.RowYAt = Bounds.Max.y - Result.RowHeight;
|
||||
}break;
|
||||
}
|
||||
|
||||
Result.Id = ++Interface->LayoutIdAcc;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static void
|
||||
ui_StartRow(ui_layout* Layout, u32 ColumnsMax)
|
||||
ui_PushLayout(ui_interface* Interface, ui_layout Layout)
|
||||
{
|
||||
Layout->DrawHorizontal = true;
|
||||
Layout->ColumnsMax = ColumnsMax;
|
||||
Layout->ColumnWidths = 0;
|
||||
Layout->ColumnsCount = 0;
|
||||
Assert(Interface->LayoutStackCount < LAYOUT_COUNT_MAX);
|
||||
Interface->LayoutStack[Interface->LayoutStackCount++] = Layout;
|
||||
}
|
||||
|
||||
static void
|
||||
ui_StartRow(ui_layout* Layout)
|
||||
ui_PopLayout(ui_interface* Interface)
|
||||
{
|
||||
ui_StartRow(Layout, 0);
|
||||
Assert(Interface->LayoutStackCount > 0);
|
||||
Interface->LayoutStackCount -= 1;
|
||||
}
|
||||
|
||||
static void
|
||||
ui_StartRow(ui_layout* Layout, u32 ColumnsMax, r32* ColumnWidths)
|
||||
ui_StartRow(ui_interface* Interface, u32 ColumnsMax = 0)
|
||||
{
|
||||
Layout->DrawHorizontal = true;
|
||||
Layout->ColumnsMax = ColumnsMax;
|
||||
Layout->ColumnWidths = ColumnWidths;
|
||||
Layout->ColumnsCount = 0;
|
||||
u64 LayoutIdx = Interface->LayoutStackCount - 1;
|
||||
Interface->LayoutStack[LayoutIdx].DrawHorizontal = true;
|
||||
Interface->LayoutStack[LayoutIdx].ColumnsMax = ColumnsMax;
|
||||
Interface->LayoutStack[LayoutIdx].ColumnWidths = 0;
|
||||
Interface->LayoutStack[LayoutIdx].ColumnsCount = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ui_EndRow(ui_layout* Layout)
|
||||
ui_StartRow(ui_interface* Interface, u32 ColumnsMax, r32* ColumnWidths)
|
||||
{
|
||||
Layout->DrawHorizontal = false;
|
||||
Layout->ColumnWidths = 0;
|
||||
Layout->RowYAt -= Layout->RowHeight;
|
||||
u64 LayoutIdx = Interface->LayoutStackCount - 1;
|
||||
Interface->LayoutStack[LayoutIdx].DrawHorizontal = true;
|
||||
Interface->LayoutStack[LayoutIdx].ColumnsMax = ColumnsMax;
|
||||
Interface->LayoutStack[LayoutIdx].ColumnWidths = ColumnWidths;
|
||||
Interface->LayoutStack[LayoutIdx].ColumnsCount = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ui_EndRow(ui_interface* Interface)
|
||||
{
|
||||
u64 LayoutIdx = Interface->LayoutStackCount - 1;
|
||||
Interface->LayoutStack[LayoutIdx].DrawHorizontal = false;
|
||||
Interface->LayoutStack[LayoutIdx].ColumnWidths = 0;
|
||||
Interface->LayoutStack[LayoutIdx].RowYAt -= Interface->LayoutStack[LayoutIdx].RowHeight;
|
||||
}
|
||||
|
||||
static b32
|
||||
|
@ -270,7 +428,21 @@ ui_TryReserveElementBounds(ui_layout* Layout, rect2* Bounds)
|
|||
{
|
||||
Bounds->Min = { Layout->Bounds.Min.x, Layout->RowYAt };
|
||||
Bounds->Max = { Layout->Bounds.Max.x, Bounds->Min.y + Layout->RowHeight };
|
||||
|
||||
switch (Layout->FillDirection)
|
||||
{
|
||||
case LayoutDirection_BottomUp:
|
||||
{
|
||||
Layout->RowYAt += Layout->RowHeight;
|
||||
}break;
|
||||
|
||||
case LayoutDirection_TopDown:
|
||||
{
|
||||
Layout->RowYAt -= Layout->RowHeight;
|
||||
}break;
|
||||
|
||||
InvalidDefaultCase;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -309,14 +481,6 @@ ui_TryReserveElementBounds(ui_layout* Layout, rect2* Bounds)
|
|||
return Result;
|
||||
}
|
||||
|
||||
static rect2
|
||||
ui_ReserveTextLineBounds(ui_interface Interface, gs_string Text, ui_layout* Layout)
|
||||
{
|
||||
rect2 Bounds = {0};
|
||||
|
||||
return Bounds;
|
||||
}
|
||||
|
||||
static rect2
|
||||
ui_ReserveElementBounds(ui_layout* Layout)
|
||||
{
|
||||
|
@ -340,6 +504,81 @@ ui_LayoutRemaining(ui_layout Layout)
|
|||
return Result;
|
||||
}
|
||||
|
||||
// Widgets
|
||||
|
||||
internal ui_widget
|
||||
ui_CreateWidget(gs_string String)
|
||||
{
|
||||
ui_widget Result = {};
|
||||
Result.String = String;
|
||||
Result.Alignment = Align_Left;
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
ui_WidgetSetFlag(ui_widget* Widget, u64 Flag)
|
||||
{
|
||||
u64 Value = ((u64)1 << Flag);
|
||||
Widget->Flags = Widget->Flags | Value;
|
||||
}
|
||||
|
||||
internal bool
|
||||
ui_WidgetIsFlagSet(ui_widget Widget, u64 Flag)
|
||||
{
|
||||
u64 Value = ((u64)1 << Flag);
|
||||
bool Result = (Widget.Flags & Value);
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal ui_eval_result
|
||||
ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds)
|
||||
{
|
||||
ui_eval_result Result = {};
|
||||
|
||||
Assert(Interface->WidgetsCount < Interface->WidgetsCountMax);
|
||||
Widget->Id.Index = Interface->WidgetsCount++;
|
||||
Widget->Id.LayoutId = Interface->LayoutStack[Interface->LayoutStackCount - 1].Id;
|
||||
|
||||
Widget->Bounds = Bounds;
|
||||
Interface->Widgets[Widget->Id.Index] = *Widget;
|
||||
|
||||
if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Clickable))
|
||||
{
|
||||
if (PointIsInRect(Widget->Bounds, Interface->Mouse.Pos))
|
||||
{
|
||||
if (ui_WidgetIdsEqual(Interface->HotWidget, Widget->Id) && MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState))
|
||||
{
|
||||
Result.Clicked = true;
|
||||
Interface->ActiveWidget = Widget->Id;
|
||||
}
|
||||
|
||||
if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id) &&
|
||||
MouseButtonTransitionedUp(Interface->Mouse.LeftButtonState))
|
||||
{
|
||||
Interface->ActiveWidget = {};
|
||||
}
|
||||
|
||||
Interface->HotWidget = Widget->Id;
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal ui_eval_result
|
||||
ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget)
|
||||
{
|
||||
rect2 Bounds = {0};
|
||||
ui_layout* Layout = Interface->LayoutStack + Interface->LayoutStackCount - 1;
|
||||
if (!ui_TryReserveElementBounds(Layout, &Bounds))
|
||||
{
|
||||
// TODO(pjs): This isn't invalid, but Idk when we'd hit this case yet
|
||||
InvalidCodePath;
|
||||
}
|
||||
|
||||
return ui_EvaluateWidget(Interface, Widget, Bounds);
|
||||
}
|
||||
|
||||
//
|
||||
// Drawing Functions
|
||||
//
|
||||
|
@ -364,78 +603,40 @@ ui_OutlineRect(ui_interface* Interface, rect2 Bounds, r32 Thickness, v4 Color)
|
|||
}
|
||||
|
||||
internal void
|
||||
ui_DrawString(ui_interface* Interface, gs_string String, rect2 Bounds, v4 Color, gs_string_alignment Alignment = Align_Left)
|
||||
ui_DrawString(ui_interface* Interface, gs_string String, rect2 Bounds, gs_string_alignment Alignment = Align_Left)
|
||||
{
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(Interface->RenderBuffer,
|
||||
String.Length,
|
||||
Interface->Style.Font->BitmapMemory,
|
||||
Interface->Style.Font->BitmapTextureHandle,
|
||||
Interface->Style.Font->BitmapWidth,
|
||||
Interface->Style.Font->BitmapHeight,
|
||||
Interface->Style.Font->BitmapBytesPerPixel,
|
||||
Interface->Style.Font->BitmapStride);
|
||||
|
||||
v2 RegisterPosition = Bounds.Min + Interface->Style.Margin;
|
||||
if (Alignment == Align_Left)
|
||||
{
|
||||
RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Interface->Style.Font, Color);
|
||||
}
|
||||
else if (Alignment == Align_Right)
|
||||
{
|
||||
RegisterPosition = DrawStringRightAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Interface->Style.Font, Color);
|
||||
}
|
||||
else
|
||||
{
|
||||
InvalidCodePath;
|
||||
}
|
||||
ui_widget Widget = ui_CreateWidget(String);
|
||||
Widget.Bounds = Bounds;
|
||||
ui_EvaluateWidget(Interface, &Widget);
|
||||
}
|
||||
|
||||
static void
|
||||
ui_LayoutDrawString(ui_interface* Interface, ui_layout* Layout, gs_string String, v4 Color, gs_string_alignment Alignment = Align_Left)
|
||||
internal void
|
||||
ui_DrawString(ui_interface* Interface, gs_string String, gs_string_alignment Alignment = Align_Left)
|
||||
{
|
||||
rect2 Bounds = {0};
|
||||
if (!ui_TryReserveElementBounds(Layout, &Bounds))
|
||||
{
|
||||
// TODO(NAME): Not invalid, just haven't implemented yet.
|
||||
// This is the case where Layout is in row mode without a fixed number of elements
|
||||
InvalidCodePath;
|
||||
}
|
||||
ui_DrawString(Interface, String, Bounds, Color, Alignment);
|
||||
}
|
||||
|
||||
static void
|
||||
ui_TextBox(ui_interface* Interface, rect2 Bounds, gs_string Text, v4 BGColor, v4 TextColor)
|
||||
{
|
||||
ui_FillRect(Interface, Bounds, BGColor);
|
||||
ui_DrawString(Interface, Text, Bounds, TextColor);
|
||||
DEBUG_TRACK_FUNCTION;
|
||||
ui_widget Widget = ui_CreateWidget(String);
|
||||
ui_EvaluateWidget(Interface, &Widget);
|
||||
}
|
||||
|
||||
static b32
|
||||
ui_Button(ui_interface* Interface, gs_string Text, rect2 Bounds, v4 InactiveColor, v4 HoverColor, v4 ClickedColor)
|
||||
ui_Button(ui_interface* Interface, gs_string Text)
|
||||
{
|
||||
b32 Pressed = false;
|
||||
v4 ButtonBG = InactiveColor;
|
||||
if (PointIsInRect(Bounds, Interface->Mouse.Pos))
|
||||
{
|
||||
ButtonBG = HoverColor;
|
||||
if (MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState))
|
||||
{
|
||||
ButtonBG = ClickedColor;
|
||||
Pressed = true;
|
||||
}
|
||||
}
|
||||
ui_TextBox(Interface, Bounds, Text, ButtonBG, Interface->Style.TextColor);
|
||||
return Pressed;
|
||||
ui_widget Widget = ui_CreateWidget(Text);
|
||||
ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable);
|
||||
ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground);
|
||||
ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget);
|
||||
return Result.Clicked;
|
||||
}
|
||||
|
||||
static b32
|
||||
ui_Button(ui_interface* Interface, gs_string Text, rect2 Bounds)
|
||||
{
|
||||
v4 BGColor = Interface->Style.ButtonColor_Inactive;
|
||||
v4 HoverColor = Interface->Style.ButtonColor_Active;
|
||||
v4 SelectedColor = Interface->Style.ButtonColor_Selected;
|
||||
return ui_Button(Interface, Text, Bounds, BGColor, HoverColor, SelectedColor);
|
||||
ui_widget Widget = ui_CreateWidget(Text);
|
||||
ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable);
|
||||
ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground);
|
||||
ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget, Bounds);
|
||||
return Result.Clicked;
|
||||
}
|
||||
|
||||
struct list_item_colors
|
||||
|
@ -465,35 +666,19 @@ ui_GetListItemColors(ui_interface* Interface, u32 ListItemIndex)
|
|||
static b32
|
||||
ui_ListButton(ui_interface* Interface, gs_string Text, rect2 Bounds, u32 ListItemIndex)
|
||||
{
|
||||
list_item_colors Colors = ui_GetListItemColors(Interface, ListItemIndex);
|
||||
return ui_Button(Interface, Text, Bounds, Colors.Hover, Colors.Selected, Colors.BGColor);
|
||||
}
|
||||
|
||||
static b32
|
||||
ui_LayoutButton(ui_interface* Interface, ui_layout* Layout, gs_string Text, v4 BGColor, v4 HoverColor, v4 SelectColor)
|
||||
{
|
||||
rect2 ButtonBounds = {0};
|
||||
if (!ui_TryReserveElementBounds(Layout, &ButtonBounds))
|
||||
{
|
||||
ButtonBounds = ui_ReserveTextLineBounds(*Interface, Text, Layout);
|
||||
}
|
||||
return ui_Button(Interface, Text, ButtonBounds, BGColor, HoverColor, SelectColor);
|
||||
}
|
||||
|
||||
static b32
|
||||
ui_LayoutButton(ui_interface* Interface, ui_layout* Layout, gs_string Text)
|
||||
{
|
||||
v4 BGColor = Interface->Style.ButtonColor_Inactive;
|
||||
v4 HoverColor = Interface->Style.ButtonColor_Active;
|
||||
v4 SelectedColor = Interface->Style.ButtonColor_Selected;
|
||||
return ui_LayoutButton(Interface, Layout, Text, BGColor, HoverColor, SelectedColor);
|
||||
ui_widget Widget = ui_CreateWidget(Text);
|
||||
ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground);
|
||||
ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable);
|
||||
// TODO(pjs): Reimplement alternating color backgrounds
|
||||
Widget.Bounds = Bounds;
|
||||
ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget);
|
||||
return Result.Clicked;
|
||||
}
|
||||
|
||||
static b32
|
||||
ui_LayoutListButton(ui_interface* Interface, ui_layout* Layout, gs_string Text, u32 ListItemIndex)
|
||||
{
|
||||
list_item_colors Colors = ui_GetListItemColors(Interface, ListItemIndex);
|
||||
return ui_LayoutButton(Interface, Layout, Text, Colors.Hover, Colors.Selected, Colors.BGColor);
|
||||
return ui_Button(Interface, Text);
|
||||
}
|
||||
|
||||
static b32
|
||||
|
@ -508,10 +693,90 @@ ui_LayoutListEntry(ui_interface* Interface, ui_layout* Layout, gs_string Text, u
|
|||
// Punting this till I have a use case
|
||||
InvalidCodePath;
|
||||
}
|
||||
v4 BGColor = ui_GetListItemBGColor(Interface->Style, Index);
|
||||
v4 HoverColor = Interface->Style.ListBGHover;
|
||||
v4 SelectedColor = Interface->Style.ListBGSelected;
|
||||
return ui_Button(Interface, Text, Bounds, BGColor, HoverColor, SelectedColor);
|
||||
return ui_Button(Interface, Text, Bounds);
|
||||
}
|
||||
|
||||
internal bool
|
||||
ui_EvaluateDropdown(ui_interface* Interface, ui_widget Widget, ui_eval_result EvalResult)
|
||||
{
|
||||
ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget.Id);
|
||||
if (!State) {
|
||||
State = ui_CreateRetainedState(Interface, Widget.Id);
|
||||
}
|
||||
|
||||
if (EvalResult.Clicked)
|
||||
{
|
||||
State->Value = !State->Value;
|
||||
}
|
||||
|
||||
if (State->Value)
|
||||
{
|
||||
ui_layout ParentLayout = Interface->LayoutStack[Interface->LayoutStackCount - 1];
|
||||
|
||||
r32 SpaceAbove = ParentLayout.Bounds.Max.y - Widget.Bounds.Max.y;
|
||||
r32 SpaceBelow = Widget.Bounds.Min.y - ParentLayout.Bounds.Min.y;
|
||||
ui_layout_direction Direction = LayoutDirection_TopDown;
|
||||
rect2 MenuBounds = {};
|
||||
|
||||
if (SpaceAbove > SpaceBelow)
|
||||
{
|
||||
r32 ParentLayoutMaxY = ParentLayout.Bounds.Max.y;
|
||||
Direction = LayoutDirection_BottomUp;
|
||||
MenuBounds = rect2{
|
||||
v2{ Widget.Bounds.Min.x, Widget.Bounds.Max.y },
|
||||
v2{ Widget.Bounds.Max.x, ParentLayoutMaxY }
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
r32 ParentLayoutMinY = ParentLayout.Bounds.Min.y;
|
||||
Direction = LayoutDirection_TopDown;
|
||||
MenuBounds = rect2{
|
||||
v2{ Widget.Bounds.Min.x, ParentLayoutMinY },
|
||||
v2{ Widget.Bounds.Max.x, Widget.Bounds.Min.y }
|
||||
};
|
||||
}
|
||||
|
||||
ui_layout Layout = ui_CreateLayout(Interface, MenuBounds, Direction);
|
||||
Layout.WidgetReference = Widget.Id;
|
||||
ui_PushLayout(Interface, Layout);
|
||||
}
|
||||
|
||||
return State->Value;
|
||||
}
|
||||
|
||||
internal bool
|
||||
ui_BeginDropdown(ui_interface* Interface, gs_string Text, rect2 Bounds)
|
||||
{
|
||||
ui_widget Widget = ui_CreateWidget(Text);
|
||||
ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable);
|
||||
ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground);
|
||||
ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget, Bounds);
|
||||
return ui_EvaluateDropdown(Interface, Widget, Result);
|
||||
}
|
||||
|
||||
internal bool
|
||||
ui_BeginDropdown(ui_interface* Interface, gs_string Text)
|
||||
{
|
||||
ui_widget Widget = ui_CreateWidget(Text);
|
||||
ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable);
|
||||
ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground);
|
||||
ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget);
|
||||
return ui_EvaluateDropdown(Interface, Widget, Result);
|
||||
}
|
||||
|
||||
internal void
|
||||
ui_EndDropdown(ui_interface* Interface)
|
||||
{
|
||||
ui_layout Layout = Interface->LayoutStack[Interface->LayoutStackCount - 1];
|
||||
ui_widget_retained_state* State = ui_GetRetainedState(Interface, Layout.WidgetReference);
|
||||
if (State)
|
||||
{
|
||||
if (State->Value)
|
||||
{
|
||||
ui_PopLayout(Interface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -423,6 +423,26 @@ Win32_SendAddressedDataBuffers_Job(gs_thread_context Context, gs_data Arg)
|
|||
Win32_SendAddressedDataBuffers(Context, *OutputData);
|
||||
}
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct test_microphone_packet
|
||||
{
|
||||
b8 ChangeAnimation;
|
||||
char AnimationFileName[32];
|
||||
b8 SetLayer;
|
||||
char LayerName[32];
|
||||
r32 LayerOpacity;
|
||||
b8 SetLayerParamColor;
|
||||
char LayerParamColor[7];
|
||||
r32 OverrideDuration;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
inline u32
|
||||
UpackB4(const u8* ptr)
|
||||
{
|
||||
return (u32)(ptr[3] | (ptr[2] << 8) | (ptr[1] << 16) | (ptr[0] << 24));
|
||||
}
|
||||
|
||||
int WINAPI
|
||||
WinMain (
|
||||
HINSTANCE HInstance,
|
||||
|
@ -434,6 +454,10 @@ WinMain (
|
|||
gs_thread_context ThreadContext = Win32CreateThreadContext();
|
||||
|
||||
#if 0
|
||||
// NOTE(pjs): UART TEST CODE
|
||||
// NOTE(pjs): UART TEST CODE
|
||||
// NOTE(pjs): UART TEST CODE
|
||||
|
||||
u32 LedCount = 48;
|
||||
u32 MessageBaseSize = sizeof(uart_header) + sizeof(uart_channel) + sizeof(uart_footer);
|
||||
MessageBaseSize += sizeof(u8) * 3 * LedCount;
|
||||
|
@ -459,8 +483,6 @@ WinMain (
|
|||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
MainWindow = Win32CreateWindow (HInstance, "Foldhaus", 1440, 768, HandleWindowEvents);
|
||||
Win32UpdateWindowDimension(&MainWindow);
|
||||
|
||||
|
@ -562,6 +584,25 @@ WinMain (
|
|||
WSAStartup(MAKEWORD(2, 2), &WSAData);
|
||||
Win32Sockets = Win32SocketArray_Create(16, &PlatformPermanent);
|
||||
|
||||
#if 0
|
||||
// NOTE(pjs): SOCKET READING TEST CODE
|
||||
// NOTE(pjs): SOCKET READING TEST CODE
|
||||
// NOTE(pjs): SOCKET READING TEST CODE
|
||||
|
||||
win32_socket TestSocket = Win32Socket_ConnectToAddress("127.0.0.1", "20185");
|
||||
test_microphone_packet* Recv = 0;
|
||||
while (true)
|
||||
{
|
||||
gs_data Data = Win32Socket_Receive(&TestSocket, ThreadContext.Transient);
|
||||
if (Data.Size > 0)
|
||||
{
|
||||
OutputDebugStringA("Received\n");
|
||||
Recv = (test_microphone_packet*)Data.Memory;
|
||||
}
|
||||
ClearArena(ThreadContext.Transient);
|
||||
}
|
||||
#endif
|
||||
|
||||
Win32SerialArray_Create(ThreadContext);
|
||||
|
||||
s32 RenderMemorySize = MB(12);
|
||||
|
|
|
@ -54,6 +54,82 @@ Win32SocketArray_Get(win32_socket_array Array, s32 Index)
|
|||
|
||||
global win32_socket_array Win32Sockets;
|
||||
|
||||
internal win32_socket
|
||||
Win32Socket_Create(s32 AddressFamily, s32 Type, s32 Protocol)
|
||||
{
|
||||
win32_socket Result = {0};
|
||||
Result.Socket = socket(AddressFamily, Type, Protocol);
|
||||
if (Result.Socket == INVALID_SOCKET)
|
||||
{
|
||||
s32 Error = WSAGetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void
|
||||
Win32Socket_Bind(win32_socket* Socket, s32 AddressFamily, char* Address, s32 Port)
|
||||
{
|
||||
sockaddr_in Service = {0};
|
||||
Service.sin_family = AddressFamily;
|
||||
Service.sin_addr.s_addr = inet_addr(Address);
|
||||
Service.sin_port = htons(Port);
|
||||
|
||||
s32 Result = bind(Socket->Socket, (SOCKADDR*)&Service, sizeof(Service));
|
||||
if (Result == SOCKET_ERROR)
|
||||
{
|
||||
s32 Error = WSAGetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
}
|
||||
|
||||
internal win32_socket
|
||||
Win32Socket_ConnectToAddress(char* Address, char* DefaultPort)
|
||||
{
|
||||
win32_socket Result = {};
|
||||
|
||||
addrinfo Hints = {0};
|
||||
Hints.ai_family = AF_UNSPEC;
|
||||
Hints.ai_socktype = SOCK_STREAM;
|
||||
Hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
addrinfo* PotentialConnections;
|
||||
s32 Error = getaddrinfo(Address, DefaultPort, &Hints, &PotentialConnections);
|
||||
if (Error == 0)
|
||||
{
|
||||
for (addrinfo* InfoAt = PotentialConnections; InfoAt != NULL; InfoAt = InfoAt->ai_next)
|
||||
{
|
||||
win32_socket Socket = Win32Socket_Create(InfoAt->ai_family, InfoAt->ai_socktype, InfoAt->ai_protocol);
|
||||
if (Socket.Socket == INVALID_SOCKET)
|
||||
{
|
||||
Error = WSAGetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
|
||||
Error = connect(Socket.Socket, InfoAt->ai_addr, (int)InfoAt->ai_addrlen);
|
||||
if (Error == SOCKET_ERROR)
|
||||
{
|
||||
closesocket(Socket.Socket);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = Socket;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error = WSAGetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
|
||||
freeaddrinfo(PotentialConnections);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal s32
|
||||
Win32Socket_SetOption(win32_socket* Socket, s32 Level, s32 Option, const char* OptionValue, s32 OptionLength)
|
||||
{
|
||||
|
@ -74,32 +150,6 @@ Win32Socket_SetOption(platform_socket_handle SocketHandle, s32 Level, s32 Option
|
|||
return Win32Socket_SetOption(Socket, Level, Option, OptionValue, OptionLength);
|
||||
}
|
||||
|
||||
PLATFORM_GET_SOCKET_HANDLE(Win32GetSocketHandle)
|
||||
{
|
||||
// NOTE(Peter): These used to be passed in as paramters, but we only use this function
|
||||
// with AF_INET, SOCK_DGRAM, and Protocol = 0. These are also platform specific values
|
||||
// so I was having to include windows.h in the platform agnostic code to accomodate that
|
||||
// function signature.
|
||||
s32 AddressFamily = AF_INET;
|
||||
s32 Type = SOCK_DGRAM;
|
||||
s32 Protocol = 0;
|
||||
|
||||
s32 Result = Win32SocketArray_Take(&Win32Sockets);
|
||||
win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, Result);
|
||||
Socket->Socket = socket(AddressFamily, Type, Protocol);
|
||||
if (Socket->Socket != INVALID_SOCKET)
|
||||
{
|
||||
int Error = Win32Socket_SetOption(Socket, IPPROTO_IP, IP_MULTICAST_TTL,
|
||||
(const char*)(&Multicast_TimeToLive), sizeof(Multicast_TimeToLive));
|
||||
}
|
||||
else
|
||||
{
|
||||
s32 Error = WSAGetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
|
||||
return (platform_socket_handle)Result;
|
||||
}
|
||||
|
||||
internal s32
|
||||
Win32Socket_SendTo(platform_socket_handle SocketHandle, u32 Address, u32 Port, const char* Buffer, s32 BufferLength, s32 Flags)
|
||||
|
@ -129,6 +179,61 @@ Win32Socket_SendTo(platform_socket_handle SocketHandle, u32 Address, u32 Port, c
|
|||
return LengthSent;
|
||||
}
|
||||
|
||||
internal void
|
||||
Win32Socket_SetListening(win32_socket* Socket)
|
||||
{
|
||||
if (listen(Socket->Socket, SOMAXCONN) == SOCKET_ERROR)
|
||||
{
|
||||
// TODO(pjs): Error logging
|
||||
s32 Error = WSAGetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
}
|
||||
|
||||
internal s32
|
||||
Win32Socket_PeekGetTotalSize(win32_socket* Socket)
|
||||
{
|
||||
s32 Flags = MSG_PEEK;
|
||||
char Temp[4];
|
||||
s32 BytesQueued = recv(Socket->Socket, Temp, 4, Flags);
|
||||
if (BytesQueued == SOCKET_ERROR)
|
||||
{
|
||||
// TODO(pjs): Error logging
|
||||
s32 Error = WSAGetLastError();
|
||||
BytesQueued = 0;
|
||||
}
|
||||
return BytesQueued;
|
||||
}
|
||||
|
||||
internal gs_data
|
||||
Win32Socket_Receive(win32_socket* Socket, gs_memory_arena* Storage)
|
||||
{
|
||||
#if 0
|
||||
gs_data Result = {};
|
||||
s32 BytesQueued = Win32Socket_PeekGetTotalSize(Socket);
|
||||
if (BytesQueued > 0)
|
||||
{
|
||||
Result = PushSizeToData(Storage, BytesQueued);
|
||||
s32 Flags = 0;
|
||||
s32 BytesReceived = recv(Socket->Socket, (char*)Result.Memory, Result.Size, Flags);
|
||||
Assert(BytesReceived == BytesQueued);
|
||||
}
|
||||
return Result;
|
||||
#else
|
||||
gs_data Result = PushSizeToData(Storage, 1024);
|
||||
s32 Flags = 0;
|
||||
s32 BytesReceived = recv(Socket->Socket, (char*)Result.Memory, Result.Size, Flags);
|
||||
if (BytesReceived == SOCKET_ERROR)
|
||||
{
|
||||
// TODO(pjs): Error logging
|
||||
s32 Error = WSAGetLastError();
|
||||
InvalidCodePath;
|
||||
}
|
||||
Result.Size = BytesReceived;
|
||||
return Result;
|
||||
#endif
|
||||
}
|
||||
|
||||
internal void
|
||||
Win32Socket_Close(win32_socket* Socket)
|
||||
{
|
||||
|
@ -136,5 +241,16 @@ Win32Socket_Close(win32_socket* Socket)
|
|||
Socket->Socket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
PLATFORM_GET_SOCKET_HANDLE(Win32GetSocketHandle)
|
||||
{
|
||||
s32 Result = Win32SocketArray_Take(&Win32Sockets);
|
||||
s32 Error = 0;
|
||||
win32_socket* Socket = Win32SocketArray_Get(Win32Sockets, Result);
|
||||
*Socket = Win32Socket_Create(AF_INET, SOCK_DGRAM, 0);
|
||||
Error = Win32Socket_SetOption(Socket, IPPROTO_IP, IP_MULTICAST_TTL,
|
||||
(const char*)(&Multicast_TimeToLive), sizeof(Multicast_TimeToLive));
|
||||
return (platform_socket_handle)Result;
|
||||
}
|
||||
|
||||
#define WIN32_FOLDHAUS_SOCKET_H
|
||||
#endif // WIN32_FOLDHAUS_SOCKET_H
|
|
@ -855,6 +855,7 @@ RectHSplit(rect2 Rect, r32 YValue, rect2* Top, rect2* Bottom)
|
|||
Bottom->Max = { Rect.Max.x, ClampedYValue };
|
||||
Bottom->Min = Rect.Min;
|
||||
}
|
||||
|
||||
internal void
|
||||
RectVSplit(rect2 Rect, r32 XValue, rect2* Left, rect2* Right)
|
||||
{
|
||||
|
|
12
src/todo.txt
12
src/todo.txt
|
@ -1,5 +1,17 @@
|
|||
TODO FOLDHAUS
|
||||
|
||||
|
||||
- engine environment
|
||||
- primary color
|
||||
- default controller
|
||||
- gets its own update function
|
||||
- has access to the engine state
|
||||
- can select animations
|
||||
- change playback speed, primary color
|
||||
- user space controller
|
||||
- socket listener
|
||||
|
||||
|
||||
STREAM #1: 3D Overhaul
|
||||
- Rendering (Working on this elsewhere)
|
||||
- OpenGL 3
|
||||
|
|
Loading…
Reference in New Issue