scratch memory, sculpture rendering, wasm updates
This commit is contained in:
parent
0e7a1b5536
commit
09db50e3d4
|
@ -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;
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue