Interface fixes. Fixed the problem with strip LUT tables where substrips were stamping over previous substrips. Some pattern building too.

This commit is contained in:
PS 2021-01-10 16:25:35 -08:00
parent 8d923a5e3c
commit 71547b05dc
20 changed files with 1562 additions and 247 deletions

View File

@ -29,6 +29,8 @@ cl %CommonCompilerFlags% %SourceCodePath%\platform_win32\win32_foldhaus.cpp /lin
cl %CommonCompilerFlags% %ProjectDevPath%\src\serial_monitor\first.cpp /Feserial_monitor.exe /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib
cl %CommonCompilerFlags% %ProjectDevPath%\src\sculpture_gen\gen_blumen_lumen.cpp /Fegen_blumen_lumen.exe /link %CommonLinkerFlags% user32.lib winmm.lib gdi32.lib
popd
call %MyPath%\_postbuild_win32.bat

2
debug.bat Normal file
View File

@ -0,0 +1,2 @@
@echo off
call remedybg.bat ./app_run_tree/win32_msvc/debug/session.rdbg

View File

@ -12,6 +12,8 @@ Editor_HandleInput (app_state* State, rect2 WindowBounds, input_queue InputQueue
b32 MouseInputHandled = HandleMousePanelInteraction(&State->PanelSystem, State->WindowBounds, Mouse, State);
gs_string TextInputString = PushString(State->Transient, 32);
panel* ActivePanel = PanelSystem_GetPanelContainingPoint(&State->PanelSystem, Mouse.Pos);
if (ActivePanel)
{
@ -47,7 +49,14 @@ Editor_HandleInput (app_state* State, rect2 WindowBounds, input_queue InputQueue
// 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);
if (!FindAndPushExistingCommand(ActiveCommands, Event, Command_Began, &State->CommandQueue))
{
char KeyASCII = KeyCodeToChar(Event.Key);
if (KeyASCII)
{
OutChar(&TextInputString, KeyASCII);
}
}
}
else if (KeyTransitionedUp(Event))
{
@ -55,7 +64,14 @@ Editor_HandleInput (app_state* State, rect2 WindowBounds, input_queue InputQueue
}
else if (KeyHeldDown(Event))
{
FindAndPushExistingCommand(ActiveCommands, Event, Command_Held, &State->CommandQueue);
if (!FindAndPushExistingCommand(ActiveCommands, Event, Command_Held, &State->CommandQueue))
{
char KeyASCII = KeyCodeToChar(Event.Key);
if (KeyASCII)
{
OutChar(&TextInputString, KeyASCII);
}
}
}
}
}
@ -74,6 +90,8 @@ Editor_HandleInput (app_state* State, rect2 WindowBounds, input_queue InputQueue
}
}
State->Interface.TempInputString = TextInputString.ConstString;
ClearCommandQueue(&State->CommandQueue);
}
@ -84,6 +102,12 @@ Editor_Update(app_state* State, context* Context, input_queue InputQueue)
State->WindowBounds = Context->WindowBounds;
State->Interface.Mouse = Context->Mouse;
State->Interface.HotWidgetFramesSinceUpdate += 1;
if (State->Interface.HotWidgetFramesSinceUpdate > 1)
{
State->Interface.HotWidget = {};
}
PanelSystem_UpdateLayout(&State->PanelSystem, State->WindowBounds);
Editor_HandleInput(State, State->WindowBounds, InputQueue, Context->Mouse, *Context);
}
@ -91,6 +115,8 @@ Editor_Update(app_state* State, context* Context, input_queue InputQueue)
internal void
Editor_DrawWidgetString(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 ClippingBox, v4 Color)
{
gs_string Temp = PushString(State->Transient, 256);
PrintF(&Temp, "%d", Widget.Id.Id);
render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer,
Widget.String.Length,
State->Interface.Style.Font->BitmapMemory,
@ -199,14 +225,14 @@ Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* Ren
FillBounds.Max.y = FillToPoint;
}
}
PushRenderQuad2DClipped(RenderBuffer, FillBounds, WidgetParentUnion, Color);
rect2 ClippedFillBounds = Rect2Union(FillBounds, WidgetParentUnion);
PushRenderQuad2D(RenderBuffer, ClippedFillBounds, Color);
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawString) && Widget.String.Length > 0)
{
// TODO(pjs): Mask this text by the horizontal fill
// TODO(pjs): add this color to the style
v4 TextColor = BlackV4;
Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, WidgetParentUnion, TextColor);
Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, ClippedFillBounds, TextColor);
}
}
@ -231,120 +257,67 @@ Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* Ren
}
}
global r32 TestSlider_Value = 5;
global r32 TestSlider_Min = 0;
global r32 TestSlider_Max = 10;
global bool TestToggle = true;
#include "../interface_test.cpp"
internal void
TestRender(app_state* State, context* Context, render_command_buffer* RenderBuffer)
FOLDHAUS_INPUT_COMMAND_PROC(ActiveWidget_TypeCharacter)
{
ui_InterfaceReset(&State->Interface);
State->Interface.RenderBuffer = RenderBuffer;
State->Interface.WindowBounds = Context->WindowBounds;
gs_string A = MakeString("TestRender Layout");
ui_PushLayout(&State->Interface, A);
ui_widget* ActiveWidget = ui_InterfaceGetWidgetWithId(&State->Interface, State->Interface.ActiveWidget);
ui_widget_retained_state* WidgetState = ui_GetRetainedState(&State->Interface, ActiveWidget->Id);
if (WidgetState)
{
#if 0
ui_Label(&State->Interface, MakeString("Spacer"));
ui_Label(&State->Interface, MakeString("Spacer"));
ui_Label(&State->Interface, MakeString("Spacer"));
ui_Label(&State->Interface, MakeString("Spacer"));
ui_Label(&State->Interface, MakeString("Spacer"));
ui_BeginList(&State->Interface, MakeString("TestList"), 5, 16);
char AsciiValue = CharacterFromKeyCode(Event.Key);
if (AsciiValue)
{
ui_BeginRow(&State->Interface, 3);
for (u32 i = 0; i < 16; i++)
{
ui_Button(&State->Interface, MakeString("B"));
ui_Button(&State->Interface, MakeString("C"));
ui_Button(&State->Interface, MakeString("D"));
}
ui_EndRow(&State->Interface);
}
ui_EndList(&State->Interface);
//ui_Button(&State->Interface, MakeString("B"));
//ui_Button(&State->Interface, MakeString("C"));
//TestSlider_Value = ui_RangeSlider(&State->Interface, MakeString("TestSlider"), TestSlider_Value, TestSlider_Min, TestSlider_Max);
#elif 1
ui_PushLayout(&State->Interface, MakeString("Outer"));
{
for (u32 i = 0; i < 3; i++)
{
ui_Button(&State->Interface, MakeString("A"));
OutChar(&WidgetState->EditString, AsciiValue);
}
}
ui_PopLayout(&State->Interface);
ui_BeginRow(&State->Interface, 2);
{
ui_PushLayout(&State->Interface, MakeString("TestLayout"));
{
for (u32 i = 0; i < 5; i++)
{
ui_Button(&State->Interface, MakeString("TestButon"));
}
}
ui_PopLayout(&State->Interface);
ui_PushLayout(&State->Interface, MakeString("TestLayout"));
{
ui_Button(&State->Interface, MakeString("TestButon"));
TestToggle = ui_Toggle(&State->Interface, MakeString("Toggle"), TestToggle);
TestSlider_Value = ui_RangeSlider(&State->Interface, MakeString("TestSlider"), TestSlider_Value, TestSlider_Min, TestSlider_Max);
if (ui_BeginDropdown(&State->Interface, MakeString("TestDropdown")))
{
ui_Button(&State->Interface, MakeString("TestButon"));
ui_Button(&State->Interface, MakeString("TestButon"));
ui_Button(&State->Interface, MakeString("TestButon"));
}
ui_EndDropdown(&State->Interface);
}
ui_PopLayout(&State->Interface);
}
ui_EndRow(&State->Interface);
ui_PushLayout(&State->Interface, MakeString("Outer"));
{
for (u32 i = 0; i < 3; i++)
{
ui_Button(&State->Interface, MakeString("B"));
}
}
ui_PopLayout(&State->Interface);
ui_PushOverlayLayout(&State->Interface, rect2{25, 25, 200, 200}, LayoutDirection_TopDown, MakeString("t"));
{
ui_Label(&State->Interface, PushStringF(State->Interface.PerFrameMemory, 256, "Mouse Pos - %f %f", State->Interface.Mouse.Pos.x, State->Interface.Mouse.Pos.y));
}
ui_PopLayout(&State->Interface);
#else
ui_BeginList(&State->Interface, MakeString("Test List"), 10);
{
for (u32 i = 0; i < 32; i++)
{
ui_Button(&State->Interface, MakeString("Option"));
}
}
ui_EndList(&State->Interface);
#endif
}
ui_PopLayout(&State->Interface);
}
FOLDHAUS_INPUT_COMMAND_PROC(ActiveWidget_DeleteBackwards)
{
ui_widget* ActiveWidget = ui_InterfaceGetWidgetWithId(&State->Interface, State->Interface.ActiveWidget);
ui_widget_retained_state* WidgetState = ui_GetRetainedState(&State->Interface, ActiveWidget->Id);
if (WidgetState)
{
WidgetState->EditString.Length -= 1;
}
}
FOLDHAUS_INPUT_COMMAND_PROC(ActiveWidget_EndTypingMode)
{
DeactivateCurrentOperationMode(&State->Modes);
}
OPERATION_RENDER_PROC(ActiveWidget_EndTypingMode)
{
ui_widget* ActiveWidget = ui_InterfaceGetWidgetWithId(&State->Interface, State->Interface.ActiveWidget);
ui_widget* LastActiveWidget = ui_InterfaceGetWidgetWithId(&State->Interface, State->Interface.LastActiveWidget);
if (ActiveWidget == 0 && LastActiveWidget != 0)
{
// if there was an active widget last frame that was typable, we want to deactivate the typing mode
DeactivateCurrentOperationMode(&State->Modes);
}
}
input_command InterfaceTypingCommands [] = {
{ KeyCode_A, KeyCode_Invalid, Command_Began | Command_Held, ActiveWidget_TypeCharacter },
{ KeyCode_B, KeyCode_Invalid, Command_Began | Command_Held, ActiveWidget_TypeCharacter },
{ KeyCode_C, KeyCode_Invalid, Command_Began | Command_Held, ActiveWidget_TypeCharacter },
{ KeyCode_D, KeyCode_Invalid, Command_Began | Command_Held, ActiveWidget_TypeCharacter },
{ KeyCode_E, KeyCode_Invalid, Command_Began | Command_Held, ActiveWidget_TypeCharacter },
{ KeyCode_Enter, KeyCode_Invalid, Command_Began, ActiveWidget_EndTypingMode },
{ KeyCode_Backspace, KeyCode_Invalid, Command_Began | Command_Held, ActiveWidget_DeleteBackwards },
};
internal void
Editor_Render(app_state* State, context* Context, render_command_buffer* RenderBuffer)
{
State->Interface.WindowBounds = Context->WindowBounds;
PushRenderOrthographic(RenderBuffer, State->WindowBounds);
PushRenderClearScreen(RenderBuffer);
#if 0
TestRender(State, Context, RenderBuffer);
InterfaceTest_Render(State, Context, RenderBuffer);
#else
ui_InterfaceReset(&State->Interface);
State->Interface.RenderBuffer = RenderBuffer;
@ -369,6 +342,19 @@ Editor_Render(app_state* State, context* Context, render_command_buffer* RenderB
{
ui_widget Widget = *State->Interface.DrawOrderRoot;
Editor_DrawWidget(State, Context, RenderBuffer, Widget, Context->WindowBounds);
#if 0
// TODO(pjs): got distracted halfway through getting typing input into the interface
if (ui_WidgetIdsEqual(State->Interface.ActiveWidget, State->Interface.LastActiveWidget))
{
ui_widget* ActiveWidget = ui_InterfaceGetWidgetWithId(&State->Interface, State->Interface.ActiveWidget);
if (ActiveWidget != 0 &&
ui_WidgetIsFlagSet(*ActiveWidget, UIWidgetFlag_Typable))
{
operation_mode* TypingMode = ActivateOperationModeWithCommands(&State->Modes, InterfaceTypingCommands, ActiveWidget_EndTypingMode);
}
}
#endif
}
Context->GeneralWorkQueue->CompleteQueueWork(Context->GeneralWorkQueue, Context->ThreadContext);

View File

@ -40,7 +40,7 @@ OperationModeSystemInit(gs_memory_arena* Storage, gs_thread_context ThreadContex
// TODO(Peter): Do we really need an arena? Can this just operate in constant memory footprint?
Result.Arena.Allocator = ThreadContext.Allocator;
Result.ModeMemoryPagesFreeList.CountMax = 16; // TODO(Peter): Static number of modes that can be active simultaneously
Result.ModeMemoryPagesFreeList.CountMax = 32; // TODO(Peter): Static number of modes that can be active simultaneously
Result.ModeMemoryPagesFreeList.Data = PushArray(Storage, gs_data, Result.ModeMemoryPagesFreeList.CountMax);
for (u32 Page = 0; Page < Result.ModeMemoryPagesFreeList.CountMax; Page++)
{

View File

@ -34,7 +34,7 @@ GetXPositionFromFrameInAnimationPanel (u32 Frame, rect2 PanelBounds, frame_range
}
internal handle
AddAnimationBlockAtCurrentTime (u32 AnimationProcHandle, u32 LayerHandle, animation_system* System)
AddAnimationBlockAtCurrentTime (animation_pattern_handle AnimationProcHandle, u32 LayerHandle, animation_system* System)
{
u32 NewBlockStart = System->CurrentFrame;
u32 NewBlockEnd = NewBlockStart + SecondsToFrames(3, *System);
@ -260,7 +260,8 @@ FOLDHAUS_INPUT_COMMAND_PROC(AddAnimationBlockCommand)
frame_range Range = ActiveAnim->PlayableRange;
u32 MouseDownFrame = GetFrameFromPointInAnimationPanel(Mouse.Pos, Panel->Bounds, Range);
handle NewBlockHandle = Animation_AddBlock(ActiveAnim, MouseDownFrame, MouseDownFrame + SecondsToFrames(3, State->AnimationSystem), 4, TimelineState->SelectedAnimationLayer);
animation_pattern_handle PatternHandle = Patterns_IndexToHandle(4);
handle NewBlockHandle = Animation_AddBlock(ActiveAnim, MouseDownFrame, MouseDownFrame + SecondsToFrames(3, State->AnimationSystem), PatternHandle, TimelineState->SelectedAnimationLayer);
TimelineState->SelectedBlockHandle = NewBlockHandle;
}
@ -575,8 +576,10 @@ PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback)
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, GlobalAnimationPatternsCount, GlobalAnimationPatterns);
animation NewAnim = AnimParser_Parse(AnimFileString, State->AnimationSystem.Storage, State->Patterns);
NewAnim.FileInfo = AnimFile.FileInfo;
u32 NewAnimIndex = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim);
State->AnimationSystem.ActiveAnimationIndex = NewAnimIndex;
@ -584,16 +587,17 @@ PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback)
}
internal void
DrawAnimationPatternList(rect2 PanelBounds, ui_interface* Interface, u32 SelectedAnimationLayerHandle, animation_system* AnimationSystem)
DrawAnimationPatternList(rect2 PanelBounds, ui_interface* Interface, u32 SelectedAnimationLayerHandle, animation_system* AnimationSystem, animation_pattern_array Patterns)
{
ui_PushLayout(Interface, PanelBounds, LayoutDirection_TopDown, MakeString("AnimClips Layout"));
for (s32 i = 0; i < GlobalAnimationPatternsCount; i++)
for (u32 i = 0; i < Patterns.Count; i++)
{
animation_pattern Pattern = GlobalAnimationPatterns[i];
animation_pattern Pattern = Patterns.Values[i];
gs_string PatternName = MakeString(Pattern.Name, Pattern.NameLength);
if (ui_Button(Interface, PatternName))
{
AddAnimationBlockAtCurrentTime(i + 1, SelectedAnimationLayerHandle, AnimationSystem);
animation_pattern_handle PatternHandle = Patterns_IndexToHandle(i);
AddAnimationBlockAtCurrentTime(PatternHandle, SelectedAnimationLayerHandle, AnimationSystem);
}
}
ui_PopLayout(Interface);
@ -624,12 +628,6 @@ PlayBar_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Pan
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);
@ -640,7 +638,11 @@ FrameCount_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_
{
ui_interface* Interface = &State->Interface;
gs_string TempString = PushString(State->Transient, 256);
frame_range VisibleFrames = TimelineState->VisibleRange;
// :FrameRange
// frame_range VisibleFrames = TimelineState->VisibleRange;
animation ActiveAnim = *AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
frame_range VisibleFrames = ActiveAnim.PlayableRange;
s32 VisibleFrameCount = VisibleFrames.Max - VisibleFrames.Min;
ui_FillRect(Interface, Bounds, Interface->Style.PanelBGColors[0]);
@ -685,7 +687,7 @@ FrameCount_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_
}
internal void
LayerList_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
LayerList_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context)
{
ui_interface* Interface = &State->Interface;
animation ActiveAnim = *AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
@ -720,9 +722,15 @@ 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);
// TODO(pjs): setting the timeline to show the entire range
// of the current animation until I reimplement the range
// slider bars
// :FrameRange
// frame_range ViewRange = TimelineState->VisibleRange;
frame_range ViewRange = ActiveAnim.PlayableRange;
handle SelectedBlockHandle = TimelineState->SelectedBlockHandle;
s32 CurrentFrame = State->AnimationSystem.CurrentFrame;
@ -785,7 +793,7 @@ TimeRange_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_c
}
internal void
AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Panel, render_command_buffer* RenderBuffer, app_state* State, context Context)
{
animation_system* AnimSystem = &State->AnimationSystem;
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(AnimSystem);
@ -795,10 +803,7 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, rende
ui_FillRect(&State->Interface, Bounds, Interface->Style.PanelBGColors[0]);
ui_BeginRow(&State->Interface, 2);
{
ui_Label(Interface, MakeString("Active Animation"));
if (ui_BeginDropdown(Interface, ActiveAnim->Name))
if (ui_BeginLabeledDropdown(Interface, MakeString("Active Animation"), ActiveAnim->Name))
{
for (u32 i = 0; i < AnimSystem->Animations.Count; i++)
{
@ -809,9 +814,73 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, rende
}
}
}
ui_EndDropdown(Interface);
ui_EndLabeledDropdown(&State->Interface);
ui_BeginRow(Interface, 3);
{
if (ui_Button(Interface, MakeString("New")))
{
animation NewAnim = {};
NewAnim.Name = PushString(State->AnimationSystem.Storage, 256);
u32 NewAnimIndex = AnimationArray_Push(&State->AnimationSystem.Animations, NewAnim);
State->AnimationSystem.ActiveAnimationIndex = NewAnimIndex;
}
ui_EndRow(&State->Interface);
if (ui_Button(Interface, MakeString("Save")))
{
// Save Animation File
// TODO(pjs): If you created the animation via the "new" button, there won't be a file attached.
// need to use the file browser to create a file
u32 ActiveAnimIndex = State->AnimationSystem.ActiveAnimationIndex;
animation ActiveAnimation = State->AnimationSystem.Animations.Values[ActiveAnimIndex];
gs_string FileText = AnimSerializer_Serialize(ActiveAnimation, State->Patterns, State->Transient);
if (WriteEntireFile(Context.ThreadContext.FileHandler, ActiveAnimation.FileInfo.Path, StringToData(FileText)))
{
InvalidCodePath;
}
}
if (ui_Button(Interface, MakeString("Load")))
{
panel* FileBrowser = PanelSystem_PushPanel(&State->PanelSystem, PanelType_FileView, State, Context);
Panel_PushModalOverride(Panel, FileBrowser, LoadAnimationFileCallback);
}
}
ui_EndRow(Interface);
ui_TextEntry(Interface, MakeString("Anim Name"), &ActiveAnim->Name);
ui_Label(Interface, MakeString("Frame Range"));
ui_BeginRow(Interface, 3);
{
ActiveAnim->PlayableRange.Min = ui_TextEntryU64(Interface, MakeString("StartFrame"), ActiveAnim->PlayableRange.Min);
ActiveAnim->PlayableRange.Max = ui_TextEntryU64(Interface, MakeString("EndFrame"), ActiveAnim->PlayableRange.Max);
}
ui_EndRow(Interface);
animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, TimelineState->SelectedBlockHandle);
if (SelectedBlock)
{
animation_pattern BlockPattern = Patterns_GetPattern(State->Patterns, SelectedBlock->AnimationProcHandle);
ui_BeginRow(Interface, 3);
ui_Label(Interface, MakeString("Selected Pattern"));
//if (ui_BeginLabeledDropdown(Interface, MakeString("Selected Pattern"), MakeString(BlockPattern.Name, BlockPattern.NameLength)))
if (ui_BeginDropdown(Interface, MakeString(BlockPattern.Name, BlockPattern.NameLength)))
{
for (u32 i = 0; i < State->Patterns.Count; i++)
{
animation_pattern Pattern = State->Patterns.Values[i];
if (ui_Button(Interface, MakeString(Pattern.Name, Pattern.NameLength)))
{
SelectedBlock->AnimationProcHandle = Patterns_IndexToHandle(i);
}
}
}
ui_EndLabeledDropdown(Interface);
}
ui_PopLayout(Interface);
}
@ -831,8 +900,10 @@ AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer*
rect2 TimelineBounds, InfoBounds;
RectVSplit(PanelBounds, 300, &InfoBounds, &TimelineBounds);
#if 0
rect2 AnimInfoBounds, SelectionInfoBounds;
RectHSplitAtPercent(InfoBounds, .65f, &AnimInfoBounds, &SelectionInfoBounds);
#endif
{ // Timeline
rect2 LayersPanelBounds, TimeRangePanelBounds;
@ -847,12 +918,11 @@ AnimationTimeline_Render(panel* Panel, rect2 PanelBounds, render_command_buffer*
PlayBar_Render(TimelineState, PlayBarBounds, Panel, RenderBuffer, State, Context);
FrameCount_Render(TimelineState, FrameCountBounds, RenderBuffer, State, Context);
LayerList_Render(TimelineState, LayersBounds, RenderBuffer, State, Context);
LayerList_Render(TimelineState, LayersBounds, Panel, RenderBuffer, State, Context);
TimeRange_Render(TimelineState, TimeRangeBounds, RenderBuffer, State, Context);
}
AnimInfoView_Render(TimelineState, AnimInfoBounds, RenderBuffer, State, Context);
SelectionInfoView_Render(TimelineState, SelectionInfoBounds, RenderBuffer, State, Context);
AnimInfoView_Render(TimelineState, InfoBounds, Panel, RenderBuffer, State, Context);
}
#define FOLDHAUS_PANEL_ANIMATION_TIMELINE_H

View File

@ -69,7 +69,12 @@ RenderProfiler_ScopeVisualization(ui_interface* Interface, ui_widget* Layout, de
ui_BeginMousePopup(Interface, rect2{ 25, 25, 300, 57 }, LayoutDirection_TopDown, MakeString("Hover"));
{
PrintF(&String, "%S : %d - %d", Name->Name, Record->StartCycles, Record->EndCycles);
s64 Cycles = (Record->EndCycles - Record->StartCycles);
r64 PercentFrame = (r64)(Cycles) / (r64)(FrameTotalCycles);
PrintF(&String, "%S : %.2f%% frame | %dcy",
Name->Name,
PercentFrame,
Cycles);
ui_Label(Interface, String);
}
ui_EndMousePopup(Interface);

View File

@ -226,14 +226,10 @@ SculptureView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren
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);
PrintF(&Tempgs_string, "Hot Id: %u, ZIndex: %u | Active Id: %u", State->Interface.HotWidget.Id,
State->Interface.HotWidget.ZIndex,State->Interface.ActiveWidget.Id);
DrawString(RenderBuffer, Tempgs_string, State->Interface.Style.Font, v2{PanelBounds.Min.x + 100, PanelBounds.Max.y - 200}, WhiteV4);
v2 BoxHalfDim = v2{ 25, 25 };
v2 BoxMin = LedOnScreenPosition - BoxHalfDim;
v2 BoxMax = LedOnScreenPosition + BoxHalfDim;
PushRenderBoundingBox2D(RenderBuffer, BoxMin, BoxMax, 2.0f, TealV4);
}
Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext);
}

View File

@ -14,6 +14,11 @@ struct frame_range
s32 Max;
};
struct animation_pattern_handle
{
s32 IndexPlusOne;
};
// 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
@ -21,7 +26,7 @@ struct frame_range
struct animation_block
{
frame_range Range;
u32 AnimationProcHandle;
animation_pattern_handle AnimationProcHandle;
u32 Layer;
};
@ -76,6 +81,9 @@ struct animation
animation_block_array Blocks_;
frame_range PlayableRange;
// The information / path to the file where this animation is to be saved / where it is loaded from
gs_file_info FileInfo;
};
struct animation_array
@ -132,6 +140,13 @@ struct animation_pattern
animation_proc* Proc;
};
struct animation_pattern_array
{
animation_pattern* Values;
u32 Count;
u32 CountMax;
};
// Serialization
enum animation_field
@ -185,6 +200,55 @@ global gs_const_string AnimationFieldStrings[] = {
ConstString("animation_name"),// AnimField_BlockAnimName
};
//////////////////////////
//
// Patterns List
internal animation_pattern_array
Patterns_Create(gs_memory_arena* Arena, s32 CountMax)
{
animation_pattern_array Result = {0};
Result.CountMax = CountMax;
Result.Values = PushArray(Arena, animation_pattern, Result.CountMax);
return Result;
}
#define Patterns_PushPattern(array, proc) Patterns_PushPattern_((array), (proc), Stringify(proc), sizeof(Stringify(proc)))
internal void
Patterns_PushPattern_(animation_pattern_array* Array, animation_proc* Proc, char* Name, u32 NameLength)
{
Assert(Array->Count < Array->CountMax);
animation_pattern Pattern = {0};
Pattern.Name = Name;
Pattern.NameLength = NameLength;
Pattern.Proc = Proc;
Array->Values[Array->Count++] = Pattern;
}
internal animation_pattern_handle
Patterns_IndexToHandle(s32 Index)
{
animation_pattern_handle Result = {};
Result.IndexPlusOne = Index + 1;
return Result;
}
internal animation_pattern
Patterns_GetPattern(animation_pattern_array Patterns, animation_pattern_handle Handle)
{
animation_pattern Result = {0};
if (Handle.IndexPlusOne > 0)
{
u32 Index = Handle.IndexPlusOne - 1;
Assert(Index < Patterns.Count);
Result = Patterns.Values[Index];
}
return Result;
}
//////////////////////////
//
// Anim Block Array
@ -291,7 +355,7 @@ AnimationArray_Push(animation_array* Array, animation Value)
// Animation
internal handle
Animation_AddBlock(animation* Animation, u32 StartFrame, s32 EndFrame, u32 AnimationProcHandle, u32 LayerIndex)
Animation_AddBlock(animation* Animation, u32 StartFrame, s32 EndFrame, animation_pattern_handle AnimationProcHandle, u32 LayerIndex)
{
Assert(LayerIndex < Animation->Layers.Count);

View File

@ -81,21 +81,19 @@ LedBlend_GetProc(blend_mode BlendMode)
}
internal void
AnimationSystem_RenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, assembly Assembly, animation_pattern* Patterns, gs_memory_arena* Transient)
AnimationSystem_RenderBlockToLedBuffer(animation_system* System, animation_block Block, led_buffer* Buffer, assembly Assembly, animation_pattern_array 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);
animation_pattern Pattern = Patterns_GetPattern(Patterns, Block.AnimationProcHandle);
Pattern.Proc(Buffer, Assembly, SecondsIntoBlock, Transient);
}
internal void
AnimationSystem_RenderToLedBuffers(animation_system* System, assembly_array Assemblies,
led_system* LedSystem,
animation_pattern* Patterns,
animation_pattern_array Patterns,
gs_memory_arena* Transient)
{
s32 CurrentFrame = System->CurrentFrame;

View File

@ -6,7 +6,7 @@
#ifndef FOLDHAUS_ANIMATION_SERIALIZER_CPP
internal gs_string
AnimSerializer_Serialize(animation Anim, animation_pattern* GlobalClips, gs_memory_arena* Arena)
AnimSerializer_Serialize(animation Anim, animation_pattern_array Patterns, gs_memory_arena* Arena)
{
serializer Serializer = {0};
Serializer.String = PushString(Arena, 4096);
@ -45,10 +45,7 @@ AnimSerializer_Serialize(animation Anim, animation_pattern* GlobalClips, gs_memo
// TODO(pjs): Handle free'd animation blocks
animation_block AnimationBlockAt = Anim.Blocks_.Values[i];
// TODO(pjs): Systematize the AnimationProcHandle
// :AnimProcHandle
u32 AnimationProcIndex = AnimationBlockAt.AnimationProcHandle - 1;
animation_pattern Animation = GlobalClips[AnimationProcIndex];
animation_pattern Animation = Patterns_GetPattern(Patterns, AnimationBlockAt.AnimationProcHandle);
Serializer_OpenStruct(&Serializer, AnimField_Block);
{
@ -70,7 +67,7 @@ AnimSerializer_Serialize(animation Anim, animation_pattern* GlobalClips, gs_memo
}
internal animation
AnimParser_Parse(gs_string File, gs_memory_arena* Arena, u32 AnimClipsCount, animation_pattern* AnimClips)
AnimParser_Parse(gs_string File, gs_memory_arena* Arena, animation_pattern_array AnimPatterns)
{
animation Result = {0};
@ -162,12 +159,13 @@ AnimParser_Parse(gs_string File, gs_memory_arena* Arena, u32 AnimClipsCount, ani
// TODO(pjs): AnimName -> Animation Proc Handle
gs_string AnimName = Parser_ReadStringValue(&Parser, AnimField_BlockAnimName);
Block.AnimationProcHandle = 0;
for (u32 i = 0; i < AnimClipsCount; i++)
Block.AnimationProcHandle = {0};
for (u32 i = 0; i < AnimPatterns.Count; i++)
{
if (StringEqualsCharArray(AnimName.ConstString, AnimClips[i].Name, CStringLength(AnimClips[i].Name)))
animation_pattern Pattern = AnimPatterns.Values[i];
if (StringEqualsCharArray(AnimName.ConstString, Pattern.Name, Pattern.NameLength))
{
Block.AnimationProcHandle = i + 1;
Block.AnimationProcHandle = Patterns_IndexToHandle(i);
break;
}
}

View File

@ -136,7 +136,7 @@ LedBufferSetLed(led_buffer* Buffer, u32 Led, v4 Position)
}
internal u32
Assembly_ConstructStrip(assembly* Assembly, led_buffer* LedBuffer, v2_strip* StripAt, strip_gen_data GenData, v4 RootPosition, u32 LedStartIndex)
Assembly_ConstructStrip(assembly* Assembly, led_buffer* LedBuffer, v2_strip* StripAt, strip_gen_data GenData, v4 RootPosition, u32 LedStartIndex, u32 LedLUTStartIndex)
{
u32 LedsAdded = 0;
@ -154,7 +154,7 @@ Assembly_ConstructStrip(assembly* Assembly, led_buffer* LedBuffer, v2_strip* Str
s32 LedIndex = LedStartIndex + LedsAdded++;
v4 LedPosition = WS_StripStart + (SingleStep * Step);
LedBufferSetLed(LedBuffer, LedIndex, LedPosition);
StripAt->LedLUT[Step] = LedIndex;
StripAt->LedLUT[Step + LedLUTStartIndex] = LedIndex;
}
}break;
@ -164,7 +164,7 @@ Assembly_ConstructStrip(assembly* Assembly, led_buffer* LedBuffer, v2_strip* Str
for (u32 i = 0; i < Sequence.ElementsCount; i++)
{
strip_gen_data SegmentGenData = Sequence.Elements[i];
LedsAdded += Assembly_ConstructStrip(Assembly, LedBuffer, StripAt, SegmentGenData, RootPosition, LedStartIndex + LedsAdded);
LedsAdded += Assembly_ConstructStrip(Assembly, LedBuffer, StripAt, SegmentGenData, RootPosition, LedStartIndex + LedsAdded, LedsAdded);
}
}break;
@ -190,7 +190,7 @@ ConstructAssemblyFromDefinition (assembly* Assembly, led_system* LedSystem)
StripAt->LedLUT = PushArray(&Assembly->Arena, u32, StripAt->LedCount);
strip_gen_data GenData = StripAt->GenerationData;
LedsAdded += Assembly_ConstructStrip(Assembly, LedBuffer, StripAt, GenData, RootPosition, LedsAdded);
LedsAdded += Assembly_ConstructStrip(Assembly, LedBuffer, StripAt, GenData, RootPosition, LedsAdded, 0);
}
}

View File

@ -10,6 +10,23 @@
////////////////////////////////////////////////////////////////////////
internal void
ClearAndPushPatterns(animation_pattern_array* Patterns)
{
if (Patterns->CountMax == 0) { return; }
Patterns->Count = 0;
Patterns_PushPattern(Patterns, TestPatternOne);
Patterns_PushPattern(Patterns, TestPatternTwo);
Patterns_PushPattern(Patterns, TestPatternThree);
Patterns_PushPattern(Patterns, Pattern_AllGreen);
Patterns_PushPattern(Patterns, Pattern_HueShift);
Patterns_PushPattern(Patterns, Pattern_HueFade);
Patterns_PushPattern(Patterns, Pattern_Spots);
Patterns_PushPattern(Patterns, Pattern_LighthouseRainbow);
Patterns_PushPattern(Patterns, Pattern_SmoothGrowRainbow);
}
RELOAD_STATIC_DATA(ReloadStaticData)
{
app_state* State = (app_state*)Context.MemoryBase;
@ -17,6 +34,8 @@ RELOAD_STATIC_DATA(ReloadStaticData)
GlobalDebugServices = DebugServices;
State->PanelSystem.PanelDefs = GlobalPanelDefs;
State->PanelSystem.PanelDefsCount = GlobalPanelDefsCount;
ClearAndPushPatterns(&State->Patterns);
}
INITIALIZE_APPLICATION(InitializeApplication)
@ -34,6 +53,9 @@ INITIALIZE_APPLICATION(InitializeApplication)
State->CommandQueue = CommandQueue_Create(&State->Permanent, 32);
State->Patterns = Patterns_Create(&State->Permanent, 10);
ClearAndPushPatterns(&State->Patterns);
// TODO(Peter): put in InitializeInterface?
r32 FontSize = 14;
{
@ -112,13 +134,14 @@ INITIALIZE_APPLICATION(InitializeApplication)
State->Interface.Widgets = PushArray(&State->Permanent, ui_widget, State->Interface.WidgetsCountMax);
State->Interface.PerFrameMemory = PushStruct(&State->Permanent, gs_memory_arena);
*State->Interface.PerFrameMemory = CreateMemoryArena(Context.ThreadContext.Allocator);
State->Interface.Permanent = &State->Permanent;
State->SACN = SACN_Initialize(Context);
State->LedSystem = LedSystemInitialize(Context.ThreadContext.Allocator, 128);
#if 1
gs_const_string SculpturePath = ConstString("data/blumen_lumen_silver_spring.fold");
gs_const_string SculpturePath = ConstString("data/test_blumen.fold");
LoadAssembly(&State->Assemblies, &State->LedSystem, State->Transient, Context, SculpturePath, State->GlobalLog);
#endif
@ -147,7 +170,7 @@ INITIALIZE_APPLICATION(InitializeApplication)
Animation_AddLayer(&Anim, MakeString("Color Layer"), BlendMode_Multiply, &State->AnimationSystem);
Animation_AddLayer(&Anim, MakeString("Sparkles"), BlendMode_Add, &State->AnimationSystem);
Animation_AddBlock(&Anim, 0, Anim.PlayableRange.Max, 4, 0);
Animation_AddBlock(&Anim, 0, Anim.PlayableRange.Max, Patterns_IndexToHandle(5), 0);
AnimationArray_Push(&State->AnimationSystem.Animations, Anim);
@ -178,7 +201,7 @@ UPDATE_AND_RENDER(UpdateAndRender)
AnimationSystem_RenderToLedBuffers(&State->AnimationSystem,
State->Assemblies,
&State->LedSystem,
GlobalAnimationPatterns,
State->Patterns,
State->Transient);
}
@ -192,19 +215,6 @@ UPDATE_AND_RENDER(UpdateAndRender)
}
Editor_Render(State, Context, RenderBuffer);
// Checking for overflows
#if 0
{
DEBUG_TRACK_SCOPE(OverflowChecks);
AssertAllocationsNoOverflow(State->Permanent);
for (u32 i = 0; i < State->Assemblies.Count; i++)
{
assembly* Assembly = &State->Assemblies.Values[i];
AssertAllocationsNoOverflow(Assembly->Arena);
}
}
#endif
}
CLEANUP_APPLICATION(CleanupApplication)

View File

@ -53,6 +53,7 @@ struct app_state
assembly_array Assemblies;
animation_system AnimationSystem;
event_log* GlobalLog;
animation_pattern_array Patterns;
// Interface
//
@ -205,8 +206,15 @@ TestPatternThree(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena*
v4 HSVToRGB (v4 In)
{
float Hue = In.x;
while (Hue > 360.0f) { Hue -= 360.0f; }
/*
while (Hue > 360.0f) { Hue -= 360.0f; }
while (Hue < 0.0f) { Hue += 360.0f; }
*/
Hue = ModR32(Hue, 360.0f);
if (Hue < 0) { Hue += 360.0f; }
if (Hue == MinR32) { Hue = 0; }
if (Hue == MaxR32) { Hue = 360; }
Assert(Hue >= 0 && Hue < 360);
float Sat = In.y;
float Value = In.z;
@ -279,9 +287,8 @@ v4 HSVToRGB (v4 In)
}
internal void
Pattern_AllGreen(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient)
Pattern_HueShift(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient)
{
#if 1
r32 Height = SinR32(Time) * 25;
r32 CycleLength = 5.0f;
@ -304,7 +311,41 @@ Pattern_AllGreen(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena*
Leds->Colors[LedIndex].G = G;
Leds->Colors[LedIndex].B = B;
}
#else
}
internal pixel
V4ToRGBPixel(v4 C)
{
pixel Result = {};
Result.R = (u8)(C.x * 255);
Result.G = (u8)(C.y * 255);
Result.B = (u8)(C.z * 255);
return Result;
}
internal void
Pattern_HueFade(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient)
{
r32 HueBase = ModR32(Time * 50, 360);
r32 CycleLength = 5.0f;
r32 CycleProgress = FractR32(Time / CycleLength);
r32 CycleBlend = (SinR32(Time) * .5f) + .5f;
for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++)
{
v4 Pos = Leds->Positions[LedIndex];
r32 Hue = HueBase + Pos.y + Pos.x;
v4 HSV = { Hue, 1, 1, 1 };
v4 RGB = HSVToRGB(HSV);
Leds->Colors[LedIndex] = V4ToRGBPixel(RGB);
}
}
internal void
Pattern_AllGreen(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient)
{
for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++)
{
u32 I = LedIndex + 1;
@ -322,6 +363,110 @@ Pattern_AllGreen(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena*
Leds->Colors[LedIndex].B = 255;
}
}
}
internal r32
PatternHash(r32 Seed)
{
return FractR32(Seed * 17.0 * FractR32(Seed * 0.3183099));
}
internal void
Pattern_Spots(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient)
{
pixel ColorA = { 0, 255, 255 };
pixel ColorB = { 255, 0, 255 };
r32 Speed = .5f;
Time *= Speed;
r32 ScaleA = 2 * SinR32(Time / 5);
r32 ScaleB = 2.4f * CosR32(Time / 2.5f);
for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++)
{
v4 P = Leds->Positions[LedIndex];
r32 V = P.y;
r32 Noise = .3f * PatternHash(V);
r32 ThetaY = (Leds->Positions[LedIndex].y / 10) + Time + Noise;
r32 ThetaX = (Leds->Positions[LedIndex].x / 13) + Time + Noise;
r32 Fade = (ScaleA * SinR32(ThetaY)) + (ScaleB * CosR32(3 * ThetaX));
Fade = RemapClampedR32(Fade, -1, 1, 0, 1);
Leds->Colors[LedIndex].R = (u8)LerpR32(Fade, ColorA.R, ColorB.R);
Leds->Colors[LedIndex].G = (u8)LerpR32(Fade, ColorA.G, ColorB.G);
Leds->Colors[LedIndex].B = (u8)LerpR32(Fade, ColorA.B, ColorB.B);
}
}
internal void
Pattern_LighthouseRainbow(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient)
{
v2 RefVector = V2Normalize(v2{ SinR32(Time), CosR32(Time) });
for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++)
{
v2 Vector = v2{
Leds->Positions[LedIndex].x,
Leds->Positions[LedIndex].z
};
Vector = V2Normalize(Vector);
r32 Angle = V2Dot(RefVector, Vector);
#if 0
r32 Fade = RemapR32(Angle, -1, 1, 0, 1);
Leds->Colors[LedIndex].R = (u8)(Fade * 255);
Leds->Colors[LedIndex].G = (u8)(Fade * 255);
Leds->Colors[LedIndex].B = (u8)(Fade * 255);
#endif
v4 HSV = { (Angle * 30) + (Time * 10) + Leds->Positions[LedIndex].y, 1, 1, 1 };
v4 RGB = HSVToRGB(HSV);
Leds->Colors[LedIndex] = V4ToRGBPixel(RGB);
}
}
internal r32
Smoothstep(r32 T)
{
r32 Result = (T * T * (3 - (2 * T)));
return Result;
}
internal void
Pattern_SmoothGrowRainbow(led_buffer* Leds, assembly Assembly, r32 Time, gs_memory_arena* Transient)
{
r32 FillCycleTime = ModR32(Time, 7.0f) / 7.0f;
r32 ColorCycleTime = ModR32(Time, 21.0f) / 21.0f;
//v4 HSV = { ColorCycleTime * 360, 1, 1, 1 };
//v4 RGB0 = HSVToRGB(HSV);
//HSV.x += ;
//v4 RGB1 = HSVToRGB(HSV);
v4 HSV = { 0, 1, 1, 1 };
for (u32 s = 0; s < Assembly.StripCount; s++)
{
v2_strip Strip = Assembly.Strips[s];
v4 RGB0 = HSVToRGB(HSV);
for (u32 l = 0; l < Strip.LedCount; l++)
{
u32 LedIndex = Strip.LedLUT[l];
Leds->Colors[LedIndex] = V4ToRGBPixel(RGB0);
}
HSV.x += 15;
}
#if 0
for (u32 LedIndex = 0; LedIndex < Leds->LedCount; LedIndex++)
{
v4 P = Leds->Positions[LedIndex];
Leds->Colors[LedIndex] = V4ToRGBPixel(RGB0);
}
#endif
}
@ -334,14 +479,6 @@ EndCurrentOperationMode(app_state* State)
DeactivateCurrentOperationMode(&State->Modes);
}
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"
#include "editor/panels/foldhaus_panel_file_view.h"

View File

@ -231,20 +231,24 @@ DrawStringWithCursor (render_command_buffer* RenderBuffer, gs_string String, s32
enum ui_widget_flag
{
UIWidgetFlag_ExpandsToFitChildren,
UIWidgetFlag_DrawBackground,
UIWidgetFlag_DrawString,
UIWidgetFlag_DrawOutline,
UIWidgetFlag_Clickable,
UIWidgetFlag_DrawHorizontalFill,
UIWidgetFlag_DrawVerticalFill,
UIWidgetFlag_DrawFillReversed,
UIWidgetFlag_DrawFillAsHandle,
UIWidgetFlag_Clickable,
UIWidgetFlag_Selectable,
UIWidgetFlag_Typable,
};
struct ui_widget_id
{
u64 Id;
u64 ParentId;
u64 ZIndex;
};
enum ui_layout_direction
@ -293,6 +297,8 @@ struct ui_widget
// we can check the retained state of that dropdown
ui_widget_id WidgetReference;
u64 ChildZIndexOffset;
ui_widget* ChildrenRoot;
ui_widget* ChildrenHead;
u32 ChildCount;
@ -334,6 +340,8 @@ struct ui_widget_retained_state
// For use in layouts that allow you to scroll / pan
v2 ChildrenDrawOffset;
gs_string EditString;
};
struct ui_interface
@ -343,6 +351,9 @@ struct ui_interface
mouse_state Mouse;
rect2 WindowBounds;
// A per-frame string of the characters which have been typed
gs_const_string TempInputString;
render_command_buffer* RenderBuffer;
ui_widget* Widgets;
@ -353,7 +364,11 @@ struct ui_interface
ui_widget* DrawOrderRoot;
ui_widget_id HotWidget;
// This should really never get higher than 1 or 2
u8 HotWidgetFramesSinceUpdate;
ui_widget_id ActiveWidget;
ui_widget_id LastActiveWidget;
ui_widget* ActiveLayout;
@ -362,6 +377,12 @@ struct ui_interface
u64 RetainedStateCount;
gs_memory_arena* PerFrameMemory;
// TODO(pjs): DONT USE THIS
// Right now you only need this to create EditStrings for ui_widget_retained_state's
// and even for those, you eventually want a better solution than "create a string and it lives forever"
// TODO(pjs): Get rid of the need for this vvv
gs_memory_arena* Permanent;
};
internal void
@ -380,12 +401,14 @@ ui_InterfaceReset(ui_interface* Interface)
Interface->RetainedState[i] = {0};
}
}
Interface->LastActiveWidget = Interface->ActiveWidget;
}
internal bool
ui_WidgetIdsEqual(ui_widget_id A, ui_widget_id B)
{
bool Result = (A.Id == B.Id) && (A.ParentId == B.ParentId);
bool Result = (A.Id == B.Id);// && (A.ParentId == B.ParentId);
return Result;
}
@ -411,6 +434,53 @@ ui_WidgetIsFlagSet(ui_widget Widget, u64 Flag)
return Result;
}
internal void
ui_WidgetSetChildrenPopover(ui_widget* Widget)
{
Widget->ChildZIndexOffset = 1000;
}
internal ui_widget*
ui_WidgetGetWidgetWithId(ui_widget* Parent, ui_widget_id Id)
{
ui_widget* Result = 0;
if (ui_WidgetIdsEqual(Parent->Id, Id))
{
Result = Parent;
}
else if (Parent->ChildrenRoot != 0)
{
for (ui_widget* At = Parent->ChildrenRoot; At != 0; At = At->Next)
{
Result = ui_WidgetGetWidgetWithId(At, Id);
if (Result != 0)
{
break;
}
}
}
return Result;
}
internal ui_widget*
ui_InterfaceGetWidgetWithId(ui_interface* Interface, ui_widget_id Id)
{
ui_widget* Result = 0;
for (ui_widget* At = Interface->DrawOrderRoot; At != 0; At = At->Next)
{
Result = ui_WidgetGetWidgetWithId(At, Id);
if (Result != 0)
{
break;
}
}
return Result;
}
internal ui_widget_retained_state*
ui_GetRetainedState(ui_interface* Interface, ui_widget_id Id)
{
@ -433,6 +503,7 @@ ui_CreateRetainedState(ui_interface* Interface, ui_widget* Widget)
u64 Index = Interface->RetainedStateCount++;
ui_widget_retained_state* Result = Interface->RetainedState + Index;
Result->Id = Widget->Id;
Result->EditString = PushString(Interface->Permanent, 256);
return Result;
}
@ -451,7 +522,8 @@ internal ui_widget*
ui_CreateWidget(ui_interface* Interface, gs_string String)
{
Assert(Interface->WidgetsCount < Interface->WidgetsCountMax);
ui_widget* Result = Interface->Widgets + Interface->WidgetsCount++;
u64 Index = Interface->WidgetsCount++;
ui_widget* Result = Interface->Widgets + Index;
ZeroStruct(Result);
Result->Parent = Interface->ActiveLayout;
@ -461,10 +533,18 @@ ui_CreateWidget(ui_interface* Interface, gs_string String)
{
Id = HashAppendDJB2ToU32(Id, Result->Parent->Id.Id);
Id = HashAppendDJB2ToU32(Id, Result->Parent->ChildCount);
Result->Id.ParentId = Result->Parent->Id.Id;
//Result->Id.ParentId = Result->Parent->Id.Id;
}
Result->Id.Id = Id;
u64 ZIndex = Index + 1;
if (Result->Parent)
{
Result->ChildZIndexOffset += Result->Parent->ChildZIndexOffset;
ZIndex += Result->Parent->ChildZIndexOffset;
}
Result->Id.ZIndex = ZIndex;
Result->String = PushStringCopy(Interface->PerFrameMemory, String.ConstString);
Result->Alignment = Align_Left;
Result->Next = 0;
@ -859,20 +939,81 @@ ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds)
Interface->ActiveLayout->ChildCount += 1;
ui_CommitBounds(Widget->Parent, Widget->Bounds);
if (PointIsInRect(Widget->Parent->Bounds, Interface->Mouse.Pos) &&
PointIsInRect(Widget->Bounds, Interface->Mouse.Pos))
{
if (MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState))
{
if (ui_WidgetIdsEqual(Interface->HotWidget, Widget->Id))
{
Result.Clicked = true;
Interface->ActiveWidget = Widget->Id;
}
}
if (Interface->HotWidget.ZIndex == 0 ||
Interface->HotWidget.ZIndex <= Widget->Id.ZIndex)
{
Interface->HotWidget = Widget->Id;
Interface->HotWidgetFramesSinceUpdate = 0;
}
}
else
{
if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id) &&
MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState))
{
Interface->ActiveWidget = {};
}
}
if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id))
{
// click & drag
if (MouseButtonHeldDown(Interface->Mouse.LeftButtonState))
{
Result.Held = true;
Result.DragDelta = Interface->Mouse.Pos - Interface->Mouse.DownPos;
}
if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Typable) &&
Interface->TempInputString.Length > 0)
{
ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
// TODO(pjs): Backspace?
for (u32 i = 0; i < Interface->TempInputString.Length; i++)
{
if (Interface->TempInputString.Str[i] == '\b')
{
State->EditString.Length -= 1;
}
else
{
OutChar(&State->EditString, Interface->TempInputString.Str[i]);
}
}
}
}
#if 0
// if you can click it
if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Clickable))
{
// updating hot widget, and handling mouse clicks
if (PointIsInRect(Widget->Parent->Bounds, Interface->Mouse.Pos) &&
PointIsInRect(Widget->Bounds, Interface->Mouse.Pos))
{
if (ui_WidgetIdsEqual(Interface->HotWidget, Widget->Id) && MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState))
{
Assert(!ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id));
Result.Clicked = true;
Interface->ActiveWidget = Widget->Id;
}
Interface->HotWidget = Widget->Id;
}
// click and drag
if (MouseButtonHeldDown(Interface->Mouse.LeftButtonState) &&
PointIsInRect(Widget->Bounds, Interface->Mouse.DownPos))
{
@ -880,13 +1021,45 @@ ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds)
Result.DragDelta = Interface->Mouse.Pos - Interface->Mouse.DownPos;
}
if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id) &&
MouseButtonTransitionedUp(Interface->Mouse.LeftButtonState))
// if this is the active widget (its been clicked)
if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id))
{
// if you can select it
if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Selectable))
{
//
if (MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState) &&
!PointIsInRect(Widget->Bounds, Interface->Mouse.Pos))
{
Interface->ActiveWidget = {};
}
if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Typable) &&
Interface->TempInputString.Length > 0)
{
ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
// TODO(pjs): Backspace?
for (u32 i = 0; i < Interface->TempInputString.Length; i++)
{
if (Interface->TempInputString.Str[i] == '\b')
{
State->EditString.Length -= 1;
}
else
{
OutChar(&State->EditString, Interface->TempInputString.Str[i]);
}
}
}
}
else if (MouseButtonTransitionedUp(Interface->Mouse.LeftButtonState))
{
Interface->ActiveWidget = {};
}
}
}
#endif
Assert(Widget->Parent != 0);
return Result;
@ -941,6 +1114,77 @@ ui_Label(ui_interface* Interface, gs_string String, gs_string_alignment Alignmen
ui_EvaluateWidget(Interface, Widget);
}
internal void
ui_TextEntrySetFlags(ui_widget* Widget, gs_string EditString)
{
Widget->String = EditString;
ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
ui_WidgetSetFlag(Widget, UIWidgetFlag_Selectable);
ui_WidgetSetFlag(Widget, UIWidgetFlag_Typable);
}
internal void
ui_TextEntry(ui_interface* Interface, gs_string Identifier, gs_string* Value)
{
ui_widget* Widget = ui_CreateWidget(Interface, Identifier);
ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
if (!State)
{
State = ui_CreateRetainedState(Interface, Widget);
}
PrintF(&State->EditString, "%S", *Value);
ui_TextEntrySetFlags(Widget, State->EditString);
ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
PrintF(Value, "%S", State->EditString);
}
internal u64
ui_TextEntryU64(ui_interface* Interface, gs_string String, u64 CurrentValue)
{
ui_widget* Widget = ui_CreateWidget(Interface, String);
ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
if (!State)
{
State = ui_CreateRetainedState(Interface, Widget);
PrintF(&State->EditString, "%u", CurrentValue);
}
ui_TextEntrySetFlags(Widget, State->EditString);
ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
parse_uint_result ParseResult = ValidateAndParseUInt(State->EditString.ConstString);
u64 ValueResult = CurrentValue;
if (ParseResult.Success)
{
ValueResult = ParseResult.Value;
}
return ValueResult;
}
internal r64
ui_TextEntryR64(ui_interface* Interface, gs_string String, r64 CurrentValue)
{
ui_widget* Widget = ui_CreateWidget(Interface, String);
ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
if (!State)
{
State = ui_CreateRetainedState(Interface, Widget);
PrintF(&State->EditString, "%f", CurrentValue);
}
ui_TextEntrySetFlags(Widget, State->EditString);
ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
parse_float_result ParseResult = ValidateAndParseFloat(State->EditString.ConstString);
r64 ValueResult = CurrentValue;
if (ParseResult.Success)
{
ValueResult = ParseResult.Value;
}
return ValueResult;
}
internal ui_widget*
ui_CreateButtonWidget(ui_interface* Interface, gs_string Text)
{
@ -1057,6 +1301,7 @@ ui_EvaluateDropdown(ui_interface* Interface, ui_widget* Widget, ui_eval_result E
Layout->Margin.y = 0;
Layout->WidgetReference = Widget->Id;
ui_WidgetClearFlag(Layout, UIWidgetFlag_DrawOutline);
ui_WidgetSetChildrenPopover(Layout);
}
return State->Value;
@ -1070,6 +1315,7 @@ ui_BeginDropdown(ui_interface* Interface, gs_string Text, rect2 Bounds)
ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline);
ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds);
return ui_EvaluateDropdown(Interface, Widget, Result);
}
@ -1266,5 +1512,21 @@ ui_EndMousePopup(ui_interface* Interface)
ui_PopLayout(Interface);
}
//
internal bool
ui_BeginLabeledDropdown(ui_interface* Interface, gs_string Label, gs_string DropdownValue)
{
ui_BeginRow(Interface, 2);
ui_Label(Interface, Label);
return ui_BeginDropdown(Interface, DropdownValue);
}
internal void
ui_EndLabeledDropdown(ui_interface* Interface)
{
ui_EndDropdown(Interface);
ui_EndRow(Interface);
}
#define INTERFACE_H
#endif // INTERFACE_H

102
src/app/interface_test.cpp Normal file
View File

@ -0,0 +1,102 @@
//
// File: interface_test.cpp
// Author: Peter Slattery
// Creation Date: 2020-11-15
//
#ifndef INTERFACE_TEST_CPP
global r32 TestSlider_Value = 5;
global r32 TestSlider_Min = 0;
global r32 TestSlider_Max = 10;
global bool TestToggle = true;
global r64 TestTextEntry = 3.1415f;
internal void
InterfaceTest_Render(app_state* State, context* Context, render_command_buffer* RenderBuffer)
{
ui_InterfaceReset(&State->Interface);
State->Interface.RenderBuffer = RenderBuffer;
State->Interface.WindowBounds = Context->WindowBounds;
gs_string A = MakeString("TestRender Layout");
ui_PushLayout(&State->Interface, A);
{
#if 1
TestTextEntry = ui_TextEntryR64(&State->Interface, MakeString("Spacer"), TestTextEntry);
ui_Button(&State->Interface, MakeString("A"));
TestSlider_Value = ui_RangeSlider(&State->Interface, MakeString("TestSlider"), TestSlider_Value, TestSlider_Min, TestSlider_Max);
#elif 0
ui_PushLayout(&State->Interface, MakeString("Outer"));
{
for (u32 i = 0; i < 3; i++)
{
ui_Button(&State->Interface, MakeString("A"));
}
}
ui_PopLayout(&State->Interface);
ui_BeginRow(&State->Interface, 2);
{
ui_PushLayout(&State->Interface, MakeString("TestLayout"));
{
for (u32 i = 0; i < 5; i++)
{
ui_Button(&State->Interface, MakeString("TestButon"));
}
}
ui_PopLayout(&State->Interface);
ui_PushLayout(&State->Interface, MakeString("TestLayout"));
{
ui_Button(&State->Interface, MakeString("TestButon"));
TestToggle = ui_Toggle(&State->Interface, MakeString("Toggle"), TestToggle);
TestSlider_Value = ui_RangeSlider(&State->Interface, MakeString("TestSlider"), TestSlider_Value, TestSlider_Min, TestSlider_Max);
if (ui_BeginDropdown(&State->Interface, MakeString("TestDropdown")))
{
ui_Button(&State->Interface, MakeString("TestButon"));
ui_Button(&State->Interface, MakeString("TestButon"));
ui_Button(&State->Interface, MakeString("TestButon"));
}
ui_EndDropdown(&State->Interface);
}
ui_PopLayout(&State->Interface);
}
ui_EndRow(&State->Interface);
ui_PushLayout(&State->Interface, MakeString("Outer"));
{
for (u32 i = 0; i < 3; i++)
{
ui_Button(&State->Interface, MakeString("B"));
}
}
ui_PopLayout(&State->Interface);
#else
ui_BeginList(&State->Interface, MakeString("Test List"), 10);
{
for (u32 i = 0; i < 32; i++)
{
ui_Button(&State->Interface, MakeString("Option"));
}
}
ui_EndList(&State->Interface);
#endif
ui_PushOverlayLayout(&State->Interface, rect2{25, 25, 400, 200}, LayoutDirection_TopDown, MakeString("t"));
{
ui_Label(&State->Interface, PushStringF(State->Interface.PerFrameMemory, 256, "Mouse Pos - %f %f", State->Interface.Mouse.Pos.x, State->Interface.Mouse.Pos.y));
ui_Label(&State->Interface, PushStringF(State->Interface.PerFrameMemory, 256, "Hot - %lld | Active - %lld",
State->Interface.HotWidget.Id, State->Interface.ActiveWidget.Id));
ui_Label(&State->Interface, PushStringF(State->Interface.PerFrameMemory, 256, "Last Active - %lld",
State->Interface.LastActiveWidget.Id));
}
ui_PopLayout(&State->Interface);
}
ui_PopLayout(&State->Interface);
}
#define INTERFACE_TEST_CPP
#endif // INTERFACE_TEST_CPP

View File

@ -276,6 +276,7 @@ HandleWindowMessage (MSG Message, window* Window, input_queue* InputQueue, mouse
case WM_KEYDOWN:
case WM_KEYUP:
{
#if 0
int VirtualKey = (int)Message.wParam;
key_code Key = Win32GetKeyCode(VirtualKey, true, false);
s32 KeyIndex = (int)Key;
@ -287,6 +288,27 @@ HandleWindowMessage (MSG Message, window* Window, input_queue* InputQueue, mouse
b32 AltDown = GetKeyState(VK_MENU) & 0x8000;
b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000;
// New Input Queue
AddInputEventEntry(InputQueue, Key, KeyWasDown, KeyIsDown,
ShiftDown, AltDown, CtrlDown, false);
#endif
TranslateMessage(&Message);
DispatchMessage(&Message);
}break;
case WM_CHAR:
{
char VirtualKey = (char)Message.wParam;
key_code Key = CharToKeyCode(VirtualKey);
s32 KeyIndex = (int)Key;
b32 KeyWasDown = (Message.lParam & (1 << 30)) != 0;
b32 KeyIsDown = (Message.lParam & (1 << 31)) == 0;
b32 ShiftDown = GetKeyState(VK_SHIFT) & 0x8000;
b32 AltDown = GetKeyState(VK_MENU) & 0x8000;
b32 CtrlDown = GetKeyState(VK_CONTROL) & 0x8000;
// New Input Queue
AddInputEventEntry(InputQueue, Key, KeyWasDown, KeyIsDown,
ShiftDown, AltDown, CtrlDown, false);
@ -370,6 +392,7 @@ Win32_SendAddressedDataBuffers(gs_thread_context Context, addressed_data_buffer_
DEBUG_TRACK_FUNCTION;
u32 BuffersSent = 0;
u32 DataSizeSent = 0;
for (addressed_data_buffer* BufferAt = OutputData.Root;
BufferAt != 0;
@ -397,6 +420,7 @@ Win32_SendAddressedDataBuffers(gs_thread_context Context, addressed_data_buffer_
if (Win32SerialPort_Write(SerialPort, BufferAt->Data))
{
BuffersSent += 1;
DataSizeSent += BufferAt->Data.Size;
}
}
}
@ -411,9 +435,9 @@ Win32_SendAddressedDataBuffers(gs_thread_context Context, addressed_data_buffer_
}
gs_string OutputStr = AllocatorAllocString(Context.Allocator, 256);
PrintF(&OutputStr, "Buffers Sent: %d\n", BuffersSent);
PrintF(&OutputStr, "Buffers Sent: %d | Size Sent: %d\n", BuffersSent, DataSizeSent);
NullTerminate(&OutputStr);
//OutputDebugStringA(OutputStr.Str);
OutputDebugStringA(OutputStr.Str);
}
internal void

View File

@ -407,5 +407,253 @@ GetMouseButtonStateAdvanced (b32 ButtonState)
return Result;
}
internal char
KeyCodeToChar(key_code Code)
{
char Result = 0;
switch (Code)
{
case KeyCode_Space: { Result = ' '; } break;
case KeyCode_Tab: { Result = '\t'; } break;
case KeyCode_Enter: { Result = '\n'; } break;
case KeyCode_Backspace: { Result = '\b'; } break;
case KeyCode_a: { Result = 'a'; } break;
case KeyCode_b: { Result = 'b'; } break;
case KeyCode_c: { Result = 'c'; } break;
case KeyCode_d: { Result = 'd'; } break;
case KeyCode_e: { Result = 'e'; } break;
case KeyCode_f: { Result = 'f'; } break;
case KeyCode_g: { Result = 'g'; } break;
case KeyCode_h: { Result = 'h'; } break;
case KeyCode_i: { Result = 'i'; } break;
case KeyCode_j: { Result = 'j'; } break;
case KeyCode_k: { Result = 'k'; } break;
case KeyCode_l: { Result = 'l'; } break;
case KeyCode_m: { Result = 'm'; } break;
case KeyCode_n: { Result = 'n'; } break;
case KeyCode_o: { Result = 'o'; } break;
case KeyCode_p: { Result = 'p'; } break;
case KeyCode_q: { Result = 'q'; } break;
case KeyCode_r: { Result = 'r'; } break;
case KeyCode_s: { Result = 's'; } break;
case KeyCode_t: { Result = 't'; } break;
case KeyCode_u: { Result = 'u'; } break;
case KeyCode_v: { Result = 'v'; } break;
case KeyCode_w: { Result = 'w'; } break;
case KeyCode_x: { Result = 'x'; } break;
case KeyCode_y: { Result = 'y'; } break;
case KeyCode_z: { Result = 'z'; } break;
case KeyCode_A: { Result = 'A'; } break;
case KeyCode_B: { Result = 'B'; } break;
case KeyCode_C: { Result = 'C'; } break;
case KeyCode_D: { Result = 'D'; } break;
case KeyCode_E: { Result = 'E'; } break;
case KeyCode_F: { Result = 'F'; } break;
case KeyCode_G: { Result = 'G'; } break;
case KeyCode_H: { Result = 'H'; } break;
case KeyCode_I: { Result = 'I'; } break;
case KeyCode_J: { Result = 'J'; } break;
case KeyCode_K: { Result = 'K'; } break;
case KeyCode_L: { Result = 'L'; } break;
case KeyCode_M: { Result = 'M'; } break;
case KeyCode_N: { Result = 'N'; } break;
case KeyCode_O: { Result = 'O'; } break;
case KeyCode_P: { Result = 'P'; } break;
case KeyCode_Q: { Result = 'Q'; } break;
case KeyCode_R: { Result = 'R'; } break;
case KeyCode_S: { Result = 'S'; } break;
case KeyCode_T: { Result = 'T'; } break;
case KeyCode_U: { Result = 'U'; } break;
case KeyCode_V: { Result = 'V'; } break;
case KeyCode_W: { Result = 'W'; } break;
case KeyCode_X: { Result = 'X'; } break;
case KeyCode_Y: { Result = 'Y'; } break;
case KeyCode_Z: { Result = 'Z'; } break;
case KeyCode_Num0:
case KeyCode_0: { Result = '0'; } break;
case KeyCode_Num1:
case KeyCode_1: { Result = '1'; } break;
case KeyCode_Num2:
case KeyCode_2: { Result = '2'; } break;
case KeyCode_Num3:
case KeyCode_3: { Result = '3'; } break;
case KeyCode_Num4:
case KeyCode_4: { Result = '4'; } break;
case KeyCode_Num5:
case KeyCode_5: { Result = '5'; } break;
case KeyCode_Num6:
case KeyCode_6: { Result = '6'; } break;
case KeyCode_Num7:
case KeyCode_7: { Result = '7'; } break;
case KeyCode_Num8:
case KeyCode_8: { Result = '8'; } break;
case KeyCode_Num9:
case KeyCode_9: { Result = '9'; } break;
case KeyCode_Bang: { Result = '!'; } break;
case KeyCode_At: { Result = '@'; } break;
case KeyCode_Pound: { Result = '#'; } break;
case KeyCode_Dollar: { Result = '$'; } break;
case KeyCode_Percent: { Result = '%'; } break;
case KeyCode_Carrot: { Result = '^'; } break;
case KeyCode_Ampersand: { Result = '&'; } break;
case KeyCode_Star: { Result = '*'; } break;
case KeyCode_LeftParen: { Result = '('; } break;
case KeyCode_RightParen: { Result = ')'; } break;
case KeyCode_Minus: { Result = '-'; } break;
case KeyCode_Plus: { Result = '+'; } break;
case KeyCode_Equals: { Result = '='; } break;
case KeyCode_Underscore: { Result = '_'; } break;
case KeyCode_LeftBrace: { Result = '{'; } break;
case KeyCode_RightBrace: { Result = '}'; } break;
case KeyCode_LeftBracket: { Result = '['; } break;
case KeyCode_RightBracket: { Result = ']'; } break;
case KeyCode_Colon: { Result = ':'; } break;
case KeyCode_SemiColon: { Result = ';'; } break;
case KeyCode_SingleQuote: { Result = '\''; } break;
case KeyCode_DoubleQuote: { Result = '"'; } break;
case KeyCode_ForwardSlash: { Result = '/'; } break;
case KeyCode_Backslash: { Result = '\\'; } break;
case KeyCode_Pipe: { Result = '|'; } break;
case KeyCode_Comma: { Result = ','; } break;
case KeyCode_Period: { Result = '.'; } break;
case KeyCode_QuestionMark: { Result = '?'; } break;
case KeyCode_LessThan: { Result = '<'; } break;
case KeyCode_GreaterThan: { Result = '>'; } break;
case KeyCode_Tilde: { Result = '~'; } break;
case KeyCode_BackQuote: { Result = '`'; } break;
default: { Result = 0; } break;
}
return Result;
}
internal bool
KeyCodeHasChar(key_code Code)
{
bool Result = KeyCodeToChar(Code) != 0;
return Result;
}
internal key_code
CharToKeyCode(char C)
{
key_code Result = KeyCode_Invalid;
switch (C)
{
case ' ': { Result = KeyCode_Space; } break;
case '\t': { Result = KeyCode_Tab; } break;
case '\n': { Result = KeyCode_Enter; } break;
case '\b': { Result = KeyCode_Backspace; } break;
case 'a': { Result = KeyCode_a; } break;
case 'b': { Result = KeyCode_b; } break;
case 'c': { Result = KeyCode_c; } break;
case 'd': { Result = KeyCode_d; } break;
case 'e': { Result = KeyCode_e; } break;
case 'f': { Result = KeyCode_f; } break;
case 'g': { Result = KeyCode_g; } break;
case 'h': { Result = KeyCode_h; } break;
case 'i': { Result = KeyCode_i; } break;
case 'j': { Result = KeyCode_j; } break;
case 'k': { Result = KeyCode_k; } break;
case 'l': { Result = KeyCode_l; } break;
case 'm': { Result = KeyCode_m; } break;
case 'n': { Result = KeyCode_n; } break;
case 'o': { Result = KeyCode_o; } break;
case 'p': { Result = KeyCode_p; } break;
case 'q': { Result = KeyCode_q; } break;
case 'r': { Result = KeyCode_r; } break;
case 's': { Result = KeyCode_s; } break;
case 't': { Result = KeyCode_t; } break;
case 'u': { Result = KeyCode_u; } break;
case 'v': { Result = KeyCode_v; } break;
case 'w': { Result = KeyCode_w; } break;
case 'x': { Result = KeyCode_x; } break;
case 'y': { Result = KeyCode_y; } break;
case 'z': { Result = KeyCode_z; } break;
case 'A': { Result = KeyCode_A; } break;
case 'B': { Result = KeyCode_B; } break;
case 'C': { Result = KeyCode_C; } break;
case 'D': { Result = KeyCode_D; } break;
case 'E': { Result = KeyCode_E; } break;
case 'F': { Result = KeyCode_F; } break;
case 'G': { Result = KeyCode_G; } break;
case 'H': { Result = KeyCode_H; } break;
case 'I': { Result = KeyCode_I; } break;
case 'J': { Result = KeyCode_J; } break;
case 'K': { Result = KeyCode_K; } break;
case 'L': { Result = KeyCode_L; } break;
case 'M': { Result = KeyCode_M; } break;
case 'N': { Result = KeyCode_N; } break;
case 'O': { Result = KeyCode_O; } break;
case 'P': { Result = KeyCode_P; } break;
case 'Q': { Result = KeyCode_Q; } break;
case 'R': { Result = KeyCode_R; } break;
case 'S': { Result = KeyCode_S; } break;
case 'T': { Result = KeyCode_T; } break;
case 'U': { Result = KeyCode_U; } break;
case 'V': { Result = KeyCode_V; } break;
case 'W': { Result = KeyCode_W; } break;
case 'X': { Result = KeyCode_X; } break;
case 'Y': { Result = KeyCode_Y; } break;
case 'Z': { Result = KeyCode_Z; } break;
case '0': { Result = KeyCode_0; } break;
case '1': { Result = KeyCode_1; } break;
case '2': { Result = KeyCode_2; } break;
case '3': { Result = KeyCode_3; } break;
case '4': { Result = KeyCode_4; } break;
case '5': { Result = KeyCode_5; } break;
case '6': { Result = KeyCode_6; } break;
case '7': { Result = KeyCode_7; } break;
case '8': { Result = KeyCode_8; } break;
case '9': { Result = KeyCode_9; } break;
case '!': { Result = KeyCode_Bang; } break;
case '@': { Result = KeyCode_At; } break;
case '#': { Result = KeyCode_Pound; } break;
case '$': { Result = KeyCode_Dollar; } break;
case '%': { Result = KeyCode_Percent; } break;
case '^': { Result = KeyCode_Carrot; } break;
case '&': { Result = KeyCode_Ampersand; } break;
case '*': { Result = KeyCode_Star; } break;
case '(': { Result = KeyCode_LeftParen; } break;
case ')': { Result = KeyCode_RightParen; } break;
case '-': { Result = KeyCode_Minus; } break;
case '+': { Result = KeyCode_Plus; } break;
case '=': { Result = KeyCode_Equals; } break;
case '_': { Result = KeyCode_Underscore; } break;
case '{': { Result = KeyCode_LeftBrace; } break;
case '}': { Result = KeyCode_RightBrace; } break;
case '[': { Result = KeyCode_LeftBracket; } break;
case ']': { Result = KeyCode_RightBracket; } break;
case ':': { Result = KeyCode_Colon; } break;
case ';': { Result = KeyCode_SemiColon; } break;
case '\'': { Result = KeyCode_SingleQuote; } break;
case '"': { Result = KeyCode_DoubleQuote; } break;
case '/': { Result = KeyCode_ForwardSlash; } break;
case '\\': { Result = KeyCode_Backslash; } break;
case '|': { Result = KeyCode_Pipe; } break;
case ',': { Result = KeyCode_Comma; } break;
case '.': { Result = KeyCode_Period; } break;
case '?': { Result = KeyCode_QuestionMark; } break;
case '<': { Result = KeyCode_LessThan; } break;
case '>': { Result = KeyCode_GreaterThan; } break;
case '~': { Result = KeyCode_Tilde; } break;
case '`': { Result = KeyCode_BackQuote; } break;
default: { Result = KeyCode_Invalid; } break;
}
return Result;
}
#define GS_INPUT_H
#endif // GS_INPUT_H

View File

@ -1592,6 +1592,21 @@ FindLastFromSet(gs_const_string String, char* SetArray)
return Result;
}
internal bool
StringContains(gs_const_string Str, char C)
{
bool Result = false;
for (u32 i = 0; i < Str.Length; i++)
{
if (Str.Str[i] == C)
{
Result = true;
break;
}
}
return Result;
}
internal bool
StringsEqualUpToLength(gs_const_string A, gs_const_string B, u64 Length)
{
@ -1695,30 +1710,66 @@ CharToUInt(char C, u64 Base)
return CharToUInt(C, GetCharSetForBase(Base));
}
internal u64
ParseUInt(gs_const_string String, u64 Base = 10, u64* ParsedLength = 0)
struct parse_uint_result
{
u64 Result = 0;
b8 Success;
u64 Value;
u32 ParsedLength;
};
internal parse_uint_result
ValidateAndParseUInt(gs_const_string String, u64 Base = 10)
{
parse_uint_result Result = {0};
gs_const_string CharSet = GetCharSetForBase(Base);
bool StringIsValid = true;
for (u32 i = 0; i < String.Length; i++)
{
if (!StringContains(CharSet, String.Str[i]))
{
StringIsValid = false;
break;
}
}
if (StringIsValid)
{
u64 Acc = 0;
u64 i = 0;
for (; i < String.Length; i++)
{
u64 CharIndex = FindFirst(CharSet, String.Str[i]);
if (CharIndex < CharSet.Length)
{
Result = CharToUInt(String.Str[i], CharSet) + (Result * Base);
Acc = CharToUInt(String.Str[i], CharSet) + (Acc * Base);
}
else
{
break;
}
}
if (ParsedLength != 0)
{
*ParsedLength = i;
Result.Success = true;
Result.Value = Acc;
Result.ParsedLength = i;
}
return Result;
}
internal u64
ParseUInt(gs_const_string String, u64 Base = 10, u64* ParsedLength = 0)
{
parse_uint_result ParseResult = ValidateAndParseUInt(String, Base);
Assert(ParseResult.Success);
if (ParsedLength)
{
*ParsedLength = ParseResult.ParsedLength;
}
return ParseResult.Value;
}
internal u64
ParseUInt(u64 Length, char* String, u64 Base = 10, u64* ParsedLength = 0)
{
@ -1756,10 +1807,32 @@ ParseInt(char* String, u64 Base = 10, u64* ParsedLength = 0)
return ParseInt(LitString(String), Base, ParsedLength);
}
internal r64
ParseFloat(gs_const_string String, u64* ParsedLength = 0)
struct parse_float_result
{
b8 Success;
r64 Value;
u64 ParsedLength;
};
internal parse_float_result
ValidateAndParseFloat(gs_const_string String)
{
parse_float_result Result = {0};
Result.Success = false;
// Validate
bool StringIsValid = true;
for (u64 i = 0; i < String.Length; i++)
{
if (!IsNumericDecimal(String.Str[i]) && String.Str[i] != '-')
{
StringIsValid = false;
break;
}
}
if (StringIsValid)
{
u64 DecimalIndex = FindFirst(String, '.');
u64 TempParsedLength = 0;
u64 PlacesAfterPoint = 0;
@ -1773,22 +1846,36 @@ ParseFloat(gs_const_string String, u64* ParsedLength = 0)
IntegerString = GetStringAfter(IntegerString, 1);
Polarity = -1;
}
r64 Result = (r64)ParseInt(IntegerString, 10, &TempParsedLength);
Result.Value = (r64)ParseInt(IntegerString, 10, &TempParsedLength);
if (TempParsedLength == IntegerString.Length)
{
r64 AfterPoint = (r64)ParseUInt(DecimalString, 10, &PlacesAfterPoint);
r64 Decimal = (AfterPoint / PowR64(10, PlacesAfterPoint));
Result = Result + Decimal;
Result *= Polarity;
Result.Value = Result.Value + Decimal;
Result.Value *= Polarity;
}
Result.ParsedLength = TempParsedLength + PlacesAfterPoint;
if (DecimalIndex < String.Length) { Result.ParsedLength += 1; }
Result.Success = true;
}
return Result;
}
internal r64
ParseFloat(gs_const_string String, u64* ParsedLength = 0)
{
parse_float_result Result = ValidateAndParseFloat(String);
Assert(Result.Success);
if (ParsedLength != 0)
{
*ParsedLength = TempParsedLength + PlacesAfterPoint;
if (DecimalIndex < String.Length) { *ParsedLength += 1; }
*ParsedLength = Result.ParsedLength;
}
return Result;
return Result.Value;
}
internal r64
ParseFloat(char* String, u64* ParsedLength = 0)
@ -2010,11 +2097,16 @@ PrintFArgsList (gs_string* String, char* Format, va_list Args)
FormatAt++;
if (IsBase10(FormatAt[0]))
{
PrecisionSpecified = true;
gs_const_string PrecisionStr = {};
PrecisionStr.Str = FormatAt;
for (char* C = FormatAt; *FormatAt && IsBase10(*C); C++)
{
PrecisionStr.Length++;
}
u64 Parsed = 0;
AssertMessage("ParseInt assumes whole string is an integer");
Precision = (s32)ParseInt(FormatAt, 10, &Parsed);
Precision = (s32)ParseInt(PrecisionStr, 10, &Parsed);
FormatAt += Parsed;
}
else if (FormatAt[0] == '*')

View File

@ -0,0 +1,217 @@
//
// File: gen_blumen_lumen.cpp
// Author: Peter Slattery
// Creation Date: 2021-01-06
//
#ifndef GEN_BLUMEN_LUMEN_CPP
#include <stdio.h>
#include <windows.h>
#include "../gs_libs/gs_types.h"
#include "../gs_libs/gs_types.cpp"
#include "../app/platform_win32/win32_foldhaus_utils.h"
#include "../app/platform_win32/win32_foldhaus_memory.h"
#include "../app/platform_win32/win32_foldhaus_fileio.h"
#include "../app/platform_win32/win32_foldhaus_work_queue.h"
#include "sculpture_gen.h"
typedef struct
{
v3 CenterStart;
v3 CenterEnd;
r32 Radius;
u32 SegmentsCount;
u32 SubsegmentsCount;
u32 SubsegmentLeds;
// Only one of these two values is needed.
// If ChannelsArray != 0, then it will be used, and assumed to
// have SegmentsCount values
// Otherwise, each segment will increment from ChannelStart
u32 ChannelStart;
u32* ChannelsArray;
char* ComPort;
char* SectionTagValue;
char* FlowerTagValue;
} loop_desc;
internal void
BuildLoop(gs_string* OutputBuffer, loop_desc Desc)
{
r32 SegmentsArc = TauR32 / Desc.SegmentsCount;
r32 SubsegmentsArc = SegmentsArc / Desc.SubsegmentsCount;
for (u32 i = 0; i < Desc.SegmentsCount; i++)
{
r32 ArcBase = SegmentsArc * i;
u32 Channel = 0;
if (Desc.ChannelsArray != 0)
{
Channel = Desc.ChannelsArray[i];
}
else
{
Channel = Desc.ChannelStart + i;
}
WriteLedStripOpen(OutputBuffer, Channel, Desc.ComPort);
WriteSegmentSequenceOpen(OutputBuffer, Desc.SubsegmentsCount);
for (u32 j = 0; j < Desc.SubsegmentsCount; j++)
{
r32 Arc = ArcBase + (SubsegmentsArc * j);
v3 Offset = v3{ SinR32(Arc), 0, CosR32(Arc) } * Desc.Radius;
v3 P0 = Desc.CenterStart + Offset;
v3 P1 = Desc.CenterEnd + Offset;
// Swap directions on the middle strip
if (j%2 != 0)
{
v3 Temp = P0;
P0 = P1;
P1 = Temp;
}
WriteSegmentSequenceSegment(OutputBuffer, P0, P1, Desc.SubsegmentLeds);
}
WriteSegmentSequenceClose(OutputBuffer);
WriteSegmentTagsOpen(OutputBuffer, 2);
WriteSegmentTag(OutputBuffer, "section", Desc.SectionTagValue);
WriteSegmentTag(OutputBuffer, "flower", Desc.FlowerTagValue);
WriteSegmentTagsClose(OutputBuffer);
WriteLedStripClose(OutputBuffer);
}
}
typedef struct
{
v3 Pos;
char* ComPort;
char* FlowerTagValue;
u32* StemChannels;
u32* BloomOuterChannels;
u32* BloomInnerChannels;
} flower_desc;
internal void
BuildFlower(gs_string* OutputBuffer, flower_desc Desc)
{
// the flower stem
loop_desc FlowerStem = {};
FlowerStem.CenterStart = v3{0, -1.5f, 0} + Desc.Pos;
FlowerStem.CenterEnd = v3{0, .5f, 0} + Desc.Pos;
FlowerStem.Radius = .05f;
FlowerStem.SegmentsCount = 6;
FlowerStem.SubsegmentsCount = 1;
FlowerStem.SubsegmentLeds = 300;
//FlowerStem.ChannelStart = 0;
FlowerStem.ChannelsArray = Desc.StemChannels;
FlowerStem.ComPort = Desc.ComPort;
FlowerStem.SectionTagValue = "stem";
FlowerStem.FlowerTagValue = Desc.FlowerTagValue;
BuildLoop(OutputBuffer, FlowerStem);
// the bloom stem outer
loop_desc BloomStemOuter = {};
BloomStemOuter.CenterStart = v3{0, .5f, 0} + Desc.Pos;
BloomStemOuter.CenterEnd = v3{0, .9f, 0} + Desc.Pos;
BloomStemOuter.Radius = .07f;
BloomStemOuter.SegmentsCount = 9;
BloomStemOuter.SubsegmentsCount = 3;
BloomStemOuter.SubsegmentLeds = 41;
//BloomStemOuter.ChannelStart = 7;
BloomStemOuter.ChannelsArray = Desc.BloomOuterChannels;
BloomStemOuter.ComPort = Desc.ComPort;
BloomStemOuter.SectionTagValue = "outer_bloom";
BloomStemOuter.FlowerTagValue = Desc.FlowerTagValue;
BuildLoop(OutputBuffer, BloomStemOuter);
// the bloom stem inner
loop_desc BloomStemInner = {};
BloomStemInner.CenterStart = v3{0, 1.4f, 0} + Desc.Pos;
BloomStemInner.CenterEnd = v3{0, .9f, 0} + Desc.Pos;
BloomStemInner.Radius = .05f;
BloomStemInner.SegmentsCount = 6;
BloomStemInner.SubsegmentsCount = 3;
BloomStemInner.SubsegmentLeds = 35;
//BloomStemInner.ChannelStart = 17;
BloomStemInner.ChannelsArray = Desc.BloomInnerChannels;
BloomStemInner.ComPort = Desc.ComPort;
BloomStemInner.SectionTagValue = "inner_bloom";
BloomStemInner.FlowerTagValue = Desc.FlowerTagValue;
BuildLoop(OutputBuffer, BloomStemInner);
}
// Just for brevity, no real function provided
#define FSC(f,c) FlowerStripToChannel((f), (c))
internal u8
FlowerStripToChannel(u8 Flower, u8 Channel)
{
Assert(Flower < 3);
Assert(Channel < 8);
u8 Result = 0;
Result |= (Flower & 0x03) << 3;
Result |= (Channel & 0x07);
return Result;
}
int main(int ArgCount, char** Args)
{
gs_thread_context Ctx = Win32CreateThreadContext();
gs_string OutputBuffer = PushString(Ctx.Transient, MB(4));
char* ComPort = "\\\\.\\COM8";
WriteAssemblyUARTOpen(&OutputBuffer,
"Blumen Lumen - Silver Spring",
100,
v3{0, 0, 0},
69,
ComPort);
u32 StemChannels[] = { FSC(2, 1), FSC(2, 2), FSC(2, 3), FSC(2, 4), FSC(2, 5), FSC(2, 6) };
u32 BloomOuterChannels[] = { FSC(1, 0), FSC(1, 1), FSC(1, 2), FSC(1, 3), FSC(1, 4), FSC(1, 5), FSC(1, 6), FSC(1, 7), FSC(2, 0) };
u32 BloomInnerChannels[] = { FSC(0, 0), FSC(0, 1), FSC(0, 2), FSC(0, 3), FSC(0, 4), FSC(0, 5) };
flower_desc F0 = {};
F0.Pos = v3{-1, 0, 0};
F0.ComPort = ComPort;
F0.FlowerTagValue = "left";
F0.StemChannels = StemChannels;
F0.BloomOuterChannels = BloomOuterChannels;
F0.BloomInnerChannels = BloomInnerChannels;
BuildFlower(&OutputBuffer, F0);
/*
flower_desc F1 = {};
F1.Pos = v3{0, 0, 0};
F1.FlowerTagValue = "center";
F1.StemChannels = StemChannels;
F1.BloomInnerChannels = BloomInnerChannels;
F1.BloomOuterChannels = BloomOuterChannels;
BuildFlower(&OutputBuffer, F1);
flower_desc F2 = {};
F2.Pos = v3{1, 0, 0};
F2.FlowerTagValue = "right";
F2.StemChannels = StemChannels;
F2.BloomInnerChannels = BloomInnerChannels;
F2.BloomOuterChannels = BloomOuterChannels;
BuildFlower(&OutputBuffer, F2);
*/
printf("%.*s\n", (u32)OutputBuffer.Length, OutputBuffer.Str);
return 0;
}
#define GEN_BLUMEN_LUMEN_CPP
#endif // GEN_BLUMEN_LUMEN_CPP

View File

@ -0,0 +1,102 @@
//
// File: sculpture_gen.h
// Author: Peter Slattery
// Creation Date: 2021-01-06
//
#ifndef SCULPTURE_GEN_H
internal void
WriteIndented(gs_string* Buffer, u32 Indent, char* Format, ...)
{
va_list Args;
va_start(Args, Format);
for (u32 i = 0; i < Indent; i++)
{
OutChar(Buffer, '\t');
}
PrintFArgsList(Buffer, Format, Args);
va_end(Args);
}
internal void
WriteAssemblyUARTOpen(gs_string* Buffer, char* Name, u32 Scale, v3 Center, u32 StripCount, char* ComPort)
{
WriteIndented(Buffer, 0, "assembly_name: \"%s\";\n", Name);
WriteIndented(Buffer, 0, "assembly_scale: %d;\n", Scale);
WriteIndented(Buffer, 0, "assembly_center: (%f, %f, %f);\n", Center.x, Center.y, Center.z);
WriteIndented(Buffer, 0, "led_strip_count: %d;\n", StripCount);
WriteIndented(Buffer, 0, "output_mode: \"UART\";\n");
WriteIndented(Buffer, 0, "com_port: \"%s\";\n", ComPort);
}
internal void
WriteLedStripOpen(gs_string* Buffer, u32 Channel, char* ComPort)
{
WriteIndented(Buffer, 0, "led_strip:\n{\n");
WriteIndented(Buffer, 1, "output_uart: {\n");
WriteIndented(Buffer, 2, "channel: %d;\n", Channel);
WriteIndented(Buffer, 2, "com_port: \"%s\";\n", ComPort);
WriteIndented(Buffer, 1, "};\n\n");
}
internal void
WriteSegmentSequenceOpen(gs_string* Buffer, u32 SegmentCount)
{
WriteIndented(Buffer, 1, "segment: {\n");
WriteIndented(Buffer, 2, "point_placement_type: \"SegmentSequence\";\n");
WriteIndented(Buffer, 2, "segment_sequence:\n");
WriteIndented(Buffer, 2, "{\n");
WriteIndented(Buffer, 3, "segment_count: %d;\n", SegmentCount);
}
internal void
WriteSegmentSequenceSegment(gs_string* Buffer, v3 P0, v3 P1, u32 LedCount)
{
WriteIndented(Buffer, 3, "segment: {\n");
WriteIndented(Buffer, 4, "point_placement_type: \"InterpolatePoints\";\n");
WriteIndented(Buffer, 4, "interpolate_points: {\n");
WriteIndented(Buffer, 5, "start: (%f, %f, %f);\n", P0.x, P0.y, P0.z);
WriteIndented(Buffer, 5, "end: (%f, %f, %f);\n", P1.x, P1.y, P1.z);
WriteIndented(Buffer, 5, "led_count: %d;\n", LedCount);
WriteIndented(Buffer, 4, "};\n");
WriteIndented(Buffer, 3, "};\n");
}
internal void
WriteSegmentSequenceClose(gs_string* Buffer)
{
WriteIndented(Buffer, 2, "};\n");
WriteIndented(Buffer, 1, "};\n");
}
internal void
WriteSegmentTagsOpen(gs_string* Buffer, u32 TagCount)
{
WriteIndented(Buffer, 1, "tags_count: %d;\n", TagCount);
}
internal void
WriteSegmentTag(gs_string* Buffer, char* TagName, char* TagValue)
{
WriteIndented(Buffer, 1, "tag: {\n");
WriteIndented(Buffer, 2, "name: \"%s\";\n", TagName);
WriteIndented(Buffer, 2, "value: \"%s\";\n", TagValue);
WriteIndented(Buffer, 1, "};\n");
}
internal void
WriteSegmentTagsClose(gs_string* Buffer)
{
}
internal void
WriteLedStripClose(gs_string* Buffer)
{
WriteIndented(Buffer, 0, "};\n");
}
#define SCULPTURE_GEN_H
#endif // SCULPTURE_GEN_H