diff --git a/src/animation/foldhaus_animation_interface.h b/src/animation/foldhaus_animation_interface.h index a2e26a1..a6b6726 100644 --- a/src/animation/foldhaus_animation_interface.h +++ b/src/animation/foldhaus_animation_interface.h @@ -15,147 +15,3 @@ FOLDHAUS_INPUT_COMMAND_PROC(DeleteAnimationBlock) State->SelectedAnimationBlockHandle = {0}; } } - -internal animation_block_handle -DrawAnimationTimeline (animation_system* AnimationSystem, v2 PanelMin, v2 PanelMax, animation_block_handle SelectedBlockHandle, render_command_buffer* RenderBuffer, interface_config Interface, mouse_state Mouse) -{ - animation_block_handle Result = SelectedBlockHandle; - -r32 AnimationPanelHeight = PanelMax.y - PanelMin.y; - r32 AnimationPanelWidth = PanelMax.x - PanelMin.x; - panel_result AnimationPanel = EvaluatePanel(RenderBuffer, PanelMin, PanelMax, - 0, Interface); - - b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Mouse.LeftButtonState); - - for (u32 i = 0; i < AnimationSystem->BlocksCount; i++) - { - animation_block_entry AnimationBlockEntry = AnimationSystem->Blocks[i]; - if (AnimationBlockIsFree(AnimationBlockEntry)) { continue; } - - animation_block_handle CurrentBlockHandle = {}; - CurrentBlockHandle.Index = i; - CurrentBlockHandle.Generation = AnimationBlockEntry.Generation; - - animation_block AnimationBlockAt = AnimationBlockEntry.Block; - -r32 StartTimePercent = AnimationBlockAt.StartTime / AnimationSystem->AnimationEnd; - r32 StartPosition = AnimationPanelWidth * StartTimePercent; - -r32 EndTimePercent = AnimationBlockAt.EndTime / AnimationSystem->AnimationEnd; - r32 EndPosition = AnimationPanelWidth * EndTimePercent; - - v2 Min = v2{StartPosition, 25}; - v2 Max = v2{EndPosition, 75}; - - v4 BlockColor = BlackV4; - if (AnimationBlockHandlesAreEqual(SelectedBlockHandle, CurrentBlockHandle)) - { - BlockColor = PinkV4; - } - - PushRenderQuad2D(RenderBuffer, Min, Max, BlockColor); - PushRenderBoundingBox2D(RenderBuffer, Min, Max, 1, WhiteV4); - - if (PointIsInRange(Mouse.Pos, Min, Max) - && MouseButtonTransitionedDown(Mouse.LeftButtonState)) - { - MouseDownAndNotHandled = false; -if (AnimationBlockHandlesAreEqual(SelectedBlockHandle, CurrentBlockHandle)) - { -// If the block is already selected, deselect it. - Result = {0}; - } - else - { -Result = CurrentBlockHandle; - } - } - } - - r32 TimePercent = AnimationSystem->Time / AnimationSystem->AnimationEnd; - r32 SliderPosition = AnimationPanelWidth * TimePercent; - PushRenderQuad2D(RenderBuffer, v2{SliderPosition, AnimationPanelHeight}, - v2{SliderPosition + 1, 0}, WhiteV4); - - if (MouseDownAndNotHandled && PointIsInRange(Mouse.Pos, PanelMin, PanelMax)) - { - r32 MouseDownPositionPercent = (Mouse.Pos.x - PanelMin.x) / AnimationPanelWidth; - r32 NewBlockTimeStart = MouseDownPositionPercent * AnimationSystem->AnimationEnd; -#define NEW_BLOCK_DURATION 1 - r32 NewBlockTimeEnd = NewBlockTimeStart + NEW_BLOCK_DURATION; - - animation_block Block = {0}; - Block.StartTime = NewBlockTimeStart; - Block.EndTime = NewBlockTimeEnd; - Block.Proc = TestPatternThree; - - animation_block_handle NewBlockHandle = AddAnimationBlock(Block, AnimationSystem); - Result = NewBlockHandle; - } - return Result; - } - -internal animation_block_handle -DrawAnimationPanel (animation_system* AnimationSystem, -v2 PanelMin, v2 PanelMax, -animation_block_handle SelectedBlockHandle, -render_command_buffer* RenderBuffer, -interface_config Interface, mouse_state Mouse) -{ - animation_block_handle Result = SelectedBlockHandle; - -r32 OptionsRowHeight = 25; - v2 TimelineMin = PanelMin; - v2 TimelineMax = v2{PanelMax.x, PanelMax.y - OptionsRowHeight}; - if (TimelineMax.y - TimelineMin.y > 0) - { - Result = DrawAnimationTimeline(AnimationSystem, -TimelineMin, TimelineMax, - SelectedBlockHandle, - RenderBuffer, Interface, Mouse); - } - - v2 OptionsRowMin = v2{ PanelMin.x, TimelineMax.y }; - v2 OptionsRowMax = PanelMax; - panel_result AnimationPanel = EvaluatePanel(RenderBuffer, OptionsRowMin, OptionsRowMax, - 0, Interface); - - r32 ButtonWidth = 35; - v2 ButtonMin = v2{0, 0}; - v2 ButtonMax = v2{35, OptionsRowHeight - 2}; - v2 ButtonAt = v2{OptionsRowMin.x + 1, OptionsRowMin.y + 1}; - - button_result PauseResult = EvaluateButton(RenderBuffer, - ButtonAt + ButtonMin, ButtonAt + ButtonMax, - MakeStringLiteral("Pause"), - Interface, Mouse); - ButtonAt.x += ButtonWidth + 2; -button_result PlayResult = EvaluateButton(RenderBuffer, - ButtonAt + ButtonMin, ButtonAt + ButtonMax, - MakeStringLiteral("Play"), - Interface, Mouse); - ButtonAt.x += ButtonWidth + 2; -button_result StopResult = EvaluateButton(RenderBuffer, - ButtonAt + ButtonMin, ButtonAt + ButtonMax, - MakeStringLiteral("Stop"), - Interface, Mouse); - - if (PauseResult.Pressed) - { - AnimationSystem->TimelineShouldAdvance = false; - } - - if (PlayResult.Pressed) - { - AnimationSystem->TimelineShouldAdvance = true; - } - - if (StopResult.Pressed) - { - AnimationSystem->TimelineShouldAdvance = false; - AnimationSystem->Time = 0; - } - - return Result; -} \ No newline at end of file diff --git a/src/foldhaus_app.cpp b/src/foldhaus_app.cpp index 6337b14..0ee0504 100644 --- a/src/foldhaus_app.cpp +++ b/src/foldhaus_app.cpp @@ -22,68 +22,6 @@ MouseToWorldRay(r32 MouseX, r32 MouseY, camera* Camera, r32 WindowWidth, r32 Win return WorldPosition; } -struct draw_leds_job_data -{ - led* LEDs; - pixel* Colors; - s32 StartIndex; - s32 OnePastLastIndex; - - render_quad_batch_constructor* Batch; - - m44 FaceCameraMatrix; - m44 ModelViewMatrix; - r32 LEDHalfWidth; -}; - -internal void -DrawLEDsInBufferRangeJob (s32 ThreadID, void* JobData) -{ - DEBUG_TRACK_FUNCTION; - - draw_leds_job_data* Data = (draw_leds_job_data*)JobData; - - s32 LEDCount = Data->OnePastLastIndex - Data->StartIndex; - - quad_batch_constructor_reserved_range BatchReservedRange = ThreadSafeReserveRangeInQuadConstructor(Data->Batch, LEDCount * 2); - s32 TrisUsed = 0; - - r32 HalfWidth = Data->LEDHalfWidth; - - v4 P0_In = v4{-HalfWidth, -HalfWidth, 0, 1}; - v4 P1_In = v4{HalfWidth, -HalfWidth, 0, 1}; - v4 P2_In = v4{HalfWidth, HalfWidth, 0, 1}; - v4 P3_In = v4{-HalfWidth, HalfWidth, 0, 1}; - - v2 UV0 = v2{0, 0}; - v2 UV1 = v2{1, 0}; - v2 UV2 = v2{1, 1}; - v2 UV3 = v2{0, 1}; - - led* LED = Data->LEDs + Data->StartIndex; - for (s32 LEDIdx = 0; - LEDIdx < LEDCount; - LEDIdx++) - { - pixel PixelColor = Data->Colors[LED->Index]; - v4 Color = v4{PixelColor.R / 255.f, PixelColor.G / 255.f, PixelColor.B / 255.f, 1.0f}; - - v4 V4Position = LED->Position; - V4Position.w = 0; - v4 P0 = P0_In + V4Position; - v4 P1 = P1_In + V4Position; - v4 P2 = P2_In + V4Position; - v4 P3 = P3_In + V4Position; - - SetTri3DInBatch(Data->Batch, BatchReservedRange.Start + TrisUsed++, - P0, P1, P2, UV0, UV1, UV2, Color, Color, Color); - SetTri3DInBatch(Data->Batch, BatchReservedRange.Start + TrisUsed++, - P0, P2, P3, UV0, UV2, UV3, Color, Color, Color); - - LED++; - } -} - struct send_sacn_job_data { @@ -194,7 +132,7 @@ INITIALIZE_APPLICATION(InitializeApplication) State->Permanent.Alloc = (gs_memory_alloc*)Context.PlatformAlloc; State->Permanent.Realloc = (gs_memory_realloc*)Context.PlatformRealloc; State->Transient = {}; -State->Transient.Alloc = (gs_memory_alloc*)Context.PlatformAlloc; + State->Transient.Alloc = (gs_memory_alloc*)Context.PlatformAlloc; State->Transient.Realloc = (gs_memory_realloc*)Context.PlatformRealloc; InitializeInputCommandRegistry(&State->DefaultInputCommandRegistry, 32, &State->Permanent); @@ -244,12 +182,12 @@ State->Transient.Alloc = (gs_memory_alloc*)Context.PlatformAlloc; u32 CodepointW, CodepointH; Context.PlatformDrawFontCodepoint( - Font->BitmapMemory, - Font->BitmapWidth, - Font->BitmapHeight, - CodepointX, CodepointY, - Codepoint, FontInfo, - &CodepointW, &CodepointH); + Font->BitmapMemory, + Font->BitmapWidth, + Font->BitmapHeight, + CodepointX, CodepointY, + Codepoint, FontInfo, + &CodepointW, &CodepointH); AddCodepointToFont(Font, Codepoint, 0, 0, CodepointW, CodepointH, CodepointX, CodepointY); } @@ -299,35 +237,44 @@ State->Transient.Alloc = (gs_memory_alloc*)Context.PlatformAlloc; ReloadStaticData(Context, GlobalDebugServices, Alloc, Free); // Setup Operation Modes - State->Modes.ActiveModesCount = 0; - State->Modes.Arena = {}; - State->Modes.Arena.Alloc = (gs_memory_alloc*)Context.PlatformAlloc; - State->Modes.Arena.Realloc = (gs_memory_realloc*)Context.PlatformRealloc; - State->Modes.Arena.FindAddressRule = FindAddress_InLastBufferOnly; + State->Modes.ActiveModesCount = 0; + State->Modes.Arena = {}; + State->Modes.Arena.Alloc = (gs_memory_alloc*)Context.PlatformAlloc; + State->Modes.Arena.Realloc = (gs_memory_realloc*)Context.PlatformRealloc; + State->Modes.Arena.FindAddressRule = FindAddress_InLastBufferOnly; { // MODES PLAYGROUND InitializeAnimationSystem(&State->AnimationSystem); - + animation_block BlockZero = {0}; BlockZero.StartTime = 0; - BlockZero.EndTime = 2; - BlockZero.Proc = TestPatternOne; - AddAnimationBlock(BlockZero, &State->AnimationSystem); - -animation_block BlockOne = {0}; -BlockOne.StartTime = 3; - BlockOne.EndTime = 5; + BlockZero.EndTime = 2; + BlockZero.Proc = TestPatternOne; + AddAnimationBlock(BlockZero, &State->AnimationSystem); + + animation_block BlockOne = {0}; + BlockOne.StartTime = 3; + BlockOne.EndTime = 5; BlockOne.Proc = TestPatternTwo; - AddAnimationBlock(BlockOne, &State->AnimationSystem); - -animation_block BlockTwo = {0}; -BlockTwo.StartTime = 5; - BlockTwo.EndTime = 8; - BlockTwo.Proc = TestPatternThree; + AddAnimationBlock(BlockOne, &State->AnimationSystem); + + animation_block BlockTwo = {0}; + BlockTwo.StartTime = 5; + BlockTwo.EndTime = 8; + BlockTwo.Proc = TestPatternThree; AddAnimationBlock(BlockTwo, &State->AnimationSystem); State->AnimationSystem.AnimationEnd = 10; } // End Animation Playground + + + { // Panels Playground + InitializePanelLayout(&State->PanelLayout); +panel* Panel = TakeNewPanel(&State->PanelLayout); + SetPanelDefinition(Panel, GlobalPanelDefs[0]); + SplitPanelVertically(Panel, .5f, v2{0, 0}, v2{Context.WindowWidth, Context.WindowHeight}, &State->PanelLayout); + SetPanelDefinition(&Panel->Right->Panel, GlobalPanelDefs[1]); + } // End Panels Playground } internal void @@ -433,25 +380,25 @@ UPDATE_AND_RENDER(UpdateAndRender) HandleInput(State, InputQueue, Mouse); if (State->AnimationSystem.TimelineShouldAdvance) { - State->AnimationSystem.Time += Context.DeltaTime; - if (State->AnimationSystem.Time > State->AnimationSystem.AnimationEnd) - { - State->AnimationSystem.Time -= State->AnimationSystem.AnimationEnd; - } - + State->AnimationSystem.Time += Context.DeltaTime; + if (State->AnimationSystem.Time > State->AnimationSystem.AnimationEnd) + { + State->AnimationSystem.Time -= State->AnimationSystem.AnimationEnd; + } + for (u32 i = 0; i < State->AnimationSystem.BlocksCount; i++) { animation_block_entry BlockEntry = State->AnimationSystem.Blocks[i]; if (!AnimationBlockIsFree(BlockEntry)) { animation_block Block = BlockEntry.Block; - if (State->AnimationSystem.Time >= Block.StartTime - && State->AnimationSystem.Time <= Block.EndTime) - { - Block.Proc(State, State->AnimationSystem.Time - Block.StartTime); + if (State->AnimationSystem.Time >= Block.StartTime + && State->AnimationSystem.Time <= Block.EndTime) + { + Block.Proc(State, State->AnimationSystem.Time - Block.StartTime); } } - } + } } s32 HeaderSize = State->NetworkProtocolHeaderSize; @@ -486,20 +433,28 @@ UPDATE_AND_RENDER(UpdateAndRender) Job->DMXBuffers = DMXBuffers; Context.GeneralWorkQueue->PushWorkOnQueue( - Context.GeneralWorkQueue, - SACNSendDMXBufferListJob, - Job); + Context.GeneralWorkQueue, + SACNSendDMXBufferListJob, + Job); }break; InvalidDefaultCase; } } +PushRenderOrthographic(RenderBuffer, 0, 0, Context.WindowWidth, Context.WindowHeight); + PushRenderClearScreen(RenderBuffer); + v2 WindowMin = v2{0, 0}; + v2 WindowMax = v2{Context.WindowWidth, Context.WindowHeight}; + HandleMousePanelInteraction(&State->PanelLayout, WindowMin, WindowMax, Mouse); + DrawAllPanels(State->PanelLayout, WindowMin, WindowMax, RenderBuffer, State->Interface, Mouse, State, Context); + //////////////////////////////// // Render Assembly /////////////////////////////// if (Context.WindowIsVisible) { + #if 0 State->Camera.AspectRatio = (r32)Context.WindowWidth / (r32)Context.WindowHeight; m44 ModelViewMatrix = GetCameraModelViewMatrix(State->Camera); @@ -507,7 +462,7 @@ UPDATE_AND_RENDER(UpdateAndRender) r32 LEDHalfWidth = .5f; - PushRenderPerspective(RenderBuffer, 0, 0, Context.WindowWidth, Context.WindowHeight, State->Camera); + PushRenderPerspective(RenderBuffer, 0, 0, Context.WindowWidth / 2, Context.WindowHeight, State->Camera); PushRenderClearScreen(RenderBuffer); // TODO(Peter): Pretty sure this isn't working right now @@ -540,9 +495,9 @@ UPDATE_AND_RENDER(UpdateAndRender) JobData->LEDHalfWidth = LEDHalfWidth; Context.GeneralWorkQueue->PushWorkOnQueue( - Context.GeneralWorkQueue, - DrawLEDsInBufferRangeJob, - JobData); + Context.GeneralWorkQueue, + DrawLEDsInBufferRangeJob, + JobData); } } @@ -561,7 +516,6 @@ UPDATE_AND_RENDER(UpdateAndRender) /////////////////////////////////////// // Menu Bar ////////////////////////////////////// - r32 TopBarHeight = 40; { panel_result TopBarPanel = EvaluatePanel(RenderBuffer, @@ -606,11 +560,11 @@ UPDATE_AND_RENDER(UpdateAndRender) v2 TimelineMin = v2{0, 0}; v2 TimelineMax = v2{Context.WindowWidth, 125}; animation_block_handle NewSelection = DrawAnimationPanel(&State->AnimationSystem, -TimelineMin, TimelineMax, -State->SelectedAnimationBlockHandle, - RenderBuffer, State->Interface, Mouse); + TimelineMin, TimelineMax, + State->SelectedAnimationBlockHandle, + RenderBuffer, State->Interface, Mouse); State->SelectedAnimationBlockHandle = NewSelection; - + for (s32 m = 0; m < State->Modes.ActiveModesCount; m++) { operation_mode OperationMode = State->Modes.ActiveModes[m]; @@ -623,16 +577,17 @@ State->SelectedAnimationBlockHandle, DrawDebugInterface(RenderBuffer, 25, State->Interface, Context.WindowWidth, Context.WindowHeight - TopBarHeight, Context.DeltaTime, State, State->Camera, Mouse, &State->Transient); +#endif } // Checking for overflows { DEBUG_TRACK_SCOPE(OverflowChecks); - AssertAllocationsNoOverflow(State->Permanent); - for (s32 i = 0; i < State->AssemblyList.Used; i++) - { - assembly* Assembly = GetElementAtIndex(i, State->AssemblyList); - AssertAllocationsNoOverflow(Assembly->Arena); + AssertAllocationsNoOverflow(State->Permanent); + for (s32 i = 0; i < State->AssemblyList.Used; i++) + { + assembly* Assembly = GetElementAtIndex(i, State->AssemblyList); + AssertAllocationsNoOverflow(Assembly->Arena); } } } diff --git a/src/foldhaus_app.h b/src/foldhaus_app.h index 87d2c4e..62004fa 100644 --- a/src/foldhaus_app.h +++ b/src/foldhaus_app.h @@ -17,6 +17,8 @@ typedef struct app_state app_state; +#include "foldhaus_panel.h" + #include "foldhaus_command_dispatch.h" #include "foldhaus_command_dispatch.cpp" #include "foldhaus_operation_mode.h" @@ -40,7 +42,7 @@ struct app_state memory_arena Permanent; memory_arena Transient; - s32 NetworkProtocolHeaderSize; +s32 NetworkProtocolHeaderSize; network_protocol NetworkProtocol; streaming_acn SACN; @@ -63,6 +65,8 @@ struct app_state animation_system AnimationSystem; animation_block_handle SelectedAnimationBlockHandle; + + panel_layout PanelLayout; }; internal void OpenColorPicker(app_state* State, v4* Address); @@ -165,4 +169,12 @@ r32 GreenSize = 20.0f; #include "foldhaus_search_lister.cpp" #include "foldhaus_interface.cpp" -#include "animation/foldhaus_animation_interface.h" \ No newline at end of file +#include "animation/foldhaus_animation_interface.h" + +#include "panels/foldhaus_panel_sculpture_view.h" +#include "panels/foldhaus_panel_profiler.h" +#include "panels/foldhaus_panel_dmx_view.h" +#include "panels/foldhaus_panel_animation_timeline.h" + +#include "generated/foldhaus_panels_generated.h" +#include "foldhaus_panel.cpp" diff --git a/src/foldhaus_debug_visuals.h b/src/foldhaus_debug_visuals.h index 4454460..f5e6641 100644 --- a/src/foldhaus_debug_visuals.h +++ b/src/foldhaus_debug_visuals.h @@ -1,4 +1,4 @@ - +#if 0 internal void RenderProfiler_ScopeVisualization(render_command_buffer* RenderBuffer, interface_config Interface, mouse_state Mouse, @@ -188,6 +188,7 @@ DrawDebugFrameList (render_command_buffer* RenderBuffer, interface_config Interf VisibleFrame, Memory); } } +#endif internal void DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_config Interface, r32 WindowWidth, r32 WindowHeight, r32 DeltaTime, app_state* State, camera Camera, mouse_state Mouse, memory_arena* Transient) @@ -311,7 +312,7 @@ DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_c v2 ProfilerMin = v2{TopOfDebugView.x, TopOfDebugView.y - 500}; v2 ProfilerMax = v2{TopOfDebugView.x + 700, TopOfDebugView.y - 64}; PushRenderQuad2D(RenderBuffer, ProfilerMin, ProfilerMax, v4{0, 0, 0, .8f}); - DrawDebugFrameList(RenderBuffer, Interface, Mouse, ProfilerMin, ProfilerMax, Transient); + //DrawDebugFrameList(RenderBuffer, Interface, Mouse, ProfilerMin, ProfilerMax, Transient); #if 0 r32 ColumnsStartX = TopOfScreenLinePos.x; diff --git a/src/foldhaus_panel.cpp b/src/foldhaus_panel.cpp new file mode 100644 index 0000000..579ce8e --- /dev/null +++ b/src/foldhaus_panel.cpp @@ -0,0 +1,313 @@ + +///////////////////////////////// +// +// Book-Keeping +// +///////////////////////////////// + +internal void +InitializePanelLayout(panel_layout* Layout) +{ + Layout->FreeList.Free.Next = &Layout->FreeList; +} + +internal panel_entry* +TakeNewPanelEntry(panel_layout* Layout) +{ + panel_entry* FreeEntry = 0; + if (Layout->FreeList.Free.Next != &Layout->FreeList) + { + FreeEntry = Layout->FreeList.Free.Next; + Layout->FreeList.Free.Next = FreeEntry->Free.Next; + } + else + { + Assert(Layout->PanelsUsed < PANELS_MAX); + FreeEntry = Layout->Panels + Layout->PanelsUsed++; + } + return FreeEntry; +} + +internal panel* +TakeNewPanel(panel_layout* Layout) +{ + panel* Result = 0; + panel_entry* FreeEntry = TakeNewPanelEntry(Layout); +Result = &FreeEntry->Panel; + *Result = {0}; + return Result; +} + +internal void +FreePanelEntry(panel_entry* Entry, panel_layout* Layout) +{ + Assert(Entry >= Layout->Panels && Entry <= Layout->Panels + PANELS_MAX); + Entry->Panel = {0}; + Entry->Free.Next = Layout->FreeList.Free.Next; + Layout->FreeList.Free.Next = Entry; +} + +internal void +FreePanelAtIndex(s32 Index, panel_layout* Layout) +{ + Assert(Index > 0 && Index < (s32)Layout->PanelsUsed); + panel_entry* EntryToFree = Layout->Panels + Index; + EntryToFree->Free.Next = Layout->FreeList.Free.Next; + Layout->FreeList.Free.Next = EntryToFree; +} + +internal void +SplitPanelVertically(panel* Parent, r32 Percent, v2 ParentMin, v2 ParentMax, panel_layout* Layout) +{ + r32 SplitX = GSLerp(ParentMin.x, ParentMax.x, Percent); + if (SplitX > ParentMin.x && SplitX < ParentMax.x) + { +Parent->SplitDirection = PanelSplit_Vertical; + Parent->SplitPercent = Percent; + + Parent->Left = TakeNewPanelEntry(Layout); + Parent->Left->Panel.Render = Parent->Render; + +Parent->Right = TakeNewPanelEntry(Layout); + Parent->Right->Panel.Render = Parent->Render; + } +} + +internal void +SplitPanelHorizontally(panel* Parent, r32 Percent, v2 ParentMin, v2 ParentMax, panel_layout* Layout) +{ + r32 SplitY = GSLerp(ParentMin.y, ParentMax.y, Percent); + if (SplitY > ParentMin.y && SplitY < ParentMax.y) + { +Parent->SplitDirection = PanelSplit_Horizontal; + Parent->SplitPercent = Percent; + + Parent->Bottom = TakeNewPanelEntry(Layout); + Parent->Bottom->Panel.Render = Parent->Render; + +Parent->Top = TakeNewPanelEntry(Layout); + Parent->Top->Panel.Render = Parent->Render; + } +} + +internal void +ConsolidatePanelsKeepOne(panel* Parent, panel_entry* PanelEntryToKeep, panel_layout* Layout) +{ + panel_entry* LeftChild = Parent->Left; + panel_entry* RightChild = Parent->Right; + + *Parent = PanelEntryToKeep->Panel; + Parent->SplitDirection = PanelSplit_NoSplit; + + FreePanelEntry(LeftChild, Layout); + FreePanelEntry(RightChild, Layout); +} + +internal void +SetPanelDefinition(panel* Panel, panel_definition Def) +{ + if(Panel->Cleanup) + { + Panel->Cleanup(Panel); + } + +Panel->Cleanup = Def.Cleanup; + Panel->Render = Def.Render; + Def.Init(Panel); +} + +///////////////////////////////// +// +// Rendering And Interaction +// +///////////////////////////////// + +internal void +HandleMousePanelInteractionOrRecurse(panel* Panel, v2 PanelMin, v2 PanelMax, panel_layout* PanelLayout, mouse_state Mouse) +{ + r32 PanelEdgeClickMaxDistance = 4; + + // TODO(Peter): Need a way to calculate this button's position more systemically + if (Panel->SplitDirection == PanelSplit_NoSplit + && PointIsInRange(Mouse.DownPos, PanelMin, PanelMin + v2{25, 25})) + { + r32 XDistance = GSAbs(Mouse.Pos.x - Mouse.DownPos.x); + r32 YDistance = GSAbs(Mouse.Pos.y - Mouse.DownPos.y); + + if (XDistance > YDistance) + { + r32 XPercent = (Mouse.Pos.x - PanelMin.x) / (PanelMax.x - PanelMin.x); + SplitPanelVertically(Panel, XPercent, PanelMin, PanelMax, PanelLayout); + } + else + { + r32 YPercent = (Mouse.Pos.y - PanelMin.y) / (PanelMax.y - PanelMin.y); + SplitPanelHorizontally(Panel, YPercent, PanelMin, PanelMax, PanelLayout); + } + } + else if (Panel->SplitDirection == PanelSplit_Horizontal) + { + r32 SplitY = GSLerp(PanelMin.y, PanelMax.y, Panel->SplitPercent); + r32 ClickDistanceFromSplit = GSAbs(Mouse.DownPos.y - SplitY); + if (ClickDistanceFromSplit < PanelEdgeClickMaxDistance) + { + r32 NewSplitY = Mouse.Pos.y; + if (NewSplitY <= PanelMin.y) + { + ConsolidatePanelsKeepOne(Panel, Panel->Top, PanelLayout); + } + else if (NewSplitY >= PanelMax.y) + { + ConsolidatePanelsKeepOne(Panel, Panel->Bottom, PanelLayout); + } + else + { + Panel->SplitPercent = (NewSplitY - PanelMin.y) / (PanelMax.y - PanelMin.y); + } + } + else + { + HandleMousePanelInteractionOrRecurse(&Panel->Bottom->Panel, PanelMin, v2{PanelMax.x, SplitY}, PanelLayout, Mouse); + HandleMousePanelInteractionOrRecurse(&Panel->Top->Panel, v2{PanelMin.x, SplitY}, PanelMax, PanelLayout, Mouse); + } + } + else if (Panel->SplitDirection == PanelSplit_Vertical) + { + r32 SplitX = GSLerp(PanelMin.x, PanelMax.x, Panel->SplitPercent); + r32 ClickDistanceFromSplit = GSAbs(Mouse.DownPos.x - SplitX); + if (ClickDistanceFromSplit < PanelEdgeClickMaxDistance) + { + r32 NewSplitX = Mouse.Pos.x; + if (NewSplitX <= PanelMin.x) + { + ConsolidatePanelsKeepOne(Panel, Panel->Right, PanelLayout); + } + else if (NewSplitX >= PanelMax.x) + { + ConsolidatePanelsKeepOne(Panel, Panel->Left, PanelLayout); + } + else + { + Panel->SplitPercent = (NewSplitX - PanelMin.x) / (PanelMax.x - PanelMin.x); + } + } + else + { + HandleMousePanelInteractionOrRecurse(&Panel->Left->Panel, PanelMin, v2{SplitX, PanelMax.y}, PanelLayout, Mouse); + HandleMousePanelInteractionOrRecurse(&Panel->Right->Panel, v2{SplitX, PanelMin.y}, PanelMax, PanelLayout, Mouse); + } + } +} + +internal void +HandleMousePanelInteraction(panel_layout* PanelLayout, v2 WindowMin, v2 WindowMax, mouse_state Mouse) +{ + r32 PanelEdgeClickMaxDistance = 4; + + if (MouseButtonTransitionedUp(Mouse.LeftButtonState)) + { + Assert(PanelLayout->PanelsUsed > 0); + panel* FirstPanel = &PanelLayout->Panels[0].Panel; + HandleMousePanelInteractionOrRecurse(FirstPanel, WindowMin, WindowMax, PanelLayout, Mouse); + } +} + +internal void +DrawPanelFooter(panel* Panel, render_command_buffer* RenderBuffer, v2 FooterMin, v2 FooterMax, interface_config Interface, mouse_state Mouse) +{ + PushRenderQuad2D(RenderBuffer, FooterMin, v2{FooterMax.x, FooterMin.y + 25}, v4{.5f, .5f, .5f, 1.f}); + PushRenderQuad2D(RenderBuffer, FooterMin, FooterMin + v2{25, 25}, WhiteV4); + + v2 PanelSelectButtonMin = FooterMin + v2{30, 1}; + v2 PanelSelectButtonMax = PanelSelectButtonMin + v2{100, 23}; + + if (Panel->PanelSelectionMenuOpen) + { + v2 ButtonDimension = v2{100, 25}; + v2 ButtonMin = v2{PanelSelectButtonMin.x, FooterMax.y}; + + v2 MenuMin = ButtonMin; + v2 MenuMax = v2{ButtonMin.x + ButtonDimension.x, ButtonMin.y + (ButtonDimension.y * GlobalPanelDefsCount)}; + if (MouseButtonTransitionedDown(Mouse.LeftButtonState) + && !PointIsInRange(Mouse.DownPos, MenuMin, MenuMax)) + { + Panel->PanelSelectionMenuOpen = false; + } + + +for (s32 i = 0; i < GlobalPanelDefsCount; i++) + { + panel_definition Def = GlobalPanelDefs[i]; + string DefName = MakeString(Def.PanelName, Def.PanelNameLength); + button_result DefinitionButton = EvaluateButton(RenderBuffer, + ButtonMin, ButtonMin + ButtonDimension, + DefName, Interface, Mouse); + if (DefinitionButton.Pressed) + { + SetPanelDefinition(Panel, Def); + Panel->PanelSelectionMenuOpen = false; + } + + ButtonMin.y += ButtonDimension.y; + } + } + +button_result ButtonResult = EvaluateButton(RenderBuffer, + PanelSelectButtonMin, +PanelSelectButtonMax, + MakeStringLiteral("Select"), Interface, Mouse); + if (ButtonResult.Pressed) + { + Panel->PanelSelectionMenuOpen = !Panel->PanelSelectionMenuOpen; + } + +} + +internal void +DrawPanelBorder(panel Panel, v2 PanelMin, v2 PanelMax, v4 Color, render_command_buffer* RenderBuffer) +{ + PushRenderBoundingBox2D(RenderBuffer, PanelMin, PanelMax, 1, Color); +} + +internal void +DrawPanelOrRecurse(panel* Panel, v2 PanelMin, v2 PanelMax, v2 WindowMin, v2 WindowMax, render_command_buffer* RenderBuffer, interface_config Interface, mouse_state Mouse, app_state* State, context Context) +{ + if (Panel->SplitDirection == PanelSplit_NoSplit) + { + v2 FooterMin = PanelMin; + v2 FooterMax = v2{PanelMax.x, PanelMin.y + 25}; + v2 PanelViewMin = v2{PanelMin.x, FooterMax.y}; + v2 PanelViewMax = PanelMax; + + Panel->Render(*Panel, PanelViewMin, PanelViewMax, RenderBuffer, State, Context, Mouse); + v4 BorderColor = v4{0, 1, 1, 1}; + if (PointIsInRange(Mouse.Pos, PanelMin, PanelMax)) + { + BorderColor = v4{1, 0, 1, 1}; + } + +PushRenderOrthographic(RenderBuffer, WindowMin.x, WindowMin.y, WindowMax.x, WindowMax.y); +DrawPanelBorder(*Panel, PanelMin, PanelMax, BorderColor, RenderBuffer); +DrawPanelFooter(Panel, RenderBuffer, FooterMin, FooterMax, Interface, Mouse); + } + else if (Panel->SplitDirection == PanelSplit_Horizontal) + { + r32 SplitY = GSLerp(PanelMin.y, PanelMax.y, Panel->SplitPercent); + DrawPanelOrRecurse(&Panel->Bottom->Panel, PanelMin, v2{PanelMax.x, SplitY}, WindowMin, WindowMax, RenderBuffer, Interface, Mouse, State, Context); + DrawPanelOrRecurse(&Panel->Top->Panel, v2{PanelMin.x, SplitY}, PanelMax, WindowMin, WindowMax, RenderBuffer, Interface, Mouse, State, Context); + } + else if (Panel->SplitDirection == PanelSplit_Vertical) + { + r32 SplitX = GSLerp(PanelMin.x, PanelMax.x, Panel->SplitPercent); + DrawPanelOrRecurse(&Panel->Left->Panel, PanelMin, v2{SplitX, PanelMax.y}, WindowMin, WindowMax, RenderBuffer, Interface, Mouse, State, Context); + DrawPanelOrRecurse(&Panel->Right->Panel, v2{SplitX, PanelMin.y}, PanelMax, WindowMin, WindowMax, RenderBuffer, Interface, Mouse, State, Context); + } +} + +internal void +DrawAllPanels(panel_layout PanelLayout, v2 WindowMin, v2 WindowMax, render_command_buffer* RenderBuffer, interface_config Interface, mouse_state Mouse, app_state* State, context Context) +{ + Assert(PanelLayout.PanelsUsed > 0); + panel* FirstPanel = &PanelLayout.Panels[0].Panel; + DrawPanelOrRecurse(FirstPanel, WindowMin, WindowMax, WindowMin, WindowMax, RenderBuffer, Interface, Mouse, State, Context); +} diff --git a/src/foldhaus_panel.h b/src/foldhaus_panel.h new file mode 100644 index 0000000..90a06bd --- /dev/null +++ b/src/foldhaus_panel.h @@ -0,0 +1,73 @@ + +typedef struct panel panel; + +#define PANEL_INIT_PROC(name) void name(panel* Panel) +typedef PANEL_INIT_PROC(panel_init_proc); + +#define PANEL_CLEANUP_PROC(name) void name(panel* Panel) +typedef PANEL_CLEANUP_PROC(panel_cleanup_proc); + +#define PANEL_RENDER_PROC(name) void name(panel Panel, v2 PanelMin, v2 PanelMax, render_command_buffer* RenderBuffer, app_state* State, context Context, mouse_state Mouse) +typedef PANEL_RENDER_PROC(panel_render_proc); + +enum panel_split_direction +{ + PanelSplit_NoSplit, +PanelSplit_Horizontal, + PanelSplit_Vertical, + + PanelSplit_Count, +}; + +typedef struct panel_entry panel_entry; + +struct panel +{ +panel_render_proc* Render; +panel_cleanup_proc* Cleanup; + + 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; + + union{ + panel_entry* Left; + panel_entry* Top; + }; + union{ + panel_entry* Right; + panel_entry* Bottom; + }; +}; + +struct free_panel +{ + panel_entry* Next; +}; + +struct panel_entry +{ + panel Panel; + free_panel Free; +}; + +#define PANELS_MAX 16 +struct panel_layout +{ +panel_entry Panels[PANELS_MAX]; + u32 PanelsUsed; + + panel_entry FreeList; +}; + +struct panel_definition +{ + char* PanelName; + s32 PanelNameLength; + panel_init_proc* Init; + panel_cleanup_proc* Cleanup; + panel_render_proc* Render; +}; \ No newline at end of file diff --git a/src/generated/foldhaus_panels_generated.h b/src/generated/foldhaus_panels_generated.h new file mode 100644 index 0000000..fc7847a --- /dev/null +++ b/src/generated/foldhaus_panels_generated.h @@ -0,0 +1,7 @@ +global_variable s32 GlobalPanelDefsCount = 4; +global_variable panel_definition GlobalPanelDefs[] = { + { "Sculpture View", 14, SculptureView_Init, SculptureView_Cleanup, SculptureView_Render }, + { "Animation Timeline", 18, AnimationTimeline_Init, AnimationTimeline_Cleanup, AnimationTimeline_Render }, + { "DMX View", 8, DMXView_Init, DMXView_Cleanup, DMXView_Render }, + { "Profiler", 8, ProfilerView_Init, ProfilerView_Cleanup, ProfilerView_Render }, +}; \ No newline at end of file diff --git a/src/gs_memory_arena.h b/src/gs_memory_arena.h index bcf4746..7cf08f3 100644 --- a/src/gs_memory_arena.h +++ b/src/gs_memory_arena.h @@ -191,7 +191,7 @@ enum gs_memory_find_address_rule typedef void* gs_memory_alloc(gs_mem_u32 Size); typedef void* gs_memory_realloc(void* Address, gs_mem_u32 OldSize, gs_mem_u32 NewSize); -typedef void gs_memory_free(void* Address, gs_mem_u32 Size); +typedef void gs_memory_free(void* Address, gs_mem_u32 Size); #ifndef GS_MEMORY_BUFFER_SIZE #define GS_MEMORY_BUFFER_SIZE 1024 diff --git a/src/panels/foldhaus_panel_animation_timeline.h b/src/panels/foldhaus_panel_animation_timeline.h new file mode 100644 index 0000000..9d965e0 --- /dev/null +++ b/src/panels/foldhaus_panel_animation_timeline.h @@ -0,0 +1,149 @@ +PANEL_INIT_PROC(AnimationTimeline_Init) +{ + +} + +PANEL_CLEANUP_PROC(AnimationTimeline_Cleanup) +{ + +} + +internal animation_block_handle +DrawAnimationTimeline (animation_system* AnimationSystem, v2 PanelMin, v2 PanelMax, animation_block_handle SelectedBlockHandle, render_command_buffer* RenderBuffer, interface_config Interface, mouse_state Mouse) +{ + animation_block_handle Result = SelectedBlockHandle; + +r32 AnimationPanelHeight = PanelMax.y - PanelMin.y; + r32 AnimationPanelWidth = PanelMax.x - PanelMin.x; + panel_result AnimationPanel = EvaluatePanel(RenderBuffer, PanelMin, PanelMax, + 0, Interface); + + b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Mouse.LeftButtonState); + + for (u32 i = 0; i < AnimationSystem->BlocksCount; i++) + { + animation_block_entry AnimationBlockEntry = AnimationSystem->Blocks[i]; + if (AnimationBlockIsFree(AnimationBlockEntry)) { continue; } + + animation_block_handle CurrentBlockHandle = {}; + CurrentBlockHandle.Index = i; + CurrentBlockHandle.Generation = AnimationBlockEntry.Generation; + + animation_block AnimationBlockAt = AnimationBlockEntry.Block; + +r32 StartTimePercent = AnimationBlockAt.StartTime / AnimationSystem->AnimationEnd; + r32 StartPosition = AnimationPanelWidth * StartTimePercent; + +r32 EndTimePercent = AnimationBlockAt.EndTime / AnimationSystem->AnimationEnd; + r32 EndPosition = AnimationPanelWidth * EndTimePercent; + + v2 Min = PanelMin + v2{StartPosition, 25}; + v2 Max = PanelMin + v2{EndPosition, 75}; + + v4 BlockColor = BlackV4; + if (AnimationBlockHandlesAreEqual(SelectedBlockHandle, CurrentBlockHandle)) + { + BlockColor = PinkV4; + } + + PushRenderQuad2D(RenderBuffer, Min, Max, BlockColor); + PushRenderBoundingBox2D(RenderBuffer, Min, Max, 1, WhiteV4); + + if (PointIsInRange(Mouse.Pos, Min, Max) + && MouseButtonTransitionedDown(Mouse.LeftButtonState)) + { + MouseDownAndNotHandled = false; +if (AnimationBlockHandlesAreEqual(SelectedBlockHandle, CurrentBlockHandle)) + { +// If the block is already selected, deselect it. + Result = {0}; + } + else + { +Result = CurrentBlockHandle; + } + } + } + + r32 TimePercent = AnimationSystem->Time / AnimationSystem->AnimationEnd; + r32 SliderX = PanelMin.x + (AnimationPanelWidth * TimePercent); + v2 SliderMin = v2{SliderX, PanelMin.y}; + v2 SliderMax = v2{SliderX + 1, PanelMax.y - 25}; + PushRenderQuad2D(RenderBuffer, SliderMin, SliderMax, WhiteV4); + + if (MouseDownAndNotHandled && PointIsInRange(Mouse.Pos, PanelMin, PanelMax)) + { + r32 MouseDownPositionPercent = (Mouse.Pos.x - PanelMin.x) / AnimationPanelWidth; + r32 NewBlockTimeStart = MouseDownPositionPercent * AnimationSystem->AnimationEnd; +#define NEW_BLOCK_DURATION 1 + r32 NewBlockTimeEnd = NewBlockTimeStart + NEW_BLOCK_DURATION; + + animation_block Block = {0}; + Block.StartTime = NewBlockTimeStart; + Block.EndTime = NewBlockTimeEnd; + Block.Proc = TestPatternThree; + + animation_block_handle NewBlockHandle = AddAnimationBlock(Block, AnimationSystem); + Result = NewBlockHandle; + } + return Result; + } + +PANEL_RENDER_PROC(AnimationTimeline_Render) +{ + animation_block_handle SelectedBlockHandle = State->SelectedAnimationBlockHandle; + +r32 OptionsRowHeight = 25; + v2 TimelineMin = PanelMin; + v2 TimelineMax = v2{PanelMax.x, PanelMax.y - OptionsRowHeight}; + if (TimelineMax.y - TimelineMin.y > 0) + { + SelectedBlockHandle = DrawAnimationTimeline(&State->AnimationSystem, +TimelineMin, TimelineMax, + SelectedBlockHandle, + RenderBuffer, State->Interface, Mouse); + } + + v2 OptionsRowMin = v2{ PanelMin.x, TimelineMax.y }; + v2 OptionsRowMax = PanelMax; + panel_result AnimationPanel = EvaluatePanel(RenderBuffer, OptionsRowMin, OptionsRowMax, + 0, State->Interface); + + r32 ButtonWidth = 35; + v2 ButtonMin = v2{0, 0}; + v2 ButtonMax = v2{35, OptionsRowHeight - 2}; + v2 ButtonAt = v2{OptionsRowMin.x + 1, OptionsRowMin.y + 1}; + + button_result PauseResult = EvaluateButton(RenderBuffer, + ButtonAt + ButtonMin, ButtonAt + ButtonMax, + MakeStringLiteral("Pause"), + State->Interface, Mouse); + ButtonAt.x += ButtonWidth + 2; +button_result PlayResult = EvaluateButton(RenderBuffer, + ButtonAt + ButtonMin, ButtonAt + ButtonMax, + MakeStringLiteral("Play"), + State->Interface, Mouse); + ButtonAt.x += ButtonWidth + 2; +button_result StopResult = EvaluateButton(RenderBuffer, + ButtonAt + ButtonMin, ButtonAt + ButtonMax, + MakeStringLiteral("Stop"), + State->Interface, Mouse); + + if (PauseResult.Pressed) + { + State->AnimationSystem.TimelineShouldAdvance = false; + } + + if (PlayResult.Pressed) + { + State->AnimationSystem.TimelineShouldAdvance = true; + } + + if (StopResult.Pressed) + { + State->AnimationSystem.TimelineShouldAdvance = false; + State->AnimationSystem.Time = 0; + } + +State->SelectedAnimationBlockHandle = SelectedBlockHandle; +} diff --git a/src/panels/foldhaus_panel_dmx_view.h b/src/panels/foldhaus_panel_dmx_view.h new file mode 100644 index 0000000..1986403 --- /dev/null +++ b/src/panels/foldhaus_panel_dmx_view.h @@ -0,0 +1,77 @@ +PANEL_INIT_PROC(DMXView_Init) +{ + +} + +PANEL_CLEANUP_PROC(DMXView_Cleanup) +{ + +} + +PANEL_RENDER_PROC(DMXView_Render) +{ +#if 0 + DEBUG_TRACK_SCOPE(DrawUniverseOutputDisplay); + + universe_view_operation_state* OpState = (universe_view_operation_state*)Operation.OpStateMemory; + + string TitleBarString = InitializeEmptyString(PushArray(State->Transient, char, 64), 64); + + v2 DisplayArea_Dimension = v2{600, 600}; + + v2 DisplayContents_Offset = OpState->DisplayOffset; + + // + // TODO(Peter): I don't like this. Dragging the Universe view should be an operation mode, just + // like rotating the 3D view, but modes don't have access to the state of modes above them in the stack + // (and attempting to cast those states to the appropriate type seems risky) + // + // :NeedToPassStateDownModeChain + // + if (OpState->MouseDown) + { + DisplayContents_Offset += (Mouse.Pos - Mouse.DownPos); + } + + v2 DisplayArea_TopLeft = v2{300, (r32)RenderBuffer->ViewHeight - 50} + DisplayContents_Offset; + v2 UniverseDisplayDimension = v2{100, 100} * OpState->Zoom; + v2 Padding = v2{25, 50} * OpState->Zoom; + + v2 UniverseDisplayTopLeft = DisplayArea_TopLeft; + + sacn_universe_buffer* UniverseList = State->SACN.UniverseBuffer; + while(UniverseList) + { + for (s32 UniverseIdx = 0; + UniverseIdx < UniverseList->Used; + UniverseIdx++) + { + sacn_universe* Universe = UniverseList->Universes + UniverseIdx; + DrawSACNUniversePixels(RenderBuffer, Universe, UniverseDisplayTopLeft, UniverseDisplayDimension); + + + if (OpState->Zoom > .5f) + { + v2 TitleDisplayStart = UniverseDisplayTopLeft + v2{0, 12}; + PrintF(&TitleBarString, "Universe %d", Universe->Universe); + DrawString(RenderBuffer, TitleBarString, State->Interface.Font, + TitleDisplayStart, WhiteV4); + } + + UniverseDisplayTopLeft.x += UniverseDisplayDimension.x + Padding.x; + if (UniverseDisplayTopLeft.x > DisplayArea_TopLeft.x + DisplayArea_Dimension.x) + { + UniverseDisplayTopLeft.x = DisplayArea_TopLeft.x; + UniverseDisplayTopLeft.y -= UniverseDisplayDimension.y + Padding.y; + } + + if (UniverseDisplayTopLeft.y < DisplayArea_TopLeft.y - DisplayArea_Dimension.y) + { + break; + } + + } + UniverseList = UniverseList->Next; + } +#endif +} \ No newline at end of file diff --git a/src/panels/foldhaus_panel_profiler.h b/src/panels/foldhaus_panel_profiler.h new file mode 100644 index 0000000..3f4e789 --- /dev/null +++ b/src/panels/foldhaus_panel_profiler.h @@ -0,0 +1,199 @@ +PANEL_INIT_PROC(ProfilerView_Init) +{ + +} + +PANEL_CLEANUP_PROC(ProfilerView_Cleanup) +{ + +} + +internal void +RenderProfiler_ScopeVisualization(render_command_buffer* RenderBuffer, + interface_config Interface, mouse_state Mouse, + v2 Min, v2 Max, + debug_frame* VisibleFrame, memory_arena* Memory) +{ + v4 ThreadColors[] = { + v4{.73f, .33f, .83f, 1}, + v4{0, .50f, .50f, 1}, + v4{.83f, 0, 0, 1}, + v4{.33f, .49f, .83f, 1}, + v4{.74f, .40f, .25f, 1}, + }; + + r32 Width = Max.x - Min.x; + r32 DepthHeight = 64; + + s64 FrameStartCycles = VisibleFrame->FrameStartCycles; + s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles; + + debug_scope_record_list* ThreadScopeCalls = GetScopeListForThreadInFrame(GlobalDebugServices, + VisibleFrame); + + MakeStringBuffer(String, 256); + for (s32 i = 0; i < ThreadScopeCalls->Count; i++) + { + scope_record* Record = ThreadScopeCalls->Calls + i; + scope_name* Name = GetOrAddNameHashEntry(VisibleFrame, Record->NameHash); + r32 PercentStart = (r32)(Record->StartCycles - FrameStartCycles) / (r32)FrameTotalCycles; + r32 PercentEnd = (r32)(Record->EndCycles - FrameStartCycles) / (r32)FrameTotalCycles; + + v2 ScopeMin = v2{Min.x + (Width * PercentStart), Max.y - ((Record->CallDepth + 1) * DepthHeight)}; + v2 ScopeMax = v2{Min.x + (Width * PercentEnd), ScopeMin.y + (DepthHeight - 4)}; + + if ((ScopeMax.x - ScopeMin.x) >= 1) + { + v4 Color = ThreadColors[0]; + if (PointIsInRange(Mouse.Pos, ScopeMin, ScopeMax)) + { + Color = GreenV4; + } + + PushRenderQuad2D(RenderBuffer, ScopeMin, ScopeMax, Color); + PushRenderBoundingBox2D(RenderBuffer, ScopeMin, ScopeMax, 1, BlackV4); + + if (PointIsInRange(Mouse.Pos, ScopeMin, ScopeMax)) + { + PushRenderQuad2D(RenderBuffer, Mouse.Pos, Mouse.Pos + v2{256, 32}, BlackV4); + PrintF(&String, "%.*s : %d - %d", Name->Name.Length, Name->Name.Memory, Record->StartCycles, Record->EndCycles); + DrawString(RenderBuffer, String, Interface.Font, Mouse.Pos, WhiteV4); + } + } + } +} + +internal void +RenderProfiler_ListVisualization(render_command_buffer* RenderBuffer, + interface_config Interface, mouse_state Mouse, + v2 Min, v2 Max, + debug_frame* VisibleFrame, memory_arena* Memory) +{ + MakeStringBuffer(String, 256); + + r32 YAt = Max.y - Interface.Font->PixelHeight; + r32 Column0X = Min.x; + r32 Column1X = Column0X + 256; + r32 Column2X = Column1X + 128; + r32 Column3X = Column2X + 128; + r32 Column4X = Column3X + 100; + + for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++) + { + scope_name NameEntry = VisibleFrame->ScopeNamesHash[n]; + if (NameEntry.Hash != 0) + { + collated_scope_record* CollatedRecord = VisibleFrame->CollatedScopes + n; + + PrintF(&String, "%.*s", NameEntry.Name.Length, NameEntry.Name.Memory); + DrawString(RenderBuffer, String, Interface.Font, v2{Column0X, YAt}, WhiteV4); + + PrintF(&String, "%f", CollatedRecord->PercentFrameTime); + DrawString(RenderBuffer, String, Interface.Font, v2{Column1X, YAt}, WhiteV4); + + PrintF(&String, "%fs", CollatedRecord->TotalSeconds); + DrawString(RenderBuffer, String, Interface.Font, v2{Column2X, YAt}, WhiteV4); + + PrintF(&String, "%dcy", CollatedRecord->TotalCycles); + DrawString(RenderBuffer, String, Interface.Font, v2{Column3X, YAt}, WhiteV4); + + PrintF(&String, "%d calls", CollatedRecord->CallCount); + DrawString(RenderBuffer, String, Interface.Font, v2{Column4X, YAt}, WhiteV4); + + YAt -= Interface.Font->PixelHeight + 4; + + if (YAt < Min.y) { break; } + } + } +} + +PANEL_RENDER_PROC(ProfilerView_Render) +{ + memory_arena* Memory = &State->Transient; +string String = InitializeEmptyString(PushArray(Memory, char, 256), 256); + + v4 FrameColors[] = { GreenV4, YellowV4, RedV4, WhiteV4 }; + + r32 FrameListHeight = 64; + v2 FrameListMin = v2{PanelMin.x + 16, PanelMax.y - (16 + FrameListHeight)}; + v2 FrameListMax = v2{PanelMax.x - 16, PanelMax.y - 16}; + + r32 FrameListPadding = 4; + r32 FrameListInnerWidth = (FrameListMax.x - FrameListMin.x) - (FrameListPadding * 2); + + r32 SingleFrameStep = FrameListInnerWidth / DEBUG_FRAME_COUNT; + r32 SingleFrameWidth = (r32)((s32)SingleFrameStep - 2); + + PushRenderBoundingBox2D(RenderBuffer, FrameListMin, FrameListMax, 2, WhiteV4); + + if (PointIsInRange(Mouse.Pos, FrameListMin, FrameListMax) && + MouseButtonHeldDown(Mouse.LeftButtonState)) + { + r32 LocalMouseX = (Mouse.Pos.x - FrameListMin.x) + FrameListPadding; + s32 ClosestFrameIndex = (LocalMouseX / SingleFrameStep); + + if (ClosestFrameIndex >= 0 && ClosestFrameIndex < DEBUG_FRAME_COUNT) + { + GlobalDebugServices->RecordFrames = false; + GlobalDebugServices->CurrentDebugFrame = ClosestFrameIndex; + } + } + + for (s32 F = 0; F < DEBUG_FRAME_COUNT; F++) + { + v2 Min = v2{FrameListMin.x + FrameListPadding + (F * SingleFrameStep), FrameListMin.y + 4}; + v2 Max = v2{Min.x + SingleFrameWidth, FrameListMax.y - 4}; + + s32 FramesAgo = (GlobalDebugServices->CurrentDebugFrame - F); + if (FramesAgo < 0) { FramesAgo += DEBUG_FRAME_COUNT; } + v4 Color = FrameColors[GSClamp(0, FramesAgo, 3)]; + PushRenderQuad2D(RenderBuffer, Min, Max, Color); + } + + debug_frame* VisibleFrame = GetLastDebugFrame(GlobalDebugServices); + s64 FrameStartCycles = VisibleFrame->FrameStartCycles; + s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles; + + PrintF(&String, "Frame %d - Total Cycles: %lld", + GlobalDebugServices->CurrentDebugFrame - 1, + FrameTotalCycles); + DrawString(RenderBuffer, String, State->Interface.Font, FrameListMin - v2{0, 32}, WhiteV4); + + v2 ButtonMin = v2{FrameListMax.x - 128, FrameListMin.y - 32}; + v2 ButtonMax = ButtonMin + v2{128, 28}; + button_result ShouldResumeRecording = EvaluateButton(RenderBuffer, ButtonMin, ButtonMax, + MakeString("Resume Recording"), State->Interface, Mouse); + if (ShouldResumeRecording.Pressed) + { + GlobalDebugServices->RecordFrames = true; + } + + ButtonMin = v2{FrameListMin.x, FrameListMin.y - 60}; + ButtonMax = v2{FrameListMin.x + 128, FrameListMin.y - 42}; + button_result ActivateScopeView = EvaluateButton(RenderBuffer, ButtonMin, ButtonMax, + MakeString("Scope View"), State->Interface, Mouse); + + ButtonMin.x += 152; + ButtonMax.x += 152; + button_result ActivateListView = EvaluateButton(RenderBuffer, ButtonMin, ButtonMax, + MakeString("List View"), State->Interface, Mouse); + + if (ActivateScopeView.Pressed) { GlobalDebugServices->Interface.FrameView = FRAME_VIEW_PROFILER; } + if (ActivateListView.Pressed) { GlobalDebugServices->Interface.FrameView = FRAME_VIEW_SCOPE_LIST; } + + v2 ViewModeMin = v2{FrameListMin.x, PanelMin.y}; + v2 ViewModeMax = v2{FrameListMax.x, FrameListMin.y - 96}; + + if (GlobalDebugServices->Interface.FrameView == FRAME_VIEW_PROFILER) + { + RenderProfiler_ScopeVisualization(RenderBuffer, State->Interface, Mouse, + ViewModeMin, ViewModeMax, + VisibleFrame, Memory); + } + else + { + RenderProfiler_ListVisualization(RenderBuffer, State->Interface, Mouse, + ViewModeMin, ViewModeMax, + VisibleFrame, Memory); + } +} \ No newline at end of file diff --git a/src/panels/foldhaus_panel_sculpture_view.h b/src/panels/foldhaus_panel_sculpture_view.h new file mode 100644 index 0000000..1200313 --- /dev/null +++ b/src/panels/foldhaus_panel_sculpture_view.h @@ -0,0 +1,123 @@ +PANEL_INIT_PROC(SculptureView_Init) +{ + +} + +PANEL_CLEANUP_PROC(SculptureView_Cleanup) +{ + +} + + +struct draw_leds_job_data +{ + led* LEDs; + pixel* Colors; + s32 StartIndex; + s32 OnePastLastIndex; + + render_quad_batch_constructor* Batch; + + m44 FaceCameraMatrix; + m44 ModelViewMatrix; + r32 LEDHalfWidth; +}; + +internal void +DrawLEDsInBufferRangeJob (s32 ThreadID, void* JobData) +{ + DEBUG_TRACK_FUNCTION; + + draw_leds_job_data* Data = (draw_leds_job_data*)JobData; + + s32 LEDCount = Data->OnePastLastIndex - Data->StartIndex; + + quad_batch_constructor_reserved_range BatchReservedRange = ThreadSafeReserveRangeInQuadConstructor(Data->Batch, LEDCount * 2); + s32 TrisUsed = 0; + + r32 HalfWidth = Data->LEDHalfWidth; + + v4 P0_In = v4{-HalfWidth, -HalfWidth, 0, 1}; + v4 P1_In = v4{HalfWidth, -HalfWidth, 0, 1}; + v4 P2_In = v4{HalfWidth, HalfWidth, 0, 1}; + v4 P3_In = v4{-HalfWidth, HalfWidth, 0, 1}; + + v2 UV0 = v2{0, 0}; + v2 UV1 = v2{1, 0}; + v2 UV2 = v2{1, 1}; + v2 UV3 = v2{0, 1}; + + led* LED = Data->LEDs + Data->StartIndex; + for (s32 LEDIdx = 0; + LEDIdx < LEDCount; + LEDIdx++) + { + pixel PixelColor = Data->Colors[LED->Index]; + v4 Color = v4{PixelColor.R / 255.f, PixelColor.G / 255.f, PixelColor.B / 255.f, 1.0f}; + + v4 V4Position = LED->Position; + V4Position.w = 0; + v4 P0 = P0_In + V4Position; + v4 P1 = P1_In + V4Position; + v4 P2 = P2_In + V4Position; + v4 P3 = P3_In + V4Position; + + SetTri3DInBatch(Data->Batch, BatchReservedRange.Start + TrisUsed++, + P0, P1, P2, UV0, UV1, UV2, Color, Color, Color); + SetTri3DInBatch(Data->Batch, BatchReservedRange.Start + TrisUsed++, + P0, P2, P3, UV0, UV2, UV3, Color, Color, Color); + + LED++; + } +} + +PANEL_RENDER_PROC(SculptureView_Render) +{ + DEBUG_TRACK_SCOPE(RenderSculpture); + +r32 PanelWidth = PanelMax.x - PanelMin.x; + r32 PanelHeight = PanelMax.y - PanelMin.y; + State->Camera.AspectRatio = PanelWidth / PanelHeight; + + m44 ModelViewMatrix = GetCameraModelViewMatrix(State->Camera); + m44 ProjectionMatrix = GetCameraPerspectiveProjectionMatrix(State->Camera); + + r32 LEDHalfWidth = .5f; + + PushRenderPerspective(RenderBuffer, PanelMin.x, PanelMin.y, PanelWidth, PanelHeight, State->Camera); + + // TODO(Peter): Pretty sure this isn't working right now + m44 FaceCameraMatrix = GetLookAtMatrix(v4{0, 0, 0, 1}, V4(State->Camera.Position, 1)); + FaceCameraMatrix = FaceCameraMatrix; + + s32 MaxLEDsPerJob = 2048; + render_quad_batch_constructor RenderLEDsBatch = PushRenderQuad3DBatch(RenderBuffer, State->TotalLEDsCount); + + for (s32 i = 0; i < State->ActiveAssemblyIndecies.Used; i++) + { + array_entry_handle AssemblyHandle = *GetElementAtIndex(i, State->ActiveAssemblyIndecies); + assembly Assembly = *GetElementWithHandle(AssemblyHandle, State->AssemblyList); + s32 JobsNeeded = IntegerDivideRoundUp(Assembly.LEDCount, MaxLEDsPerJob); + + for (s32 Job = 0; Job < JobsNeeded; Job++) + { + draw_leds_job_data* JobData = PushStruct(&State->Transient, draw_leds_job_data); + JobData->LEDs = Assembly.LEDs; + JobData->Colors = Assembly.Colors; + JobData->StartIndex = Job * MaxLEDsPerJob; + JobData->OnePastLastIndex = GSMin(JobData->StartIndex + MaxLEDsPerJob, Assembly.LEDCount); + JobData->Batch = &RenderLEDsBatch; + JobData->FaceCameraMatrix; + JobData->ModelViewMatrix = ModelViewMatrix; + JobData->LEDHalfWidth = LEDHalfWidth; + + Context.GeneralWorkQueue->PushWorkOnQueue( + Context.GeneralWorkQueue, + DrawLEDsInBufferRangeJob, + JobData); + } + } + + Context.GeneralWorkQueue->DoQueueWorkUntilDone(Context.GeneralWorkQueue, 0); + Context.GeneralWorkQueue->ResetWorkQueue(Context.GeneralWorkQueue); +}