diff --git a/run_tree/wasm/debug/lumenarium_wasm_imports.js b/run_tree/wasm/debug/lumenarium_wasm_imports.js index 29d372c..ff42920 100644 --- a/run_tree/wasm/debug/lumenarium_wasm_imports.js +++ b/run_tree/wasm/debug/lumenarium_wasm_imports.js @@ -92,20 +92,69 @@ var lumenarium_wasm_imports = { }, wasm_mem_grow: (new_size) => { - let pages = new_size / WASM_PAGE_SIZE; + let new_size_ = new_size >>> 0; + let pages = new_size_ / WASM_PAGE_SIZE; let pages_rem = fract(pages); if (pages_rem > 0) pages = Math.floor(pages) + 1; let size_before = lumenarium_wasm_instance.exports.memory.buffer.byteLength; let old_page_count = lumenarium_wasm_instance.exports.memory.grow(pages); console.log("mem_grow\n", - "req size: ", new_size, "\n", + "req size: ", new_size_, "\n", "old size: ", (old_page_count * WASM_PAGE_SIZE), "\n", "old size: ", size_before, "\n", "grew by: ", (pages * WASM_PAGE_SIZE), "\n", "new size: ", lumenarium_wasm_instance.exports.memory.buffer.byteLength, ""); }, + malloc: (size) => { + + }, + + free: (base) => { + + }, + + sin: Math.sin, + sinf: Math.sin, + cos: Math.cos, + cosf: Math.cos, + tan: Math.tan, + tanf: Math.tan, + asin: Math.asin, + asinf: Math.asin, + acos: Math.acos, + acosf: Math.acos, + atan: Math.atan, + atanf: Math.atan, + pow: Math.pow, + powf: Math.pow, + fmodf: (f,d) => { return f % d; }, + strlen: (ptr) => { + let len = 0; + let len_checked = 0; + let len_to_check = 256; + let found_end = false; + while (true) + { + let string = wasm_mem_get_u8_arr(lumenarium_wasm_instance, ptr, len_checked); + for (let i = len_checked; i < len_to_check; i++) + { + if (string[i] == 0) + { + len = i; + break; + } + } + len_checked *= 2; + } + return len_checked; + }, + + wasm_platform_file_async_work_on_job: (path, path_len, data, data_size, read, write) => { + + }, + wasm_performance_now: () => { return performance.now(); }, @@ -260,6 +309,14 @@ function glBufferData(target, size, ptr, usage) return r; } +function glBufferSubData(target, offset, size, ptr) +{ + let data = wasm_mem_get_u8_arr(lumenarium_wasm_instance, ptr, size); + const r = gl.bufferSubData(target, offset, data, 0, size); + glErrorReport(arguments); + return r; +} + function glCreateShader(kind) { let shader = gl.createShader(kind); @@ -395,6 +452,17 @@ function glTexSubImage2D(target, level, offsetx, offsety, width, height, format, return r; } +function glGetUniformLocation(program, name, name_len) +{ + // TODO(PS): complete + return 0; +} + +function glUniformMatrix4fv() +{ + // TODO(PS): +} + function webgl_add_imports (canvas_selector, imports) { const canvas = document.querySelector(canvas_selector); if (!canvas) return console.error("no canvas"); @@ -414,6 +482,7 @@ function webgl_add_imports (canvas_selector, imports) { imports.glCreateBuffer = glCreateBuffer; imports.glBindBuffer = glBindBuffer; imports.glBufferData = glBufferData; + imports.glBufferSubData = glBufferSubData; imports.glCreateShader = glCreateShader; imports.glShaderSource = glShaderSource; imports.glCompileShader = glCompileShader; @@ -431,5 +500,8 @@ function webgl_add_imports (canvas_selector, imports) { imports.glTexImage2D = glTexImage2D; imports.glTexSubImage2D = glTexSubImage2D; imports.glBindTexture = glBindTexture; + imports.glGetUniformLocation = glGetUniformLocation; + imports.glUniformMatrix4fv = glUniformMatrix4fv; + return imports; } \ No newline at end of file diff --git a/run_tree/win32/intel/debug/debug.rdbg b/run_tree/win32/intel/debug/debug.rdbg index 24dbe61..3304be4 100644 Binary files a/run_tree/win32/intel/debug/debug.rdbg and b/run_tree/win32/intel/debug/debug.rdbg differ diff --git a/run_tree/win32/intel/debug/lumenarium_first_win32.obj b/run_tree/win32/intel/debug/lumenarium_first_win32.obj index 7c9356e..6284fe5 100644 Binary files a/run_tree/win32/intel/debug/lumenarium_first_win32.obj and b/run_tree/win32/intel/debug/lumenarium_first_win32.obj differ diff --git a/src/app/editor/foldhaus_panel.h b/src/app/editor/foldhaus_panel.h index aec8cb9..b8a59a8 100644 --- a/src/app/editor/foldhaus_panel.h +++ b/src/app/editor/foldhaus_panel.h @@ -11,11 +11,11 @@ enum panel_split_direction { - PanelSplit_NoSplit, - PanelSplit_Horizontal, - PanelSplit_Vertical, - - PanelSplit_Count, + PanelSplit_NoSplit, + PanelSplit_Horizontal, + PanelSplit_Vertical, + + PanelSplit_Count, }; typedef struct panel panel; @@ -25,32 +25,32 @@ typedef PANEL_MODAL_OVERRIDE_CALLBACK(panel_modal_override_callback); struct panel { - s32 TypeIndex; - gs_data StateMemory; - - panel* ModalOverride; - panel* IsModalOverrideFor; - panel_modal_override_callback* ModalOverrideCB; - - rect2 Bounds; - panel_split_direction SplitDirection; - r32 SplitPercent; - - panel* Parent; - - union{ - panel* Left; - panel* Top; - }; - union{ - panel* Right; - panel* Bottom; - }; + s32 TypeIndex; + gs_data StateMemory; + + panel* ModalOverride; + panel* IsModalOverrideFor; + panel_modal_override_callback* ModalOverrideCB; + + rect2 Bounds; + panel_split_direction SplitDirection; + r32 SplitPercent; + + panel* Parent; + + union{ + panel* Left; + panel* Top; + }; + union{ + panel* Right; + panel* Bottom; + }; }; struct free_panel { - free_panel* Next; + free_panel* Next; }; #define PANEL_INIT_PROC(name) void name(panel* Panel, app_state* State, context Context) @@ -65,25 +65,25 @@ typedef PANEL_RENDER_PROC(panel_render_proc); // NOTE(Peter): This is used by the meta system to generate panel type info struct panel_definition { - char* PanelName; - s32 PanelNameLength; - panel_init_proc* Init; - panel_cleanup_proc* Cleanup; - panel_render_proc* Render; - input_command* InputCommands; - s32 InputCommandsCount; + char* PanelName; + s32 PanelNameLength; + panel_init_proc* Init; + panel_cleanup_proc* Cleanup; + panel_render_proc* Render; + input_command* InputCommands; + s32 InputCommandsCount; }; #define PANELS_MAX 16 struct panel_system { - panel_definition* PanelDefs; - u32 PanelDefsCount; - - panel* Panels; - u32 PanelsUsed; - - free_panel* FreeList; + panel_definition* PanelDefs; + u32 PanelDefsCount; + + panel* Panels; + u32 PanelsUsed; + + free_panel* FreeList; }; ///////////////////////////////// @@ -95,164 +95,164 @@ struct panel_system internal void PanelSystem_Init(panel_system* PanelSystem, panel_definition* PanelDefs, u32 PanelDefsCount, gs_memory_arena* Storage) { - PanelSystem->FreeList = 0; - PanelSystem->PanelDefs = PanelDefs; - PanelSystem->PanelDefsCount = PanelDefsCount; - - PanelSystem->Panels = PushArray(Storage, panel, PANELS_MAX); + PanelSystem->FreeList = 0; + PanelSystem->PanelDefs = PanelDefs; + PanelSystem->PanelDefsCount = PanelDefsCount; + + PanelSystem->Panels = PushArray(Storage, panel, PANELS_MAX); } internal panel* PanelSystem_TakePanel(panel_system* PanelSystem) { - panel* FreeEntry = 0; - if (PanelSystem->FreeList != 0) - { - free_panel* FreePanel = PanelSystem->FreeList; - PanelSystem->FreeList = FreePanel->Next; - FreeEntry = (panel*)FreePanel; - } - else - { - Assert(PanelSystem->PanelsUsed < PANELS_MAX); - FreeEntry = PanelSystem->Panels + PanelSystem->PanelsUsed++; - } - return FreeEntry; + panel* FreeEntry = 0; + if (PanelSystem->FreeList != 0) + { + free_panel* FreePanel = PanelSystem->FreeList; + PanelSystem->FreeList = FreePanel->Next; + FreeEntry = (panel*)FreePanel; + } + else + { + Assert(PanelSystem->PanelsUsed < PANELS_MAX); + FreeEntry = PanelSystem->Panels + PanelSystem->PanelsUsed++; + } + return FreeEntry; } internal void PanelSystem_FreePanel(panel* Panel, panel_system* PanelSystem) { - Assert(Panel >= PanelSystem->Panels && Panel <= PanelSystem->Panels + PANELS_MAX); - - free_panel* FreeEntry = (free_panel*)Panel; - FreeEntry->Next = PanelSystem->FreeList; - PanelSystem->FreeList = FreeEntry; + Assert(Panel >= PanelSystem->Panels && Panel <= PanelSystem->Panels + PANELS_MAX); + + free_panel* FreeEntry = (free_panel*)Panel; + FreeEntry->Next = PanelSystem->FreeList; + PanelSystem->FreeList = FreeEntry; } internal void PanelSystem_FreePanelRecursive(panel* Panel, panel_system* PanelSystem) { - if (Panel->SplitDirection != PanelSplit_NoSplit) - { - PanelSystem_FreePanelRecursive(Panel->Left, PanelSystem); - PanelSystem_FreePanelRecursive(Panel->Right, PanelSystem); - } - PanelSystem_FreePanel(Panel, PanelSystem); + if (Panel->SplitDirection != PanelSplit_NoSplit) + { + PanelSystem_FreePanelRecursive(Panel->Left, PanelSystem); + PanelSystem_FreePanelRecursive(Panel->Right, PanelSystem); + } + PanelSystem_FreePanel(Panel, PanelSystem); } internal panel* Panel_GetModalOverride(panel* Panel) { - panel* Result = Panel; - if (Panel->ModalOverride != 0) - { - Result = Panel_GetModalOverride(Panel->ModalOverride); - } - return Result; + panel* Result = Panel; + if (Panel->ModalOverride != 0) + { + Result = Panel_GetModalOverride(Panel->ModalOverride); + } + return Result; } internal void Panel_PushModalOverride(panel* Root, panel* Override, panel_modal_override_callback* Callback) { - Root->ModalOverride = Override; - Root->ModalOverrideCB = Callback; - Override->IsModalOverrideFor = Root; - Override->Bounds = Root->Bounds; + Root->ModalOverride = Override; + Root->ModalOverrideCB = Callback; + Override->IsModalOverrideFor = Root; + Override->Bounds = Root->Bounds; } internal void Panel_PopModalOverride(panel* Parent, panel_system* System) { - // TODO(pjs): Free the overrided panel - PanelSystem_FreePanel(Parent->ModalOverride, System); - Parent->ModalOverride = 0; + // TODO(pjs): Free the overrided panel + PanelSystem_FreePanel(Parent->ModalOverride, System); + Parent->ModalOverride = 0; } internal void Panel_SetCurrentType(panel* Panel, panel_system* System, s32 NewPanelType, gs_data TypeStateMemory, app_state* State, context Context) { - s32 OldTypeIndex = Panel->TypeIndex; - - Panel->TypeIndex = NewPanelType; - Panel->StateMemory = TypeStateMemory; - - if(OldTypeIndex >= 0) - { - System->PanelDefs[OldTypeIndex].Cleanup(Panel, State); - } + s32 OldTypeIndex = Panel->TypeIndex; + + Panel->TypeIndex = NewPanelType; + Panel->StateMemory = TypeStateMemory; + + if(OldTypeIndex >= 0) + { + System->PanelDefs[OldTypeIndex].Cleanup(Panel, State); + } } internal void Panel_SetType(panel* Panel, panel_system* System, s32 NewPanelTypeIndex, app_state* State, context Context) { - gs_data EmptyStateData = {0}; - Panel_SetCurrentType(Panel, System, NewPanelTypeIndex, EmptyStateData, State, Context); - System->PanelDefs[NewPanelTypeIndex].Init(Panel, State, Context); + gs_data EmptyStateData = {0}; + Panel_SetCurrentType(Panel, System, NewPanelTypeIndex, EmptyStateData, State, Context); + System->PanelDefs[NewPanelTypeIndex].Init(Panel, State, Context); } #define Panel_GetStateStruct(p, type) (type*)Panel_GetStateMemory((p), sizeof(type)).Memory internal gs_data Panel_GetStateMemory(panel* Panel, u64 Size) { - Assert(Panel->StateMemory.Size == Size); - gs_data Result = Panel->StateMemory; - return Result; + Assert(Panel->StateMemory.Size == Size); + gs_data Result = Panel->StateMemory; + return Result; } internal panel* PanelSystem_PushPanel(panel_system* PanelSystem, s32 PanelTypeIndex, app_state* State, context Context) { - panel* Panel = PanelSystem_TakePanel(PanelSystem); - Panel_SetType(Panel, PanelSystem, PanelTypeIndex, State, Context); - return Panel; + panel* Panel = PanelSystem_TakePanel(PanelSystem); + Panel_SetType(Panel, PanelSystem, PanelTypeIndex, State, Context); + return Panel; } internal void SplitPanel(panel* Parent, r32 Percent, panel_split_direction SplitDirection, panel_system* PanelSystem, app_state* State, context Context) { - if (Percent >= 0.0f && Percent <= 1.0f) - { - Parent->SplitDirection = SplitDirection; - Parent->SplitPercent = Percent; - - s32 ParentTypeIndex = Parent->TypeIndex; - gs_data ParentStateMemory = Parent->StateMemory; - - Parent->Left = PanelSystem_TakePanel(PanelSystem); - Panel_SetCurrentType(Parent->Left, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context); - Parent->Left->Parent = Parent; - - Parent->Right = PanelSystem_TakePanel(PanelSystem); - Panel_SetCurrentType(Parent->Right, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context); - Parent->Right->Parent = Parent; - } + if (Percent >= 0.0f && Percent <= 1.0f) + { + Parent->SplitDirection = SplitDirection; + Parent->SplitPercent = Percent; + + s32 ParentTypeIndex = Parent->TypeIndex; + gs_data ParentStateMemory = Parent->StateMemory; + + Parent->Left = PanelSystem_TakePanel(PanelSystem); + Panel_SetCurrentType(Parent->Left, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context); + Parent->Left->Parent = Parent; + + Parent->Right = PanelSystem_TakePanel(PanelSystem); + Panel_SetCurrentType(Parent->Right, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context); + Parent->Right->Parent = Parent; + } } internal void SplitPanelVertically(panel* Parent, r32 Percent, panel_system* PanelSystem, app_state* State, context Context) { - SplitPanel(Parent, Percent, PanelSplit_Vertical, PanelSystem, State, Context); + SplitPanel(Parent, Percent, PanelSplit_Vertical, PanelSystem, State, Context); } internal void SplitPanelHorizontally(panel* Parent, r32 Percent, panel_system* PanelSystem, app_state* State, context Context) { - SplitPanel(Parent, Percent, PanelSplit_Horizontal, PanelSystem, State, Context); + SplitPanel(Parent, Percent, PanelSplit_Horizontal, PanelSystem, State, Context); } internal void ConsolidatePanelsKeepOne(panel* Parent, panel* PanelToKeep, panel_system* PanelSystem) { - panel* LeftChild = Parent->Left; - panel* RightChild = Parent->Right; - - panel* PanelToDestroy = PanelToKeep == LeftChild ? RightChild : LeftChild; - - *Parent = *PanelToKeep; - - PanelSystem_FreePanel(PanelToKeep, PanelSystem); - PanelSystem_FreePanelRecursive(PanelToDestroy, PanelSystem); + panel* LeftChild = Parent->Left; + panel* RightChild = Parent->Right; + + panel* PanelToDestroy = PanelToKeep == LeftChild ? RightChild : LeftChild; + + *Parent = *PanelToKeep; + + PanelSystem_FreePanel(PanelToKeep, PanelSystem); + PanelSystem_FreePanelRecursive(PanelToDestroy, PanelSystem); } ///////////////////////////////// @@ -264,178 +264,178 @@ ConsolidatePanelsKeepOne(panel* Parent, panel* PanelToKeep, panel_system* PanelS internal rect2 GetTopPanelBounds(panel* Panel) { - rect2 Result = {}; - Result.Min = v2{ - Panel->Bounds.Min.x, - LerpR32(Panel->SplitPercent, Panel->Bounds.Min.y, Panel->Bounds.Max.y) - }; - Result.Max = Panel->Bounds.Max; - return Result; + rect2 Result = {}; + Result.Min = v2{ + Panel->Bounds.Min.x, + LerpR32(Panel->SplitPercent, Panel->Bounds.Min.y, Panel->Bounds.Max.y) + }; + Result.Max = Panel->Bounds.Max; + return Result; } internal rect2 GetBottomPanelBounds(panel* Panel) { - rect2 Result = {}; - Result.Min = Panel->Bounds.Min; - Result.Max = v2{ - Panel->Bounds.Max.x, - LerpR32(Panel->SplitPercent, Panel->Bounds.Min.y, Panel->Bounds.Max.y) - }; - return Result; + rect2 Result = {}; + Result.Min = Panel->Bounds.Min; + Result.Max = v2{ + Panel->Bounds.Max.x, + LerpR32(Panel->SplitPercent, Panel->Bounds.Min.y, Panel->Bounds.Max.y) + }; + return Result; } internal rect2 GetRightPanelBounds(panel* Panel) { - rect2 Result = {}; - Result.Min = v2{ - LerpR32(Panel->SplitPercent, Panel->Bounds.Min.x, Panel->Bounds.Max.x), - Panel->Bounds.Min.y - }; - Result.Max = Panel->Bounds.Max; - return Result; + rect2 Result = {}; + Result.Min = v2{ + LerpR32(Panel->SplitPercent, Panel->Bounds.Min.x, Panel->Bounds.Max.x), + Panel->Bounds.Min.y + }; + Result.Max = Panel->Bounds.Max; + return Result; } internal rect2 GetLeftPanelBounds(panel* Panel) { - rect2 Result = {}; - Result.Min = Panel->Bounds.Min; - Result.Max = v2{ - LerpR32(Panel->SplitPercent, Panel->Bounds.Min.x, Panel->Bounds.Max.x), - Panel->Bounds.Max.y - }; - return Result; + rect2 Result = {}; + Result.Min = Panel->Bounds.Min; + Result.Max = v2{ + LerpR32(Panel->SplitPercent, Panel->Bounds.Min.x, Panel->Bounds.Max.x), + Panel->Bounds.Max.y + }; + return Result; } internal rect2 GetTopPanelBounds(panel* Panel, rect2 PanelBounds) { - rect2 Result = {}; - Result.Min = v2{ - PanelBounds.Min.x, - LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y) - }; - Result.Max = PanelBounds.Max; - return Result; + rect2 Result = {}; + Result.Min = v2{ + PanelBounds.Min.x, + LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y) + }; + Result.Max = PanelBounds.Max; + return Result; } internal rect2 GetBottomPanelBounds(panel* Panel, rect2 PanelBounds) { - rect2 Result = {}; - Result.Min = PanelBounds.Min; - Result.Max = v2{ - PanelBounds.Max.x, - LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y) - }; - return Result; + rect2 Result = {}; + Result.Min = PanelBounds.Min; + Result.Max = v2{ + PanelBounds.Max.x, + LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y) + }; + return Result; } internal rect2 GetRightPanelBounds(panel* Panel, rect2 PanelBounds) { - rect2 Result = {}; - Result.Min = v2{ - LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x), - PanelBounds.Min.y - }; - Result.Max = PanelBounds.Max; - return Result; + rect2 Result = {}; + Result.Min = v2{ + LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x), + PanelBounds.Min.y + }; + Result.Max = PanelBounds.Max; + return Result; } internal rect2 GetLeftPanelBounds(panel* Panel, rect2 PanelBounds) { - rect2 Result = {}; - Result.Min = PanelBounds.Min; - Result.Max = v2{ - LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x), - PanelBounds.Max.y - }; - return Result; + rect2 Result = {}; + Result.Min = PanelBounds.Min; + Result.Max = v2{ + LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x), + PanelBounds.Max.y + }; + return Result; } internal void Panel_UpdateLayout(panel* Panel, rect2 Bounds) { - Panel->Bounds = Bounds; - - if (Panel->SplitDirection != PanelSplit_NoSplit) + Panel->Bounds = Bounds; + + if (Panel->SplitDirection != PanelSplit_NoSplit) + { + rect2 LeftOrTopBounds = {}; + rect2 RightOrBottomBounds = {}; + switch (Panel->SplitDirection) { - rect2 LeftOrTopBounds = {}; - rect2 RightOrBottomBounds = {}; - switch (Panel->SplitDirection) - { - case PanelSplit_Horizontal: - { - LeftOrTopBounds = GetTopPanelBounds(Panel); - RightOrBottomBounds = GetBottomPanelBounds(Panel); - } break; - - case PanelSplit_Vertical: - { - LeftOrTopBounds = GetLeftPanelBounds(Panel); - RightOrBottomBounds = GetRightPanelBounds(Panel); - } break; - - InvalidDefaultCase; - } - - Panel_UpdateLayout(Panel->Left, LeftOrTopBounds); - Panel_UpdateLayout(Panel->Right, RightOrBottomBounds); + case PanelSplit_Horizontal: + { + LeftOrTopBounds = GetTopPanelBounds(Panel); + RightOrBottomBounds = GetBottomPanelBounds(Panel); + } break; + + case PanelSplit_Vertical: + { + LeftOrTopBounds = GetLeftPanelBounds(Panel); + RightOrBottomBounds = GetRightPanelBounds(Panel); + } break; + + InvalidDefaultCase; } + + Panel_UpdateLayout(Panel->Left, LeftOrTopBounds); + Panel_UpdateLayout(Panel->Right, RightOrBottomBounds); + } } internal void PanelSystem_UpdateLayout(panel_system* System, rect2 WindowBounds) { - panel* Root = System->Panels; - Panel_UpdateLayout(Root, WindowBounds); + panel* Root = System->Panels; + Panel_UpdateLayout(Root, WindowBounds); } internal panel* GetPanelContainingPoint(panel* Panel, v2 Point) { - panel* Result = 0; - - if (PointIsInRect(Panel->Bounds, Point)) + panel* Result = 0; + + if (PointIsInRect(Panel->Bounds, Point)) + { + switch (Panel->SplitDirection) { - switch (Panel->SplitDirection) + case PanelSplit_NoSplit: + { + Result = Panel; + }break; + + case PanelSplit_Vertical: + case PanelSplit_Horizontal: + {asdfasdfasdfasdfasdf + if (PointIsInRect(Panel->Left->Bounds, Point)) { - case PanelSplit_NoSplit: - { - Result = Panel; - }break; - - case PanelSplit_Vertical: - case PanelSplit_Horizontal: - { - if (PointIsInRect(Panel->Left->Bounds, Point)) - { - Result = GetPanelContainingPoint(Panel->Left, Point); - } - else if (PointIsInRect(Panel->Right->Bounds, Point)) - { - Result = GetPanelContainingPoint(Panel->Right, Point); - } - }break; - - InvalidDefaultCase; + Result = GetPanelContainingPoint(Panel->Left, Point); } + else if (PointIsInRect(Panel->Right->Bounds, Point)) + { + Result = GetPanelContainingPoint(Panel->Right, Point); + } + }break; + + InvalidDefaultCase; } - - return Result; + } + + return Result; } internal panel* PanelSystem_GetPanelContainingPoint(panel_system* System, v2 Point) { - panel* Result = 0; - panel* Root = System->Panels; - Result = GetPanelContainingPoint(Root, Point); - return Result; + panel* Result = 0; + panel* Root = System->Panels; + Result = GetPanelContainingPoint(Root, Point); + return Result; } #define FOLDHAUS_PANEL_H diff --git a/src_v2/editor/lumenarium_editor.cpp b/src_v2/editor/lumenarium_editor.cpp index 4157692..1aba45d 100644 --- a/src_v2/editor/lumenarium_editor.cpp +++ b/src_v2/editor/lumenarium_editor.cpp @@ -131,12 +131,72 @@ ed_load_font_cb(Platform_File_Async_Job_Args result, u8* user_data) platform_texture_update(ui->atlas_texture, ui->atlas.pixels, 1024, 1024, 1024); } +internal void +ed_draw_panel(u8* user_data, BSP_Node_Id id, BSP_Node node, BSP_Area area) +{ + App_State* state = (App_State*)user_data; + UI* ui = &state->editor->ui; + scratch_get(scratch); + + UI_Layout title_layout = {}; + ui_layout_set_row_info(ui, &title_layout); + title_layout.bounds_min = v2{ area.min.x, area.max.y - title_layout.row_height }; + title_layout.bounds_max = area.max; + title_layout.at = title_layout.bounds_min; + + UI_Layout panel_layout = {}; + ui_layout_set_row_info(ui, &panel_layout); + panel_layout.bounds_min = area.min; + panel_layout.bounds_max = v2{ area.max.x, title_layout.bounds_max.y }; + panel_layout.at = panel_layout.bounds_min; + + ui_layout_push(ui, &panel_layout); + + String title = {}; + switch (node.user_data) + { + case 0: + { + title = lit_str("None"); + } break; + + case 1: + { + ed_sculpture_visualizer(state); + title = lit_str("Sculpture"); + } break; + + invalid_default_case; + } + ui_layout_pop(ui); + + ui_layout_push(ui, &title_layout); + UI_Widget_Desc bg = {}; + bg.style.flags = UIWidgetStyle_Bg; + bg.style.color_bg = v4{.4f,.4f,.4f,1}; + bg.style.sprite = WHITE_SPRITE_ID; + bg.string = string_f(scratch.a, "%.*s_%u_title_bg", str_varg(title), id.value); + bg.p_min = title_layout.bounds_min; + bg.p_max = title_layout.bounds_max; + UI_Widget_Result r = ui_widget_push(ui, bg); + ui_layout_row_begin(&title_layout, 4); + { + ui_text(ui, title, BLACK_V4); + } + ui_layout_row_end(&title_layout); + ui_widget_pop(ui, r.id); + ui_layout_pop(ui); +} + internal void ed_init(App_State* state) { Editor* editor = allocator_alloc_struct(permanent, Editor); state->editor = editor; - state->editor->ui = ui_create(4096, 4096, state->input_state, permanent); + editor->ui = ui_create(4096, 4096, state->input_state, permanent); + editor->ui.draw_panel_cb = ed_draw_panel; + editor->ui.draw_panel_cb_data = (u8*)state; + //bsp_split(&editor->ui.panels, editor->ui.panels.root, 700, BSPSplit_YAxis, 0, 1); // make the default quad for us to draw with // TODO(PS): this might be unnecessary with the per-frame buffer we use now @@ -144,6 +204,119 @@ ed_init(App_State* state) platform_file_async_read(lit_str("data/font.ttf"), ed_load_font_cb); + ed_sculpture_visualizer_init(state); +} + +internal void +ed_sculpture_updated(App_State* state, r32 scale, r32 led_size) +{ + Editor* ed = state->editor; + if (!ed) return; + + scratch_get(scratch); + + // NOTE(PS): we need to know the total number of leds in order to give them + // texture coordinates + u32 leds_count = 0; + for (u32 a = 0; a < state->assemblies.len; a++) + { + Assembly_Pixel_Buffer pixels = state->assemblies.pixel_buffers[a]; + leds_count += pixels.len; + } + + // round up to a texture whose sides are powers of two + u32 pixels_dim = (u32)floorf(sqrtf((r32)leds_count)); + pixels_dim = round_up_to_pow2(pixels_dim); + u32 pixels_count = pixels_dim * pixels_dim; + r32 texel_dim = 1 / (r32)pixels_dim; + + // NOTE(PS): Rebuild the sculpture geometry to point to the new + // sculpture. + Geo_Vertex_Buffer_Storage storage = ( + GeoVertexBufferStorage_Position | + GeoVertexBufferStorage_TexCoord + ); + u32 verts_cap = leds_count * 4; + u32 indices_cap = leds_count * 6; + Geo_Quad_Buffer_Builder geo = geo_quad_buffer_builder_create(scratch.a, verts_cap, storage, indices_cap); + r32 r = led_size; + + u32 pixels_created = 0; + for (u32 a = 0; a < state->assemblies.len; a++) + { + Assembly_Pixel_Buffer pixels = state->assemblies.pixel_buffers[a]; + for (u32 p = 0; p < pixels.len; p++) + { + v3 c = pixels.positions[p].xyz; + c *= scale; + + u32 pixel_count = pixels_created++; + u32 pixel_x = pixel_count % pixels_dim; + u32 pixel_y = pixel_count / pixels_dim; + r32 texel_x_min = (r32)pixel_x / (r32)pixels_dim; + r32 texel_y_min = (r32)pixel_y / (r32)pixels_dim; + r32 texel_x_max = texel_x_min + texel_dim; + r32 texel_y_max = texel_y_min + texel_dim; + + v2 t0 = v2{texel_x_min, texel_y_min}; + v2 t1 = v2{texel_x_max, texel_y_min}; + v2 t2 = v2{texel_x_max, texel_y_max}; + v2 t3 = v2{texel_x_min, texel_y_max}; + + v3 p0 = c + v3{ -r, -r, 0 }; + v3 p1 = c + v3{ r, -r, 0 }; + v3 p2 = c + v3{ r, r, 0 }; + v3 p3 = c + v3{ -r, r, 0 }; + geo_quad_buffer_builder_push(&geo, p0, p1, p2, p3, t0, t1, t2, t3); + } + } + + if (ed->sculpture_geo.indices_len != 0) + { + invalid_code_path; + // TODO(PS): destroy the old geometry buffer or update it + } + ed->sculpture_geo = platform_geometry_buffer_create( + geo.buffer_vertex.values, + geo.buffer_vertex.len, + geo.buffer_index.values, + geo.buffer_index.len + ); + + platform_vertex_attrib_pointer( + ed->sculpture_geo, ed->sculpture_shd, 3, ed->sculpture_shd.attrs[0], 5, 0 + ); + platform_vertex_attrib_pointer( + ed->sculpture_geo, ed->sculpture_shd, 2, ed->sculpture_shd.attrs[1], 5, 3 + ); + + // TODO(PS): make this have enough pixels for the sculpture + // TODO(PS): map leds to pixels + + if (ed->sculpture_tex.w != 0) + { + invalid_code_path; + // TODO(PS): destroy the old texture + } + + u32* pixels = allocator_alloc_array(scratch.a, u32, pixels_count); + for (u32 y = 0; y < pixels_dim; y++) + { + for (u32 x = 0; x < pixels_dim; x++) + { + r32 rp = (r32)y / (r32)pixels_dim; + r32 bp = (r32)x / (r32)pixels_dim; + u8 rb = (u8)(255 * rp); + u8 bb = (u8)(255 * bp); + u32 c = ( + 0xFF0000FF | + (rb << 8) | + (bb << 16) + ); + pixels[(y * pixels_dim) + x] = c; + } + } + ed->sculpture_tex = platform_texture_create((u8*)pixels, pixels_dim, pixels_dim, pixels_dim); } internal void @@ -152,59 +325,11 @@ ed_frame_prepare(App_State* state) ui_frame_prepare(&state->editor->ui, state->editor->window_dim); } -global r32 p = 0.3f; - internal void ed_frame(App_State* state) { UI* ui = &state->editor->ui; - UI_Layout layout = {}; - layout.bounds_min = v2{ 500, 200 }; - layout.bounds_max = v2{ 700, 500 }; - ui_layout_set_row_info(ui, &layout); - layout.at = layout.bounds_min; - ui_layout_push(ui, &layout); - - ui_text_f(ui, "Hi there! %d", 1000); - show = ui_toggle(ui, lit_str("my toggle"), show); - if (show) - { - ui_layout_row_begin(ui, 2); - { - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("you there")); - } - ui_layout_row_end(ui); - } - ui_button(ui, lit_str("Hi there my good sir")); - - ui_scroll_view_begin(ui, lit_str("scroll area"), v2{800, 200}, v2{1000,500}, 8); - { - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("you there")); - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("you there")); - - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("you there")); - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("you there")); - - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("you there")); - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("you there")); - - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("you there")); - ui_button(ui, lit_str("Sup")); - ui_button(ui, lit_str("I'm lastf;")); - } - ui_scroll_view_end(ui); - - ui_layout_pop(ui); - edr_render_begin(state); ui_draw(&state->editor->ui); edr_render(state); diff --git a/src_v2/editor/lumenarium_editor.h b/src_v2/editor/lumenarium_editor.h index 9cd17c2..b623912 100644 --- a/src_v2/editor/lumenarium_editor.h +++ b/src_v2/editor/lumenarium_editor.h @@ -8,6 +8,16 @@ struct Editor v2 window_dim; Editor_Renderer renderer; UI ui; + + v3 camera_pos; + + Platform_Geometry_Buffer sculpture_geo; + Platform_Shader sculpture_shd; + Platform_Texture sculpture_tex; }; +// NOTE(PS): call this any time sculpture data is updated if +// you want to see the sculpture in the visualizer +internal void ed_sculpture_updated(App_State* state); + #endif //LUMENARIUM_EDITOR_H diff --git a/src_v2/editor/lumenarium_editor_sculpture_visualizer.cpp b/src_v2/editor/lumenarium_editor_sculpture_visualizer.cpp new file mode 100644 index 0000000..8a0de1d --- /dev/null +++ b/src_v2/editor/lumenarium_editor_sculpture_visualizer.cpp @@ -0,0 +1,100 @@ + +static String sculpture_shd_vert_win32 = lit_str( + "#version 330 core\n" + "layout (location = 0) in vec3 a_pos;\n" + "layout (location = 1) in vec2 a_uv;\n" + "out vec2 uv;\n" + "uniform mat4 proj;\n" + "void main(void) {\n" + " gl_Position = proj * vec4(a_pos, 1.0);\n" + " uv = a_uv;\n" + "}" + ); + +static String sculpture_shd_vert_wasm = lit_str( + "precision highp float;\n" + "attribute vec3 a_pos;\n" + "attribute vec2 a_uv;\n" + "varying vec2 uv;\n" + "uniform mat4 proj;\n" + "void main(void) {\n" + " gl_Position = proj * vec4(a_pos, 1.0);\n" + " uv = a_uv;\n" + "}" + ); + +static String sculpture_shd_frag_win32 = lit_str( + "#version 330 core\n" + "in vec2 uv;\n" + "out vec4 FragColor;\n" + "uniform sampler2D texture;\n" + "void main(void) {\n" + " FragColor = texture(texture, uv);\n" + "}" + ); + +static String sculpture_shd_frag_wasm = lit_str( + "precision highp float;\n" + "varying vec2 uv;\n" + "uniform sampler2D texture;\n" + "void main(void) {\n" + " //gl_FragColor = texture2D(texture, uv) * color;\n" + " gl_FragColor = vec4(uv.x,1,uv.y,1);\n" + "}" + ); + +internal void +ed_sculpture_visualizer_init(App_State* state) +{ + Editor* editor = state->editor; + + +#if defined(PLATFORM_win32) + String vert = sculpture_shd_vert_win32; + String frag = sculpture_shd_frag_win32; +#elif defined(PLATFORM_wasm) + String vert = sculpture_shd_vert_wasm; + String frag = sculpture_shd_frag_wasm; +#endif + + String attrs[] = { lit_str("a_pos"), lit_str("a_uv") }; + String uniforms[] = { lit_str("proj") }; + editor->sculpture_shd = platform_shader_create( + vert, frag, attrs, 2, uniforms, 1 + ); +} + +r32 cam_theta = 0; + +internal void +ed_sculpture_visualizer(App_State* state) +{ + Editor* ed = state->editor; + + // Set the viewport to the current layout's region so that the sculpture + // never overlaps any other ui elements + UI_Layout l = *ed->ui.layout; + v2 view_dim = l.bounds_max - l.bounds_min; + glViewport( + (s32)l.bounds_min.x, + (s32)l.bounds_min.y, + (s32)view_dim.x, + (s32)view_dim.y + ); + + // TODO(PS): TEMPORARY CAMERA CODE + cam_theta += 0.05f; + v3 camera_pos = v3{sinf(cam_theta) * 50, 0, cosf(cam_theta) * 50}; + r32 aspect = view_dim.x / view_dim.y; + m44 proj = HMM_Perspective(45.0, aspect, 0.01f, 500); + m44 view = HMM_LookAt(camera_pos, v3{0,0,0}, v3{0,1,0}); + + platform_shader_bind(ed->sculpture_shd); + platform_set_uniform(ed->sculpture_shd, 0, proj * view); + platform_texture_bind(ed->sculpture_tex); + platform_geometry_bind(ed->sculpture_geo); + platform_geometry_draw(ed->sculpture_geo, ed->sculpture_geo.indices_len); + + // reset the viewport for all other rendering + glViewport(0, 0, (s32)ed->window_dim.x, (s32)ed->window_dim.y); +} \ No newline at end of file diff --git a/src_v2/editor/lumenarium_editor_ui.cpp b/src_v2/editor/lumenarium_editor_ui.cpp index 08985c1..44bccc5 100644 --- a/src_v2/editor/lumenarium_editor_ui.cpp +++ b/src_v2/editor/lumenarium_editor_ui.cpp @@ -2,19 +2,34 @@ static String ui_shader_vert_win32 = lit_str( "#version 330 core\n" - "layout (location = 0) in vec4 a_pos;\n" + "layout (location = 0) in vec3 a_pos;\n" "layout (location = 1) in vec2 a_uv;\n" "layout (location = 2) in vec4 a_color;\n" "out vec2 uv;\n" "out vec4 color;\n" "uniform mat4 proj;\n" "void main(void) {\n" - " gl_Position = proj * a_pos;\n" + " gl_Position = proj * vec4(a_pos, 1.0);\n" " uv = a_uv;\n" " color = a_color;\n" "}" ); +static String ui_shader_vert_wasm = lit_str( + "precision highp float;\n" + "attribute vec3 a_pos;\n" + "attribute vec2 a_uv;\n" + "attribute vec4 a_color;\n" + "varying vec2 uv;\n" + "varying vec4 color;\n" + "uniform mat4 proj;\n" + "void main(void) {\n" + " gl_Position = proj * vec4(a_pos, 1.0);\n" + " uv = a_uv;\n" + " color = a_color;\n" + "}" + ); + static String ui_shader_frag_win32 = lit_str( "#version 330 core\n" "in vec2 uv;\n" @@ -26,10 +41,21 @@ static String ui_shader_frag_win32 = lit_str( "}" ); +static String ui_shader_frag_wasm = lit_str( + "precision highp float;\n" + "varying vec2 uv;\n" + "varying vec4 color;\n" + "uniform sampler2D texture;\n" + "void main(void) {\n" + " gl_FragColor = texture2D(texture, uv) * color;\n" + "}" + ); + internal UI ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a) { UI result = {}; + zero_struct(result); result.input = input; // Widgets @@ -39,36 +65,46 @@ ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a) result.widgets.states = allocator_alloc_array(a, UI_Widget_State, result.widgets.states_cap); result.widgets.states_hash = allocator_alloc_array(a, u32, result.widgets.states_cap); - // Per Frame Vertex Buffer - result.verts_cap = verts_cap; - result.verts = allocator_alloc_array(a, UI_Vertex, verts_cap); - result.indices_cap = verts_cap * 2; - result.indices = allocator_alloc_array(a, u32, result.indices_cap); + result.panels = bsp_create(a, 32); + result.panels.root = bsp_push(&result.panels, {0}, {v2{},v2{1400, 800}}, 1); + + // Per Frame Vertex Buffer + Geo_Vertex_Buffer_Storage storage = ( + GeoVertexBufferStorage_Position | + GeoVertexBufferStorage_TexCoord | + GeoVertexBufferStorage_Color + ); + result.geo = geo_quad_buffer_builder_create(a, verts_cap, storage, verts_cap * 2); result.per_frame_buffer = platform_geometry_buffer_create( - (r32*)result.verts, - result.verts_cap, - result.indices, - result.indices_cap + result.geo.buffer_vertex.values, + result.geo.buffer_vertex.cap, + result.geo.buffer_index.values, + result.geo.buffer_index.cap ); +#if defined(PLATFORM_win32) + String vert = ui_shader_vert_win32; + String frag = ui_shader_frag_win32; +#elif defined(PLATFORM_wasm) + String vert = ui_shader_vert_wasm; + String frag = ui_shader_frag_wasm; +#endif + String attrs[] = { lit_str("a_pos"), lit_str("a_uv"), lit_str("a_color") }; String uniforms[] = { lit_str("proj") }; result.shader = platform_shader_create( - ui_shader_vert_win32, - ui_shader_frag_win32, - attrs, 3, - uniforms, 1 + vert, frag, attrs, 3, uniforms, 1 ); platform_vertex_attrib_pointer( - result.per_frame_buffer, result.shader, 4, result.shader.attrs[0], 10, 0 + result.per_frame_buffer, result.shader, 3, result.shader.attrs[0], 9, 0 ); platform_vertex_attrib_pointer( - result.per_frame_buffer, result.shader, 2, result.shader.attrs[1], 10, 4 + result.per_frame_buffer, result.shader, 2, result.shader.attrs[1], 9, 3 ); platform_vertex_attrib_pointer( - result.per_frame_buffer, result.shader, 4, result.shader.attrs[2], 10, 6 + result.per_frame_buffer, result.shader, 4, result.shader.attrs[2], 9, 5 ); // Texture Atlas @@ -87,41 +123,18 @@ ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a) return result; } -internal u32 -ui_vert_push(UI* ui, v3 p, v2 t, v4 c) -{ - assert(ui->verts_len < ui->verts_cap); - u32 index = ui->verts_len++; - - ui->verts[index].pos = {p.x, p.y, p.z, 1}; - ui->verts[index].uv = t; - ui->verts[index].color = c; - - return index; -} - -internal void -ui_index_push(UI* ui, u32 i) -{ - assert(ui->indices_len < ui->indices_cap); - ui->indices[ui->indices_len++] = i; -} - internal void ui_quad_push(UI* ui, v3 pmin, v3 pmax, v2 tmin, v2 tmax, v4 c) { - u32 bl = ui_vert_push(ui, pmin, tmin, c); - u32 br = ui_vert_push(ui, v3{pmax.x, pmin.y, pmin.z}, v2{tmax.x,tmin.y}, c); - u32 tr = ui_vert_push(ui, pmax, tmax, c); - u32 tl = ui_vert_push(ui, v3{pmin.x, pmax.y, pmin.z}, v2{tmin.x,tmax.y}, c); - - ui_index_push(ui, bl); - ui_index_push(ui, br); - ui_index_push(ui, tr); - - ui_index_push(ui, bl); - ui_index_push(ui, tr); - ui_index_push(ui, tl); + v3 p0 = pmin; + v3 p1 = v3{pmax.x, pmin.y, pmin.z}; + v3 p2 = pmax; + v3 p3 = v3{pmin.x, pmax.y, pmin.z}; + v2 t0 = tmin; + v2 t1 = v2{tmax.x,tmin.y}; + v2 t2 = tmax; + v2 t3 = v2{tmin.x,tmax.y}; + geo_quad_buffer_builder_push(&ui->geo, p0, p1, p2, p3, t0, t1, t2, t3, c); } internal void @@ -159,6 +172,7 @@ internal UI_Char_Draw_Cmd ui_sprite_char_get_draw_cmd(UI* ui, v3 at, u32 codepoint) { UI_Char_Draw_Cmd result = {}; + zero_struct(result); Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(&ui->atlas, codepoint); result.uv = texture_atlas_sprite_get_uvs(&ui->atlas, sprite); @@ -181,14 +195,23 @@ ui_sprite_char_get_draw_cmd(UI* ui, v3 at, u32 codepoint) internal void ui_frame_prepare(UI* ui, v2 window_dim) { - ui->verts_len = 0; - ui->indices_len = 0; + ui->geo.buffer_vertex.len = 0; + ui->geo.buffer_index.len = 0; ui->widgets.free_len = 0; ui->widgets.active_parent = 0; ui->widgets.root = ui_widget_pool_push(&ui->widgets, lit_str("root")); ui->widgets.active_parent = ui->widgets.root; + BSP_Node* panel_root = bsp_get(&ui->panels, ui->panels.root); + if (window_dim.x != 0 && window_dim.y != 0 && window_dim != panel_root->area.max) + { + BSP_Area area = {}; + area.min = v2{0,0}; + area.max = window_dim; + bsp_node_area_update(&ui->panels, ui->panels.root, area); + } + v2 half_d = window_dim * 0.5f; ui->proj = HMM_Orthographic(0, window_dim.x, window_dim.y, 0, 0.01f, 100); @@ -206,9 +229,42 @@ ui_frame_prepare(UI* ui, v2 window_dim) global bool show = false; +internal void +ui_draw_panel(BSP* tree, BSP_Node_Id id, BSP_Node* node, u8* user_data) +{ + if (node->split.kind != BSPSplit_None) return; + UI* ui = (UI*)user_data; + BSP_Area area = node->area; + + if (ui->draw_panel_cb) ui->draw_panel_cb(ui->draw_panel_cb_data, id, *node, area); + + r32 z = -1; + v3 l0p0 = v3{ area.min.x, area.min.y, z }; // left side + v3 l0p1 = v3{ area.min.x + 1, area.max.y, z }; + v3 l1p0 = v3{ area.max.x - 1, area.min.y, z }; // right side + v3 l1p1 = v3{ area.max.x, area.max.y, z }; + v3 l2p0 = v3{ area.min.x, area.min.y , z }; // bottom side + v3 l2p1 = v3{ area.max.x, area.min.y + 1, z }; + v3 l3p0 = v3{ area.min.x, area.max.y , z }; // top side + v3 l3p1 = v3{ area.max.x, area.max.y + 1, z }; + u32 sid = WHITE_SPRITE_ID; + v4 c = WHITE_V4; + if (rect2_contains(area.min, area.max, ui->input->frame_hot->mouse_pos)) + { + c = PINK_V4; + } + + ui_sprite_push(ui, l0p0, l0p1, sid, c); + ui_sprite_push(ui, l1p0, l1p1, sid, c); + ui_sprite_push(ui, l2p0, l2p1, sid, c); + ui_sprite_push(ui, l3p0, l3p1, sid, c); +} + internal void ui_draw(UI* ui) { + bsp_walk_inorder(&ui->panels, ui->panels.root, ui_draw_panel, (u8*)ui); + u32 widget_count = ui->widgets.free_len; r32 range_min = -10; r32 range_max = -1; @@ -217,20 +273,18 @@ ui_draw(UI* ui) platform_geometry_buffer_update( &ui->per_frame_buffer, - (r32*)ui->verts, + (r32*)ui->geo.buffer_vertex.values, 0, - ui->verts_len * 10, - ui->indices, + ui->geo.buffer_vertex.len * ui->geo.buffer_vertex.stride, + ui->geo.buffer_index.values, 0, - ui->indices_len + ui->geo.buffer_index.len ); platform_shader_bind(ui->shader); platform_set_uniform(ui->shader, 0, ui->proj); platform_texture_bind(ui->atlas_texture); platform_geometry_bind(ui->per_frame_buffer); - platform_geometry_draw(ui->per_frame_buffer, ui->indices_len); - - OutputDebugStringA("Frame\n\n"); + platform_geometry_draw(ui->per_frame_buffer, ui->geo.buffer_index.len); } //////////////////////////////////////////// @@ -241,6 +295,7 @@ ui_widget_id_create(String string, u32 index) { assert(string.len != 0 && string.str != 0); UI_Widget_Id result = {}; + zero_struct(result); result.value = hash_djb2_to_u32(string); result.index = index; return result; @@ -272,7 +327,11 @@ ui_widget_pool_push(UI_Widget_Pool* pool, String string) result->child_first = 0; result->child_last = 0; - u32 index = hash_table_register(pool->states_hash, pool->states_cap, result->id.value); + u32 index = hash_table_find(pool->states_hash, pool->states_cap, result->id.value); + if (index == pool->states_cap) + { + index = hash_table_register(pool->states_hash, pool->states_cap, result->id.value); + } assert(index != pool->states_cap); UI_Widget_State* state = pool->states + index; zero_struct(*state); @@ -286,6 +345,7 @@ ui_widget_pool_push(UI_Widget_Pool* pool, String string) result ); } + pool->active_parent = result; return result; } @@ -337,6 +397,7 @@ internal UI_Widget_Result ui_widget_push(UI* ui, UI_Widget_Desc desc) { UI_Widget_Result result = {}; + zero_struct(result); v2 dim = desc.p_max - desc.p_min; if (dim.x == 0 || dim.y == 0) return result; @@ -427,9 +488,10 @@ ui_widget_push(UI* ui, UI_Widget_Desc desc) } internal void -ui_widget_pop(UI* ui, UI_Widget* widget) +ui_widget_pop(UI* ui, UI_Widget_Id widget_id) { - assert(ui_widget_id_equals(widget->id, ui->widgets.active_parent->id)); + if (!ui_widget_id_is_valid(widget_id)) return; + assert(ui_widget_id_equals(widget_id, ui->widgets.active_parent->id)); ui_widget_pool_pop(&ui->widgets); } @@ -475,7 +537,9 @@ ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_s if (has_flag(child->desc.style.flags, (UIWidgetStyle_FillH | UIWidgetStyle_FillV))) { v3 fill_min = {}; + zero_struct(fill_min); v3 fill_max = {}; + zero_struct(fill_max); if (has_flag(child->desc.style.flags, UIWidgetStyle_FillH)) { r32 fill_x = HMM_Lerp(bg_min.x, child->desc.fill_pct.x, bg_max.x); @@ -523,6 +587,7 @@ ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_s { u8 at = child->desc.string.str[i]; UI_Char_Draw_Cmd cmd = {}; + zero_struct(cmd); if (!char_is_space(at)) { cmd = ui_sprite_char_get_draw_cmd(ui, baseline, (u32)at); @@ -616,8 +681,9 @@ internal UI_Layout_Bounds ui_layout_get_next(UI_Layout* layout) { UI_Layout_Bounds result = {}; + zero_struct(result); if (layout->at.x >= layout->bounds_max.x || layout->at.y >= layout->bounds_max.y || - layout->at.y + layout->row_height >= layout->bounds_max.y) + layout->at.y + layout->row_height > layout->bounds_max.y) { return result; } @@ -653,8 +719,8 @@ ui_layout_get_next(UI_Layout* layout) if (result.min.x < layout->bounds_min.x || result.min.y < layout->bounds_min.y || result.max.x < layout->bounds_min.x || result.max.y < layout->bounds_min.y) { - result.min = {}; - result.max = {}; + zero_struct(result.min); + zero_struct(result.max); } return result; @@ -716,26 +782,40 @@ ui_layout_next_widget(UI* ui, UI_Widget_Kind kind) { UI_Layout_Bounds b = ui_layout_get_next(ui); UI_Widget_Desc d = {}; + zero_struct(d); d.p_min = b.min; d.p_max = b.max; d.style = ui_get_style(ui, kind); return d; } +internal void +ui_text(UI* ui, String string, v4 color) +{ + UI_Widget_Desc d = ui_layout_next_widget(ui, UIWidget_Text); + d.string = string; + d.style.color_fg = color; + UI_Widget_Result r = ui_widget_push(ui, d); + ui_widget_pop(ui, r.id); +} + internal void ui_text(UI* ui, String string) { UI_Widget_Desc d = ui_layout_next_widget(ui, UIWidget_Text); d.string = string; - ui_widget_push(ui, d); + UI_Widget_Result r = ui_widget_push(ui, d); + ui_widget_pop(ui, r.id); } internal void ui_text_f(UI* ui, char* fmt, ...) { + scratch_get(scratch); + va_list args; va_start(args, fmt); - String string = string_fv(scratch, fmt, args); + String string = string_fv(scratch.a, fmt, args); va_end(args); return ui_text(ui, string); @@ -747,6 +827,7 @@ ui_button(UI* ui, String string) UI_Widget_Desc d = ui_layout_next_widget(ui, UIWidget_Button); d.string = string; UI_Widget_Result r = ui_widget_push(ui, d); + ui_widget_pop(ui, r.id); return has_flag(r.flags, UIWidgetResult_MouseLeft_WentUp); } @@ -761,6 +842,7 @@ ui_toggle(UI* ui, String string, bool value) } d.string = string; UI_Widget_Result r = ui_widget_push(ui, d); + ui_widget_pop(ui, r.id); bool result = value; if (has_flag(r.flags, UIWidgetResult_MouseLeft_WentUp)) result = !result; return result; @@ -769,6 +851,8 @@ ui_toggle(UI* ui, String string, bool value) internal UI_Layout* ui_scroll_view_begin(UI* ui, String string, v2 bounds_min, v2 bounds_max, u32 rows) { + scratch_get(scratch); + r32 scroll_bar_dim = 15; v2 scroll_bars_area = v2{0, 0}; v2 scroll_area_min = bounds_min; @@ -776,6 +860,7 @@ ui_scroll_view_begin(UI* ui, String string, v2 bounds_min, v2 bounds_max, u32 ro v2 scroll_area_dim = scroll_area_max - scroll_area_min; v2 scroll_offset = {}; + zero_struct(scroll_offset); r32 rows_avail = floorf(scroll_area_dim.y / ui->layout->row_height); if (rows > rows_avail) { @@ -785,11 +870,13 @@ ui_scroll_view_begin(UI* ui, String string, v2 bounds_min, v2 bounds_max, u32 ro scroll_area_dim = scroll_area_max - scroll_area_min; UI_Widget_Desc vscroll_d = {}; + zero_struct(vscroll_d); vscroll_d.p_min = { bounds_max.x - scroll_bar_dim, bounds_min.y }; vscroll_d.p_max = { bounds_max.x, bounds_max.y }; vscroll_d.style = ui_get_style(ui, UIWidget_VScroll); - vscroll_d.string = string_f(scratch, "%.*s_vscroll", str_varg(string)); + vscroll_d.string = string_f(scratch.a, "%.*s_vscroll", str_varg(string)); UI_Widget_Result r = ui_widget_push(ui, vscroll_d); + ui_widget_pop(ui, r.id); UI_Widget_State* vscroll_state = ui_widget_state_get(ui, r.id); scroll_offset.y = vscroll_state->scroll.y; @@ -800,7 +887,7 @@ ui_scroll_view_begin(UI* ui, String string, v2 bounds_min, v2 bounds_max, u32 ro scroll_offset *= v2{ 0, y_scroll_dist }; - UI_Layout* layout = allocator_alloc_struct(scratch, UI_Layout); + UI_Layout* layout = allocator_alloc_struct(scratch.a, UI_Layout); layout->mode = UILayout_Columns; layout->bounds_min = scroll_area_min; layout->bounds_max = scroll_area_max; diff --git a/src_v2/editor/lumenarium_editor_ui.h b/src_v2/editor/lumenarium_editor_ui.h index 4e53539..6a9b4e7 100644 --- a/src_v2/editor/lumenarium_editor_ui.h +++ b/src_v2/editor/lumenarium_editor_ui.h @@ -165,15 +165,11 @@ struct UI_Layout_Bounds v2 max; }; +typedef void UI_Draw_Panel_Cb(u8* user_data, BSP_Node_Id id, BSP_Node node, BSP_Area area); + struct UI { - UI_Vertex* verts; - u32 verts_len; - u32 verts_cap; - - u32* indices; - u32 indices_len; - u32 indices_cap; + Geo_Quad_Buffer_Builder geo; Texture_Atlas atlas; r32 font_ascent, font_descent, font_line_gap, font_space_width; @@ -186,6 +182,10 @@ struct UI UI_Layout* layout; + BSP panels; + UI_Draw_Panel_Cb* draw_panel_cb; + u8* draw_panel_cb_data; + // frames since these values were set u16 widget_next_hot_frames; u16 widget_hot_frames; @@ -206,7 +206,7 @@ internal void ui_sprite_register(UI* ui, u8* pixels, u32 w, u32 h, u32 id); internal void ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id, v4 color); internal void ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id); internal v3 ui_sprite_char_push(UI* ui, v2 at, u32 codepoint, v4 color); -internal void ui_draw(UI* ui); +internal void ui_draw(App_State* state); // Widgets @@ -223,7 +223,7 @@ internal UI_Widget* ui_widget_pool_push(UI_Widget_Pool* pool, String string); internal void ui_widget_pool_pop(UI_Widget_Pool* pool); internal UI_Widget_Result ui_widget_push(UI* ui, UI_Widget_Desc desc); -internal void ui_widget_pop(UI* ui, UI_Widget* widget); +internal void ui_widget_pop(UI* ui, UI_Widget_Id widget_id); internal r32 ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_step); diff --git a/src_v2/engine/lumenarium_engine.cpp b/src_v2/engine/lumenarium_engine.cpp index 2f9bb08..93f3e1e 100644 --- a/src_v2/engine/lumenarium_engine.cpp +++ b/src_v2/engine/lumenarium_engine.cpp @@ -18,7 +18,7 @@ en_frame_prepare(App_State* state) internal void en_frame(App_State* state) { - +#if 0 /////////////////////////////////////// // Output Data @@ -65,6 +65,7 @@ en_frame(App_State* state) invalid_code_path; } } +#endif } internal void diff --git a/src_v2/engine/lumenarium_engine_assembly.cpp b/src_v2/engine/lumenarium_engine_assembly.cpp index 1e58678..156b3fc 100644 --- a/src_v2/engine/lumenarium_engine_assembly.cpp +++ b/src_v2/engine/lumenarium_engine_assembly.cpp @@ -100,3 +100,22 @@ assembly_add_led( assert(strip->pixels_len < strip->pixels_cap); strip->pixels[strip->pixels_len++] = pixel_index; } + +void +assembly_strip_create_leds( + Assembly_Array* a, + Assembly_Handle h, + Assembly_Strip* strip, + v3 start, v3 end, + u32 led_count + ){ + v3 delta_total = end - start; + v3 delta_step = delta_total / (r32)led_count; + + for (u32 i = 0; i < led_count; i++) + { + v4 pos = {0,0,0,1}; + pos.XYZ = start + ((r32)i * delta_step); + assembly_add_led(a, h, strip, pos); + } +} \ No newline at end of file diff --git a/src_v2/libs/stb_sprintf.h b/src_v2/libs/stb_sprintf.h new file mode 100644 index 0000000..64bce7e --- /dev/null +++ b/src_v2/libs/stb_sprintf.h @@ -0,0 +1,1906 @@ +// stb_sprintf - v1.10 - public domain snprintf() implementation +// originally by Jeff Roberts / RAD Game Tools, 2015/10/20 +// http://github.com/nothings/stb +// +// allowed types: sc uidBboXx p AaGgEef n +// lengths : hh h ll j z t I64 I32 I +// +// Contributors: +// Fabian "ryg" Giesen (reformatting) +// github:aganm (attribute format) +// +// Contributors (bugfixes): +// github:d26435 +// github:trex78 +// github:account-login +// Jari Komppa (SI suffixes) +// Rohit Nirmal +// Marcin Wojdyr +// Leonard Ritter +// Stefano Zanotti +// Adam Allison +// Arvid Gerstmann +// Markus Kolb +// +// LICENSE: +// +// See end of file for license information. + +#ifndef STB_SPRINTF_H_INCLUDE +#define STB_SPRINTF_H_INCLUDE + +/* +Single file sprintf replacement. + +Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. +Hereby placed in public domain. + +This is a full sprintf replacement that supports everything that +the C runtime sprintfs support, including float/double, 64-bit integers, +hex floats, field parameters (%*.*d stuff), length reads backs, etc. + +Why would you need this if sprintf already exists? Well, first off, +it's *much* faster (see below). It's also much smaller than the CRT +versions code-space-wise. We've also added some simple improvements +that are super handy (commas in thousands, callbacks at buffer full, +for example). Finally, the format strings for MSVC and GCC differ +for 64-bit integers (among other small things), so this lets you use +the same format strings in cross platform code. + +It uses the standard single file trick of being both the header file +and the source itself. If you just include it normally, you just get +the header file function definitions. To get the code, you include +it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. + +It only uses va_args macros from the C runtime to do it's work. It +does cast doubles to S64s and shifts and divides U64s, which does +drag in CRT code on most platforms. + +It compiles to roughly 8K with float support, and 4K without. +As a comparison, when using MSVC static libs, calling sprintf drags +in 16K. + +API: +==== +int stbsp_sprintf( char * buf, char const * fmt, ... ) +int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) + Convert an arg list into a buffer. stbsp_snprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) +int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) + Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va ) + typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len ); + Convert into a buffer, calling back every STB_SPRINTF_MIN chars. + Your callback can then copy the chars out, print them or whatever. + This function is actually the workhorse for everything else. + The buffer you pass in must hold at least STB_SPRINTF_MIN characters. + // you return the next buffer to use or 0 to stop converting + +void stbsp_set_separators( char comma, char period ) + Set the comma and period characters to use. + +FLOATS/DOUBLES: +=============== +This code uses a internal float->ascii conversion method that uses +doubles with error correction (double-doubles, for ~105 bits of +precision). This conversion is round-trip perfect - that is, an atof +of the values output here will give you the bit-exact double back. + +One difference is that our insignificant digits will be different than +with MSVC or GCC (but they don't match each other either). We also +don't attempt to find the minimum length matching float (pre-MSVC15 +doesn't either). + +If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT +and you'll save 4K of code space. + +64-BIT INTS: +============ +This library also supports 64-bit integers and you can use MSVC style or +GCC style indicators (%I64d or %lld). It supports the C99 specifiers +for size_t and ptr_diff_t (%jd %zd) as well. + +EXTRAS: +======= +Like some GCCs, for integers and floats, you can use a ' (single quote) +specifier and commas will be inserted on the thousands: "%'d" on 12345 +would print 12,345. + +For integers and floats, you can use a "$" specifier and the number +will be converted to float and then divided to get kilo, mega, giga or +tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is +"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn +2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three +$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the +suffix, add "_" specifier: "%_$d" -> "2.53M". + +In addition to octal and hexadecimal conversions, you can print +integers in binary: "%b" for 256 would print 100. + +PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): +=================================================================== +"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) +"%24d" across all 32-bit ints (4.5x/4.2x faster) +"%x" across all 32-bit ints (4.5x/3.8x faster) +"%08x" across all 32-bit ints (4.3x/3.8x faster) +"%f" across e-10 to e+10 floats (7.3x/6.0x faster) +"%e" across e-10 to e+10 floats (8.1x/6.0x faster) +"%g" across e-10 to e+10 floats (10.0x/7.1x faster) +"%f" for values near e-300 (7.9x/6.5x faster) +"%f" for values near e+300 (10.0x/9.1x faster) +"%e" for values near e-300 (10.1x/7.0x faster) +"%e" for values near e+300 (9.2x/6.0x faster) +"%.320f" for values near e-300 (12.6x/11.2x faster) +"%a" for random values (8.6x/4.3x faster) +"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) +"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) +"%s%s%s" for 64 char strings (7.1x/7.3x faster) +"...512 char string..." ( 35.0x/32.5x faster!) +*/ + +#if defined(__clang__) +#if defined(__has_feature) && defined(__has_attribute) +#if __has_feature(address_sanitizer) +#if __has_attribute(__no_sanitize__) +#define STBSP__ASAN __attribute__((__no_sanitize__("address"))) +#elif __has_attribute(__no_sanitize_address__) +#define STBSP__ASAN __attribute__((__no_sanitize_address__)) +#elif __has_attribute(__no_address_safety_analysis__) +#define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) +#endif +#endif +#endif +#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) +#if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ +#define STBSP__ASAN __attribute__((__no_sanitize_address__)) +#endif +#endif + +#ifndef STBSP__ASAN +#define STBSP__ASAN +#endif + +#ifdef STB_SPRINTF_STATIC +#define STBSP__PUBLICDEC static +#define STBSP__PUBLICDEF static STBSP__ASAN +#else +#ifdef __cplusplus +#define STBSP__PUBLICDEC extern "C" +#define STBSP__PUBLICDEF extern "C" STBSP__ASAN +#else +#define STBSP__PUBLICDEC extern +#define STBSP__PUBLICDEF STBSP__ASAN +#endif +#endif + +#if defined(__has_attribute) +#if __has_attribute(format) +#define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va))) +#endif +#endif + +#ifndef STBSP__ATTRIBUTE_FORMAT +#define STBSP__ATTRIBUTE_FORMAT(fmt,va) +#endif + +#ifdef _MSC_VER +#define STBSP__NOTUSED(v) (void)(v) +#else +#define STBSP__NOTUSED(v) (void)sizeof(v) +#endif + +#include // for va_arg(), va_list() +#include // size_t, ptrdiff_t + +#ifndef STB_SPRINTF_MIN +#define STB_SPRINTF_MIN 512 // how many characters per callback +#endif +typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); + +#ifndef STB_SPRINTF_DECORATE +#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names +#endif + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4); + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); + +#endif // STB_SPRINTF_H_INCLUDE + +#ifdef STB_SPRINTF_IMPLEMENTATION + +#define stbsp__uint32 unsigned int +#define stbsp__int32 signed int + +#ifdef _MSC_VER +#define stbsp__uint64 unsigned __int64 +#define stbsp__int64 signed __int64 +#else +#define stbsp__uint64 unsigned long long +#define stbsp__int64 signed long long +#endif +#define stbsp__uint16 unsigned short + +#ifndef stbsp__uintptr +#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) +#define stbsp__uintptr stbsp__uint64 +#else +#define stbsp__uintptr stbsp__uint32 +#endif +#endif + +#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define STB_SPRINTF_MSVC_MODE +#endif +#endif + +#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses +#define STBSP__UNALIGNED(code) +#else +#define STBSP__UNALIGNED(code) code +#endif + +#ifndef STB_SPRINTF_NOFLOAT +// internal float utility functions +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits); +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); +#define STBSP__SPECIAL 0x7000 +#endif + +static char stbsp__period = '.'; +static char stbsp__comma = ','; +static struct +{ + short temp; // force next field to be 2-byte aligned + char pair[201]; +} stbsp__digitpair = +{ + 0, + "00010203040506070809101112131415161718192021222324" + "25262728293031323334353637383940414243444546474849" + "50515253545556575859606162636465666768697071727374" + "75767778798081828384858687888990919293949596979899" +}; + +STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) +{ + stbsp__period = pperiod; + stbsp__comma = pcomma; +} + +#define STBSP__LEFTJUST 1 +#define STBSP__LEADINGPLUS 2 +#define STBSP__LEADINGSPACE 4 +#define STBSP__LEADING_0X 8 +#define STBSP__LEADINGZERO 16 +#define STBSP__INTMAX 32 +#define STBSP__TRIPLET_COMMA 64 +#define STBSP__NEGATIVE 128 +#define STBSP__METRIC_SUFFIX 256 +#define STBSP__HALFWIDTH 512 +#define STBSP__METRIC_NOSPACE 1024 +#define STBSP__METRIC_1024 2048 +#define STBSP__METRIC_JEDEC 4096 + +static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) +{ + sign[0] = 0; + if (fl & STBSP__NEGATIVE) { + sign[0] = 1; + sign[1] = '-'; + } else if (fl & STBSP__LEADINGSPACE) { + sign[0] = 1; + sign[1] = ' '; + } else if (fl & STBSP__LEADINGPLUS) { + sign[0] = 1; + sign[1] = '+'; + } +} + +static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) +{ + char const * sn = s; + + // get up to 4-byte alignment + for (;;) { + if (((stbsp__uintptr)sn & 3) == 0) + break; + + if (!limit || *sn == 0) + return (stbsp__uint32)(sn - s); + + ++sn; + --limit; + } + + // scan over 4 bytes at a time to find terminating 0 + // this will intentionally scan up to 3 bytes past the end of buffers, + // but becase it works 4B aligned, it will never cross page boundaries + // (hence the STBSP__ASAN markup; the over-read here is intentional + // and harmless) + while (limit >= 4) { + stbsp__uint32 v = *(stbsp__uint32 *)sn; + // bit hack to find if there's a 0 byte in there + if ((v - 0x01010101) & (~v) & 0x80808080UL) + break; + + sn += 4; + limit -= 4; + } + + // handle the last few characters to find actual size + while (limit && *sn) { + ++sn; + --limit; + } + + return (stbsp__uint32)(sn - s); +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) +{ + static char hex[] = "0123456789abcdefxp"; + static char hexu[] = "0123456789ABCDEFXP"; + char *bf; + char const *f; + int tlen = 0; + + bf = buf; + f = fmt; + for (;;) { + stbsp__int32 fw, pr, tz; + stbsp__uint32 fl; + + // macros for the callback buffer stuff +#define stbsp__chk_cb_bufL(bytes) \ +{ \ +int len = (int)(bf - buf); \ +if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ +tlen += len; \ +if (0 == (bf = buf = callback(buf, user, len))) \ +goto done; \ +} \ +} +#define stbsp__chk_cb_buf(bytes) \ +{ \ +if (callback) { \ +stbsp__chk_cb_bufL(bytes); \ +} \ +} +#define stbsp__flush_cb() \ +{ \ +stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ +} // flush if there is even one byte in the buffer +#define stbsp__cb_buf_clamp(cl, v) \ +cl = v; \ +if (callback) { \ +int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ +if (cl > lg) \ +cl = lg; \ +} + + // fast copy everything up to the next % (or end of string) + for (;;) { + while (((stbsp__uintptr)f) & 3) { + schk1: + if (f[0] == '%') + goto scandd; + schk2: + if (f[0] == 0) + goto endfmt; + stbsp__chk_cb_buf(1); + *bf++ = f[0]; + ++f; + } + for (;;) { + // Check if the next 4 bytes contain %(0x25) or end of string. + // Using the 'hasless' trick: + // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord + stbsp__uint32 v, c; + v = *(stbsp__uint32 *)f; + c = (~v) & 0x80808080; + if (((v ^ 0x25252525) - 0x01010101) & c) + goto schk1; + if ((v - 0x01010101) & c) + goto schk2; + if (callback) + if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) + goto schk1; +#ifdef STB_SPRINTF_NOUNALIGNED + if(((stbsp__uintptr)bf) & 3) { + bf[0] = f[0]; + bf[1] = f[1]; + bf[2] = f[2]; + bf[3] = f[3]; + } else +#endif + { + *(stbsp__uint32 *)bf = v; + } + bf += 4; + f += 4; + } + } + scandd: + + ++f; + + // ok, we have a percent, read the modifiers first + fw = 0; + pr = -1; + fl = 0; + tz = 0; + + // flags + for (;;) { + switch (f[0]) { + // if we have left justify + case '-': + fl |= STBSP__LEFTJUST; + ++f; + continue; + // if we have leading plus + case '+': + fl |= STBSP__LEADINGPLUS; + ++f; + continue; + // if we have leading space + case ' ': + fl |= STBSP__LEADINGSPACE; + ++f; + continue; + // if we have leading 0x + case '#': + fl |= STBSP__LEADING_0X; + ++f; + continue; + // if we have thousand commas + case '\'': + fl |= STBSP__TRIPLET_COMMA; + ++f; + continue; + // if we have kilo marker (none->kilo->kibi->jedec) + case '$': + if (fl & STBSP__METRIC_SUFFIX) { + if (fl & STBSP__METRIC_1024) { + fl |= STBSP__METRIC_JEDEC; + } else { + fl |= STBSP__METRIC_1024; + } + } else { + fl |= STBSP__METRIC_SUFFIX; + } + ++f; + continue; + // if we don't want space between metric suffix and number + case '_': + fl |= STBSP__METRIC_NOSPACE; + ++f; + continue; + // if we have leading zero + case '0': + fl |= STBSP__LEADINGZERO; + ++f; + goto flags_done; + default: goto flags_done; + } + } + flags_done: + + // get the field width + if (f[0] == '*') { + fw = va_arg(va, stbsp__uint32); + ++f; + } else { + while ((f[0] >= '0') && (f[0] <= '9')) { + fw = fw * 10 + f[0] - '0'; + f++; + } + } + // get the precision + if (f[0] == '.') { + ++f; + if (f[0] == '*') { + pr = va_arg(va, stbsp__uint32); + ++f; + } else { + pr = 0; + while ((f[0] >= '0') && (f[0] <= '9')) { + pr = pr * 10 + f[0] - '0'; + f++; + } + } + } + + // handle integer size overrides + switch (f[0]) { + // are we halfwidth? + case 'h': + fl |= STBSP__HALFWIDTH; + ++f; + if (f[0] == 'h') + ++f; // QUARTERWIDTH + break; + // are we 64-bit (unix style) + case 'l': + fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); + ++f; + if (f[0] == 'l') { + fl |= STBSP__INTMAX; + ++f; + } + break; + // are we 64-bit on intmax? (c99) + case 'j': + fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit on size_t or ptrdiff_t? (c99) + case 'z': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + case 't': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit (msft style) + case 'I': + if ((f[1] == '6') && (f[2] == '4')) { + fl |= STBSP__INTMAX; + f += 3; + } else if ((f[1] == '3') && (f[2] == '2')) { + f += 3; + } else { + fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); + ++f; + } + break; + default: break; + } + + // handle each replacement + switch (f[0]) { +#define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 + char num[STBSP__NUMSZ]; + char lead[8]; + char tail[8]; + char *s; + char const *h; + stbsp__uint32 l, n, cs; + stbsp__uint64 n64; +#ifndef STB_SPRINTF_NOFLOAT + double fv; +#endif + stbsp__int32 dp; + char const *sn; + + case 's': + // get the string + s = va_arg(va, char *); + if (s == 0) + s = (char *)"null"; + // get the length, limited to desired precision + // always limit to ~0u chars since our counts are 32b + l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + // copy the string in + goto scopy; + + case 'c': // char + // get the character + s = num + STBSP__NUMSZ - 1; + *s = (char)va_arg(va, int); + l = 1; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + + case 'n': // weird write-bytes specifier + { + int *d = va_arg(va, int *); + *d = tlen + (int)(bf - buf); + } break; + +#ifdef STB_SPRINTF_NOFLOAT + case 'A': // float + case 'a': // hex float + case 'G': // float + case 'g': // float + case 'E': // float + case 'e': // float + case 'f': // float + va_arg(va, double); // eat it + s = (char *)"No float"; + l = 8; + lead[0] = 0; + tail[0] = 0; + pr = 0; + cs = 0; + STBSP__NOTUSED(dp); + goto scopy; +#else + case 'A': // hex float + case 'a': // hex float + h = (f[0] == 'A') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) + fl |= STBSP__NEGATIVE; + + s = num + 64; + + stbsp__lead_sign(fl, lead); + + if (dp == -1023) + dp = (n64) ? -1022 : 0; + else + n64 |= (((stbsp__uint64)1) << 52); + n64 <<= (64 - 56); + if (pr < 15) + n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); + // add leading chars + +#ifdef STB_SPRINTF_MSVC_MODE + *s++ = '0'; + *s++ = 'x'; +#else + lead[1 + lead[0]] = '0'; + lead[2 + lead[0]] = 'x'; + lead[0] += 2; +#endif + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + if (pr) + *s++ = stbsp__period; + sn = s; + + // print the bits + n = pr; + if (n > 13) + n = 13; + if (pr > (stbsp__int32)n) + tz = pr - n; + pr = 0; + while (n--) { + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + } + + // print the expo + tail[1] = h[17]; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; + n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + + dp = (int)(s - sn); + l = (int)(s - (num + 64)); + s = num + 64; + cs = 1 + (3 << 24); + goto scopy; + + case 'G': // float + case 'g': // float + h = (f[0] == 'G') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; + else if (pr == 0) + pr = 1; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) + fl |= STBSP__NEGATIVE; + + // clamp the precision and delete extra zeros after clamp + n = pr; + if (l > (stbsp__uint32)pr) + l = pr; + while ((l > 1) && (pr) && (sn[l - 1] == '0')) { + --pr; + --l; + } + + // should we use %e + if ((dp <= -4) || (dp > (stbsp__int32)n)) { + if (pr > (stbsp__int32)l) + pr = l - 1; + else if (pr) + --pr; // when using %e, there is one digit before the decimal + goto doexpfromg; + } + // this is the insane action to get the pr to match %g semantics for %f + if (dp > 0) { + pr = (dp < (stbsp__int32)l) ? l - dp : 0; + } else { + pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr); + } + goto dofloatfromg; + + case 'E': // float + case 'e': // float + h = (f[0] == 'E') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) + fl |= STBSP__NEGATIVE; + doexpfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + // handle leading chars + *s++ = sn[0]; + + if (pr) + *s++ = stbsp__period; + + // handle after decimal + if ((l - 1) > (stbsp__uint32)pr) + l = pr + 1; + for (n = 1; n < l; n++) + *s++ = sn[n]; + // trailing zeros + tz = pr - (l - 1); + pr = 0; + // dump expo + tail[1] = h[0xe]; + dp -= 1; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; +#ifdef STB_SPRINTF_MSVC_MODE + n = 5; +#else + n = (dp >= 100) ? 5 : 4; +#endif + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + cs = 1 + (3 << 24); // how many tens + goto flt_lead; + + case 'f': // float + fv = va_arg(va, double); + doafloat: + // do kilos + if (fl & STBSP__METRIC_SUFFIX) { + double divisor; + divisor = 1000.0f; + if (fl & STBSP__METRIC_1024) + divisor = 1024.0; + while (fl < 0x4000000) { + if ((fv < divisor) && (fv > -divisor)) + break; + fv /= divisor; + fl += 0x1000000; + } + } + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) + fl |= STBSP__NEGATIVE; + dofloatfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + + // handle the three decimal varieties + if (dp <= 0) { + stbsp__int32 i; + // handle 0.000*000xxxx + *s++ = '0'; + if (pr) + *s++ = stbsp__period; + n = -dp; + if ((stbsp__int32)n > pr) + n = pr; + i = n; + while (i) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + i -= 4; + } + while (i) { + *s++ = '0'; + --i; + } + if ((stbsp__int32)(l + n) > pr) + l = pr - n; + i = l; + while (i) { + *s++ = *sn++; + --i; + } + tz = pr - (n + l); + cs = 1 + (3 << 24); // how many tens did we write (for commas below) + } else { + cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; + if ((stbsp__uint32)dp >= l) { + // handle xxxx000*000.0 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= l) + break; + } + } + if (n < (stbsp__uint32)dp) { + n = dp - n; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (n) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --n; + } + while (n >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + n -= 4; + } + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = '0'; + --n; + } + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) { + *s++ = stbsp__period; + tz = pr; + } + } else { + // handle xxxxx.xxxx000*000 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= (stbsp__uint32)dp) + break; + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) + *s++ = stbsp__period; + if ((l - dp) > (stbsp__uint32)pr) + l = pr + dp; + while (n < l) { + *s++ = sn[n]; + ++n; + } + tz = pr - (l - dp); + } + } + pr = 0; + + // handle k,m,g,t + if (fl & STBSP__METRIC_SUFFIX) { + char idx; + idx = 1; + if (fl & STBSP__METRIC_NOSPACE) + idx = 0; + tail[0] = idx; + tail[1] = ' '; + { + if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. + if (fl & STBSP__METRIC_1024) + tail[idx + 1] = "_KMGT"[fl >> 24]; + else + tail[idx + 1] = "_kMGT"[fl >> 24]; + idx++; + // If printing kibits and not in jedec, add the 'i'. + if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { + tail[idx + 1] = 'i'; + idx++; + } + tail[0] = idx; + } + } + }; + + flt_lead: + // get the length that we copied + l = (stbsp__uint32)(s - (num + 64)); + s = num + 64; + goto scopy; +#endif + + case 'B': // upper binary + case 'b': // lower binary + h = (f[0] == 'B') ? hexu : hex; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[0xb]; + } + l = (8 << 4) | (1 << 8); + goto radixnum; + + case 'o': // octal + h = hexu; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 1; + lead[1] = '0'; + } + l = (3 << 4) | (3 << 8); + goto radixnum; + + case 'p': // pointer + fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; + pr = sizeof(void *) * 2; + fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros + // fall through - to X + + case 'X': // upper hex + case 'x': // lower hex + h = (f[0] == 'X') ? hexu : hex; + l = (4 << 4) | (4 << 8); + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[16]; + } + radixnum: + // get the number + if (fl & STBSP__INTMAX) + n64 = va_arg(va, stbsp__uint64); + else + n64 = va_arg(va, stbsp__uint32); + + s = num + STBSP__NUMSZ; + dp = 0; + // clear tail, and clear leading if value is zero + tail[0] = 0; + if (n64 == 0) { + lead[0] = 0; + if (pr == 0) { + l = 0; + cs = 0; + goto scopy; + } + } + // convert to string + for (;;) { + *--s = h[n64 & ((1 << (l >> 8)) - 1)]; + n64 >>= (l >> 8); + if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) + break; + if (fl & STBSP__TRIPLET_COMMA) { + ++l; + if ((l & 15) == ((l >> 4) & 15)) { + l &= ~15; + *--s = stbsp__comma; + } + } + }; + // get the tens and the comma pos + cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + // copy it + goto scopy; + + case 'u': // unsigned + case 'i': + case 'd': // integer + // get the integer and abs it + if (fl & STBSP__INTMAX) { + stbsp__int64 i64 = va_arg(va, stbsp__int64); + n64 = (stbsp__uint64)i64; + if ((f[0] != 'u') && (i64 < 0)) { + n64 = (stbsp__uint64)-i64; + fl |= STBSP__NEGATIVE; + } + } else { + stbsp__int32 i = va_arg(va, stbsp__int32); + n64 = (stbsp__uint32)i; + if ((f[0] != 'u') && (i < 0)) { + n64 = (stbsp__uint32)-i; + fl |= STBSP__NEGATIVE; + } + } + +#ifndef STB_SPRINTF_NOFLOAT + if (fl & STBSP__METRIC_SUFFIX) { + if (n64 < 1024) + pr = 0; + else if (pr == -1) + pr = 1; + fv = (double)(stbsp__int64)n64; + goto doafloat; + } +#endif + + // convert to string + s = num + STBSP__NUMSZ; + l = 0; + + for (;;) { + // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) + char *o = s - 8; + if (n64 >= 100000000) { + n = (stbsp__uint32)(n64 % 100000000); + n64 /= 100000000; + } else { + n = (stbsp__uint32)n64; + n64 = 0; + } + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + do { + s -= 2; + *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + } while (n); + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = (char)(n % 10) + '0'; + n /= 10; + } + } + if (n64 == 0) { + if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) + ++s; + break; + } + while (s != o) + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = '0'; + } + } + + tail[0] = 0; + stbsp__lead_sign(fl, lead); + + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + if (l == 0) { + *--s = '0'; + l = 1; + } + cs = l + (3 << 24); + if (pr < 0) + pr = 0; + + scopy: + // get fw=leading/trailing space, pr=leading zeros + if (pr < (stbsp__int32)l) + pr = l; + n = pr + lead[0] + tail[0] + tz; + if (fw < (stbsp__int32)n) + fw = n; + fw -= n; + pr -= l; + + // handle right justify and leading zeros + if ((fl & STBSP__LEFTJUST) == 0) { + if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr + { + pr = (fw > pr) ? fw : pr; + fw = 0; + } else { + fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas + } + } + + // copy the spaces and/or zeros + if (fw + pr) { + stbsp__int32 i; + stbsp__uint32 c; + + // copy leading spaces (or when doing %8.4d stuff) + if ((fl & STBSP__LEFTJUST) == 0) + while (fw > 0) { + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = ' '; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leader + sn = lead + 1; + while (lead[0]) { + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leading zeros + c = cs >> 24; + cs &= 0xffffff; + cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; + while (pr > 0) { + stbsp__cb_buf_clamp(i, pr); + pr -= i; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + } + while (i) { + if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { + cs = 0; + *bf++ = stbsp__comma; + } else + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + } + + // copy leader if there is still one + sn = lead + 1; + while (lead[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy the string + n = l; + while (n) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, n); + n -= i; + STBSP__UNALIGNED(while (i >= 4) { + *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; + bf += 4; + s += 4; + i -= 4; + }) + while (i) { + *bf++ = *s++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy trailing zeros + while (tz) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tz); + tz -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy tail if there is one + sn = tail + 1; + while (tail[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tail[0]); + tail[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // handle the left justify + if (fl & STBSP__LEFTJUST) + if (fw > 0) { + while (fw) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i--) + *bf++ = ' '; + stbsp__chk_cb_buf(1); + } + } + break; + + default: // unknown, just copy code + s = num + STBSP__NUMSZ - 1; + *s = f[0]; + l = 1; + fw = fl = 0; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + } + ++f; + } + endfmt: + + if (!callback) + *bf = 0; + else + stbsp__flush_cb(); + + done: + return tlen + (int)(bf - buf); +} + +// cleanup +#undef STBSP__LEFTJUST +#undef STBSP__LEADINGPLUS +#undef STBSP__LEADINGSPACE +#undef STBSP__LEADING_0X +#undef STBSP__LEADINGZERO +#undef STBSP__INTMAX +#undef STBSP__TRIPLET_COMMA +#undef STBSP__NEGATIVE +#undef STBSP__METRIC_SUFFIX +#undef STBSP__NUMSZ +#undef stbsp__chk_cb_bufL +#undef stbsp__chk_cb_buf +#undef stbsp__flush_cb +#undef stbsp__cb_buf_clamp + +// ============================================================================ +// wrapper functions + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); + va_end(va); + return result; +} + +typedef struct stbsp__context { + char *buf; + int count; + int length; + char tmp[STB_SPRINTF_MIN]; +} stbsp__context; + +static char *stbsp__clamp_callback(const char *buf, void *user, int len) +{ + stbsp__context *c = (stbsp__context *)user; + c->length += len; + + if (len > c->count) + len = c->count; + + if (len) { + if (buf != c->buf) { + const char *s, *se; + char *d; + d = c->buf; + s = buf; + se = buf + len; + do { + *d++ = *s++; + } while (s < se); + } + c->buf += len; + c->count -= len; + } + + if (c->count <= 0) + return c->tmp; + return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can +} + +static char * stbsp__count_clamp_callback( const char * buf, void * user, int len ) +{ + stbsp__context * c = (stbsp__context*)user; + (void) sizeof(buf); + + c->length += len; + return c->tmp; // go direct into buffer if you can +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) +{ + stbsp__context c; + + if ( (count == 0) && !buf ) + { + c.length = 0; + + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); + } + else + { + int l; + + c.buf = buf; + c.count = count; + c.length = 0; + + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); + + // zero-terminate + l = (int)( c.buf - buf ); + if ( l >= count ) // should never be greater, only equal (or less) than count + l = count - 1; + buf[l] = 0; + } + + return c.length; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + + result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); + va_end(va); + + return result; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) +{ + return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); +} + +// ======================================================================= +// low level float utility functions + +#ifndef STB_SPRINTF_NOFLOAT + +// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) +#define STBSP__COPYFP(dest, src) \ +{ \ +int cn; \ +for (cn = 0; cn < 8; cn++) \ +((char *)&dest)[cn] = ((char *)&src)[cn]; \ +} + +// get float info +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) +{ + double d; + stbsp__int64 b = 0; + + // load value and round at the frac_digits + d = value; + + STBSP__COPYFP(b, d); + + *bits = b & ((((stbsp__uint64)1) << 52) - 1); + *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); + + return (stbsp__int32)((stbsp__uint64) b >> 63); +} + +static double const stbsp__bot[23] = { + 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, + 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022 +}; +static double const stbsp__negbot[22] = { + 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, + 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022 +}; +static double const stbsp__negboterr[22] = { + -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, + 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, + -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, + 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039 +}; +static double const stbsp__top[13] = { + 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299 +}; +static double const stbsp__negtop[13] = { + 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299 +}; +static double const stbsp__toperr[13] = { + 8388608, + 6.8601809640529717e+028, + -7.253143638152921e+052, + -4.3377296974619174e+075, + -1.5559416129466825e+098, + -3.2841562489204913e+121, + -3.7745893248228135e+144, + -1.7356668416969134e+167, + -3.8893577551088374e+190, + -9.9566444326005119e+213, + 6.3641293062232429e+236, + -5.2069140800249813e+259, + -5.2504760255204387e+282 +}; +static double const stbsp__negtoperr[13] = { + 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, + -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, + 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, + 8.0970921678014997e-317 +}; + +#if defined(_MSC_VER) && (_MSC_VER <= 1200) +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000U +}; +#define stbsp__tento19th ((stbsp__uint64)1000000000000000000) +#else +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000ULL, + 100000000000ULL, + 1000000000000ULL, + 10000000000000ULL, + 100000000000000ULL, + 1000000000000000ULL, + 10000000000000000ULL, + 100000000000000000ULL, + 1000000000000000000ULL, + 10000000000000000000ULL +}; +#define stbsp__tento19th (1000000000000000000ULL) +#endif + +#define stbsp__ddmulthi(oh, ol, xh, yh) \ +{ \ +double ahi = 0, alo, bhi = 0, blo; \ +stbsp__int64 bt; \ +oh = xh * yh; \ +STBSP__COPYFP(bt, xh); \ +bt &= ((~(stbsp__uint64)0) << 27); \ +STBSP__COPYFP(ahi, bt); \ +alo = xh - ahi; \ +STBSP__COPYFP(bt, yh); \ +bt &= ((~(stbsp__uint64)0) << 27); \ +STBSP__COPYFP(bhi, bt); \ +blo = yh - bhi; \ +ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ +} + +#define stbsp__ddtoS64(ob, xh, xl) \ +{ \ +double ahi = 0, alo, vh, t; \ +ob = (stbsp__int64)xh; \ +vh = (double)ob; \ +ahi = (xh - vh); \ +t = (ahi - xh); \ +alo = (xh - (ahi - t)) - (vh + t); \ +ob += (stbsp__int64)(ahi + alo + xl); \ +} + +#define stbsp__ddrenorm(oh, ol) \ +{ \ +double s; \ +s = oh + ol; \ +ol = ol - (s - oh); \ +oh = s; \ +} + +#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); + +#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); + +static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350 +{ + double ph, pl; + if ((power >= 0) && (power <= 22)) { + stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); + } else { + stbsp__int32 e, et, eb; + double p2h, p2l; + + e = power; + if (power < 0) + e = -e; + et = (e * 0x2c9) >> 14; /* %23 */ + if (et > 13) + et = 13; + eb = e - (et * 23); + + ph = d; + pl = 0.0; + if (power < 0) { + if (eb) { + --eb; + stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); + stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); + ph = p2h; + pl = p2l; + } + } else { + if (eb) { + e = eb; + if (eb > 22) + eb = 22; + e -= eb; + stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); + if (e) { + stbsp__ddrenorm(ph, pl); + stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); + stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); + ph = p2h; + pl = p2l; + } + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); + ph = p2h; + pl = p2l; + } + } + } + stbsp__ddrenorm(ph, pl); + *ohi = ph; + *olo = pl; +} + +// given a float value, returns the significant bits in bits, and the position of the +// decimal point in decimal_pos. +/-INF and NAN are specified by special values +// returned in the decimal_pos parameter. +// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits) +{ + double d; + stbsp__int64 bits = 0; + stbsp__int32 expo, e, ng, tens; + + d = value; + STBSP__COPYFP(bits, d); + expo = (stbsp__int32)((bits >> 52) & 2047); + ng = (stbsp__int32)((stbsp__uint64) bits >> 63); + if (ng) + d = -d; + + if (expo == 2047) // is nan or inf? + { + *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; + *decimal_pos = STBSP__SPECIAL; + *len = 3; + return ng; + } + + if (expo == 0) // is zero or denormal + { + if (((stbsp__uint64) bits << 1) == 0) // do zero + { + *decimal_pos = 1; + *start = out; + out[0] = '0'; + *len = 1; + return ng; + } + // find the right expo for denormals + { + stbsp__int64 v = ((stbsp__uint64)1) << 51; + while ((bits & v) == 0) { + --expo; + v >>= 1; + } + } + } + + // find the decimal exponent as well as the decimal bits of the value + { + double ph, pl; + + // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 + tens = expo - 1023; + tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); + + // move the significant bits into position and stick them into an int + stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); + + // get full as much precision from double-double as possible + stbsp__ddtoS64(bits, ph, pl); + + // check if we undershot + if (((stbsp__uint64)bits) >= stbsp__tento19th) + ++tens; + } + + // now do the rounding in integer land + frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); + if ((frac_digits < 24)) { + stbsp__uint32 dg = 1; + if ((stbsp__uint64)bits >= stbsp__powten[9]) + dg = 10; + while ((stbsp__uint64)bits >= stbsp__powten[dg]) { + ++dg; + if (dg == 20) + goto noround; + } + if (frac_digits < dg) { + stbsp__uint64 r; + // add 0.5 at the right position and round + e = dg - frac_digits; + if ((stbsp__uint32)e >= 24) + goto noround; + r = stbsp__powten[e]; + bits = bits + (r / 2); + if ((stbsp__uint64)bits >= stbsp__powten[dg]) + ++tens; + bits /= r; + } + noround:; + } + + // kill long trailing runs of zeros + if (bits) { + stbsp__uint32 n; + for (;;) { + if (bits <= 0xffffffff) + break; + if (bits % 1000) + goto donez; + bits /= 1000; + } + n = (stbsp__uint32)bits; + while ((n % 1000) == 0) + n /= 1000; + bits = n; + donez:; + } + + // convert to string + out += 64; + e = 0; + for (;;) { + stbsp__uint32 n; + char *o = out - 8; + // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) + if (bits >= 100000000) { + n = (stbsp__uint32)(bits % 100000000); + bits /= 100000000; + } else { + n = (stbsp__uint32)bits; + bits = 0; + } + while (n) { + out -= 2; + *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + e += 2; + } + if (bits == 0) { + if ((e) && (out[0] == '0')) { + ++out; + --e; + } + break; + } + while (out != o) { + *--out = '0'; + ++e; + } + } + + *decimal_pos = tens; + *start = out; + *len = e; + return ng; +} + +#undef stbsp__ddmulthi +#undef stbsp__ddrenorm +#undef stbsp__ddmultlo +#undef stbsp__ddmultlos +#undef STBSP__SPECIAL +#undef STBSP__COPYFP + +#endif // STB_SPRINTF_NOFLOAT + +// clean up +#undef stbsp__uint16 +#undef stbsp__uint32 +#undef stbsp__int32 +#undef stbsp__uint64 +#undef stbsp__int64 +#undef STBSP__UNALIGNED + +#endif // STB_SPRINTF_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ \ No newline at end of file diff --git a/src_v2/lumenarium_bsp.h b/src_v2/lumenarium_bsp.h new file mode 100644 index 0000000..bd18720 --- /dev/null +++ b/src_v2/lumenarium_bsp.h @@ -0,0 +1,492 @@ +/* date = April 11th 2022 9:57 am */ + +#ifndef LUMENARIUM_BSP_H +#define LUMENARIUM_BSP_H + +// NOTE(PS): Functionality Notes +// - there must always be a root node that contains the area of the tree as a whole +// - a node with no children has not been split + +#define BTREE_NODE_ID_VALID_BIT (1 << 31) +struct BSP_Node_Id +{ + u32 value; +}; + +enum BSP_Split_Kind +{ + BSPSplit_XAxis = 1, + BSPSplit_YAxis = 0, + BSPSplit_ZAxis = 2, + BSPSplit_None = 3, +}; + +struct BSP_Split +{ + BSP_Split_Kind kind; + r32 value; +}; + +enum BSP_Split_Update_Flags +{ + BSPSplitUpdate_None = 0, + BSPSplitUpdate_FreeZeroAreaChildren = 1, +}; + +enum BSP_Child_Selector +{ + // NOTE(PS): these values are intentionally overlapping since + // they access the data structure of the B-Tree in a particular + // way. ie. left and top are the same space in memory as are + // right and bottom + BSPChild_Left = 0, + BSPChild_Top = 0, + + BSPChild_Right = 1, + BSPChild_Bottom = 1, +}; + +struct BSP_Area +{ + v2 min; + v2 max; +}; + +struct BSP_Node +{ + union + { + BSP_Node_Id parent; + BSP_Node_Id next_free; + }; + + BSP_Split split; + + union + { + BSP_Node_Id children[2]; + struct + { + union + { + BSP_Node_Id left; + BSP_Node_Id top; + }; + union + { + BSP_Node_Id right; + BSP_Node_Id bottom; + }; + }; + }; + u32 user_data; + + BSP_Area area; +}; + +struct BSP +{ + BSP_Node* nodes; + u32 nodes_cap; + u32 nodes_len; + + BSP_Node_Id root; + BSP_Node_Id free_first; +}; + +typedef void BSP_Walk_Cb(BSP* tree, BSP_Node_Id id, BSP_Node* node, u8* user_data); + +internal BSP bsp_create(Allocator* allocator, u32 cap); + +internal BSP_Node* bsp_get(BSP* tree, BSP_Node_Id id); +internal BSP_Node_Id bsp_push(BSP* tree, BSP_Node_Id parent, BSP_Area area, u32 user_data); +internal void bsp_free(BSP* tree, BSP_Node_Id id); +internal void bsp_free_cb(BSP* tree, BSP_Node_Id id, BSP* node, u8* user_data); + +union BSP_Split_Result +{ + BSP_Node_Id children[2]; + struct + { + union + { + BSP_Node_Id left; + BSP_Node_Id top; + }; + union + { + BSP_Node_Id right; + BSP_Node_Id bottom; + }; + }; +}; + +internal BSP_Split_Result bsp_split(BSP* tree, BSP_Node_Id id, r32 split, BSP_Split_Kind kind, u32 user_data_0, u32 user_data_1); +internal void bsp_join_recursive(BSP* tree, BSP_Node* parent, BSP_Child_Selector keep); + +// left, parent, right +internal void bsp_walk_inorder(BSP* tree, BSP_Node_Id first, BSP_Walk_Cb* cb, u8* user_data); +// parent, left right +internal void bsp_walk_preorder(BSP* tree, BSP_Node_Id first, BSP_Walk_Cb* cb, u8* user_data); +// parent, right, parent +internal void bsp_walk_postorder(BSP* tree, BSP_Node_Id first, BSP_Walk_Cb* cb, u8* user_data); + +internal void bsp_node_update_child_areas(BSP* tree, BSP_Node_Id id, BSP_Node* node, u8* user_data); +internal void bsp_node_area_update(BSP* tree, BSP_Node_Id id, BSP_Area new_area); +internal void bsp_child_split_update(BSP* tree, BSP_Node_Id node, u32 new_split); + +/////////////////////////////////////////////////// +// IMPLEMENTATION + + +internal BSP +bsp_create(Allocator* allocator, u32 cap) +{ + BSP result = {}; + zero_struct(result); + result.nodes_cap = cap; + result.nodes = allocator_alloc_array(allocator, BSP_Node, cap); + return result; +} + +#define bsp_node_id_is_valid(id) (has_flag(id.value, BTREE_NODE_ID_VALID_BIT)) +#define bsp_node_id_equals(a,b) (a.value == b.value) +#define bsp_node_id_to_index(id) (id.value & (u32)(~BTREE_NODE_ID_VALID_BIT)) + +internal BSP_Node* +bsp_get(BSP* tree, BSP_Node_Id id) +{ + if (!bsp_node_id_is_valid(id)) return 0; + u32 index = bsp_node_id_to_index(id); + if (index > tree->nodes_len) return 0; + return tree->nodes + index; +} + +internal BSP_Node_Id +bsp_push(BSP* tree, BSP_Node_Id parent_id, BSP_Area area, u32 user_data) +{ + BSP_Node_Id result = BSP_Node_Id{0}; + BSP_Node* node = 0; + + if (tree->nodes_len >= tree->nodes_cap) + { + if (bsp_node_id_is_valid(tree->free_first)) + { + result = tree->free_first; + node = bsp_get(tree, result); + tree->free_first = node->next_free; + zero_struct(node->parent); + } + } + else + { + result.value = tree->nodes_len++; + assert(!has_flag(result.value, BTREE_NODE_ID_VALID_BIT)); + add_flag(result.value, BTREE_NODE_ID_VALID_BIT); + node = tree->nodes + bsp_node_id_to_index(result); + } + + if (bsp_node_id_is_valid(result)) + { + node->split.kind = BSPSplit_None; + node->parent = parent_id; + node->area = area; + node->user_data = user_data; + } + + return result; +} + +internal void +bsp_free_(BSP* tree, BSP_Node_Id id, BSP_Node* now_free) +{ + if (bsp_node_id_is_valid(now_free->parent)) + { + BSP_Node* parent = bsp_get(tree, now_free->parent); + if (bsp_node_id_equals(parent->children[0], id)) + { + zero_struct(parent->children[0]); + } + else if (bsp_node_id_equals(parent->children[1], id)) + { + zero_struct(parent->children[1]); + } + else + { + // NOTE(PS): in this case, a child node had a reference to + // a parent that didn't have a reference back to the child + // this means the tree itself is messed up + invalid_code_path; + } + } + + zero_struct(*now_free); + now_free->next_free = tree->free_first; + tree->free_first = id; +} + +internal void +bsp_free(BSP* tree, BSP_Node_Id id) +{ + BSP_Node* now_free = bsp_get(tree, id); + bsp_free_(tree, id, now_free); +} + +internal void +bsp_free_cb(BSP* tree, BSP_Node_Id id, BSP_Node* node, u8* user_data) +{ + bsp_free_(tree, id, node); +} + +internal BSP_Split_Result +bsp_split(BSP* tree, BSP_Node_Id node_id, r32 split, BSP_Split_Kind kind, u32 user_data_0, u32 user_data_1) +{ + BSP_Node* node = bsp_get(tree, node_id); + split = clamp(node->area.min.Elements[kind], split, node->area.max.Elements[kind]); + node->split.value = split; + node->split.kind = kind; + node->children[0] = bsp_push(tree, node_id, {}, user_data_0); + node->children[1] = bsp_push(tree, node_id, {}, user_data_1); + bsp_node_update_child_areas(tree, node_id, node, 0); + + BSP_Split_Result result = {}; + result.children[0] = node->children[0]; + result.children[1] = node->children[1]; + return result; +} + +internal void +bsp_join_recursive(BSP* tree, BSP_Node_Id parent_id, BSP_Child_Selector keep) +{ + BSP_Node* parent = bsp_get(tree, parent_id); + BSP_Node keep_node = *bsp_get(tree, parent->children[keep]); + bsp_walk_preorder(tree, parent->children[0], bsp_free_cb, 0); + bsp_walk_preorder(tree, parent->children[1], bsp_free_cb, 0); + parent->user_data = keep_node.user_data; + zero_struct(parent->children[0]); + zero_struct(parent->children[1]); +} + +// NOTE(PS): the other three walk functions all require allocation of a stack +// while this is fast with our scratch allocator, there are cases where, for +// correctness, we walk a tree that is very likely to be a single node. In +// those cases, we can avoid allocating anything by just visiting the single +// node and returning early. +// This function provides that functionality for all walk functions +internal bool +bsp_walk_single_node_check(BSP* tree, BSP_Node_Id first, BSP_Walk_Cb* visit, u8* user_data) +{ + BSP_Node* node = bsp_get(tree, first); + if (node->split.kind == BSPSplit_None) + { + visit(tree, first, node, user_data); + return true; + } + return false; +} + +// left, parent, right +internal void +bsp_walk_inorder(BSP* tree, BSP_Node_Id first, BSP_Walk_Cb* visit, u8* user_data) +{ + if (!bsp_node_id_is_valid(first)) return; + if (bsp_walk_single_node_check(tree, first, visit, user_data)) return; + scratch_get(scratch); + BSP_Node_Id* stack = allocator_alloc_array(scratch.a, BSP_Node_Id, tree->nodes_len); + u32 stack_len = 0; + memory_zero_array(stack, BSP_Node_Id, tree->nodes_len); + + BSP_Node_Id at = first; + while (true) + { + if (bsp_node_id_is_valid(at)) + { + stack[stack_len++] = at; + BSP_Node* n = bsp_get(tree, at); + at = n->children[0]; + } + else + { + if (stack_len == 0) break; + + at = stack[--stack_len]; + BSP_Node* n = bsp_get(tree, at); + visit(tree, at, n, user_data); + at = n->children[1]; + } + } +} + +// parent, left right +internal void +bsp_walk_preorder(BSP* tree, BSP_Node_Id first, BSP_Walk_Cb* visit, u8* user_data) +{ + if (bsp_walk_single_node_check(tree, first, visit, user_data)) return; + scratch_get(scratch); + BSP_Node_Id* stack = allocator_alloc_array(scratch.a, BSP_Node_Id, tree->nodes_len); + u32 stack_len = 0; + memory_zero_array(stack, BSP_Node_Id, tree->nodes_len); + + BSP_Node_Id at = first; + + while (true) + { + while (bsp_node_id_is_valid(at)) + { + BSP_Node* n = bsp_get(tree, at); + visit(tree, at, n, user_data); + stack[stack_len++] = at; + at = n->children[0]; + } + + if (!bsp_node_id_is_valid(at) && stack_len == 0) break; + + at = stack[--stack_len]; + BSP_Node* n = bsp_get(tree, at); + at = n->children[1]; + } +} + +// parent, right, parent +internal void +bsp_walk_postorder(BSP* tree, BSP_Node_Id first, BSP_Walk_Cb* visit, u8* user_data) +{ + if (bsp_walk_single_node_check(tree, first, visit, user_data)) return; + scratch_get(scratch); + BSP_Node_Id* stack = allocator_alloc_array(scratch.a, BSP_Node_Id, tree->nodes_len); + u32 stack_len = 0; + memory_zero_array(stack, BSP_Node_Id, tree->nodes_len); + + BSP_Node_Id at = first; + while (true) + { + if (bsp_node_id_is_valid(at)) + { + BSP_Node* n = bsp_get(tree, at); + if (bsp_node_id_is_valid(n->children[1])) stack[stack_len++] = n->children[1]; + stack[stack_len++] = at; + at = n->children[0]; + } + else + { + if (stack_len == 0) break; + + at = stack[--stack_len]; + BSP_Node* n = bsp_get(tree, at); + assert(n != 0); + + if (bsp_node_id_is_valid(n->children[1]) && bsp_node_id_equals(n->children[1], stack[stack_len - 1])) + { + BSP_Node_Id at_temp = stack[stack_len - 1]; + stack[stack_len - 1] = at; + at = at_temp; + } + else + { + visit(tree, at, n, user_data); + zero_struct(at); + } + } + } +} + +internal void +bsp_node_update_child_areas(BSP* tree, BSP_Node_Id id, BSP_Node* node, u8* user_data) +{ + // assume that node's area is correct. Using that, clamp its split as appropriate + // and then update its children. If a child has an area of zero, rely on flags + // to determine behavior + + if (node->split.kind == BSPSplit_None) return; + + BSP_Split_Update_Flags flags = (BSP_Split_Update_Flags)0; + if (user_data) flags = *(BSP_Split_Update_Flags*)user_data; + + BSP_Split_Kind kind = node->split.kind; + + BSP_Node* node_min = bsp_get(tree, node->children[0]); + BSP_Node* node_max = bsp_get(tree, node->children[1]); + node_min->area = node->area; + node_max->area = node->area; + node_min->area.max.Elements[kind] = node->split.value; + node_max->area.min.Elements[kind] = node->split.value; + + if (has_flag(flags, BSPSplitUpdate_FreeZeroAreaChildren)) + { + bool free_children = false; + if (node_min->area.max.Elements[kind] <= node_min->area.min.Elements[kind]) + { + node->user_data = node_max->user_data; + free_children= true; + } + else if (node_max->area.max.Elements[kind] <= node_max->area.min.Elements[kind]) + { + node->user_data = node_min->user_data; + free_children= true; + } + + if (free_children) + { + bsp_walk_postorder(tree, node->children[0], bsp_free_cb, 0); + bsp_walk_postorder(tree, node->children[1], bsp_free_cb, 0); + } + + } + + // NOTE(PS): no need to recurse, this function assumes its either being + // called on a particular node or its the callback of one of the tree + // walk functions +} + +internal void +bsp_node_area_update(BSP* tree, BSP_Node_Id node_id, BSP_Area area) +{ + BSP_Node* node = bsp_get(tree, node_id); + node->area = area; + BSP_Split_Update_Flags flags = BSPSplitUpdate_FreeZeroAreaChildren; + bsp_walk_preorder(tree, node_id, bsp_node_update_child_areas, (u8*)&flags); +} + +internal void +bsp_child_split_update(BSP* tree, BSP_Node_Id node_id, r32 new_split, BSP_Split_Update_Flags flags) +{ + BSP_Node* node = bsp_get(tree, node_id); + node->split.value = new_split; + bsp_walk_preorder(tree, node_id, bsp_node_update_child_areas, (u8*)&flags); +} + +#if defined(DEBUG) + +internal void +bsp_tests() +{ + scratch_get(scratch); + BSP tree = bsp_create(scratch.a, 256); + tree.root = bsp_push(&tree, {0}, {{0,0},{512,512}}, 0); + + BSP_Split_Result r0 = bsp_split(&tree, tree.root, 256, BSPSplit_YAxis, 0, 0); + BSP_Node* root = bsp_get(&tree, tree.root); + BSP_Node* n0 = bsp_get(&tree, r0.children[0]); + BSP_Node* n1 = bsp_get(&tree, r0.children[1]); + assert(root != 0 && n0 != 0 && n1 != 0); + assert(n0->area.min == root->area.min); + assert(n0->area.max.x == 256 && n0->area.max.y == root->area.max.y); + assert(n1->area.max == root->area.max); + assert(n1->area.min.x == 256 && n0->area.min.y == root->area.min.y); + assert(n0->split.kind == BSPSplit_None); + assert(n1->split.kind == BSPSplit_None); + assert(root->split.kind == BSPSplit_YAxis); + + BSP_Split_Result r1 = bsp_split(&tree, root->children[0], 32, BSPSplit_YAxis, 0, 0); + BSP_Split_Result r2 = bsp_split(&tree, r1.children[1], 64, BSPSplit_XAxis, 0, 0); + + bsp_walk_postorder(&tree, root->children[0], bsp_free_cb, 0); +} + +#else + +#define bsp_tests() + +#endif + +#endif //LUMENARIUM_BSP_H diff --git a/src_v2/lumenarium_first.cpp b/src_v2/lumenarium_first.cpp index ea5f7d5..b4b0b36 100644 --- a/src_v2/lumenarium_first.cpp +++ b/src_v2/lumenarium_first.cpp @@ -6,12 +6,12 @@ lumenarium_init() { App_State* state = 0; - permanent = bump_allocator_create_reserve(GB(1)); - scratch = bump_allocator_create_reserve(KB(64)); + permanent = bump_allocator_create_reserve(GB(2)); + scratch_ = bump_allocator_create_reserve(GB(8)); platform_file_jobs_init(); run_tests(); - + scratch_get(scratch); App_Init_Desc desc = incenter_get_init_desc(); // TODO(PS): make sure the values make sense in desc @@ -21,6 +21,13 @@ lumenarium_init() state->input_state = input_state_create(permanent); + String exe_file_path = platform_get_exe_path(scratch.a); + u64 run_tree_start = string_find_substring(exe_file_path, lit_str("run_tree"), 0, StringMatch_FindLast); + u64 run_tree_end = run_tree_start + lit_str("run_tree").len; + String run_tree_path = string_get_prefix(exe_file_path, run_tree_end); + String run_tree_path_nullterm = string_copy(run_tree_path, scratch.a); + platform_pwd_set(run_tree_path_nullterm); + en_init(state, desc); if (has_flag(state->flags, AppState_RunEditor)) { @@ -33,7 +40,7 @@ lumenarium_init() internal void lumenarium_frame_prepare(App_State* state) { - allocator_clear(scratch); + allocator_clear(scratch_); input_state_swap_frames(state->input_state); @@ -50,7 +57,7 @@ lumenarium_frame_prepare(App_State* state) internal void lumenarium_frame(App_State* state) { - //en_frame(state); + en_frame(state); if (has_flag(state->flags, AppState_RunEditor)) { ed_frame(state); diff --git a/src_v2/lumenarium_first.h b/src_v2/lumenarium_first.h index 1d6c20c..85a5171 100644 --- a/src_v2/lumenarium_first.h +++ b/src_v2/lumenarium_first.h @@ -8,9 +8,17 @@ typedef struct App_State App_State; // Environment #include "lumenarium_memory.cpp" #include "lumenarium_string.cpp" +#include "lumenarium_random.h" #include "lumenarium_input.cpp" #include "lumenarium_texture_atlas.cpp" #include "lumenarium_hash.cpp" +#include "lumenarium_geometry.h" + +global Allocator* scratch_; // gets reset at frame boundaries + +#define scratch_get(ident) Allocator_Scratch ident = Allocator_Scratch(scratch_) + +#include "lumenarium_bsp.h" // Engine typedef struct Assembly_Strip Assembly_Strip; @@ -28,7 +36,6 @@ typedef struct Assembly_Strip Assembly_Strip; // Lumenarium Runtime Environment global Allocator* permanent; -global Allocator* scratch; // gets reset at frame boundaries #if defined(DEBUG) # include "lumenarium_tests.cpp" @@ -72,6 +79,7 @@ struct App_State #include "editor/lumenarium_editor_ui.cpp" #include "editor/lumenarium_editor_renderer.cpp" +#include "editor/lumenarium_editor_sculpture_visualizer.cpp" #include "editor/lumenarium_editor.cpp" diff --git a/src_v2/lumenarium_geometry.h b/src_v2/lumenarium_geometry.h new file mode 100644 index 0000000..abfe1ce --- /dev/null +++ b/src_v2/lumenarium_geometry.h @@ -0,0 +1,268 @@ +/* date = April 11th 2022 3:22 pm */ + +#ifndef LUMENARIUM_GEOMETRY_H +#define LUMENARIUM_GEOMETRY_H + +// Utility functions for working with 3d geometry + +// NOTE(PS): the Buffer vs Buffer_Builder distinction +// lets a static sized buffer exist, and be auto 'exported' or 'available' +// from a larger data structure that also stores enough data to incrementally +// build the buffer + +// NOTE(PS): @IMPORTANT!!! +// at the moment this builder doesn't support allowing for the elements +// of a vertex to be in an order other than position tex color +// and in any other packing than tightly packed +typedef u32 Geo_Vertex_Buffer_Storage; +enum +{ + GeoVertexBufferStorage_None = 0, + GeoVertexBufferStorage_Position = 1, + GeoVertexBufferStorage_TexCoord = 2, + GeoVertexBufferStorage_Color = 4 +}; + +struct Geo_Vertex_Buffer +{ + r32* values; + u32 len; + Geo_Vertex_Buffer_Storage storage; + u32 stride; +}; + +struct Geo_Vertex_Buffer_Builder +{ + union + { + Geo_Vertex_Buffer buffer; + struct + { + // NOTE(PS): @Maintainence + // this must always match the layout of Geo_Vertex_Buffer + r32* values; + u32 len; + Geo_Vertex_Buffer_Storage storage; + u32 stride; + }; + }; + u32 cap; +}; + +struct Geo_Index_Buffer +{ + u32* values; + u32 len; +}; + +struct Geo_Index_Buffer_Builder +{ + union + { + Geo_Index_Buffer buffer; + struct + { + // NOTE(PS): @Maintainence + // this must always match the layout of Geo_Index_Buffer + u32* values; + u32 len; + }; + }; + u32 cap; +}; + +struct Geo_Quad_Buffer +{ + Geo_Vertex_Buffer buffer_vertex; + Geo_Index_Buffer buffer_index; +}; + +struct Geo_Quad_Buffer_Builder +{ + Geo_Vertex_Buffer_Builder buffer_vertex; + Geo_Index_Buffer_Builder buffer_index; +}; + +internal Geo_Vertex_Buffer_Builder geo_vertex_buffer_builder_create(Allocator* a, u32 cap); +internal Geo_Index_Buffer_Builder geo_index_buffer_builder_create(Allocator* a, u32 cap); +internal Geo_Quad_Buffer_Builder geo_quad_buffer_builder_create(Allocator* a, u32 vertex_cap, u32 index_cap); + +// Vertex Buffer +internal u32 geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v, v2 t, v4 c); +internal u32 geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v, v2 t); +internal u32 geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v); + +// Index Buffer +internal u32 geo_index_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, u32 i); +internal void geo_index_buffer_builder_push_tri(Geo_Vertex_Buffer_Builder* b, u32 i0, u32 i1, u32 i2); +internal void geo_index_buffer_builder_push_quad(Geo_Vertex_Buffer_Builder* b, u32 i0, u32 i1, u32 i2, u32 i3); + +// Quad Buffer +internal void geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3, v2 t0, v2 t1, v2 t2, v2 t3, v4 c); +internal void geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3, v2 t0, v2 t1, v2 t2, v2 t3); +internal void geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3); +internal Geo_Quad_Buffer geo_quad_buffer_builder_get_static_buffer(Geo_Quad_Buffer_Builder* b); + +///////////////////////////////////////////// +// Implementation + +internal u32 +geo_vertex_buffer_builder_stride(Geo_Vertex_Buffer_Storage storage) +{ + u32 result = 0; + result += has_flag(storage, GeoVertexBufferStorage_Position) ? 3 : 0; + result += has_flag(storage, GeoVertexBufferStorage_TexCoord) ? 2 : 0; + result += has_flag(storage, GeoVertexBufferStorage_Color) ? 4 : 0; + return result; +} + +internal Geo_Vertex_Buffer_Builder +geo_vertex_buffer_builder_create(Allocator* a, u32 cap, Geo_Vertex_Buffer_Storage storage) +{ + u32 stride = geo_vertex_buffer_builder_stride(storage); + u32 size = cap * stride; + + Geo_Vertex_Buffer_Builder result = {}; + zero_struct(result); + result.cap = cap; + result.storage = storage; + result.stride = stride; + result.values = allocator_alloc_array(a, r32, size); + + return result; +} + +internal Geo_Index_Buffer_Builder +geo_index_buffer_builder_create(Allocator* a, u32 cap) +{ + + Geo_Index_Buffer_Builder result = {}; + zero_struct(result); + result.cap = cap; + result.values = allocator_alloc_array(a, u32, cap); + return result; +} + +internal Geo_Quad_Buffer_Builder +geo_quad_buffer_builder_create(Allocator* a, u32 vertex_cap, Geo_Vertex_Buffer_Storage storage, u32 index_cap) +{ + Geo_Quad_Buffer_Builder result = {}; + result.buffer_vertex = geo_vertex_buffer_builder_create(a, vertex_cap, storage); + result.buffer_index = geo_index_buffer_builder_create(a, index_cap); + return result; +} + +// Vertex Buffer + +internal u32 +geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v, v2 t, v4 c) +{ + assert(b->len < b->cap); + u32 result = b->len++; + u32 offset = result * b->stride; + if (has_flag(b->storage, GeoVertexBufferStorage_Position)) + { + b->values[offset++] = v.x; + b->values[offset++] = v.y; + b->values[offset++] = v.z; + } + if (has_flag(b->storage, GeoVertexBufferStorage_TexCoord)) + { + b->values[offset++] = t.x; + b->values[offset++] = t.y; + } + if (has_flag(b->storage, GeoVertexBufferStorage_Color)) + { + b->values[offset++] = c.x; + b->values[offset++] = c.y; + b->values[offset++] = c.z; + b->values[offset++] = c.w; + } + return result; +} + +internal u32 +geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v, v2 t) +{ + return geo_vertex_buffer_builder_push(b, v, t, v4{0}); +} + +internal u32 +geo_vertex_buffer_builder_push(Geo_Vertex_Buffer_Builder* b, v3 v) +{ + return geo_vertex_buffer_builder_push(b, v, v2{0}, v4{0}); +} + + +// Index Buffer + +internal u32 +geo_index_buffer_builder_push(Geo_Index_Buffer_Builder* b, u32 i) +{ + assert(b->len < b->cap); + u32 result = b->len++; + b->values[result] = i; + return result; +} + +internal void +geo_index_buffer_builder_push_tri(Geo_Index_Buffer_Builder* b, u32 i0, u32 i1, u32 i2) +{ + geo_index_buffer_builder_push(b, i0); + geo_index_buffer_builder_push(b, i1); + geo_index_buffer_builder_push(b, i2); +} + +internal void +geo_index_buffer_builder_push_quad(Geo_Index_Buffer_Builder* b, u32 i0, u32 i1, u32 i2, u32 i3) +{ + geo_index_buffer_builder_push_tri(b, i0, i1, i2); + geo_index_buffer_builder_push_tri(b, i0, i2, i3); +} + + +// Quad Buffer + +internal void +geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3, v2 t0, v2 t1, v2 t2, v2 t3, v4 c) +{ + u32 i0 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p0, t0, c); + u32 i1 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p1, t1, c); + u32 i2 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p2, t2, c); + u32 i3 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p3, t3, c); + + geo_index_buffer_builder_push_quad(&b->buffer_index, i0, i1, i2, i3); +} + +internal void +geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3, v2 t0, v2 t1, v2 t2, v2 t3) +{ + u32 i0 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p0, t0, v4{}); + u32 i1 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p1, t1, v4{}); + u32 i2 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p2, t2, v4{}); + u32 i3 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p3, t3, v4{}); + + geo_index_buffer_builder_push_quad(&b->buffer_index, i0, i1, i2, i3); +} + +internal void +geo_quad_buffer_builder_push(Geo_Quad_Buffer_Builder* b, v3 p0, v3 p1, v3 p2, v3 p3) +{ + u32 i0 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p0, v2{}, v4{}); + u32 i1 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p1, v2{}, v4{}); + u32 i2 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p2, v2{}, v4{}); + u32 i3 = geo_vertex_buffer_builder_push(&b->buffer_vertex, p3, v2{}, v4{}); + + geo_index_buffer_builder_push_quad(&b->buffer_index, i0, i1, i2, i3); +} + +internal Geo_Quad_Buffer +geo_quad_buffer_builder_get_static_buffer(Geo_Quad_Buffer_Builder* b) +{ + Geo_Quad_Buffer result = {}; + result.buffer_vertex = b->buffer_vertex.buffer; + result.buffer_index = b->buffer_index.buffer; + return result; +} + +#endif //LUMENARIUM_GEOMETRY_H diff --git a/src_v2/lumenarium_memory.cpp b/src_v2/lumenarium_memory.cpp index 103396f..a133df8 100644 --- a/src_v2/lumenarium_memory.cpp +++ b/src_v2/lumenarium_memory.cpp @@ -195,6 +195,16 @@ bump_allocator_destroy(Allocator* allocator) allocator_destroy_(allocator, sizeof(Allocator_Bump)); } +internal void +bump_allocator_rewind(Allocator* allocator, u64 to_point) +{ + Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data; +#if defined(DEBUG) + memory_zero(bump->base + to_point, bump->at - to_point); +#endif + bump->at = to_point; +} + internal Allocator* bump_allocator_create_() { @@ -248,6 +258,28 @@ bump_allocator_create_child(Allocator* parent, u64 init_size) return result; } + +///////////////////////////////////////// +// Scratch Allocator + +struct Allocator_Scratch +{ + Allocator* a; + u64 at_before; + + Allocator_Scratch(Allocator* allocator) + { + this->a = allocator; + Allocator_Bump* bump = (Allocator_Bump*)this->a->allocator_data; + this->at_before = bump->at; + } + + ~Allocator_Scratch() + { + bump_allocator_rewind(this->a, this->at_before); + } +}; + ///////////////////////////////////////// // Paged Allocator diff --git a/src_v2/lumenarium_random.h b/src_v2/lumenarium_random.h new file mode 100644 index 0000000..625a842 --- /dev/null +++ b/src_v2/lumenarium_random.h @@ -0,0 +1,47 @@ +/* date = April 11th 2022 6:11 pm */ + +#ifndef LUMENARIUM_RANDOM_H +#define LUMENARIUM_RANDOM_H + +struct Random_Series +{ + u32 last_value; +}; + +internal Random_Series +random_series_create(u32 seed) +{ + Random_Series result = {}; + result.last_value = seed; + return result; +} + +internal u32 +random_series_next(Random_Series* s) +{ + u32 result = s->last_value; + result ^= result << 13; + result ^= result >> 17; + result ^= result << 5; + s->last_value = result; + return result; +} + +internal r32 +random_series_next_unilateral(Random_Series* s) +{ + r32 result = random_series_next(s) / (r32)(0xFFFFFFFF); + return result; +} + +internal r32 +random_series_next_bilateral(Random_Series* s) +{ + r32 result = random_series_next(s) / (r32)(0xFFFFFFFF); + result = (result * 2.0f) - 1.0f; + return result; +} + + + +#endif //LUMENARIUM_RANDOM_H diff --git a/src_v2/lumenarium_string.cpp b/src_v2/lumenarium_string.cpp index e9f2a8b..6c6ed7e 100644 --- a/src_v2/lumenarium_string.cpp +++ b/src_v2/lumenarium_string.cpp @@ -250,9 +250,9 @@ string_fv(Allocator* a, char* fmt, va_list args) { va_list args1; va_copy(args1, args); - s32 needed = vsnprintf(0, 0, fmt, args); + s32 needed = stbsp_vsnprintf(0, 0, fmt, args); String result = allocator_alloc_string(a, needed + 1); - result.len = vsnprintf((char*)result.str, result.cap, fmt, args1); + result.len = stbsp_vsnprintf((char*)result.str, (int)result.cap, fmt, args1); result.str[result.len] = 0; va_end(args1); return result; diff --git a/src_v2/lumenarium_tests.cpp b/src_v2/lumenarium_tests.cpp index a7f99f5..6379b7b 100644 --- a/src_v2/lumenarium_tests.cpp +++ b/src_v2/lumenarium_tests.cpp @@ -70,7 +70,12 @@ memory_tests() { // TestGroup("Platform Allocation") { - u8* base = platform_mem_reserve(GB(32)); + u64 size = GB(32); +#if defined(PLATFORM_wasm) + size = KB(4); +#endif + + u8* base = platform_mem_reserve(size); platform_mem_commit(base, KB(4)); base[4095] = 200; assert(base[4095] == 200); @@ -85,18 +90,54 @@ memory_tests() memory_allocator_tests(bump, false); allocator_destroy(bump); - Allocator* paged = paged_allocator_create_reserve(KB(32)); + Allocator* paged = paged_allocator_create_reserve(KB(32), KB(4)); memory_allocator_tests(paged, true); allocator_destroy(paged); } +enum test_flags +{ + TestNone = 0, + Test1 = 1, + Test2 = 2, + Test3 = 4, + Test4 = 8, +}; + internal void run_tests() { + scratch_get(scratch); + + // basic + + u8 b = TestNone; + assert(!has_flag(b, TestNone)); + assert(!has_flag(b, Test1)); + add_flag(b, Test1); + assert(has_flag(b, Test1)); + assert(!has_flag(b, Test2)); + add_flag(b, Test2); + assert(has_flag(b, Test1)); + assert(has_flag(b, Test2)); + assert(has_flag(b, Test1 | Test2)); + add_flag(b, Test4); + assert(has_flag(b, Test1)); + assert(has_flag(b, Test2)); + assert(has_flag(b, Test4)); + assert(has_flag(b, Test1 | Test2 | Test4)); + assert(!has_flag(b, Test3)); + rem_flag(b, Test2); + assert(has_flag(b, Test1)); + assert(!has_flag(b, Test2)); + assert(has_flag(b, Test4)); + assert(has_flag(b, Test1 | Test4)); + assert(!has_flag(b, Test3)); + // memory tests - u8* a0 = allocator_alloc_array(scratch, u8, 32); - u8* a1 = allocator_alloc_array(scratch, u8, 32); + u8* a0 = allocator_alloc_array(scratch.a, u8, 32); + u8* a1 = allocator_alloc_array(scratch.a, u8, 32); assert(a0 != a1); assert((a0 + 32) <= a1); @@ -113,7 +154,15 @@ run_tests() assert(a1[i] == (100 + i)); } + + assert(round_up_to_pow2(1) == 1); + assert(round_up_to_pow2(3) == 4); + assert(round_up_to_pow2(29) == 32); + assert(round_up_to_pow2(32) == 32); + assert(round_up_to_pow2(120) == 128); + memory_tests(); + bsp_tests(); #if defined(PLATFORM_wasm) // NOTE(PS): the tests below this point don't make sense on a web assembly @@ -123,21 +172,21 @@ run_tests() // testing strings and exe path - String exe_file_path = platform_get_exe_path(scratch); + String exe_file_path = platform_get_exe_path(scratch.a); assert(exe_file_path.str != 0); u64 run_tree_start = string_find_substring(exe_file_path, lit_str("run_tree"), 0, StringMatch_FindLast); u64 run_tree_end = run_tree_start + lit_str("run_tree").len; assert(run_tree_start < exe_file_path.len); String run_tree_path = string_get_prefix(exe_file_path, run_tree_end); - String run_tree_path_nullterm = string_copy(run_tree_path, scratch); + String run_tree_path_nullterm = string_copy(run_tree_path, scratch.a); assert(run_tree_path_nullterm.len > 0); assert(platform_pwd_set(run_tree_path_nullterm)); // testing file io Platform_File_Handle f = platform_file_open(lit_str("text.txt"), FileAccess_Read | FileAccess_Write, FileCreate_OpenExisting); - Platform_File_Info i = platform_file_get_info(f, scratch); + Platform_File_Info i = platform_file_get_info(f, scratch.a); - Data d0 = platform_file_read_all(f, scratch); + Data d0 = platform_file_read_all(f, scratch.a); assert(d0.size > 0); String s = lit_str("foooooooooobbbbbbaaaarrrrrr"); @@ -163,6 +212,4 @@ run_tests() } #endif - allocator_clear(scratch); - } \ No newline at end of file diff --git a/src_v2/lumenarium_types.h b/src_v2/lumenarium_types.h index 8851563..988b636 100644 --- a/src_v2/lumenarium_types.h +++ b/src_v2/lumenarium_types.h @@ -37,6 +37,41 @@ typedef s64 b64; typedef float r32; typedef double r64; +#define u8_max 0xFF +#define u16_max 0xFFFF +#define u32_max 0xFFFFFFFF +#define u64_max 0xFFFFFFFFFFFFFFFF + +#define s8_max 127 +#define s16_max 32767 +#define s32_max 2147483647 +#define s64_max 9223372036854775807 + +#define s8_min -127 - 1 +#define s16_min -32767 - 1 +#define s32_min -2147483647 - 1 +#define s64_min -9223372036854775807 - 1 + +#define r32_max 3.402823466e+38f +#define r32_min -3.402823466e+38f +#define r32_smallest_positive 1.1754943508e-38f +#define r32_epsilon 5.96046448e-8f +#define r32_pi 3.14159265359f +#define r32_half_pi 1.5707963267f +#define r32_tau 6.28318530717f + +#define r64_max 1.79769313486231e+308 +#define r64_min -1.79769313486231e+308 +#define r64_smallest_positive 4.94065645841247e-324 +#define r64_epsilon 1.11022302462515650e-16 +#define r64_pi 3.14159265359 +#define r64_half_pi 1.5707963267 +#define r64_tau 6.28318530717 + +#define NanosToSeconds 1 / 10000000.0 +#define SecondsToNanos 10000000.0 + + #define get_byte(value, byte_index) ((value >> (8 * byte_index)) & 0xFF) struct Data @@ -54,12 +89,21 @@ data_create(u8* base, u64 size) return result; } +#define memory_zero_array(b,t,c) memory_zero((u8*)(b), sizeof(t) * (c)) internal void memory_zero(u8* base, u64 size); internal void memory_copy(u8* from, u8* to, u64 size); ////////////////////////////////////////////// // Math +#ifndef max +# define max(a,b) (a) > (b) ? (a) : (b) +#endif + +#ifndef min +# define min(a,b) (a) > (b) ? (b) : (a) +#endif + #define lerp(a,t,b) (a) + ((1.0f - (t)) * (b)) #define clamp(r0,v,r1) min((r1),max((r0),(v))) #define lerp_clamp(a,t,b) clamp((a),lerp((a),(t),(b)),(b)) @@ -247,6 +291,22 @@ hash_table_find(u32* ids, u32 cap, u32 value) return result; } +////////////////////////////////////////////// +// Math + +internal u32 +round_up_to_pow2(u32 v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + ////////////////////////////////////////////// // Vector Extensions @@ -254,6 +314,15 @@ hash_table_find(u32* ids, u32 cap, u32 value) #define v2_floor(v) v2{ floorf(v.x), floorf(v.y) } #define v3_floor(v) v3{ floorf(v.x), floorf(v.y), floorf(v.z) } +internal bool +rect2_contains(v2 min, v2 max, v2 point) +{ + return ( + min.x <= point.x && min.y <= point.y && + max.x >= point.x && max.y >= point.y + ); +} + ////////////////////////////////////////////// // Color Constants diff --git a/src_v2/platform/lumenarium_platform.h b/src_v2/platform/lumenarium_platform.h index 4f06e9c..fff914d 100644 --- a/src_v2/platform/lumenarium_platform.h +++ b/src_v2/platform/lumenarium_platform.h @@ -85,12 +85,12 @@ bool platform_pwd_set(String path); typedef u32 Platform_File_Async_Job_Flags; enum { - PlatformFileAsyncJob_Invalid = 0, - PlatformFileAsyncJob_Read = 1, - PlatformFileAsyncJob_Write = 2, - PlatformFileAsyncJob_InFlight = 4, - PlatformFileAsyncJob_Success = 8, - PlatformFileAsyncJob_Failed = 16, + PlatformFileAsyncJob_Invalid = 0, + PlatformFileAsyncJob_Read = 1 << 0, + PlatformFileAsyncJob_Write = 1 << 1, + PlatformFileAsyncJob_InFlight = 1 << 2, + PlatformFileAsyncJob_Success = 1 << 3, + PlatformFileAsyncJob_Failed = 1 << 4, }; struct Platform_File_Async_Job_Args diff --git a/src_v2/platform/lumenarium_platform_common_includes.h b/src_v2/platform/lumenarium_platform_common_includes.h index 1b6799a..f6e0496 100644 --- a/src_v2/platform/lumenarium_platform_common_includes.h +++ b/src_v2/platform/lumenarium_platform_common_includes.h @@ -17,6 +17,9 @@ #define STBTT_assert(x) assert(x) #include "../libs/stb_truetype.h" +#define STB_SPRINTF_IMPLEMENTATION +#include "../libs/stb_sprintf.h" + // NOTE(PS): only need the opengl extension headers // when running on a platform that is using opengl 3.3+ #if !defined(PLATFORM_wasm) diff --git a/src_v2/platform/wasm/lumenarium_first_wasm.cpp b/src_v2/platform/wasm/lumenarium_first_wasm.cpp index 8446aa6..6e60e26 100644 --- a/src_v2/platform/wasm/lumenarium_first_wasm.cpp +++ b/src_v2/platform/wasm/lumenarium_first_wasm.cpp @@ -9,6 +9,7 @@ #include "../lumenarium_platform_common_includes.h" #include "../../lumenarium_types.h" +#include "../../lumenarium_memory.h" #include "../lumenarium_platform.h" #include "../../lumenarium_first.cpp" diff --git a/src_v2/platform/wasm/lumenarium_wasm_file.cpp b/src_v2/platform/wasm/lumenarium_wasm_file.cpp index ef11ded..4cf4aad 100644 --- a/src_v2/platform/wasm/lumenarium_wasm_file.cpp +++ b/src_v2/platform/wasm/lumenarium_wasm_file.cpp @@ -1,5 +1,6 @@ WASM_EXTERN u32 wasm_fetch(char* file_path, u32 file_path_len, u8* dest, u32 size); +WASM_EXTERN void wasm_platform_file_async_work_on_job(char* path, u32 path_len, u8* data, u32 data_len, bool read, bool write); Platform_File_Handle platform_file_open(String path, Platform_File_Access_Flags flags_access, Platform_File_Create_Flags flags_create) @@ -43,3 +44,13 @@ platform_pwd_set(String path) return false; } +void +platform_file_async_work_on_job(Platform_File_Async_Job* job) +{ + wasm_platform_file_async_work_on_job( + (char*)job->args.path.str, job->args.path.len, + job->args.data.base, job->args.data.size, + has_flag(job->args.flags, PlatformFileAsyncJob_Read), + has_flag(job->args.flags, PlatformFileAsyncJob_Write) + ); +} \ No newline at end of file diff --git a/src_v2/platform/wasm/lumenarium_wasm_imports.js b/src_v2/platform/wasm/lumenarium_wasm_imports.js index 29d372c..ff42920 100644 --- a/src_v2/platform/wasm/lumenarium_wasm_imports.js +++ b/src_v2/platform/wasm/lumenarium_wasm_imports.js @@ -92,20 +92,69 @@ var lumenarium_wasm_imports = { }, wasm_mem_grow: (new_size) => { - let pages = new_size / WASM_PAGE_SIZE; + let new_size_ = new_size >>> 0; + let pages = new_size_ / WASM_PAGE_SIZE; let pages_rem = fract(pages); if (pages_rem > 0) pages = Math.floor(pages) + 1; let size_before = lumenarium_wasm_instance.exports.memory.buffer.byteLength; let old_page_count = lumenarium_wasm_instance.exports.memory.grow(pages); console.log("mem_grow\n", - "req size: ", new_size, "\n", + "req size: ", new_size_, "\n", "old size: ", (old_page_count * WASM_PAGE_SIZE), "\n", "old size: ", size_before, "\n", "grew by: ", (pages * WASM_PAGE_SIZE), "\n", "new size: ", lumenarium_wasm_instance.exports.memory.buffer.byteLength, ""); }, + malloc: (size) => { + + }, + + free: (base) => { + + }, + + sin: Math.sin, + sinf: Math.sin, + cos: Math.cos, + cosf: Math.cos, + tan: Math.tan, + tanf: Math.tan, + asin: Math.asin, + asinf: Math.asin, + acos: Math.acos, + acosf: Math.acos, + atan: Math.atan, + atanf: Math.atan, + pow: Math.pow, + powf: Math.pow, + fmodf: (f,d) => { return f % d; }, + strlen: (ptr) => { + let len = 0; + let len_checked = 0; + let len_to_check = 256; + let found_end = false; + while (true) + { + let string = wasm_mem_get_u8_arr(lumenarium_wasm_instance, ptr, len_checked); + for (let i = len_checked; i < len_to_check; i++) + { + if (string[i] == 0) + { + len = i; + break; + } + } + len_checked *= 2; + } + return len_checked; + }, + + wasm_platform_file_async_work_on_job: (path, path_len, data, data_size, read, write) => { + + }, + wasm_performance_now: () => { return performance.now(); }, @@ -260,6 +309,14 @@ function glBufferData(target, size, ptr, usage) return r; } +function glBufferSubData(target, offset, size, ptr) +{ + let data = wasm_mem_get_u8_arr(lumenarium_wasm_instance, ptr, size); + const r = gl.bufferSubData(target, offset, data, 0, size); + glErrorReport(arguments); + return r; +} + function glCreateShader(kind) { let shader = gl.createShader(kind); @@ -395,6 +452,17 @@ function glTexSubImage2D(target, level, offsetx, offsety, width, height, format, return r; } +function glGetUniformLocation(program, name, name_len) +{ + // TODO(PS): complete + return 0; +} + +function glUniformMatrix4fv() +{ + // TODO(PS): +} + function webgl_add_imports (canvas_selector, imports) { const canvas = document.querySelector(canvas_selector); if (!canvas) return console.error("no canvas"); @@ -414,6 +482,7 @@ function webgl_add_imports (canvas_selector, imports) { imports.glCreateBuffer = glCreateBuffer; imports.glBindBuffer = glBindBuffer; imports.glBufferData = glBufferData; + imports.glBufferSubData = glBufferSubData; imports.glCreateShader = glCreateShader; imports.glShaderSource = glShaderSource; imports.glCompileShader = glCompileShader; @@ -431,5 +500,8 @@ function webgl_add_imports (canvas_selector, imports) { imports.glTexImage2D = glTexImage2D; imports.glTexSubImage2D = glTexSubImage2D; imports.glBindTexture = glBindTexture; + imports.glGetUniformLocation = glGetUniformLocation; + imports.glUniformMatrix4fv = glUniformMatrix4fv; + return imports; } \ No newline at end of file diff --git a/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp b/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp index 55a8efd..e17ff7f 100644 --- a/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp +++ b/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp @@ -48,6 +48,7 @@ typedef unsigned int GLsizei; #define GL_RGB 0x1907 #define GL_RGBA 0x1908 #define GL_UNSIGNED_BYTE 0x1401 +#define GL_CLAMP_TO_EDGE 0x812F WASM_EXTERN bool glHadError(); WASM_EXTERN void glClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a); @@ -61,6 +62,7 @@ WASM_EXTERN void glClear(GLuint i); WASM_EXTERN GLuint glCreateBuffer(); WASM_EXTERN void glBindBuffer(GLenum buffer_kind, GLuint buffer_id); WASM_EXTERN void glBufferData(GLenum target, size_t size, const void* data, GLenum usage); +WASM_EXTERN void glBufferSubData(GLenum target, size_t offset, size_t size, const void* data); WASM_EXTERN GLuint glCreateShader(GLenum kind); WASM_EXTERN GLuint glShaderSource(GLuint shader_id, char* shader_code, GLuint shader_code_len); WASM_EXTERN void glCompileShader(GLuint shader_id); @@ -70,6 +72,7 @@ WASM_EXTERN void glLinkProgram(GLuint program); WASM_EXTERN void glUseProgram(GLuint program); WASM_EXTERN GLuint glGetAttribLocation(GLuint program, const char* name, GLuint name_len); WASM_EXTERN GLuint glGetUniformLocation(GLuint program, const char* name, u32 len); +WASM_EXTERN void glUniformMatrix4fv(GLuint uniform, GLuint count, GLenum normalize, GLfloat* elements); WASM_EXTERN void glVertexAttribPointer(GLuint attr, GLuint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); WASM_EXTERN void glEnableVertexAttribArray(GLuint index); WASM_EXTERN void glDrawElements(GLenum type, GLuint count, GLenum ele_type, void* indices); @@ -109,6 +112,53 @@ platform_geometry_buffer_create( return result; } +void +platform_geometry_buffer_update( + Platform_Geometry_Buffer* buffer, + r32* verts, + u32 verts_offset, + u32 verts_len, + u32* indices, + u32 indices_offset, + u32 indices_len + ){ + glBindBuffer(GL_ARRAY_BUFFER, buffer->buffer_id_vertices); + if (verts_len > buffer->vertices_len) + { + // NOTE(PS): this is because we're going to delete the old buffer and + // create a new one. In order to do that and not lose data, the update + // function needs to have been passed all the buffer's data + assert(verts_offset == 0); + glBufferData( + GL_ARRAY_BUFFER, verts_len * sizeof(r32), (void*)verts, GL_STATIC_DRAW + ); + } + else + { + glBufferSubData( + GL_ARRAY_BUFFER, verts_offset * sizeof(r32), verts_len * sizeof(r32), (void*)verts + ); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->buffer_id_indices); + if (indices_len > buffer->indices_len) + { + // NOTE(PS): this is because we're going to delete the old buffer and + // create a new one. In order to do that and not lose data, the update + // function needs to have been passed all the buffer's data + assert(indices_offset == 0); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, indices_len * sizeof(u32), (void*)indices, GL_STATIC_DRAW + ); + } + else + { + glBufferSubData( + GL_ELEMENT_ARRAY_BUFFER, indices_offset * sizeof(u32), indices_len * sizeof(u32), (void*)indices + ); + } +} + Platform_Shader platform_shader_create( String code_vert, String code_frag, String* attrs, GLuint attrs_len, String* uniforms, GLuint uniforms_len @@ -174,7 +224,7 @@ platform_shader_bind(Platform_Shader shader) void platform_set_uniform(Platform_Shader shader, u32 index, m44 u) { - glUniformMatrix4fv(shader.uniforms[index], 1, GL_FALSE, &u.Elements[0]); + glUniformMatrix4fv(shader.uniforms[index], 1, GL_FALSE, (r32*)u.Elements); } void diff --git a/src_v2/platform/win32/lumenarium_first_win32.cpp b/src_v2/platform/win32/lumenarium_first_win32.cpp index 9dc68a8..df682e2 100644 --- a/src_v2/platform/win32/lumenarium_first_win32.cpp +++ b/src_v2/platform/win32/lumenarium_first_win32.cpp @@ -205,8 +205,8 @@ WinMain( &win32_main_window, hInstance, "Lumenariumtest0", - 1600, - 900, + 1400, + 800, win32_window_event_handler ); win32_window_update_dim(&win32_main_window); diff --git a/src_v2/platform/win32/lumenarium_win32_graphics.cpp b/src_v2/platform/win32/lumenarium_win32_graphics.cpp index cb02e6b..354bf93 100644 --- a/src_v2/platform/win32/lumenarium_win32_graphics.cpp +++ b/src_v2/platform/win32/lumenarium_win32_graphics.cpp @@ -57,15 +57,16 @@ platform_geometry_buffer_create( return result; } -void platform_geometry_buffer_update( - Platform_Geometry_Buffer* buffer, - r32* verts, - u32 verts_offset, - u32 verts_len, - u32* indices, - u32 indices_offset, - u32 indices_len - ){ +void +platform_geometry_buffer_update( + Platform_Geometry_Buffer* buffer, + r32* verts, + u32 verts_offset, + u32 verts_len, + u32* indices, + u32 indices_offset, + u32 indices_len + ){ gl.glBindVertexArray(buffer->buffer_id_vao); gl.glBindBuffer(GL_ARRAY_BUFFER, buffer->buffer_id_vertices); win32_gl_no_error(); diff --git a/src_v2/user_space/user_space_incenter.cpp b/src_v2/user_space/user_space_incenter.cpp index 124f476..8a894c2 100644 --- a/src_v2/user_space/user_space_incenter.cpp +++ b/src_v2/user_space/user_space_incenter.cpp @@ -7,33 +7,53 @@ incenter_get_init_desc() return result; } +#define INCENTER_METER 1.0f +#define INCENTER_FOOT 0.3048f +#define INCENTER_METERS(count) (count) * INCENTER_METER +#define INCENTER_FEET(count) (count) * INCENTER_FOOT +#define INCENTER_PER_METER(count) INCENTER_METER / (r32)(count) + internal void incenter_init(App_State* state) { // create a fake sculpture - Assembly_Handle ah = assembly_add(&state->assemblies, lit_str("test"), 3000, 100); + Assembly_Handle ah = assembly_add(&state->assemblies, lit_str("test"), 5043, 41); - r32 scale = 1; + scratch_get(scratch); + Allocator* s = scratch.a; - // strips - for (int strip_x = 0; strip_x < 10; strip_x++) + v3 start_p = v3{0, 0, 0}; + + Assembly_Strip* vertical_strip = assembly_add_strip(&state->assemblies, ah, 123); + assembly_strip_create_leds( + &state->assemblies, + ah, + vertical_strip, + start_p, + v3{0, INCENTER_FEET(-6.5f), 0}, + 123 + ); + + r32 radius = INCENTER_FEET(10); + + Random_Series rand = random_series_create(hash_djb2_to_u32("slkdjfalksdjf")); + for (u32 i = 0; i < 40; i++) { - for (int strip_y = 0; strip_y < 10; strip_y++) - { - if (strip_x == 5 && strip_y == 7) - { - int x= 5; - } - Assembly_Strip* strip = assembly_add_strip(&state->assemblies, ah, 30); - - // leds go up - for (int led_z = 0; led_z < 30; led_z++) - { - v4 pos = { strip_x * scale, strip_y * scale, led_z * scale, 1 }; - assembly_add_led(&state->assemblies, ah, strip, pos); - } - } + Assembly_Strip* strip = assembly_add_strip(&state->assemblies, ah, 123); + + r32 theta = random_series_next_unilateral(&rand) * r32_tau; + r32 phi = random_series_next_unilateral(&rand) * r32_tau; + + // spherical to cartesian conversion + v3 end_p = { + radius * sinf(phi) * cosf(theta), + radius * sinf(phi) * sinf(theta), + radius * cosf(phi) + }; + assembly_strip_create_leds(&state->assemblies, ah, strip, start_p, end_p, 123); } + + ed_sculpture_updated(state, 5, 0.025f); } internal void