diff --git a/admin.txt b/admin.txt index 8586538..98ecab0 100644 --- a/admin.txt +++ b/admin.txt @@ -7,14 +7,20 @@ ## TODO -1. Upgrade Lumenarium's plubming - 1. switch over to compiling with clang & bash based build scripts - 2. better platform layer separation +1. Upgrade Lumenarium's plumbing + x. switch over to compiling with clang & bash based build scripts + x. better platform layer separation 3. osx and webgl layers, possibly linux? - 4. remove dll compiling, just build all in one go + x. remove dll compiling, just build all in one go 5. improve ui - 1. default to using a basic layout object - 2. improve widget handling (see a_trick_of_fate) + [ ] get widgets and widget ids working + - see a trick of fate + [ ] clip widgets to regions + [ ] text rendering + [ ] interaction + [ ] layout manager + - do layout the way youre doing styling - optional pointer to a struct + and fallback on some global default 2. Incenter 1. Sculpture generation from list of lat-long coordinates diff --git a/build/build_.sh b/build/build_.sh index b7d0978..81df051 100644 --- a/build/build_.sh +++ b/build/build_.sh @@ -144,7 +144,7 @@ CompilerFlags_DEBUG_win32="" add_flag CompilerFlags_DEBUG_win32 "-Od" # add_flag CompilerFlags_DEBUG_win32 "-Zi" # add_flag CompilerFlags_DEBUG_win32 "-DDEBUG" # -add_flag CompilerFlags_DEBUG_win32 "-DPRINT_ASSERTS" +# add_flag CompilerFlags_DEBUG_win32 "-DPRINT_ASSERTS" CompilerFlags_DEBUG="-O0" add_flag CompilerFlags_DEBUG "-g" # diff --git a/run_tree/data/font.ttf b/run_tree/data/font.ttf new file mode 100644 index 0000000..06aafc0 Binary files /dev/null and b/run_tree/data/font.ttf differ diff --git a/run_tree/wasm/debug/lumenarium.wasm b/run_tree/wasm/debug/lumenarium.wasm deleted file mode 100644 index 2960aba..0000000 Binary files a/run_tree/wasm/debug/lumenarium.wasm and /dev/null differ diff --git a/run_tree/win32/intel/debug/debug.rdbg b/run_tree/win32/intel/debug/debug.rdbg index f78c41e..24dbe61 100644 Binary files a/run_tree/win32/intel/debug/debug.rdbg and b/run_tree/win32/intel/debug/debug.rdbg differ diff --git a/run_tree/win32/intel/debug/lumenarium_first_win32.obj b/run_tree/win32/intel/debug/lumenarium_first_win32.obj index f70d129..848a54b 100644 Binary files a/run_tree/win32/intel/debug/lumenarium_first_win32.obj and b/run_tree/win32/intel/debug/lumenarium_first_win32.obj differ diff --git a/src_v2/editor/lumenarium_editor.cpp b/src_v2/editor/lumenarium_editor.cpp index af166ce..9d32da7 100644 --- a/src_v2/editor/lumenarium_editor.cpp +++ b/src_v2/editor/lumenarium_editor.cpp @@ -81,7 +81,7 @@ void make_quad(Platform_Geometry_Buffer* geo, Platform_Shader* shd, Platform_Tex lit_str("uv"), }; *shd = platform_shader_create( - shader_code_vert, shader_code_frag, attribs, 2 + shader_code_vert, shader_code_frag, attribs, 2, 0, 0 ); platform_vertex_attrib_pointer(*geo, *shd, 4, shd->attrs[0], 6, 0); @@ -90,30 +90,38 @@ void make_quad(Platform_Geometry_Buffer* geo, Platform_Shader* shd, Platform_Tex *tex = platform_texture_create((u8*)pix, 4, 4, 4); } +internal void +ed_load_font_cb(Platform_File_Async_Job_Args result) +{ + s32 x = 5; +} + 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); + + // make the default quad for us to draw with + // TODO(PS): this might be unnecessary with the per-frame buffer we use now make_quad(&editor->renderer.geo, &editor->renderer.shd, &editor->renderer.tex); + + platform_file_async_read(lit_str("data/font.ttf"), ed_load_font_cb); + } internal void ed_frame_prepare(App_State* state) { - + ui_frame_prepare(&state->editor->ui, state->editor->window_dim); } internal void ed_frame(App_State* state) { - for (u32 i = 0; i < 16; i++) - { - if (i % 2 == 1) continue; - pix[i] += 1; - } - platform_texture_update(state->editor->renderer.tex, (u8*)pix, 4, 4, 4); - + edr_render_begin(state); + ui_draw(&state->editor->ui); edr_render(state); } diff --git a/src_v2/editor/lumenarium_editor.h b/src_v2/editor/lumenarium_editor.h index 08ab6df..9cd17c2 100644 --- a/src_v2/editor/lumenarium_editor.h +++ b/src_v2/editor/lumenarium_editor.h @@ -7,6 +7,7 @@ struct Editor { v2 window_dim; Editor_Renderer renderer; + UI ui; }; #endif //LUMENARIUM_EDITOR_H diff --git a/src_v2/editor/lumenarium_editor_renderer.cpp b/src_v2/editor/lumenarium_editor_renderer.cpp index deedaa7..67d9e34 100644 --- a/src_v2/editor/lumenarium_editor_renderer.cpp +++ b/src_v2/editor/lumenarium_editor_renderer.cpp @@ -1,7 +1,6 @@ - internal void -edr_render(App_State* state) +edr_render_begin(App_State* state) { Platform_Graphics_Frame_Desc desc = {}; desc.clear_color = { 0.1f, 0.1f, 0.1f, 1 }; @@ -9,9 +8,15 @@ edr_render(App_State* state) desc.viewport_max = state->editor->window_dim; platform_frame_begin(desc); platform_frame_clear(); - +} + +internal void +edr_render(App_State* state) +{ +#if 0 platform_geometry_bind(state->editor->renderer.geo); - platform_texture_bind(state->editor->renderer.tex); + platform_texture_bind(state->editor->ui.atlas_texture); platform_shader_bind(state->editor->renderer.shd); platform_geometry_draw(state->editor->renderer.geo); +#endif } \ No newline at end of file diff --git a/src_v2/editor/lumenarium_editor_ui.cpp b/src_v2/editor/lumenarium_editor_ui.cpp new file mode 100644 index 0000000..75ffc24 --- /dev/null +++ b/src_v2/editor/lumenarium_editor_ui.cpp @@ -0,0 +1,452 @@ +#define WHITE_SPRITE_ID 511 + +static String ui_shader_vert_win32 = lit_str( + "#version 330 core\n" + "layout (location = 0) in vec4 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" + " uv = a_uv;\n" + " color = a_color;\n" + "}" + ); + +static String ui_shader_frag_win32 = lit_str( + "#version 330 core\n" + "in vec2 uv;\n" + "in vec4 color;\n" + "out vec4 FragColor;\n" + "uniform sampler2D texture;\n" + "void main(void) {\n" + " FragColor = texture(texture, uv) * color;\n" + " if (FragColor.w <= 0.01f) discard;\n" + "}" + ); + +internal UI +ui_create(u32 widget_pool_cap, u32 verts_cap, Input_State* input, Allocator* a) +{ + UI result = {}; + result.input = input; + + // Widgets + result.widgets.free = allocator_alloc_array(a, UI_Widget, widget_pool_cap); + result.widgets.free_cap = widget_pool_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.per_frame_buffer = platform_geometry_buffer_create( + (r32*)result.verts, + result.verts_cap, + result.indices, + result.indices_cap + ); + + 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 + ); + + platform_vertex_attrib_pointer( + result.per_frame_buffer, result.shader, 4, result.shader.attrs[0], 10, 0 + ); + platform_vertex_attrib_pointer( + result.per_frame_buffer, result.shader, 2, result.shader.attrs[1], 10, 4 + ); + platform_vertex_attrib_pointer( + result.per_frame_buffer, result.shader, 4, result.shader.attrs[2], 10, 6 + ); + + // Texture Atlas + result.atlas = texture_atlas_create(1024, 1024, 512, permanent); + result.atlas_texture = platform_texture_create(result.atlas.pixels, 1024, 1024, 1024); + + u32 white_sprite[] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + ui_sprite_register(&result, (u8*)white_sprite, 2, 2, WHITE_SPRITE_ID); + + 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); +} + +internal void +ui_sprite_register(UI* ui, u8* pixels, u32 w, u32 h, u32 id) +{ + texture_atlas_register(&ui->atlas, pixels, w, h, id); + platform_texture_update(ui->atlas_texture, ui->atlas.pixels, ui->atlas.width, ui->atlas.height, ui->atlas.width); +} + +internal void +ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id, v4 color) +{ + v4 uv = texture_atlas_sprite_get_uvs(&ui->atlas, id); + ui_quad_push(ui, pmin, pmax, uv.xy, uv.zw, color); +} + +internal void +ui_sprite_push(UI* ui, v3 pmin, v3 pmax, u32 id) +{ + ui_sprite_push(ui, pmin, pmax, id, v4{1,1,1,1}); +} + +internal void +ui_frame_prepare(UI* ui, v2 window_dim) +{ + ui->verts_len = 0; + ui->indices_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; + + v2 half_d = window_dim * 0.5f; + ui->proj = HMM_Orthographic(0, window_dim.x, window_dim.y, 0, 0.01f, 100); + + if (ui->widget_next_hot.value != 0) + { + ui->widget_next_hot_frames += 1; + if (ui->widget_hot_frames > 1) + { + ui->widget_next_hot = UI_Widget_Id{0}; + } + } + if (ui->widget_hot.value != 0) + { + ui->widget_hot_frames += 1; + if (ui->widget_hot_frames > 1) ui->widget_hot = UI_Widget_Id{0}; + } +} + +global bool show = false; + +internal void +ui_draw(UI* ui) +{ + UI_Widget_Desc d0 = { + { + (UIWidgetStyle_Bg), + WHITE_V4, + BLACK_V4, + WHITE_SPRITE_ID, + }, + lit_str("Hi there!"), + v2{ 32.0f, 32.0f }, + v2{ 128.0f, 64.0f }, + }; + UI_Widget_Result r0 = ui_widget_push(ui, d0); + + UI_Widget_Desc d1 = d0; + d1.style.flags |= UIWidgetStyle_Outline | UIWidgetStyle_MouseClick; + d1.style.color_bg = PINK_V4; + d1.style.color_fg = GREEN_V4; + d1.p_min = v2{ 512, 32 }; + d1.p_max = v2{ 640, 128 }; + d1.string = lit_str("Hello"); + UI_Widget_Result r1 = ui_widget_push(ui, d1); + bool clicked_r1 = has_flag(r1.flags, UIWidgetResult_MouseLeft_WentUp); + if (clicked_r1) show = !show; + + UI_Widget_Result r2 = {}; + if (show) + { + UI_Widget_Desc d2 = d1; + d1.string = lit_str("Hello There"); + d1.p_min = v2{ 560, 64 }; + d1.p_max = v2{ 700, 256 }; + r2 = ui_widget_push(ui, d1); + } + + bool clicked_r2 = has_flag(r2.flags, UIWidgetResult_MouseLeft_WentUp); + assert( + (!clicked_r1 && !clicked_r2) || + (clicked_r1 && !clicked_r2) || + (!clicked_r1 && clicked_r2) + ); + + u32 widget_count = ui->widgets.free_len; + r32 range_min = -10; + r32 range_max = -1; + r32 range_step = (range_max - range_min) / (r32)widget_count; + ui_widgets_to_geometry_recursive(ui, ui->widgets.root, -10, range_step); + + platform_geometry_buffer_update( + &ui->per_frame_buffer, + (r32*)ui->verts, + 0, + ui->verts_len * 10, + ui->indices, + 0, + ui->indices_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); +} + +//////////////////////////////////////////// +// Widgets + +internal UI_Widget_Id +ui_widget_id_create(String string, u32 index) +{ + assert(string.len != 0 && string.str != 0); + UI_Widget_Id result = {}; + result.value = hash_djb2_to_u32(string); + result.index = index; + return result; +} + +internal UI_Widget* +ui_widget_pool_push(UI_Widget_Pool* pool, String string) +{ + assert(pool->free_len < pool->free_cap); + UI_Widget* result = pool->free + pool->free_len++; + + result->id = ui_widget_id_create(string, pool->free_len); + result->parent = 0; + result->next = 0; + result->child_first = 0; + result->child_last = 0; + + if (pool->active_parent) + { + result->parent = pool->active_parent; + sll_push( + pool->active_parent->child_first, + pool->active_parent->child_last, + result + ); + } + + return result; +} + +internal void +ui_widget_pool_pop(UI_Widget_Pool* pool) +{ + if (pool->active_parent->parent) + { + pool->active_parent = pool->active_parent->parent; + } +} + +internal bool +ui_widget_id_equals(UI_Widget_Id a, UI_Widget_Id b) +{ + return (a.value == b.value); +} + +internal bool +ui_widget_id_is_valid(UI_Widget_Id h) +{ + return h.value != 0; +} + +internal void +ui_widget_next_hot_set(UI* ui, UI_Widget* w) +{ + ui->widget_next_hot = w->id; + ui->widget_next_hot_frames = 0; +} + +internal void +ui_widget_hot_set(UI* ui, UI_Widget* w) +{ + ui->widget_hot = w->id; + ui->widget_hot_frames = 0; +} + +internal UI_Widget_Result +ui_widget_push(UI* ui, UI_Widget_Desc desc) +{ + UI_Widget_Result result = {}; + + UI_Widget* w = ui_widget_pool_push(&ui->widgets, desc.string); + w->desc = desc; + + if (has_flag(desc.style.flags, UIWidgetStyle_MouseClick)) + { + // CASES: + // Mouse Over | Mouse Clicked | Is Next Hot | Response + // f | f | t | clear next hot + // f | f | f | do nothing + // f | t | f | do nothing + // t | f | f | beome next hot + // t | t | f | become next hot + // t | t | t | become hot + + v2 mouse_p = ui->input->frame_hot->mouse_pos; + bool mouse_over = ( + mouse_p.x >= desc.p_min.x && mouse_p.x <= desc.p_max.x && + mouse_p.y >= desc.p_min.y && mouse_p.y <= desc.p_max.y + ); + + if (mouse_over) + { + if (ui_widget_id_equals(w->id, ui->widget_next_hot)) + { + if (input_key_is_down(ui->input, KeyCode_MouseLeftButton)) + { + ui_widget_hot_set(ui, w); + result.flags |= UIWidgetResult_MouseLeft_IsDown; + } + if (input_key_went_up(ui->input, KeyCode_MouseLeftButton)) + { + result.flags |= UIWidgetResult_MouseLeft_WentUp; + ui->widget_hot = UI_Widget_Id{0}; + } + } + else if ((w->id.index >= ui->widget_next_hot.index) && ui->widget_hot.value == 0) + { + ui_widget_next_hot_set(ui, w); + } + } + else + { + if (ui_widget_id_equals(w->id, ui->widget_next_hot)) + { + ui->widget_next_hot = UI_Widget_Id{0}; + } + } + } + + return result; +} + +internal void +ui_widget_pop(UI* ui, UI_Widget* widget) +{ + assert(ui_widget_id_equals(widget->id, ui->widgets.active_parent->id)); + ui_widget_pool_pop(&ui->widgets); +} + +internal r32 +ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_step) +{ + r32 z_at = z_start; + for (UI_Widget* child = widget->child_first; child != 0; child = child->next) + { + UI_Widget_Desc desc = child->desc; + v3 bg_min = v2_to_v3(desc.p_min, z_at); + v3 bg_max = v2_to_v3(desc.p_max, z_at); + + v4 color_fg = desc.style.color_fg; + v4 color_bg = desc.style.color_bg; + if (ui_widget_id_equals(ui->widget_next_hot, child->id)) + { + color_fg = desc.style.color_bg; + color_bg = desc.style.color_fg; + } + if (ui_widget_id_equals(ui->widget_hot, child->id)) + { + color_fg = desc.style.color_fg; + color_bg = desc.style.color_bg; + } + + if (has_flag(child->desc.style.flags, UIWidgetStyle_Outline)) + { + ui_sprite_push(ui, bg_min, bg_max, WHITE_SPRITE_ID, color_fg); + bg_min += v3{ 3, 3, 0}; + bg_max -= v3{ 3, 3, 0}; + } + + if (has_flag(child->desc.style.flags, UIWidgetStyle_Bg)) + { + bg_min.z += z_step; + bg_max.z += z_step; + ui_sprite_push(ui, bg_min, bg_max, desc.style.sprite, color_bg); + } + + if (has_flag(child->desc.style.flags, UIWidgetStyle_Text)) + { + // TODO(PS): + } + + if (child->child_first) + { + z_at = ui_widgets_to_geometry_recursive(ui, child, z_at + z_step, z_step); + } + + z_at += z_step; + } + return z_at; +} + +/////////////////////////////////////////// +// Specific Widget Implementations + +global UI_Style_Sheet ui_default_style_sheet = { + { + + } +}; + +internal UI_Widget_Style +ui_get_style(UI* ui, UI_Widget_Kind kind) +{ + if (ui->style_sheet) return ui->style_sheet->styles[kind]; + return ui_default_style_sheet.styles[kind]; +} + +internal void +ui_text(UI* ui, String string) +{ + +} + +internal bool +ui_button(UI* ui, String string) +{ + return false; +} + diff --git a/src_v2/editor/lumenarium_editor_ui.h b/src_v2/editor/lumenarium_editor_ui.h new file mode 100644 index 0000000..d2254c2 --- /dev/null +++ b/src_v2/editor/lumenarium_editor_ui.h @@ -0,0 +1,175 @@ +/* date = March 28th 2022 10:52 pm */ + +#ifndef LUMENARIUM_UI_H +#define LUMENARIUM_UI_H + +///////////////////////////////////////////////////////////// +// Interface + +struct UI_Vertex +{ + v4 pos; + v2 uv; + v4 color; +}; + +#define UI_WIDGET_ID_VALID_BIT 1 << 31 + +union UI_Widget_Id +{ + // equality of widget id's only relies on the value field + // which is a hash of the widget's string + u32 value; + + // this struct tracks the index of the widget only to be able + // to override next hot in cases where an earlier element became + // next hot that is in the same location as the current one + u32 index; +}; + +typedef u32 UI_Widget_Style_Flags; +enum +{ + UIWidgetStyle_None = 0, + UIWidgetStyle_Bg = 1, + UIWidgetStyle_Text = 2, + UIWidgetStyle_Outline = 4, + UIWidgetStyle_MouseClick = 8, + UIWidgetStyle_MouseDrag = 16, +}; + +// akin to a css class, could be used to style multiple +// elements +struct UI_Widget_Style +{ + UI_Widget_Style_Flags flags; + v4 color_bg; + v4 color_fg; + + u32 sprite; +}; + +// combination of style info and per-instance data +struct UI_Widget_Desc +{ + UI_Widget_Style style; + String string; + v2 p_min; + v2 p_max; +}; + +struct UI_Widget +{ + UI_Widget_Id id; + UI_Widget_Desc desc; + + UI_Widget* parent; + UI_Widget* next; + UI_Widget* child_first; + UI_Widget* child_last; +}; + +typedef u32 UI_Widget_Result_Flags; +enum +{ + UIWidgetResult_None = 0, + UIWidgetResult_MouseLeft_IsDown = 1, + UIWidgetResult_MouseLeft_WentUp = 2, +}; + +struct UI_Widget_Result +{ + UI_Widget_Result_Flags flags; +}; + +enum UI_Widget_Kind +{ + UIWidget_Text, + + // Buttons + UIWidget_Button, + UIWidget_Toggle, + UIWidget_Menu, + UIWidget_Dropdown, + + // Sliders + UIWidget_HSlider, + UIWidget_VSlider, + UIWidget_HScroll, + UIWidget_VScroll, + + // Panels + UIWidget_Window, + + UIWidget_Count, +}; + +struct UI_Style_Sheet +{ + UI_Widget_Style styles[UIWidget_Count]; +}; + +struct UI_Widget_Pool +{ + UI_Widget* free; + u32 free_cap; + u32 free_len; + + UI_Widget* root; + UI_Widget* active_parent; +}; + +struct UI +{ + UI_Vertex* verts; + u32 verts_len; + u32 verts_cap; + + u32* indices; + u32 indices_len; + u32 indices_cap; + + Texture_Atlas atlas; + + UI_Widget_Pool widgets; + UI_Style_Sheet* style_sheet; + + UI_Widget_Id widget_next_hot; + UI_Widget_Id widget_hot; + + // frames since these values were set + u16 widget_next_hot_frames; + u16 widget_hot_frames; + + Input_State* input; + + m44 proj; + Platform_Shader shader; + Platform_Texture atlas_texture; + Platform_Geometry_Buffer per_frame_buffer; +}; + +// Interface + +internal UI ui_create(); +internal void ui_quad_push(UI* ui, v3 pmin, v3 pmax, v2 tmin, v2 tmax, v4 c); +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 void ui_draw(UI* ui); + +// Widgets + +internal UI_Widget_Id ui_widget_id_create(u32 index_in_parent, String string); +internal bool ui_widget_id_equals(UI_Widget_Id a, UI_Widget_Id b); +internal bool ui_widget_id_is_valid(UI_Widget_Id h); + +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 r32 ui_widgets_to_geometry_recursive(UI* ui, UI_Widget* widget, r32 z_start, r32 z_step); + +#endif //LUMENARIUM_UI_H diff --git a/src_v2/editor/lumenarium_interface.cpp b/src_v2/editor/lumenarium_interface.cpp deleted file mode 100644 index ab0c014..0000000 --- a/src_v2/editor/lumenarium_interface.cpp +++ /dev/null @@ -1 +0,0 @@ -// \ No newline at end of file diff --git a/src_v2/editor/lumenarium_ui.h b/src_v2/editor/lumenarium_ui.h deleted file mode 100644 index 72c6af8..0000000 --- a/src_v2/editor/lumenarium_ui.h +++ /dev/null @@ -1,36 +0,0 @@ -/* date = March 28th 2022 10:52 pm */ - -#ifndef LUMENARIUM_UI_H -#define LUMENARIUM_UI_H - -struct Font_Glyph -{ - u32 code_point; - v2 uv_min; - v2 uv_max; -}; - -struct Font_Glyph_Table -{ - Font_Glyph* values; - u32 cap; - u32 len; -}; - -struct Font_Bitmap -{ - u8* pixels; - u32 width; - u32 height; - u32 stride; - - Platform_Texture texture; -}; - -struct Font -{ - Font_Glyph_Table glyphs; - Font_Bitmap bitmap; -}; - -#endif //LUMENARIUM_UI_H diff --git a/src_v2/engine/lumenarium_engine.cpp b/src_v2/engine/lumenarium_engine.cpp index 9870e8b..2f9bb08 100644 --- a/src_v2/engine/lumenarium_engine.cpp +++ b/src_v2/engine/lumenarium_engine.cpp @@ -3,6 +3,10 @@ internal void en_init(App_State* state, App_Init_Desc desc) { state->assemblies = assembly_array_create(permanent, desc.assembly_cap); + + Output* o = &state->output; + register_output_method(o, OutputData_NetworkSACN, output_network_sacn_build, output_network_sacn_init()); + register_output_method(o, OutputData_ComUART, output_network_sacn_build, output_com_uart_init()); } internal void @@ -15,6 +19,52 @@ internal void en_frame(App_State* state) { + /////////////////////////////////////// + // Output Data + + Output_Data_Queue output_queue = {}; + output_queue.a = scratch; + + // build output data buffers + Output_Methods methods; + Assembly_Array assemblies = state->assemblies; + for (u32 i = 0; i < assemblies.len; i++) + { + Assembly_Strip_Array strips = assemblies.strip_arrays[i]; + for (u32 j = 0; j < strips.len; j++) + { + Assembly_Strip* strip = strips.strips + j; + Build_Output_Data_Buffer* method_proc = methods.procs[strip->output_kind]; + u8* method_data = methods.method_data[strip->output_kind]; + if (method_proc == 0) continue; + method_proc(state, i, strip, method_data, &output_queue); + } + } + + // output the buffers + // TODO(PS): we could sort these first if switchig between output + // types is time consuming + for (Output_Data* at = output_queue.first; at != 0; at = at->next) + { + // NOTE(PS): we can overload each switch case as more methods come in + // ie. OutputData_NetworkSACN and OutputData_NetworkArtNet could use + // the same case since at this point they just need to push data out + // over a socket + switch (at->kind) + { + case OutputData_NetworkSACN: + { + // TODO(PS): platform network io + } break; + + case OutputData_ComUART: + { + // TODO(PS): platform com io + } break; + + invalid_code_path; + } + } } internal void diff --git a/src_v2/engine/lumenarium_engine_assembly.h b/src_v2/engine/lumenarium_engine_assembly.h index f105ff9..2c94fac 100644 --- a/src_v2/engine/lumenarium_engine_assembly.h +++ b/src_v2/engine/lumenarium_engine_assembly.h @@ -42,6 +42,9 @@ struct Assembly_Strip u32 pixels_len; // array of indices into the Assembly_Pixel_Buffer for the same assembly u32* pixels; + + Output_Data_Kind output_kind; + u32 sacn_universe; }; struct Assembly_Strip_Array diff --git a/src_v2/engine/lumenarium_engine_output.cpp b/src_v2/engine/lumenarium_engine_output.cpp new file mode 100644 index 0000000..92c23ad --- /dev/null +++ b/src_v2/engine/lumenarium_engine_output.cpp @@ -0,0 +1,24 @@ + +internal void +register_output_method(Output* output, Output_Data_Kind kind, Build_Output_Data_Buffer* proc, u8* method_data) +{ + output->methods.procs[kind] = proc; + output->methods.method_data[kind] = method_data; +} + +internal Output_Data* +output_data_queue_push(Output_Data_Queue* q, u32 size, Output_Data_Kind kind) +{ + Output_Data* d = allocator_alloc_struct(q->a, Output_Data); + d->kind = kind; + d->data.size = size; + d->data.base = allocator_alloc(q->a, size); + return d; +} + +internal void +output_data_set_network_addr(Output_Data* d, u32 send_addr, u32 port) +{ + d->network.v4_addr = send_addr; + d->network.port = port; +} diff --git a/src_v2/engine/lumenarium_engine_output.h b/src_v2/engine/lumenarium_engine_output.h new file mode 100644 index 0000000..d8b9a76 --- /dev/null +++ b/src_v2/engine/lumenarium_engine_output.h @@ -0,0 +1,67 @@ +/* date = March 30th 2022 9:23 am */ + +#ifndef LUMENARIUM_ENGINE_OUTPUT_H +#define LUMENARIUM_ENGINE_OUTPUT_H + +typedef u8 Output_Data_Kind; +enum +{ + OutputData_Invalid = 0, + OutputData_NetworkSACN, + OutputData_ComUART, + OutputData_Count, +}; + +struct Output_Data_Network +{ + // Platform_Socket_Handle socket; + u32 v4_addr; + u32 port; +}; + +struct Output_Data_Com +{ + String port; +}; + +struct Output_Data +{ + Output_Data_Kind kind; + union + { + Output_Data_Network network; + Output_Data_Com com; + }; + Data data; + + Output_Data* next; +}; + +struct Output_Data_Queue +{ + Output_Data* first; + Output_Data* last; + u32 len; + Allocator* a; +}; + +typedef void Build_Output_Data_Buffer(App_State* state, u32 assembly_id, Assembly_Strip* strip, u8* method_data, Output_Data_Queue* queue); + +struct Output_Methods +{ + Build_Output_Data_Buffer* procs[OutputData_Count]; + u8* method_data[OutputData_Count]; +}; + +struct Output +{ + Output_Methods methods; +}; + +internal void register_output_method(Output* output, Output_Data_Kind kind, Build_Output_Data_Buffer* proc, u8* method_data); + +internal Output_Data* output_data_queue_push(Output_Data_Queue* q, u32 size, Output_Data_Kind kind); + +internal void output_data_set_network_addr(Output_Data* d, u32 send_addr, u32 port); + +#endif //LUMENARIUM_ENGINE_OUTPUT_H diff --git a/src_v2/engine/output/lumenarium_output_sacn.cpp b/src_v2/engine/output/lumenarium_output_sacn.cpp new file mode 100644 index 0000000..c7f961a --- /dev/null +++ b/src_v2/engine/output/lumenarium_output_sacn.cpp @@ -0,0 +1,207 @@ +#define SACN_DEFAULT_PORT 5568 + + +// a description of the address space being used +#define SACN_PREAMBLE_SIZE_ADDR 0 +#define SACN_POSTAMBLE_SIZE_ADDR 2 +#define SACN_ACN_IDENTIFIER_ADDR 4 +#define SACN_ROOT_FLAGS_AND_LEN_ADDR 16 +#define SACN_ROOT_VECTOR_ADDR 18 +#define SACN_CID_ADDR 22 +#define SACN_FRAMING_FLAGS_AND_LEN_ADDR 38 +#define SACN_FRAMING_VECTOR_ADDR 40 +#define SACN_SOURCE_NAME_ADDR 44 +#define SACN_PRIORITY_ADDR 108 +#define SACN_RESERVED_ADDR 109 +#define SACN_SEQ_NUM_ADDR 111 +#define SACN_OPTIONS_ADDR 112 +#define SACN_UNIVERSE_ADDR 113 +#define SACN_DMP_FLAGS_AND_LEN_ADDR 115 +#define SACN_DMP_VECTOR_ADDR 117 +#define SACN_DMP_ADDRESS_AND_DATA_ADDR 118 +#define SACN_FIRST_PROPERTY_ADDRESS_ADDR 119 +#define SACN_ADDRESS_INC_ADDR 121 +#define SACN_PROP_COUNT_ADDR 123 +#define SACN_START_CODE_ADDR 125 +#define SACN_PROP_VALUES_ADDR (SACN_START_CODE_ADDR + 1) + +// Common Sizes +#define SACN_BUFFER_HEADER_SIZE 126 +#define SACN_BUFFER_BODY_SIZE 512 +#define SACN_BUFFER_SIZE (SACN_BUFFER_HEADER_SIZE + SACN_BUFFER_BODY_SIZE) + +#define SACN_SOURCE_NAME_SIZE 64 +#define SACN_ACN_IDENTIFIER_SIZE 12 +#define SACN_RLP_PREAMBLE_SIZE 16 +#define SACN_RLP_POSTAMBLE_SIZE 0 + +// Data Definitions +#define SACN_ACN_IDENTIFIER lit_str("ASC-E1.17\0\0\0") +#define SACN_ROOT_VECTOR 4 +#define SACN_FRAMING_VECTOR 2 +#define SACN_DMP_VECTOR 2 +#define SACN_ADDRESS_AND_DATA_FORMAT 0xa1 +#define SACN_ADDR_INC 1 +#define SACN_DMP_FIRST_PROPERTY_ADDRESS_FORCE 0 +#define SACN_RESERVED_VALUE 0 + +#define SACN_VHD_L_FLAG 0x80 +#define SACN_VHD_V_FLAG 0x40 +#define SACN_VHD_H_FLAG 0x20 +#define SACN_VHD_D_FLAG 0x10 + +#define SACN_VHD_MAXFLAGBYTES 7 //The maximum amount of bytes used to pack the flags, len, and vector +#define SACN_VHD_MAXLEN 0x0fffff //The maximum packet length is 20 bytes long +#define SACN_VHD_MAXMINLENGTH 4095 //The highest length that will fit in the "smallest" length pack + + +internal void +sacn_vhd_pack_flags(Data_Writer* w, b8 inherit_vec, b8 inherit_head, b8 inherit_data) +{ + u8 last_byte = dw_get_u8(w) & 0x8F; + w->at -= 1; + + if (!inherit_vec) { last_byte |= SACN_VHD_V_FLAG; } + if (!inherit_head) { last_byte |= SACN_VHD_H_FLAG; } + if (!inherit_data) { last_byte |= SACN_VHD_D_FLAG; } + + dw_put_u8(w, last_byte); +} + +internal void +sacn_vhd_pack_len(Data_Writer* w, u32 len, b8 include_len) +{ + u32 len_adjusted = len; + if (include_len) + { + if (len + 1 > SACN_VHD_MAXMINLENGTH) + { + len_adjusted += 2; + } + else + { + len_adjusted += 1; + } + } + + // Mask out the length bits to keep flags intact + u8 last_byte = dw_get_u8(w) & 0x70; + w->at -= 1; + + if (len_adjusted > SACN_VHD_MAXMINLENGTH) last_byte |= SACN_VHD_L_FLAG; + + u8* pack_buffer = (u8*)&len_adjusted; + if (len_adjusted <= SACN_VHD_MAXMINLENGTH) + { + last_byte |= (pack_buffer[1] & 0x0f); + dw_put_u8(w, last_byte); + dw_put_u8(w, pack_buffer[0]); + } + else + { + last_byte |= (pack_buffer[2] & 0x0f); + dw_put_u8(w, pack_buffer[1]); + dw_put_u8(w, pack_buffer[0]); + } +} + +internal void +sacn_fill_buffer_header(Output_Data* d, u16 universe, Sacn* sacn) +{ + assert(d && d->data.size > 0); + + // TODO(PS): these should be passed in? + u16 slot_count = 0; + u8 start_code = 0; + u8 priority = 0; + u16 reserved = 0; + u8 options = 0; + + Data_Writer w = {}; + w.data = d->data; + + dw_put_u16_b(&w, SACN_RLP_PREAMBLE_SIZE); + dw_put_u16_b(&w, SACN_RLP_POSTAMBLE_SIZE); + dw_put_str(&w, SACN_ACN_IDENTIFIER); + + sacn_vhd_pack_flags(&w, false, false, false); + sacn_vhd_pack_len(&w, SACN_BUFFER_HEADER_SIZE - SACN_RLP_PREAMBLE_SIZE + slot_count, false); + + dw_put_u32_b(&w, SACN_ROOT_VECTOR); + + for (u32 i = 0; i < SACN_CID_BYTES; i++) dw_put_u8(&w, sacn->cid.bytes[i]); + + sacn_vhd_pack_flags(&w, false, false, false); + sacn_vhd_pack_len(&w, SACN_BUFFER_HEADER_SIZE - SACN_FRAMING_FLAGS_AND_LEN_ADDR + slot_count, false); + + dw_put_u32_b(&w, SACN_FRAMING_VECTOR); + dw_put_str(&w, sacn->source_name); + + dw_put_u8(&w, priority); + dw_put_u16_b(&w, reserved); + dw_put_u8(&w, 0); // Sequence Number starts at 0, gets updated + dw_put_u8(&w, options); + dw_put_u16_b(&w, universe); + + sacn_vhd_pack_flags(&w, false, false, false); + sacn_vhd_pack_len(&w, SACN_BUFFER_HEADER_SIZE - SACN_DMP_FLAGS_AND_LEN_ADDR + slot_count, false); + + dw_put_u8(&w, SACN_DMP_VECTOR); + dw_put_u8(&w, SACN_ADDRESS_AND_DATA_FORMAT); + dw_put_u8(&w, 0); // dmp first priority addr + dw_put_u8(&w, SACN_ADDR_INC); // dmp address increment + dw_put_u16_b(&w, slot_count + 1); + dw_put_u8(&w, start_code); + + assert(w.at == SACN_BUFFER_HEADER_SIZE); +} + +internal void +sacn_fill_buffer_body(Output_Data* d, u32* leds_placed) +{ + +} + +internal Sacn_Cid +sacn_string_to_cid(String str) +{ + return {}; +} + +internal u32 +sacn_universe_to_send_addr(u32 universe) +{ + return 0; +} + +internal u8* +output_network_sacn_init() +{ + Sacn* result = allocator_alloc_struct(permanent, Sacn); + // TODO(PS): get platform send socket + + String cid_str = lit_str("{67F9D986-544E-4abb-8986-D5F79382586C}"); + result->cid = sacn_string_to_cid(cid_str); + + return (u8*)result; +} + +internal void +output_network_sacn_build(App_State* state, u32 assembly_id, Assembly_Strip* strip, u8* method_data, Output_Data_Queue* queue) +{ + Sacn* sacn = (Sacn*)method_data; + + u16 universe = (u16)strip->sacn_universe; + u32 send_port = SACN_DEFAULT_PORT; + for (u32 leds_placed = 0; leds_placed < strip->pixels_len;) + { + u32 v4_send_addr = sacn_universe_to_send_addr(universe); + + Output_Data* d = output_data_queue_push(queue, SACN_BUFFER_SIZE, OutputData_NetworkSACN); + output_data_set_network_addr(d, v4_send_addr, send_port); + + sacn_fill_buffer_header(d, universe, sacn); + sacn_fill_buffer_body(d, &leds_placed); + universe += 1; + } +} diff --git a/src_v2/engine/output/lumenarium_output_sacn.h b/src_v2/engine/output/lumenarium_output_sacn.h new file mode 100644 index 0000000..568020a --- /dev/null +++ b/src_v2/engine/output/lumenarium_output_sacn.h @@ -0,0 +1,22 @@ +/* date = March 30th 2022 9:55 am */ + +#ifndef LUMENARIUM_OUTPUT_SACN_H +#define LUMENARIUM_OUTPUT_SACN_H + +#define SACN_CID_BYTES 16 +struct Sacn_Cid +{ + u8 bytes[SACN_CID_BYTES]; +}; + +struct Sacn +{ + Sacn_Cid cid; + s32 sequence_iter; + String source_name; +}; + +internal u8* output_network_sacn_init(); +internal void output_network_sacn_build(App_State* state, u32 assembly_id, Assembly_Strip* strip, u8* method_data, Output_Data_Queue* queue); + +#endif //LUMENARIUM_OUTPUT_SACN_H diff --git a/src_v2/engine/output/lumenarium_output_uart.cpp b/src_v2/engine/output/lumenarium_output_uart.cpp new file mode 100644 index 0000000..cc34f65 --- /dev/null +++ b/src_v2/engine/output/lumenarium_output_uart.cpp @@ -0,0 +1,12 @@ + +internal u8* +output_com_uart_init() +{ + return 0; +} + +internal void +output_com_uart_build(App_State* state, u32 assembly_id, Assembly_Strip* strip, u8* method_data, Output_Data_Queue* queue) +{ + +} diff --git a/src_v2/engine/output/lumenarium_output_uart.h b/src_v2/engine/output/lumenarium_output_uart.h new file mode 100644 index 0000000..c23a6aa --- /dev/null +++ b/src_v2/engine/output/lumenarium_output_uart.h @@ -0,0 +1,9 @@ +/* date = March 30th 2022 9:57 am */ + +#ifndef LUMENARIUM_OUTPUT_UART_H +#define LUMENARIUM_OUTPUT_UART_H + +internal u8* output_com_uart_init(); +internal void output_com_uart_build(App_State* state, u32 assembly_id, Assembly_Strip* strip, u8* method_data, Output_Data_Queue* queue); + +#endif //LUMENARIUM_OUTPUT_UART_H diff --git a/src_v2/libs/HandmadeMath.h b/src_v2/libs/HandmadeMath.h index c80d4c3..8f71b31 100644 --- a/src_v2/libs/HandmadeMath.h +++ b/src_v2/libs/HandmadeMath.h @@ -462,6 +462,12 @@ extern "C" float Ignored0_; float Ignored1_; }; + struct + { + hmm_vec2 xy; + float Ignored0b_; + float Ignored1b_; + }; struct { @@ -469,6 +475,12 @@ extern "C" hmm_vec2 YZ; float Ignored3_; }; + struct + { + float Ignored2b_; + hmm_vec2 yz; + float Ignored3b_; + }; struct { @@ -477,6 +489,13 @@ extern "C" hmm_vec2 ZW; }; + struct + { + float Ignored4b_; + float Ignored5b_; + hmm_vec2 zw; + }; + float Elements[4]; } hmm_vec4; diff --git a/src_v2/lumenarium_first.cpp b/src_v2/lumenarium_first.cpp index 003f8d5..e4f4e0d 100644 --- a/src_v2/lumenarium_first.cpp +++ b/src_v2/lumenarium_first.cpp @@ -6,8 +6,9 @@ lumenarium_init() { App_State* state = 0; - permanent = bump_allocator_create_reserve(MB(4)); + permanent = bump_allocator_create_reserve(GB(1)); scratch = bump_allocator_create_reserve(KB(64)); + platform_file_jobs_init(); run_tests(); @@ -18,7 +19,7 @@ lumenarium_init() add_flag(state->flags, AppState_IsRunning); add_flag(state->flags, AppState_RunEditor); - state->input_state = input_state_create(); + state->input_state = input_state_create(permanent); en_init(state, desc); if (has_flag(state->flags, AppState_RunEditor)) @@ -34,7 +35,7 @@ lumenarium_frame_prepare(App_State* state) { allocator_clear(scratch); - input_state_swap_frames(&state->input_state); + input_state_swap_frames(state->input_state); en_frame_prepare(state); if (has_flag(state->flags, AppState_RunEditor)) @@ -42,12 +43,14 @@ lumenarium_frame_prepare(App_State* state) ed_frame_prepare(state); } incenter_frame_prepare(state); + + platform_file_async_jobs_do_work(4); } internal void lumenarium_frame(App_State* state) { - en_frame(state); + //en_frame(state); if (has_flag(state->flags, AppState_RunEditor)) { ed_frame(state); @@ -58,7 +61,7 @@ lumenarium_frame(App_State* state) internal void lumenarium_event(Platform_Window_Event evt, App_State* state) { - Input_Frame* frame = state->input_state.frame_hot; + Input_Frame* frame = state->input_state->frame_hot; switch (evt.kind) { case WindowEvent_MouseScroll: @@ -66,6 +69,13 @@ lumenarium_event(Platform_Window_Event evt, App_State* state) frame->mouse_scroll = evt.scroll_amt; } break; + case WindowEvent_MouseMoved: + { + v2 mouse_pos_old = frame->mouse_pos; + frame->mouse_pos = v2{ (r32)evt.mouse_x, (r32)evt.mouse_y }; + state->input_state->mouse_pos_delta = frame->mouse_pos - mouse_pos_old; + } break; + case WindowEvent_ButtonDown: case WindowEvent_ButtonUp: { diff --git a/src_v2/lumenarium_first.h b/src_v2/lumenarium_first.h index 441ef69..1d6c20c 100644 --- a/src_v2/lumenarium_first.h +++ b/src_v2/lumenarium_first.h @@ -9,11 +9,18 @@ typedef struct App_State App_State; #include "lumenarium_memory.cpp" #include "lumenarium_string.cpp" #include "lumenarium_input.cpp" +#include "lumenarium_texture_atlas.cpp" +#include "lumenarium_hash.cpp" // Engine +typedef struct Assembly_Strip Assembly_Strip; +#include "engine/lumenarium_engine_output.h" #include "engine/lumenarium_engine_assembly.h" +#include "engine/output/lumenarium_output_uart.h" +#include "engine/output/lumenarium_output_sacn.h" // Editor +#include "editor/lumenarium_editor_ui.h" #include "editor/lumenarium_editor_renderer.h" #include "editor/lumenarium_editor.h" @@ -49,15 +56,21 @@ struct App_State { App_State_Flags flags; - Input_State input_state; + Input_State* input_state; + Assembly_Array assemblies; + Output output; Editor* editor; }; #include "engine/lumenarium_engine_assembly.cpp" #include "engine/lumenarium_engine.cpp" +#include "engine/lumenarium_engine_output.cpp" +#include "engine/output/lumenarium_output_uart.cpp" +#include "engine/output/lumenarium_output_sacn.cpp" +#include "editor/lumenarium_editor_ui.cpp" #include "editor/lumenarium_editor_renderer.cpp" #include "editor/lumenarium_editor.cpp" diff --git a/src_v2/lumenarium_hash.cpp b/src_v2/lumenarium_hash.cpp new file mode 100644 index 0000000..d219110 --- /dev/null +++ b/src_v2/lumenarium_hash.cpp @@ -0,0 +1,58 @@ +/* date = April 1st 2022 7:29 pm */ + +#ifndef LUMENARIUM_HASH_CPP +#define LUMENARIUM_HASH_CPP + +// +// DJB2 +// Source: http://www.cse.yorku.ca/~oz/hash.html + +internal u32 +hash_djb2_to_u32(char* str, u64 len) +{ + u32 result = 5381; + for (u64 i = 0; i < len; i++) + { + result = ((result << 5) + result) + (u8)str[i]; + } + return result; +} + +internal u32 +hash_djb2_to_u32(char* str) +{ + u64 len = c_str_len(str); + return hash_djb2_to_u32(str, len); +} + +internal u32 +hash_djb2_to_u32(String str) +{ + return hash_djb2_to_u32((char*)str.str, str.len); +} + +internal u64 +hash_djb2_to_u64(char* str, u64 len) +{ + u64 result = 5381; + for (u64 i = 0; i < len; i++) + { + result = ((result << 5) + result) + (u8)str[i]; + } + return result; +} + +internal u64 +hash_djb2_to_u64(char* str) +{ + u64 len = c_str_len(str); + return hash_djb2_to_u64(str, len); +} + +internal u64 +hash_djb2_to_u64(String str) +{ + return hash_djb2_to_u64((char*)str.str, str.len); +} + +#endif //LUMENARIUM_HASH_CPP diff --git a/src_v2/lumenarium_input.cpp b/src_v2/lumenarium_input.cpp index 8937018..9b6344b 100644 --- a/src_v2/lumenarium_input.cpp +++ b/src_v2/lumenarium_input.cpp @@ -24,15 +24,30 @@ struct Input_State #define key_was_down(key_flags) has_flag((key_flags), KeyFlag_State_WasDown) #define key_is_down(key_flags) has_flag((key_flags), KeyFlag_State_IsDown) -#define key_was_up(key_flags) (!has_flag((key_flags), KeyFlag_State_WasDown) -#define key_is_up(key_flags) (!has_flag((key_flags), KeyFlag_State_IsDown) +#define key_was_up(key_flags) (!has_flag((key_flags), KeyFlag_State_WasDown)) +#define key_is_up(key_flags) (!has_flag((key_flags), KeyFlag_State_IsDown)) -internal Input_State -input_state_create() +internal Input_State* +input_state_create(Allocator* a) { - Input_State result = {}; - result.frame_hot = result.frames + 0; - result.frame_cold = result.frames + 1; + Input_State* result = allocator_alloc_struct(a, Input_State); + result->frame_hot = result->frames + 0; + result->frame_cold = result->frames + 1; + return result; +} + +internal Platform_Key_Flags +input_key_advance(Platform_Key_Flags flag) +{ + Platform_Key_Flags result = flag; + if (key_is_down(flag)) + { + add_flag(result, KeyFlag_State_WasDown); + } + if (key_is_up(flag)) + { + rem_flag(result, KeyFlag_State_WasDown); + } return result; } @@ -46,7 +61,10 @@ input_state_swap_frames(Input_State* input_state) // Clear the new hot input frame Platform_Key_Flags* hot_key_flags = input_state->frame_hot->key_flags; Platform_Key_Flags* cold_key_flags = input_state->frame_cold->key_flags; - for (u32 i = 0; i < KeyCode_Count; i++) hot_key_flags[i] = cold_key_flags[i]; + for (u32 i = 0; i < KeyCode_Count; i++) + { + hot_key_flags[i] = input_key_advance(cold_key_flags[i]); + } input_state->frame_hot->string_input_len = 0; } diff --git a/src_v2/lumenarium_memory.cpp b/src_v2/lumenarium_memory.cpp index f6a3421..103396f 100644 --- a/src_v2/lumenarium_memory.cpp +++ b/src_v2/lumenarium_memory.cpp @@ -32,21 +32,25 @@ memory_copy_simd(u8* from, u8* to, u64 size) memory_copy_no_simd(from, to, size); } -# define memory_zero(b,s) memory_zero_simd((b),(s)) -# define memory_copy(f,t,s) memory_copy_simd((f),(t),(s)) +# define memory_zero_(b,s) memory_zero_simd((b),(s)) +# define memory_copy_(f,t,s) memory_copy_simd((f),(t),(s)) #else -# define memory_zero(b,s) memory_zero_no_simd((b),(s)) -# define memory_copy(f,t,s) memory_copy_no_simd((f),(t),(s)) +# define memory_zero_(b,s) memory_zero_no_simd((b),(s)) +# define memory_copy_(f,t,s) memory_copy_no_simd((f),(t),(s)) #endif // defined(PLATFORM_HAS_SIMD) #define zero_struct(s) memory_zero((u8*)(&s), sizeof(s)) +internal void memory_zero(u8* base, u64 size) { memory_zero_(base, size); } +internal void memory_copy(u8* from, u8* to, u64 size) { + memory_copy_(from, to, size); +} + u64 -size_to_pages(u64 size) +round_size_to_page_multiple(u64 size, u64 page_size) { - u64 page_size = platform_page_size(); u64 rem = size % page_size; if (rem != 0 || size < page_size) { @@ -56,6 +60,13 @@ size_to_pages(u64 size) return size; } +u64 +round_size_to_page_multiple(u64 size) +{ + u64 page_size = platform_page_size(); + return round_size_to_page_multiple(size, page_size); +} + ///////////////////////////////////////// // Allocator // @@ -64,37 +75,14 @@ size_to_pages(u64 size) // To implement a complete allocator, all that is really required // is to create its Allocator_Alloc function -typedef struct Allocator Allocator; - -typedef u8* Allocator_Alloc(Allocator* allocator, u64 size); -typedef void Allocator_Free(Allocator* allocator, u8* base, u64 size); -typedef u8* Allocator_Realloc(Allocator* allocator, u8* base, u64 old_size, u64 new_size); -typedef void Allocator_Clear(Allocator* allocator); - -struct Allocator +internal void +allocator_destroy_(Allocator* allocator, u64 custom_data_size) { - Allocator_Alloc* alloc; - Allocator_Free* free; - Allocator_Realloc* realloc; - Allocator_Clear* clear; - - Allocator* parent; - - u8* allocator_data; -}; - -#define allocator_alloc(a,s) (a)->alloc((a),(s)) -#define allocator_alloc_struct(a,t) (t*)(a)->alloc((a),sizeof(t)) -#define allocator_alloc_array(a,t,c) (t*)(a)->alloc((a),sizeof(t)*(c)) - -#define allocator_free(a,b,s) (a)->free((a),(b),(s)) -#define allocator_free_struct(a,b,t) (a)->free((a),(b),sizeof(t)) -#define allocator_free_array(a,b,t,c) (a)->free((a),(b),sizeof(t)*(c)) - -#define allocator_realloc(a,b,os,ns) (a)->realloc((a),(b),(os),(ns)) -#define allocator_realloc_array(a,b,t,oc,nc) (t*)(a)->realloc((a),(b),sizeof(t)*(oc),sizeof(t)*(nc)) - -#define allocator_clear(a) (a)->clear(a) + zero_struct(*allocator); + u64 size = sizeof(Allocator) + custom_data_size; + platform_mem_decommit((u8*)allocator, size); + platform_mem_release((u8*)allocator, size); +} ///////////////////////////////////////// // Bump Allocator @@ -105,13 +93,12 @@ struct Allocator_Bump u64 at; u64 size_committed; u64 size_reserved; + u64 page_size; }; internal u8* -bump_allocator_alloc(Allocator* allocator, u64 size) +bump_allocator_alloc_inner(Allocator* allocator, Allocator_Bump* bump, u64 size) { - Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data; - u64 at_after = bump->at + size; // TODO(PS): align up to 8 bytes @@ -119,8 +106,15 @@ bump_allocator_alloc(Allocator* allocator, u64 size) { // determine new size of the arena u64 new_size = bump->size_committed * 2; - if (new_size == 0) new_size = platform_page_size(); - if (new_size < at_after) new_size = size_to_pages(at_after); + if (new_size == 0) + { + if (bump->page_size == 0) bump->page_size = platform_page_size(); + new_size = bump->page_size; + } + if (new_size < at_after) + { + new_size = round_size_to_page_multiple(at_after, bump->page_size); + } if (allocator->parent) { @@ -140,7 +134,7 @@ bump_allocator_alloc(Allocator* allocator, u64 size) { if (new_size <= bump->size_reserved) { - u64 next_page = size_to_pages(bump->at); + u64 next_page = round_size_to_page_multiple(bump->at); if (bump->at == 0 && bump->size_committed == 0) next_page = 0; u64 commit_amt = new_size - next_page; u8* new_page = platform_mem_commit(bump->base + next_page, commit_amt); @@ -162,6 +156,14 @@ bump_allocator_alloc(Allocator* allocator, u64 size) return result; } +internal u8* +bump_allocator_alloc(Allocator* allocator, u64 size) +{ + Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data; + u8* result = bump_allocator_alloc_inner(allocator, bump, size); + return result; +} + internal u8* bump_allocator_realloc(Allocator* allocator, u8* base, u64 old_size, u64 new_size) { @@ -178,6 +180,21 @@ bump_allocator_clear(Allocator* allocator) bump->at = 0; } +internal void +bump_allocator_destroy_(Allocator_Bump* bump) +{ + platform_mem_decommit(bump->base, bump->size_committed); + platform_mem_release(bump->base, bump->size_reserved); +} + +internal void +bump_allocator_destroy(Allocator* allocator) +{ + Allocator_Bump* bump = (Allocator_Bump*)allocator->allocator_data; + bump_allocator_destroy_(bump); + allocator_destroy_(allocator, sizeof(Allocator_Bump)); +} + internal Allocator* bump_allocator_create_() { @@ -195,6 +212,7 @@ bump_allocator_create_() result->alloc = bump_allocator_alloc; result->realloc = bump_allocator_realloc; result->clear = bump_allocator_clear; + result->destroy = bump_allocator_destroy; result->allocator_data = (u8*)bump; return result; @@ -206,7 +224,7 @@ bump_allocator_create_reserve(u64 reserve_size) Allocator* result = bump_allocator_create_(); Allocator_Bump* bump = (Allocator_Bump*)result->allocator_data; - u64 reserve_pages = size_to_pages(reserve_size); + u64 reserve_pages = round_size_to_page_multiple(reserve_size); bump->base = platform_mem_reserve(reserve_pages); if (bump->base != 0) bump->size_reserved = reserve_pages; @@ -229,3 +247,286 @@ bump_allocator_create_child(Allocator* parent, u64 init_size) return result; } + +///////////////////////////////////////// +// Paged Allocator + +struct Allocator_Paged_Free_Region +{ + u64 pages; + Allocator_Paged_Free_Region* prev; + Allocator_Paged_Free_Region* next; +}; + +struct Allocator_Paged +{ + Allocator_Bump bump; + Allocator_Paged_Free_Region* free_first; +}; + +internal u8* +paged_allocator_alloc(Allocator* allocator, u64 size) +{ + // 1. Find the number of pages we need + // 2. Find a run of free pages that we can use + // If found, + // remove those pages from the run they are in + // return those pages of memory + // 3. Commit pages on the end + + Allocator_Paged* paged = (Allocator_Paged*)allocator->allocator_data; + if (paged->bump.page_size == 0) paged->bump.page_size = platform_page_size(); + + u64 rounded_size = round_size_to_page_multiple(size, paged->bump.page_size); + u64 pages_needed = rounded_size / paged->bump.page_size; + + u8* result = 0; + + // Find free pages + if (paged->free_first) + { + Allocator_Paged_Free_Region* found = 0; + for (Allocator_Paged_Free_Region* at = paged->free_first; at != 0; at = at->next) + { + // NOTE(PS): this set of conditions checks to see if is bigger than what + // we need. If it is, we also check to see if this is smaller than any + // region we've found before. And we abort the search if this region + // perfectly fits the size needed. + // + // This should make sure that we are always choosing the closest fit we + // can. I'm not sure this is the best strategy for dealing with fragmentation + // but its a decent first pass + if (at->pages >= pages_needed) + { + if (!found || (found->pages > at->pages)) + { + found = at; + if (found->pages == pages_needed) break; + } + } + } + + if (found) + { + result = (u8*)found; + if (found->pages > pages_needed) + { + Allocator_Paged_Free_Region* region_after = (Allocator_Paged_Free_Region*)(result + rounded_size); + if (found->prev != 0) found->prev->next = region_after; + region_after = found->next; + } + else + { + if (found->prev != 0) found->prev->next = found->next; + } + } + } + + if (!result) + { + result = bump_allocator_alloc_inner(allocator, &paged->bump, size); + } + + return result; +} + +#define region_end(r,page_size) ((u8*)(r) + ((r)->pages * page_size)) + +internal void +paged_region_insert( + Allocator_Paged_Free_Region* before, + Allocator_Paged_Free_Region* new_region, + Allocator_Paged_Free_Region* after, + u64 page_size + ){ + assert(after == 0 || before < after); + assert(before < new_region); + assert(after == 0 || new_region < after); + assert(new_region->prev == 0 && new_region->next == 0); + + u8* before_end = region_end(before, page_size); + u8* new_region_end = region_end(new_region, page_size); + + // Before + if (before_end == (u8*)new_region) + { + // merge the regions + before->pages += new_region->pages; + new_region = before; + assert(new_region_end == region_end(new_region, page_size)); + } + else + { + assert(before_end < (u8*)new_region); + before->next = new_region; + new_region->prev = before; + } + + // After + if (after != 0) + { + if (new_region_end == (u8*)after) + { + // merge the regions + new_region->pages += after->pages; + u8* a = region_end(after, page_size); + u8* b = region_end(new_region, page_size); + assert(a == b); + } + else + { + assert(new_region_end < (u8*)after); + new_region->next = after; + after->prev = new_region; + } + } +} + +internal void +paged_allocator_free(Allocator* allocator, u8* base, u64 size) +{ + // Figure out which page base is the base of, assert its the base + // figure out how many pages size represents. + // create a free range + // stick it in between contiguous free ranges + // if the ranges before or after meet this new one, merge them all + + Allocator_Paged* paged = (Allocator_Paged*)allocator->allocator_data; + + u64 page_base_rel = (base - paged->bump.base); + assert((page_base_rel % paged->bump.page_size) == 0); + u64 page_index = page_base_rel / paged->bump.page_size; + u64 size_pages_mult = round_size_to_page_multiple(size, paged->bump.page_size); + assert((size_pages_mult % paged->bump.page_size) == 0); + u64 page_count = size_pages_mult / paged->bump.page_size; + + Allocator_Paged_Free_Region* region = (Allocator_Paged_Free_Region*)base; + zero_struct(*region); + region->pages = page_count; + + Allocator_Paged_Free_Region* prev = 0; + Allocator_Paged_Free_Region* next = 0; + for (Allocator_Paged_Free_Region* at = paged->free_first; at != 0; at = at->next) + { + if (at < region) + { + prev = at; + next = at->next; + if (next != 0) + { + assert(next > region); + assert((u8*)next >= ((u8*)region + size_pages_mult)); + } + } + } + + if (prev && next) + { + // found a region to insert into + paged_region_insert(prev, region, next, paged->bump.page_size); + } + else if (prev) + { + // got to the end and all were before the free region in memory + paged_region_insert(prev, region, 0, paged->bump.page_size); + } + else + { + // free list is empty + paged->free_first = region; + } +} + +internal u8* +paged_allocator_realloc(Allocator* allocator, u8* base, u64 old_size, u64 new_size) +{ + // TODO(PS): + // Process: + // 1. Figure out which page base starts on + // 2. Find if there is a free region after base that is big enough to house + // the new size + // 3. If there is a free region, pull the needed memory out of it + // 4. Otherwise, alloc new_size, copy base into it, and free base + + // TODO(PS): you could do a simple version where you just always alloc, copy, free + return 0; +} + +internal void +paged_allocator_clear(Allocator* allocator) +{ + if (!allocator->allocator_data) return; + Allocator_Paged* paged = (Allocator_Paged*)allocator->allocator_data; + paged->bump.at = 0; + paged->free_first = 0; +} + +internal void +paged_allocator_destroy(Allocator* allocator) +{ + Allocator_Paged* paged = (Allocator_Paged*)allocator->allocator_data; + bump_allocator_destroy_(&paged->bump); + allocator_destroy_(allocator, sizeof(Allocator_Paged)); +} + +internal Allocator* +paged_allocator_create_() +{ + u64 size_needed = sizeof(Allocator) + sizeof(Allocator_Bump); + + u8* base = platform_mem_reserve(size_needed); + base = platform_mem_commit(base, size_needed); + + Allocator* result = (Allocator*)base; + zero_struct(*result); + + Allocator_Bump* bump = (Allocator_Bump*)base + sizeof(Allocator); + zero_struct(*bump); + + result->alloc = paged_allocator_alloc; + result->free = paged_allocator_free; + result->realloc = paged_allocator_realloc; + result->clear = paged_allocator_clear; + result->destroy = paged_allocator_destroy; + result->allocator_data = (u8*)bump; + + return result; +} + +internal Allocator* +paged_allocator_create_reserve(u64 reserve_size, u64 page_size) +{ + Allocator* result = paged_allocator_create_(); + Allocator_Paged* paged = (Allocator_Paged*)result->allocator_data; + + u64 reserve_pages = round_size_to_page_multiple(reserve_size); + paged->bump.page_size = page_size; + paged->bump.base = platform_mem_reserve(reserve_pages); + if (paged->bump.base != 0) paged->bump.size_reserved = reserve_pages; + + return result; +} + +internal Allocator* +paged_allocator_create_reserve(u64 reserve_size) +{ + u64 page_size = platform_page_size(); + return paged_allocator_create_reserve(reserve_size, page_size); +} + +internal Allocator* +paged_allocator_create_child(Allocator* parent, u64 init_size) +{ + Allocator* result = bump_allocator_create_(); + result->parent = parent; + + Allocator_Paged* paged = (Allocator_Paged*)result->allocator_data; + paged->bump.base = allocator_alloc(result->parent, init_size); + if (paged->bump.base != 0) + { + paged->bump.size_reserved = init_size; + paged->bump.size_committed = init_size; + } + + return result; +} diff --git a/src_v2/lumenarium_memory.h b/src_v2/lumenarium_memory.h new file mode 100644 index 0000000..8b6a79f --- /dev/null +++ b/src_v2/lumenarium_memory.h @@ -0,0 +1,45 @@ +/* date = April 6th 2022 7:55 pm */ + +#ifndef LUMENARIUM_MEMORY_H +#define LUMENARIUM_MEMORY_H + +///////////////////////////////////////// +// Allocator + +typedef struct Allocator Allocator; + +typedef u8* Allocator_Alloc(Allocator* allocator, u64 size); +typedef void Allocator_Free(Allocator* allocator, u8* base, u64 size); +typedef u8* Allocator_Realloc(Allocator* allocator, u8* base, u64 old_size, u64 new_size); +typedef void Allocator_Clear(Allocator* allocator); +typedef void Allocator_Destroy(Allocator* allocator); + +struct Allocator +{ + Allocator_Alloc* alloc; + Allocator_Free* free; + Allocator_Realloc* realloc; + Allocator_Clear* clear; + Allocator_Destroy* destroy; + + Allocator* parent; + + u8* allocator_data; +}; + +#define allocator_alloc(a,s) (a)->alloc((a),(s)) +#define allocator_alloc_struct(a,t) (t*)(a)->alloc((a),sizeof(t)) +#define allocator_alloc_array(a,t,c) (t*)(a)->alloc((a),sizeof(t)*(c)) + +#define allocator_free(a,b,s) (a)->free((a),(b),(s)) +#define allocator_free_struct(a,b,t) (a)->free((a),(b),sizeof(t)) +#define allocator_free_array(a,b,t,c) (a)->free((a),(b),sizeof(t)*(c)) + +#define allocator_realloc(a,b,os,ns) (a)->realloc((a),(b),(os),(ns)) +#define allocator_realloc_array(a,b,t,oc,nc) (t*)(a)->realloc((a),(b),sizeof(t)*(oc),sizeof(t)*(nc)) + +#define allocator_clear(a) (a)->clear(a) +#define allocator_destroy(a) (a)->destroy(a) + +internal Allocator* paged_allocator_create_reserve(u64 reserve_size, u64 page_size); +#endif //LUMENARIUM_MEMORY_H diff --git a/src_v2/lumenarium_string.cpp b/src_v2/lumenarium_string.cpp index 157f2c6..adff229 100644 --- a/src_v2/lumenarium_string.cpp +++ b/src_v2/lumenarium_string.cpp @@ -54,6 +54,16 @@ char_to_forward_slash(u8 c) // Note that these don't actually modify any memory // just return structures that let you view it differently +internal String +string_create(u8* str, u64 len, u64 cap) +{ + String result = {}; + result.str = str; + result.len = len; + result.cap = cap; + return result; +} + internal String string_substring(String s, u64 min, u64 max) { @@ -213,15 +223,25 @@ string_chop_last_slash(String s) ///////////////////////////////////// // String Modifications +internal u64 +string_copy_to(String* dest, String src) +{ + u64 len_to_copy = dest->cap < src.len ? dest->cap : src.len; + memory_copy(src.str, dest->str, len_to_copy); + u64 null_term_index = len_to_copy; + if (null_term_index >= dest->cap) null_term_index -= 1; + dest->str[null_term_index] = 0; + dest->len = null_term_index; + return null_term_index; +} + internal String string_copy(String s, Allocator* a) { u64 size = s.cap; if (s.str[s.cap] != 0) size += 1; String result = allocator_alloc_string(a, size); - memory_copy(s.str, result.str, s.cap); - result.str[size] = 0; - result.len = size; + string_copy_to(&result, s); return result; } diff --git a/src_v2/lumenarium_tests.cpp b/src_v2/lumenarium_tests.cpp index 7472451..a7f99f5 100644 --- a/src_v2/lumenarium_tests.cpp +++ b/src_v2/lumenarium_tests.cpp @@ -6,6 +6,90 @@ thread_proc(Platform_Thread_Data* td) return {}; } +void +memory_allocator_tests(Allocator* a, bool run_free_tests) +{ + // TestGroup("Allocator Push") + { + for (u32 i = 0; i < 3; i++) + { + u8* buf0 = allocator_alloc(a, 256); + buf0[0] = 200; + buf0[255] = 199; + assert(buf0[0] == 200); + assert(buf0[255] == 199); + + u8* buf1 = allocator_alloc(a, 256); + buf1[0] = 201; + buf1[255] = 202; + assert(buf1 >= (buf0 + 256)); + assert(buf0[0] == 200); + assert(buf0[255] == 199); + assert(buf1[0] == 201); + assert(buf1[255] == 202); + + allocator_clear(a); + } + } + + // TestGroup("Allocator Free") + if (run_free_tests) + { + for (u32 i = 0; i < 3; i++) + { + u8* buf0 = allocator_alloc(a, KB(4)); + u8* buf1 = allocator_alloc(a, KB(4)); + u8* buf2 = allocator_alloc(a, KB(4)); + u8* buf3 = allocator_alloc(a, KB(4)); + u8* buf4 = allocator_alloc(a, KB(4)); + assert((buf1 - buf0) >= KB(4)); + assert((buf2 - buf0) >= KB(8)); + assert((buf3 - buf0) >= KB(12)); + assert((buf4 - buf0) >= KB(16)); + + allocator_free(a, buf1, KB(4)); + allocator_free(a, buf2, KB(4)); + u8* buf5 = allocator_alloc(a, KB(7)); + // buf5 should get put in the place of buf1 since buf1 and 2 get + // merged + assert(buf5 == buf1); + + allocator_free(a, buf4, KB(4)); + allocator_free(a, buf3, KB(4)); + allocator_free(a, buf0, KB(4)); + u8* buf6 = allocator_alloc(a, KB(4)); + assert(buf0 == buf6); + + allocator_clear(a); + } + } +} + +void +memory_tests() +{ + // TestGroup("Platform Allocation") + { + u8* base = platform_mem_reserve(GB(32)); + platform_mem_commit(base, KB(4)); + base[4095] = 200; + assert(base[4095] == 200); + platform_mem_commit(base + KB(4), KB(4)); + base[5000] = 200; + assert(base[5000] == 200); + platform_mem_decommit(base, KB(8)); + platform_mem_release(base, GB(32)); + } + + Allocator* bump = bump_allocator_create_reserve(KB(32)); + memory_allocator_tests(bump, false); + allocator_destroy(bump); + + Allocator* paged = paged_allocator_create_reserve(KB(32)); + memory_allocator_tests(paged, true); + allocator_destroy(paged); +} + internal void run_tests() { @@ -29,6 +113,8 @@ run_tests() assert(a1[i] == (100 + i)); } + memory_tests(); + #if defined(PLATFORM_wasm) // NOTE(PS): the tests below this point don't make sense on a web assembly // platform diff --git a/src_v2/lumenarium_texture_atlas.cpp b/src_v2/lumenarium_texture_atlas.cpp new file mode 100644 index 0000000..e56888f --- /dev/null +++ b/src_v2/lumenarium_texture_atlas.cpp @@ -0,0 +1,128 @@ +/* date = March 31st 2022 8:30 pm */ + +#ifndef LUMENARIUM_TEXTURE_ATLAS_CPP +#define LUMENARIUM_TEXTURE_ATLAS_CPP + +struct Texture_Atlas_Sprite +{ + u16 min_x; + u16 min_y; + u16 max_x; + u16 max_y; +}; + +struct Texture_Atlas +{ + u8* pixels; + u16 width; + u16 height; + + u16 next_x; + u16 next_y; + u16 y_used; + + u32* ids; + Texture_Atlas_Sprite* sprites; + u32 cap; + u32 used; +}; + +internal Texture_Atlas +texture_atlas_create(u32 width, u32 height, u32 cap, Allocator* allocator) +{ + Texture_Atlas result = {}; + result.pixels = allocator_alloc_array(allocator, u8, width * height * 4); + result.width = (u16)width; + result.height = (u16)height; + for (u32 i = 0; i < width * height; i++) { + u8* base = result.pixels + (i * 4); + *(u32*)base = 0xFFFFFFFF; + } + + result.ids = allocator_alloc_array(allocator, u32, cap); + result.sprites = allocator_alloc_array(allocator, Texture_Atlas_Sprite, cap); + result.cap = cap; + hash_table_init(result.ids, cap); + return result; +} + +internal void +texture_atlas_register(Texture_Atlas* ta, u8* pixels, u32 width, u32 height, u32 id) +{ + u16 min_x = ta->next_x; + u16 min_y = ta->next_y; + u16 max_x = min_x + (u16)width; + u16 max_y = min_y + (u16)height; + + // TODO(PS): if the sprite won't fit in this row, then we need to shift it to + // the next one + + // copy the data + for (u16 y = 0; y < height; y++) + { + u16 src_row = (y * (u16)width) * 4; + u16 dst_row = (((y + min_y) * ta->width) + min_x) * 4; + for (u16 x = 0; x < width; x++) + { + ta->pixels[dst_row++] = pixels[src_row++]; + ta->pixels[dst_row++] = pixels[src_row++]; + ta->pixels[dst_row++] = pixels[src_row++]; + ta->pixels[dst_row++] = pixels[src_row++]; + } + } + + // register a new slot + u32 index = hash_table_register(ta->ids, ta->cap, id); + + Texture_Atlas_Sprite* sprite = ta->sprites + index; + sprite->min_x = min_x; + sprite->min_y = min_y; + sprite->max_x = max_x; + sprite->max_y = max_y; + + // Prepare for next registration + if (max_y > ta->y_used) + { + ta->y_used = max_y; + } + + ta->next_x = max_x + 1; + if (ta->next_x > ta->width) + { + ta->next_x = 0; + ta->next_y = ta->y_used; + } +} + +internal Texture_Atlas_Sprite +texture_atlas_sprite_get(Texture_Atlas* ta, u32 id) +{ + Texture_Atlas_Sprite result = {}; + u32 index = hash_table_find(ta->ids, ta->cap, id); + if (index == ta->cap) return result; + result = ta->sprites[index]; + return result; +} + +internal v4 +texture_atlas_sprite_get_uvs(Texture_Atlas* ta, u32 id) +{ + Texture_Atlas_Sprite sprite = texture_atlas_sprite_get(ta, id); + v4 result = {}; + + // uv min + result.x = (r32)sprite.min_x / (r32)ta->width; + result.y = (r32)sprite.min_y / (r32)ta->height; + + // uv max + result.z = (r32)sprite.max_x / (r32)ta->width; + result.w = (r32)sprite.max_y / (r32)ta->height; + + // inset + v2 half_texel = v2{1.0f / ta->width, 1.0f / ta->height}; + result.xy += half_texel; + result.zw -= half_texel; + + return result; +} +#endif //LUMENARIUM_TEXTURE_ATLAS_CPP diff --git a/src_v2/lumenarium_types.h b/src_v2/lumenarium_types.h index e9eac22..d26977a 100644 --- a/src_v2/lumenarium_types.h +++ b/src_v2/lumenarium_types.h @@ -37,16 +37,149 @@ typedef s64 b64; typedef float r32; typedef double r64; +#define get_byte(value, byte_index) ((value >> (8 * byte_index)) & 0xFF) + struct Data { u8* base; u64 size; }; +internal Data +data_create(u8* base, u64 size) +{ + Data result = {}; + result.base = base; + result.size = size; + return result; +} + +internal void memory_zero(u8* base, u64 size); +internal void memory_copy(u8* from, u8* to, u64 size); + +////////////////////////////////////////////// +// String + +// NOTE(PS): even though this has a len and cap, it should always be +// null terminated +struct String +{ + u8* str; + u64 len; + u64 cap; +}; + +internal String string_create(u8* str, u64 len, u64 cap); +internal u64 string_copy_to(String* dest, String src); + +////////////////////////////////////////////// +// Data Writer + +struct Data_Writer +{ + Data data; + u64 at; +}; + +// NOTE(PS): functions ending in _b treat data in the Data_Writer as big endian +// order (network ordering) where functions ending in _l treat data into little +// endian order +// It is always assumed that values not in the Data_Writer (ie the other args +// to the function or the functions return value) are in little endian order + +internal void +dw_put_u8(Data_Writer* w, u8 b) +{ + if (w->at < w->data.size) + { + w->data.base[w->at++] = b; + } +} + +internal u8 +dw_get_u8(Data_Writer* w) +{ + u8 result = 0; + if (w->at < w->data.size) + { + result = w->data.base[w->at++]; + } + return result; +} + +internal void +dw_put_u16_b(Data_Writer* w, u16 b) +{ + dw_put_u8(w, get_byte(b, 1)); + dw_put_u8(w, get_byte(b, 0)); +} + +internal void +dw_put_u16_l(Data_Writer* w, u16 b) +{ + dw_put_u8(w, get_byte(b, 0)); + dw_put_u8(w, get_byte(b, 1)); +} + +internal void +dw_put_u32_b(Data_Writer* w, u32 b) +{ + dw_put_u8(w, get_byte(b, 3)); + dw_put_u8(w, get_byte(b, 2)); + dw_put_u8(w, get_byte(b, 1)); + dw_put_u8(w, get_byte(b, 0)); +} + +internal void +dw_put_u32_l(Data_Writer* w, u32 b) +{ + dw_put_u8(w, get_byte(b, 0)); + dw_put_u8(w, get_byte(b, 1)); + dw_put_u8(w, get_byte(b, 2)); + dw_put_u8(w, get_byte(b, 3)); +} + +internal void +dw_put_u64_b(Data_Writer* w, u64 b) +{ + dw_put_u8(w, get_byte(b, 7)); + dw_put_u8(w, get_byte(b, 6)); + dw_put_u8(w, get_byte(b, 5)); + dw_put_u8(w, get_byte(b, 4)); + dw_put_u8(w, get_byte(b, 3)); + dw_put_u8(w, get_byte(b, 2)); + dw_put_u8(w, get_byte(b, 1)); + dw_put_u8(w, get_byte(b, 0)); +} + +internal void +dw_put_u64_l(Data_Writer* w, u64 b) +{ + dw_put_u8(w, get_byte(b, 0)); + dw_put_u8(w, get_byte(b, 1)); + dw_put_u8(w, get_byte(b, 2)); + dw_put_u8(w, get_byte(b, 3)); + dw_put_u8(w, get_byte(b, 4)); + dw_put_u8(w, get_byte(b, 5)); + dw_put_u8(w, get_byte(b, 6)); + dw_put_u8(w, get_byte(b, 7)); +} + +internal void +dw_put_str(Data_Writer* w, String str) +{ + for (u64 i = 0; i < str.len; i++) + { + dw_put_u8(w, str.str[i]); + } +} + +// TODO(PS): get functions + #define Bytes(x) (x) #define KB(x) ((x) << 10) #define MB(x) ((x) << 20) -#define GB(x) ((x) << 30) +#define GB(x) (((u64)x) << 30) #define TB(x) (((u64)x) << 40) #define has_flag(data, flag) (((data) & (flag)) != 0) @@ -65,17 +198,66 @@ else { (last)->next = (ele); } \ // TODO(PS): Stack, Queue, DLL ops ////////////////////////////////////////////// -// String +// Hash Table +// +// Rather than define a data structure, to allow the most flexibility, +// this is just a set of functions that can be integrated into other +// routines. +// In general, they expect you to track a u32* of ids and a u32 capacity -// NOTE(PS): even though this has a len and cap, it should always be -// null terminated -struct String +internal void +hash_table_init(u32* ids, u32 cap) { - u8* str; - u64 len; - u64 cap; -}; + for (u32 i = 0; i < cap; i++) ids[i] = 0; +} + +internal u32 +hash_table_find_(u32* ids, u32 cap, u32 start_id, u32 target_value) +{ + u32 index = start_id % cap; + u32 start = index; + do { + if (ids[index] == target_value) break; + index = (index + 1) % cap; + } while (index != start); + return index; +} + +internal u32 +hash_table_register(u32* ids, u32 cap, u32 new_id) +{ + u32 index = hash_table_find_(ids, cap, new_id, 0); + if (ids[index] != 0) return cap; + ids[index] = new_id; + return index; +} + +internal u32 +hash_table_find(u32* ids, u32 cap, u32 value) +{ + u32 result = hash_table_find_(ids, cap, value, value); + if (ids[result] != value) return cap; + return result; +} + +////////////////////////////////////////////// +// Vector Extensions + +#define v2_to_v3(xy,z) v3{(xy).x, (xy).y, (z)} + +////////////////////////////////////////////// +// Color Constants + +#define WHITE_V4 v4{1,1,1,1} +#define BLACK_V4 v4{0,0,0,1} +#define RED_V4 v4{1,0,0,1} +#define GREEN_V4 v4{0,1,0,1} +#define BLUE_V4 v4{0,0,1,1} +#define YELLOW_V4 v4{1,1,0,1} +#define TEAL_V4 v4{0,1,1,1} +#define PINK_V4 v4{1,0,1,1} typedef struct Allocator Allocator; + #endif //LUMENARIUM_TYPES_H diff --git a/src_v2/platform/lumenarium_assert.h b/src_v2/platform/lumenarium_assert.h index 213d041..be84e78 100644 --- a/src_v2/platform/lumenarium_assert.h +++ b/src_v2/platform/lumenarium_assert.h @@ -34,7 +34,12 @@ WASM_EXTERN void wasm_assert_always(char* file, unsigned int file_len, unsigned #endif // defined(PLATFORM_WASM) #ifdef USE_ASSERTS -# define assert(c) if (!(c)) { err_write("Assert Hit: %s:%d\n", __FILE__, (u32)__LINE__); close_err_file(); assert_always; } +# define assert(c) \ +if (!(c)) { \ +err_write("Assert Hit: %s:%d\n", __FILE__, (u32)__LINE__); \ +close_err_file(); \ +assert_always; \ +} // useful for catching cases that you aren't sure you'll hit, but // want to be alerted when they happen diff --git a/src_v2/platform/lumenarium_platform.h b/src_v2/platform/lumenarium_platform.h index a96a830..0f50ddf 100644 --- a/src_v2/platform/lumenarium_platform.h +++ b/src_v2/platform/lumenarium_platform.h @@ -80,6 +80,148 @@ bool platform_file_write_all(Platform_File_Handle file_handle, Data file_data); String platform_get_exe_path(Allocator* allocator); bool platform_pwd_set(String path); +// For Cross Platform File Operations use these: + +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, +}; + +struct Platform_File_Async_Job_Args +{ + String path; + Data data; + Platform_File_Async_Job_Flags flags; + u32 error; +}; + +typedef void Platform_File_Async_Cb(Platform_File_Async_Job_Args args); + +struct Platform_File_Async_Job +{ + Data job_memory; + Platform_File_Async_Job_Args args; + Platform_File_Async_Cb* cb; +}; + +global Allocator* platform_file_jobs_arena = 0; +#define PLATFORM_FILE_ASYNC_MAX_JOBS 32 +global Platform_File_Async_Job platform_file_async_jobs[PLATFORM_FILE_ASYNC_MAX_JOBS]; +global u32 platform_file_async_jobs_len = 0; + +void +platform_file_jobs_init() +{ + platform_file_jobs_arena = paged_allocator_create_reserve(MB(4), 256); +} + +bool +platform_file_async_job_add(Platform_File_Async_Job job) +{ + if (platform_file_async_jobs_len >= PLATFORM_FILE_ASYNC_MAX_JOBS) return false; + + // Copy data to job local memory + u64 size_needed = job.args.path.len + job.args.data.size + 1; + u8* job_mem = allocator_alloc(platform_file_jobs_arena, size_needed); + String job_path = string_create(job_mem, 0, job.args.path.len + 1); + u64 copied = string_copy_to(&job_path, job.args.path); + Data job_data = data_create(job_mem + job_path.cap + 1, size_needed - (job_path.cap + 1)); + memory_copy(job.args.data.base, job_data.base, job.args.data.size); + job.args.path = job_path; + job.args.data = job_data; + job.job_memory = data_create(job_mem, size_needed); + + platform_file_async_jobs[platform_file_async_jobs_len++] = job; + return true; +} + +Platform_File_Async_Job +platform_file_async_job_rem(u64 index) +{ + assert(index < platform_file_async_jobs_len); + Platform_File_Async_Job result = platform_file_async_jobs[index]; + + platform_file_async_jobs_len -= 1; + if (platform_file_async_jobs_len > 0) + { + u32 last_job = platform_file_async_jobs_len; + platform_file_async_jobs[index] = platform_file_async_jobs[last_job]; + } + + return result; +} + +bool +platform_file_async_read(String path, Platform_File_Async_Cb* cb) +{ + Platform_File_Async_Job job = {}; + job.args.path = path; + job.args.flags = ( + PlatformFileAsyncJob_Read | + PlatformFileAsyncJob_InFlight + ); + job.cb = cb; + bool result = platform_file_async_job_add(job); + return result; +} + +bool +platform_file_async_write(String path, Data data, Platform_File_Async_Cb* cb) +{ + Platform_File_Async_Job job = {}; + job.args.path = path; + job.args.data = data; + job.args.flags = ( + PlatformFileAsyncJob_Write | + PlatformFileAsyncJob_InFlight + ); + job.cb = cb; + bool result = platform_file_async_job_add(job); + return result; +} + +void +platform_file_async_job_complete(Platform_File_Async_Job* job) +{ + job->cb(job->args); + allocator_free(platform_file_jobs_arena, job->job_memory.base, job->job_memory.size); + if (has_flag(job->args.flags, PlatformFileAsyncJob_Write)) + { + allocator_free(platform_file_jobs_arena, job->args.data.base, job->args.data.size); + } +} + +void platform_file_async_work_on_job(Platform_File_Async_Job* job); + +void +platform_file_async_jobs_do_work(u64 max_jobs) +{ + u64 to_do = max_jobs; + if (max_jobs > platform_file_async_jobs_len) to_do = platform_file_async_jobs_len; + + Platform_File_Async_Job_Flags completed = ( + PlatformFileAsyncJob_Success | + PlatformFileAsyncJob_Failed + ); + + for (u64 i = to_do - 1; i < to_do; i--) + { + Platform_File_Async_Job* job = platform_file_async_jobs + i; + platform_file_async_work_on_job(job); + if (has_flag(job->args.flags, completed)) + { + platform_file_async_job_complete(job); + platform_file_async_job_rem(i); + } + } +} + typedef u32 Platform_Enum_Dir_Flags; enum { @@ -100,6 +242,7 @@ enum Platform_Window_Event_Kind WindowEvent_Invalid = 0, WindowEvent_MouseScroll, + WindowEvent_MouseMoved, WindowEvent_ButtonDown, WindowEvent_ButtonUp, WindowEvent_Char, @@ -299,6 +442,7 @@ struct Platform_Shader { u32 id; u32 attrs[PLATFORM_SHADER_MAX_ATTRS]; + u32 uniforms[PLATFORM_SHADER_MAX_ATTRS]; }; struct Platform_Geometry_Buffer @@ -306,6 +450,7 @@ struct Platform_Geometry_Buffer u32 buffer_id_vao; u32 buffer_id_vertices; u32 buffer_id_indices; + u32 vertices_len; u32 indices_len; }; @@ -329,16 +474,19 @@ void platform_frame_clear(); // Geometry Platform_Geometry_Buffer platform_geometry_buffer_create(r32* vertices, u32 vertices_len, u32* indices, u32 indices_len); Platform_Shader platform_shader_create( - String code_vert, String code_frag, String* attribs, u32 attribs_len + String code_vert, String code_frag, String* attribs, u32 attribs_len, String* uniforms, u32 uniforms_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); // Shaders void platform_geometry_bind(Platform_Geometry_Buffer geo); void platform_shader_bind(Platform_Shader shader); +void platform_geometry_draw(Platform_Geometry_Buffer geo, u32 indices); void platform_geometry_draw(Platform_Geometry_Buffer geo); void platform_vertex_attrib_pointer( Platform_Geometry_Buffer geo, Platform_Shader shader, u32 count, u32 attr_index, u32 stride, u32 offset ); +void platform_set_uniform(Platform_Shader shader, u32 index, m44 u); // Textures Platform_Texture platform_texture_create(u8* pixels, u32 width, u32 height, u32 stride); diff --git a/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp b/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp index 88be506..0d04110 100644 --- a/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp +++ b/src_v2/platform/wasm/lumenarium_wasm_webgl.cpp @@ -20,8 +20,9 @@ typedef unsigned int GLsizei; // I resorted to hard coding these rather than passing them in because // passing them in didn't seem to be working. +#define GL_FALSE 0 +#define GL_TRUE 1 #define GL_TEXTURE_2D 0x0DE1 - #define GL_DEPTH_BUFFER_BIT 0x00000100 #define GL_STENCIL_BUFFER_BIT 0x00000400 #define GL_COLOR_BUFFER_BIT 0x00004000 @@ -68,6 +69,7 @@ WASM_EXTERN void glAttachShader(GLuint program, GLuint shader); 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 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,7 +111,7 @@ platform_geometry_buffer_create( Platform_Shader platform_shader_create( - String code_vert, String code_frag, String* attrs, GLuint attrs_len + String code_vert, String code_frag, String* attrs, GLuint attrs_len, String* uniforms, GLuint uniforms_len ){ Platform_Shader result = {}; @@ -139,6 +141,16 @@ platform_shader_create( } result.attrs[attrs_len] = PLATFORM_SHADER_ATTR_LAST; + assert(uniforms_len < PLATFORM_SHADER_MAX_ATTRS); + for (GLuint i = 0; i < uniforms_len; i++) + { + s32 len = (s32)uniforms[i].len; + result.uniforms[i] = glGetUniformLocation( + result.id, (char*)uniforms[i].str, uniforms[i].len + ); + } + result.uniforms[uniforms_len] = PLATFORM_SHADER_ATTR_LAST; + return result; } @@ -159,6 +171,19 @@ 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]); +} + +void +platform_geometry_draw( + Platform_Geometry_Buffer geo, u32 indices + ){ + glDrawElements(GL_TRIANGLES, indices, GL_UNSIGNED_INT, 0); +} + void platform_geometry_draw( Platform_Geometry_Buffer geo diff --git a/src_v2/platform/win32/lumenarium_first_win32.cpp b/src_v2/platform/win32/lumenarium_first_win32.cpp index d27f084..a18e2d3 100644 --- a/src_v2/platform/win32/lumenarium_first_win32.cpp +++ b/src_v2/platform/win32/lumenarium_first_win32.cpp @@ -7,6 +7,7 @@ #include #include "../../lumenarium_types.h" +#include "../../lumenarium_memory.h" #include "../lumenarium_platform.h" #include "../../lumenarium_first.cpp" @@ -215,6 +216,20 @@ WinMain( lumenarium_event(evt, state); } + // Get the position of the mouse every frame + { + POINT mouse_p; + GetCursorPos(&mouse_p); + ScreenToClient(win32_main_window.window_handle, &mouse_p); + + Platform_Window_Event evt = {}; + evt.kind = WindowEvent_MouseMoved; + evt.mouse_x = mouse_p.x; + evt.mouse_y = mouse_p.y; + + lumenarium_event(evt, state); + } + // Pass Window Events to the runtime MSG window_msg; while (PeekMessageA( diff --git a/src_v2/platform/win32/lumenarium_win32_file.cpp b/src_v2/platform/win32/lumenarium_win32_file.cpp index 344e785..6db4fc4 100644 --- a/src_v2/platform/win32/lumenarium_win32_file.cpp +++ b/src_v2/platform/win32/lumenarium_win32_file.cpp @@ -256,3 +256,36 @@ platform_pwd_set(String path) if (!result) win32_get_last_error(); return result; } + +void +platform_file_async_work_on_job(Platform_File_Async_Job* job) +{ + Platform_File_Handle file = {}; + if (has_flag(job->args.flags, PlatformFileAsyncJob_Read)) + { + file = platform_file_open(job->args.path, FileAccess_Read, FileCreate_OpenExisting); + Data result = platform_file_read_all(file, platform_file_jobs_arena); + if (result.base != 0) + { + job->args.data = result; + add_flag(job->args.flags, PlatformFileAsyncJob_Success); + } + else + { + add_flag(job->args.flags, PlatformFileAsyncJob_Failed); + } + } + else if (has_flag(job->args.flags, PlatformFileAsyncJob_Write)) + { + file = platform_file_open(job->args.path, FileAccess_Write, FileCreate_OpenAlways); + if (platform_file_write_all(file, job->args.data)) + { + add_flag(job->args.flags, PlatformFileAsyncJob_Success); + } + else + { + add_flag(job->args.flags, PlatformFileAsyncJob_Failed); + } + } + platform_file_close(file); +} \ No newline at end of file diff --git a/src_v2/platform/win32/lumenarium_win32_graphics.cpp b/src_v2/platform/win32/lumenarium_win32_graphics.cpp index 848d91d..cc9bd19 100644 --- a/src_v2/platform/win32/lumenarium_win32_graphics.cpp +++ b/src_v2/platform/win32/lumenarium_win32_graphics.cpp @@ -39,6 +39,7 @@ platform_geometry_buffer_create( GL_ARRAY_BUFFER, sizeof(r32) * vertices_len, vertices, GL_STATIC_DRAW ); win32_gl_no_error(); + result.vertices_len = vertices_len; // Indices gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, result.buffer_id_indices); @@ -56,9 +57,61 @@ 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 + ){ + gl.glBindVertexArray(buffer->buffer_id_vao); + gl.glBindBuffer(GL_ARRAY_BUFFER, buffer->buffer_id_vertices); + win32_gl_no_error(); + + 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); + gl.glBufferData( + GL_ARRAY_BUFFER, verts_len * sizeof(r32), (void*)verts, GL_STATIC_DRAW + ); + } + else + { + gl.glBufferSubData( + GL_ARRAY_BUFFER, verts_offset * sizeof(r32), verts_len * sizeof(r32), (void*)verts + ); + } + win32_gl_no_error(); + + gl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->buffer_id_indices); + win32_gl_no_error(); + 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); + gl.glBufferData( + GL_ELEMENT_ARRAY_BUFFER, indices_len * sizeof(u32), (void*)indices, GL_STATIC_DRAW + ); + } + else + { + gl.glBufferSubData( + GL_ELEMENT_ARRAY_BUFFER, indices_offset * sizeof(u32), indices_len * sizeof(u32), (void*)indices + ); + } + win32_gl_no_error(); +} + Platform_Shader platform_shader_create( - String code_vert, String code_frag, String* attrs, u32 attrs_len + String code_vert, String code_frag, String* attrs, u32 attrs_len, String* uniforms, u32 uniforms_len ){ Platform_Shader result = {}; @@ -117,12 +170,29 @@ platform_shader_create( result.attrs[i] = gl.glGetAttribLocation( result.id, (char*)attrs[i].str ); + win32_gl_no_error(); } result.attrs[attrs_len] = PLATFORM_SHADER_ATTR_LAST; + assert(uniforms_len < PLATFORM_SHADER_MAX_ATTRS); + for (GLuint i = 0; i < uniforms_len; i++) + { + s32 len = (s32)uniforms[i].len; + result.uniforms[i] = gl.glGetUniformLocation( + result.id, (char*)uniforms[i].str + ); + } + result.uniforms[uniforms_len] = PLATFORM_SHADER_ATTR_LAST; + return result; } +void +platform_set_uniform(Platform_Shader shader, u32 index, m44 u) +{ + gl.glUniformMatrix4fv(shader.uniforms[index], 1, GL_FALSE, (r32*)u.Elements); +} + void platform_geometry_bind(Platform_Geometry_Buffer geo) { @@ -150,12 +220,19 @@ platform_shader_bind(Platform_Shader shader) } } +void +platform_geometry_draw( + Platform_Geometry_Buffer geo, u32 indices + ){ + glDrawElements(GL_TRIANGLES, indices, GL_UNSIGNED_INT, 0); + win32_gl_no_error(); +} + void platform_geometry_draw( Platform_Geometry_Buffer geo ){ - glDrawElements(GL_TRIANGLES, geo.indices_len, GL_UNSIGNED_INT, 0); - win32_gl_no_error(); + platform_geometry_draw(geo, geo.indices_len); } void platform_vertex_attrib_pointer( diff --git a/src_v2/platform/win32/lumenarium_win32_memory.cpp b/src_v2/platform/win32/lumenarium_win32_memory.cpp index 8a82089..3c39c44 100644 --- a/src_v2/platform/win32/lumenarium_win32_memory.cpp +++ b/src_v2/platform/win32/lumenarium_win32_memory.cpp @@ -5,7 +5,7 @@ u64 platform_page_size() { return WIN32_PAGE_SIZE; } u8* platform_mem_reserve(u64 size) { - size_t size_cvt = (size_t)size_to_pages(size); + size_t size_cvt = (size_t)round_size_to_page_multiple(size); DWORD alloc_type = MEM_RESERVE; DWORD protection = PAGE_READWRITE; u8* result = (u8*)VirtualAlloc(0, size_cvt, alloc_type, protection); @@ -16,7 +16,7 @@ platform_mem_reserve(u64 size) u8* platform_mem_commit(u8* base, u64 size) { - size_t size_cvt = (size_t)size_to_pages(size); + size_t size_cvt = (size_t)round_size_to_page_multiple(size); DWORD alloc_type = MEM_COMMIT; DWORD protection = PAGE_READWRITE; u8* result = (u8*)VirtualAlloc(base, size_cvt, alloc_type, protection); @@ -37,7 +37,9 @@ bool platform_mem_release(u8* base, u64 size) { DWORD free_type = MEM_RELEASE; - bool result = VirtualFree(base, size, free_type); + // according to the docs, if we're releasing memory, the size must be + // zero as its implied VirtualFree will free the entire region + bool result = VirtualFree(base, 0, free_type); if (!result) win32_get_last_error(); return result; } diff --git a/src_v2/platform/win32/lumenarium_win32_opengl.h b/src_v2/platform/win32/lumenarium_win32_opengl.h index 9ad2cee..29526bb 100644 --- a/src_v2/platform/win32/lumenarium_win32_opengl.h +++ b/src_v2/platform/win32/lumenarium_win32_opengl.h @@ -17,6 +17,7 @@ typedef void proc_glBindVertexArray(GLuint array); typedef void proc_glGenBuffers (GLsizei n, GLuint *buffers); typedef void proc_glBindBuffer(GLenum target, GLuint buffer); typedef void proc_glBufferData(GLenum target, size_t size, const void *data, GLenum usage); +typedef void proc_glBufferSubData(GLenum target, size_t offset, size_t size, const void* data); typedef GLuint proc_glCreateShader(GLenum type); typedef void proc_glShaderSource(GLuint shader, u32 count, const char* const* string, const GLint *length); typedef void proc_glCompileShader(GLuint shader); @@ -31,7 +32,8 @@ typedef void proc_glGetShaderiv(GLuint shader, GLenum ele, GLint* value_out); typedef void proc_glGetShaderInfoLog(GLuint shader, GLuint buf_len, GLsizei* len_out, GLchar* buf); typedef void proc_glGetProgramiv(GLuint program, GLenum ele, GLint* value_out); typedef void proc_glGetProgramInfoLog(GLuint program, GLuint cap, GLsizei* len_out, GLchar* buf); - +typedef GLuint proc_glGetUniformLocation(GLuint program, const char* name); +typedef void proc_glUniformMatrix4fv(GLuint uniform, GLuint count, GLenum normalize, GLfloat* elements); struct Win32_OpenGL_Extensions { proc_wglGetExtensionsStringARB* wglGetExtensionsStringARB; @@ -43,6 +45,7 @@ struct Win32_OpenGL_Extensions proc_glGenBuffers* glGenBuffers; proc_glBindBuffer* glBindBuffer; proc_glBufferData* glBufferData; + proc_glBufferSubData* glBufferSubData; proc_glCreateShader* glCreateShader; proc_glShaderSource* glShaderSource; proc_glCompileShader* glCompileShader; @@ -57,6 +60,8 @@ struct Win32_OpenGL_Extensions proc_glGetShaderInfoLog* glGetShaderInfoLog; proc_glGetProgramiv* glGetProgramiv; proc_glGetProgramInfoLog* glGetProgramInfoLog; + proc_glGetUniformLocation* glGetUniformLocation; + proc_glUniformMatrix4fv* glUniformMatrix4fv; }; //////////////////////////////////////// diff --git a/src_v2/platform/win32/lumenarium_win32_window.cpp b/src_v2/platform/win32/lumenarium_win32_window.cpp index 54ca09f..bc5030c 100644 --- a/src_v2/platform/win32/lumenarium_win32_window.cpp +++ b/src_v2/platform/win32/lumenarium_win32_window.cpp @@ -381,6 +381,7 @@ win32_window_opengl_get_wgl_ext(HINSTANCE hinstance) wgl_load_ext(result, glGenBuffers); wgl_load_ext(result, glBindBuffer); wgl_load_ext(result, glBufferData); + wgl_load_ext(result, glBufferSubData); wgl_load_ext(result, glCreateShader); wgl_load_ext(result, glShaderSource); wgl_load_ext(result, glCompileShader); @@ -395,6 +396,8 @@ win32_window_opengl_get_wgl_ext(HINSTANCE hinstance) wgl_load_ext(result, glGetShaderInfoLog); wgl_load_ext(result, glGetProgramiv); wgl_load_ext(result, glGetProgramInfoLog); + wgl_load_ext(result, glGetUniformLocation); + wgl_load_ext(result, glUniformMatrix4fv); } wglMakeCurrent(NULL, NULL); diff --git a/src_v2/user_space/user_space_incenter.cpp b/src_v2/user_space/user_space_incenter.cpp index 180161b..124f476 100644 --- a/src_v2/user_space/user_space_incenter.cpp +++ b/src_v2/user_space/user_space_incenter.cpp @@ -10,8 +10,6 @@ incenter_get_init_desc() internal void incenter_init(App_State* state) { - return; - // create a fake sculpture Assembly_Handle ah = assembly_add(&state->assemblies, lit_str("test"), 3000, 100);