From c93c0ed567350a885c3c6e2bd60cbf4b364d7fcd Mon Sep 17 00:00:00 2001 From: Ed_ Date: Mon, 24 Jun 2024 16:36:22 -0400 Subject: [PATCH] Minor lifting in the render side, getting ready to just hunker down and take notes on whats next * VEFontCache needs to get fixed up (possibly bring back ELFHash) * Problably going to do conversion early to odin's array and map usage * Need get the quad tree setup for the ui so that I can do fast and efficient traversal for the layer based rendering. --- code/sectr/engine/client_api.odin | 2 +- code/sectr/engine/render.odin | 502 ++++++++++-------- code/sectr/engine/update.odin | 1 + code/sectr/font/provider.odin | 10 +- code/sectr/ui/core/base.odin | 317 +++++------ code/sectr/ui/core/box.odin | 2 +- code/sectr/ui/core/layout_compute.odin | 2 +- ...Lorem Ipsum.txt => Lorem Ipsum (1022).txt} | 0 examples/{temp.txt => Lorem_Ispsum (197).txt} | 0 9 files changed, 440 insertions(+), 396 deletions(-) rename examples/{Lorem Ipsum.txt => Lorem Ipsum (1022).txt} (100%) rename examples/{temp.txt => Lorem_Ispsum (197).txt} (100%) diff --git a/code/sectr/engine/client_api.odin b/code/sectr/engine/client_api.odin index 5c18acd..0014027 100644 --- a/code/sectr/engine/client_api.odin +++ b/code/sectr/engine/client_api.odin @@ -325,7 +325,7 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem ui_startup( & workspace.ui, cache_allocator = persistent_slab_allocator() ) } - debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/Lorem Ipsum.txt", allocator = persistent_slab_allocator()) + debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/Lorem_Ispsum (197).txt", allocator = persistent_slab_allocator()) alloc_error : AllocatorError; success : bool debug.lorem_content, success = os.read_entire_file( debug.path_lorem, persistent_slab_allocator() ) diff --git a/code/sectr/engine/render.odin b/code/sectr/engine/render.odin index b42fa03..7edf056 100644 --- a/code/sectr/engine/render.odin +++ b/code/sectr/engine/render.odin @@ -19,133 +19,6 @@ RenderState :: struct { pass_actions : PassActions, } -#region("Helpers") - -draw_filled_circle :: proc(x, y, radius: f32, edges: int) -{ - if edges < 3 do return // Need at least 3 edges to form a shape - - triangles := make([]gp.Triangle, edges) - center := gp.Point{x, y} - edge_quotient := 1 / f32(edges) - angle_factor := 2 * math.PI * edge_quotient - for edge_id in 0..< edges - { - angle1 := f32(edge_id ) * angle_factor - angle2 := f32(edge_id +1) * angle_factor - - p1 := gp.Point{ - x + radius * math.cos(angle1), - y + radius * math.sin(angle1), - } - p2 := gp.Point{ - x + radius * math.cos(angle2), - y + radius * math.sin(angle2), - } - triangles[edge_id] = gp.Triangle{center, p1, p2} - } - - gp.draw_filled_triangles(raw_data(triangles), u32(len(triangles))) -} - -draw_rect :: proc( rect : Range2, color : RGBA8 ) { - using rect - render_set_color( color ) - - size := max - min - position := min - gp.draw_filled_rect( position.x, position.y, size.x, size.y ) -} - -draw_rect_border :: proc( rect : Range2, border_width: f32) -{ - rect_size := rect.max - rect.min - border_width := lalg.min(border_width, min(rect_size.x, rect_size.y) * 0.5) - - top := gp.Rect{ rect.min.x, rect.min.y, rect_size.x, border_width } - bottom := gp.Rect{ rect.min.x, rect.max.y - border_width, rect_size.x, border_width } - left := gp.Rect{ rect.min.x, rect.min.y + border_width, border_width, rect_size.y - 2 * border_width } - right := gp.Rect{ rect.max.x - border_width, rect.min.y + border_width, border_width, rect_size.y - 2 * border_width } - - borders := []gp.Rect{ top, bottom, left, right } - gp.draw_filled_rects( raw_data(borders), u32(len(borders)) ) -} - -// Draw text using a string and normalized render coordinates -draw_text_string_pos_norm :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := Color_White, scale : f32 = 1.0 ) -{ - state := get_state(); using state - width := app_window.extent.x * 2 - height := app_window.extent.y * 2 - - ve_id := font_provider_resolve_draw_id( id, size ) - color_norm := normalize_rgba8(color) - - ve.set_colour( & font_provider_data.ve_font_cache, color_norm ) - ve.draw_text( & font_provider_data.ve_font_cache, ve_id, content, pos, Vec2{1 / width, 1 / height} * scale ) - return -} - -// Draw text using a string and extent-based screen coordinates -draw_text_string_pos_extent :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := Color_White ) -{ - // profile(#procedure) - state := get_state(); using state - screen_size := app_window.extent * 2 - render_pos := screen_to_render_pos(pos) - normalized_pos := render_pos * (1.0 / screen_size) - draw_text_string_pos_norm( content, id, size, normalized_pos, color ) -} - -draw_text_string_pos_extent_zoomed :: proc( content : string, id : FontID, size : f32, pos : Vec2, cam : Camera, color := Color_White ) -{ - // profile(#procedure) - cam_offset := Vec2 { - cam.position.x, - cam.position.y, - } - - pos_offset := pos + cam_offset * cam.zoom - - cam_zoom_ratio := 1 / cam.zoom - - state := get_state(); using state - screen_size := app_window.extent * 2 - render_pos := screen_to_render_pos(pos_offset) - normalized_pos := render_pos * (1.0 / screen_size) - draw_text_string_pos_norm( content, id, size, normalized_pos, color, cam.zoom ) -} - -// TODO(Ed): Eventually the workspace will need a viewport for drawing text - -render_flush_gp :: #force_inline proc() -{ - gfx.begin_pass( gfx.Pass { action = get_state().render_data.pass_actions.empty_action, swapchain = sokol_glue.swapchain() }) - gp.flush() - gfx.end_pass() -} - -@(deferred_none=gp.reset_transform) -render_set_camera :: #force_inline proc( cam : Camera ) -{ - gp.translate( cam.position.x * cam.zoom, cam.position.y * cam.zoom ) - gp.scale( cam.zoom, cam.zoom ) -} - -render_set_color :: #force_inline proc( color : RGBA8 ) { - color := normalize_rgba8(color); - gp.set_color( color.r, color.g, color.b, color.a ) -} - -render_set_view_space :: #force_inline proc( extent : Extents2 ) -{ - size := extent * 2 - gp.viewport(0, 0, i32(size.x), i32(size.y)) - gp.project( -extent.x, extent.x, extent.y, -extent.y ) -} - -#endregion("Helpers") - render :: proc() { profile(#procedure) @@ -224,59 +97,15 @@ render_mode_2d_workspace :: proc() render_set_camera(cam) ui := & project.workspace.ui - render_list := array_to_slice( ui.render_list ) - text_enqueued : b32 = false - shape_enqueued : b32 = false - - for entry, id in render_list - { - already_passed_signal := id > 0 && render_list[ id - 1 ].layer_signal - if !already_passed_signal && entry.layer_signal - { - profile("render ui layer") - render_flush_gp() - if text_enqueued do render_text_layer() - continue - } - using entry - - profile("enqueue box") - - GP_Render: - { - // profile("draw_shapes") - if style.bg_color.a != 0 - { - draw_rect( bounds, style.bg_color ) - shape_enqueued = true - } - - if style.border_color.a != 0 && border_width > 0 { - render_set_color( style.border_color ) - draw_rect_border( bounds, border_width ) - shape_enqueued = true - } - - if debug.draw_ui_box_bounds_points - { - render_set_color(Color_Red) - draw_filled_circle(bounds.min.x, bounds.min.y, 3, 24) - - render_set_color(Color_Blue) - draw_filled_circle(bounds.max.x, bounds.max.y, 3, 24) - shape_enqueued = true - } - } - - if len(text.str) > 0 && style.font.key != 0 { - draw_text_string_pos_extent_zoomed( text.str, default_font, font_size, computed.text_pos, cam, style.text_color ) - text_enqueued = true - } + when UI_Render_Method == .Layers { + render_list := array_to_slice( ui.render_list ) + render_ui_via_box_list( render_list, & cam ) + } + when UI_Render_Method == .Depth_First + { + render_ui_via_box_tree( ui.root, & cam ) } - - if shape_enqueued do render_flush_gp() - if text_enqueued do render_text_layer() } render_mode_screenspace :: proc() @@ -441,69 +270,19 @@ render_screen_ui :: proc() text_enqueued : b32 = false shape_enqueued : b32 = false - render_list := array_to_slice( ui.render_list ) - for entry, id in render_list - { - if entry.layer_signal - { - profile("render ui layer") - render_flush_gp() - if text_enqueued do render_text_layer() - continue - } - using entry - - profile("enqueue box") - - GP_Render: - { - profile("draw_shapes") - - draw_rect :: proc( rect : Range2, color : RGBA8 ) { - using rect - render_set_color( color ) - - size := max - min - position := min - gp.draw_filled_rect( position.x, position.y, size.x, size.y ) - } - - if style.bg_color.a != 0 - { - draw_rect( bounds, style.bg_color ) - shape_enqueued = true - } - - if style.border_color.a != 0 && border_width > 0 { - render_set_color( style.border_color ) - draw_rect_border( bounds, border_width ) - shape_enqueued = true - } - - if debug.draw_ui_box_bounds_points - { - render_set_color(Color_Red) - draw_filled_circle(bounds.min.x, bounds.min.y, 3, 24) - - render_set_color(Color_Blue) - draw_filled_circle(bounds.max.x, bounds.max.y, 3, 24) - shape_enqueued = true - } - } - - if len(text.str) > 0 && style.font.key != 0 { - draw_text_string_pos_extent( text.str, default_font, font_size, computed.text_pos, style.text_color ) - text_enqueued = true - } + when UI_Render_Method == .Layers { + render_list := array_to_slice( ui.render_list ) + render_ui_via_box_list( render_list ) + } + when UI_Render_Method == .Depth_First + { + render_ui_via_box_tree( ui.root ) } - - if shape_enqueued do render_flush_gp() - if text_enqueued do render_text_layer() } render_text_layer :: proc() { - profile("VEFontCache: render text layer") + // profile("VEFontCache: render text layer") Bindings :: gfx.Bindings Range :: gfx.Range @@ -681,3 +460,254 @@ render_text_layer :: proc() gfx.end_pass() } } + +render_ui_via_box_tree :: proc( root : ^UI_Box, cam : ^Camera = nil ) +{ + debug := get_state().debug + default_font := get_state().default_font + + for box := root.first; box != nil; box = ui_box_tranverse_next_depth_based( box ) + { + text_enqueued : b32 = false + shape_enqueued : b32 = false + + border_width := box.layout.border_width + computed := box.computed + font_size := box.layout.font_size + style := box.style + text := box.text + + using computed + + // profile("enqueue box") + + GP_Render: + { + // profile("draw_shapes") + if style.bg_color.a != 0 + { + draw_rect( bounds, style.bg_color ) + shape_enqueued = true + } + + if style.border_color.a != 0 && border_width > 0 { + render_set_color( style.border_color ) + draw_rect_border( bounds, border_width ) + shape_enqueued = true + } + + if debug.draw_ui_box_bounds_points + { + render_set_color(Color_Red) + draw_filled_circle(bounds.min.x, bounds.min.y, 3, 24) + + render_set_color(Color_Blue) + draw_filled_circle(bounds.max.x, bounds.max.y, 3, 24) + shape_enqueued = true + } + } + + if len(text.str) > 0 && style.font.key != 0 { + if cam != nil { + draw_text_string_pos_extent_zoomed( text.str, default_font, font_size, computed.text_pos, cam^, style.text_color ) + } + else { + draw_text_string_pos_extent( text.str, default_font, font_size, computed.text_pos, style.text_color ) + } + text_enqueued = true + } + + if shape_enqueued do render_flush_gp() + if text_enqueued do render_text_layer() + } +} + +render_ui_via_box_list :: proc( render_list : []UI_RenderBoxInfo, cam : ^Camera = nil ) +{ + debug := get_state().debug + default_font := get_state().default_font + + text_enqueued : b32 = false + shape_enqueued : b32 = false + + for entry, id in render_list + { + already_passed_signal := id > 0 && render_list[ id - 1 ].layer_signal + if !already_passed_signal && entry.layer_signal + { + profile("render ui layer") + render_flush_gp() + if text_enqueued do render_text_layer() + continue + } + using entry + + profile("enqueue box") + + GP_Render: + { + // profile("draw_shapes") + if style.bg_color.a != 0 + { + draw_rect( bounds, style.bg_color ) + shape_enqueued = true + } + + if style.border_color.a != 0 && border_width > 0 { + render_set_color( style.border_color ) + draw_rect_border( bounds, border_width ) + shape_enqueued = true + } + + if debug.draw_ui_box_bounds_points + { + render_set_color(Color_Red) + draw_filled_circle(bounds.min.x, bounds.min.y, 3, 24) + + render_set_color(Color_Blue) + draw_filled_circle(bounds.max.x, bounds.max.y, 3, 24) + shape_enqueued = true + } + } + + if len(text.str) > 0 && style.font.key != 0 { + if cam != nil { + draw_text_string_pos_extent_zoomed( text.str, default_font, font_size, computed.text_pos, cam^, style.text_color ) + } + else { + draw_text_string_pos_extent( text.str, default_font, font_size, computed.text_pos, style.text_color ) + } + text_enqueued = true + } + } + + if shape_enqueued do render_flush_gp() + if text_enqueued do render_text_layer() +} + +#region("Helpers") + +draw_filled_circle :: proc(x, y, radius: f32, edges: int) +{ + if edges < 3 do return // Need at least 3 edges to form a shape + + triangles := make([]gp.Triangle, edges) + center := gp.Point{x, y} + edge_quotient := 1 / f32(edges) + angle_factor := 2 * math.PI * edge_quotient + for edge_id in 0..< edges + { + angle1 := f32(edge_id ) * angle_factor + angle2 := f32(edge_id +1) * angle_factor + + p1 := gp.Point{ + x + radius * math.cos(angle1), + y + radius * math.sin(angle1), + } + p2 := gp.Point{ + x + radius * math.cos(angle2), + y + radius * math.sin(angle2), + } + triangles[edge_id] = gp.Triangle{center, p1, p2} + } + + gp.draw_filled_triangles(raw_data(triangles), u32(len(triangles))) +} + +draw_rect :: proc( rect : Range2, color : RGBA8 ) { + using rect + render_set_color( color ) + + size := max - min + position := min + gp.draw_filled_rect( position.x, position.y, size.x, size.y ) +} + +draw_rect_border :: proc( rect : Range2, border_width: f32) +{ + rect_size := rect.max - rect.min + border_width := lalg.min(border_width, min(rect_size.x, rect_size.y) * 0.5) + + top := gp.Rect{ rect.min.x, rect.min.y, rect_size.x, border_width } + bottom := gp.Rect{ rect.min.x, rect.max.y - border_width, rect_size.x, border_width } + left := gp.Rect{ rect.min.x, rect.min.y + border_width, border_width, rect_size.y - 2 * border_width } + right := gp.Rect{ rect.max.x - border_width, rect.min.y + border_width, border_width, rect_size.y - 2 * border_width } + + borders := []gp.Rect{ top, bottom, left, right } + gp.draw_filled_rects( raw_data(borders), u32(len(borders)) ) +} + +// Draw text using a string and normalized render coordinates +draw_text_string_pos_norm :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := Color_White, scale : f32 = 1.0 ) +{ + state := get_state(); using state + width := app_window.extent.x * 2 + height := app_window.extent.y * 2 + + ve_id := font_provider_resolve_draw_id( id, size ) + color_norm := normalize_rgba8(color) + + ve.set_colour( & font_provider_data.ve_font_cache, color_norm ) + ve.draw_text( & font_provider_data.ve_font_cache, ve_id, content, pos, Vec2{1 / width, 1 / height} * scale ) + return +} + +// Draw text using a string and extent-based screen coordinates +draw_text_string_pos_extent :: proc( content : string, id : FontID, size : f32, pos : Vec2, color := Color_White ) +{ + // profile(#procedure) + state := get_state(); using state + screen_size := app_window.extent * 2 + render_pos := screen_to_render_pos(pos) + normalized_pos := render_pos * (1.0 / screen_size) + draw_text_string_pos_norm( content, id, size, normalized_pos, color ) +} + +draw_text_string_pos_extent_zoomed :: proc( content : string, id : FontID, size : f32, pos : Vec2, cam : Camera, color := Color_White ) +{ + // profile(#procedure) + cam_offset := Vec2 { + cam.position.x, + cam.position.y, + } + + pos_offset := pos + cam_offset * cam.zoom + + cam_zoom_ratio := 1 / cam.zoom + + state := get_state(); using state + screen_size := app_window.extent * 2 + render_pos := screen_to_render_pos(pos_offset) + normalized_pos := render_pos * (1.0 / screen_size) + draw_text_string_pos_norm( content, id, size * cam.zoom, normalized_pos, color, cam.zoom ) +} + +// TODO(Ed): Eventually the workspace will need a viewport for drawing text + +render_flush_gp :: #force_inline proc() +{ + gfx.begin_pass( gfx.Pass { action = get_state().render_data.pass_actions.empty_action, swapchain = sokol_glue.swapchain() }) + gp.flush() + gfx.end_pass() +} + +@(deferred_none=gp.reset_transform) +render_set_camera :: #force_inline proc( cam : Camera ) +{ + gp.translate( cam.position.x * cam.zoom, cam.position.y * cam.zoom ) + gp.scale( cam.zoom, cam.zoom ) +} + +render_set_color :: #force_inline proc( color : RGBA8 ) { + color := normalize_rgba8(color); + gp.set_color( color.r, color.g, color.b, color.a ) +} + +render_set_view_space :: #force_inline proc( extent : Extents2 ) +{ + size := extent * 2 + gp.viewport(0, 0, i32(size.x), i32(size.y)) + gp.project( -extent.x, extent.x, extent.y, -extent.y ) +} + +#endregion("Helpers") diff --git a/code/sectr/engine/update.odin b/code/sectr/engine/update.odin index d791ffd..676e008 100644 --- a/code/sectr/engine/update.odin +++ b/code/sectr/engine/update.odin @@ -167,6 +167,7 @@ update :: proc( delta_time : f64 ) -> b32 config.cam_max_zoom = 30 config.cam_zoom_sensitivity_digital = 0.04 // config.cam_min_zoom = 0.04 + config.cam_zoom_sensitivity_smooth = 0.02 config.cam_zoom_mode = .Smooth switch config.cam_zoom_mode { diff --git a/code/sectr/font/provider.odin b/code/sectr/font/provider.odin index e8d60e9..0ce53b3 100644 --- a/code/sectr/font/provider.odin +++ b/code/sectr/font/provider.odin @@ -8,7 +8,7 @@ import sokol_glue "thirdparty:sokol/glue" Font_Provider_Use_Freetype :: false -Font_Largest_Px_Size :: 72 +Font_Largest_Px_Size :: 132 Font_Size_Interval :: 2 Font_Default :: FontID { 0, "" } @@ -217,8 +217,8 @@ font_provider_startup :: proc() }) glyph_rt_sampler = sokol_gfx.make_sampler( SamplerDescription { - min_filter = Filter.NEAREST, - mag_filter = Filter.NEAREST, + min_filter = Filter.LINEAR, + mag_filter = Filter.LINEAR, mipmap_filter = Filter.NONE, wrap_u = .CLAMP_TO_EDGE, wrap_v = .CLAMP_TO_EDGE, @@ -354,8 +354,8 @@ font_provider_startup :: proc() verify( sokol_gfx.query_image_state(atlas_rt_depth) < ResourceState.FAILED, "Failed to make atlas_rt_depth") atlas_rt_sampler = sokol_gfx.make_sampler( SamplerDescription { - min_filter = Filter.NEAREST, - mag_filter = Filter.NEAREST, + min_filter = Filter.LINEAR, + mag_filter = Filter.LINEAR, mipmap_filter = Filter.NONE, wrap_u = .CLAMP_TO_EDGE, wrap_v = .CLAMP_TO_EDGE, diff --git a/code/sectr/ui/core/base.odin b/code/sectr/ui/core/base.odin index f329e09..0757048 100644 --- a/code/sectr/ui/core/base.odin +++ b/code/sectr/ui/core/base.odin @@ -84,6 +84,13 @@ UI_RenderBoxInfo :: struct { layer_signal : b32, } +UI_RenderMethod :: enum u32 { + Depth_First, + Layers, +} + +UI_Render_Method :: UI_RenderMethod.Depth_First + // TODO(Ed): Rename to UI_Context UI_State :: struct { // TODO(Ed) : Use these? @@ -96,6 +103,7 @@ UI_State :: struct { prev_cache : ^HMapZPL( UI_Box ), curr_cache : ^HMapZPL( UI_Box ), + // For rendering via a set of layers organized into a single command list // render_queue_builder : SubArena, render_queue : Array(UI_RenderLayer), render_list : Array(UI_RenderBoxInfo), @@ -214,186 +222,191 @@ ui_graph_build_end :: proc( ui : ^UI_State ) computed.content = computed.bounds } + // Auto-layout and initial render_queue generation render_queue := array_to_slice(ui.render_queue) - for current := root.first; current != nil; current = ui_box_tranverse_next( current ) + for current := root.first; current != nil; current = ui_box_tranverse_next_depth_based( current ) { if ! current.computed.fresh { ui_box_compute_layout( current ) } - // TODO(Ed): Eventually put this into a sub-arena - entry, error := new(UI_RenderEntry) - (entry^) = UI_RenderEntry { - info = { - current.computed, - current.style, - current.text, - current.layout.font_size, - current.layout.border_width, - current.label, - false, - }, - layer_id = current.ancestors -1, - } + when UI_Render_Method == .Layers + { - if entry.layer_id >= i32(ui.render_queue.num) { - append( & ui.render_queue, UI_RenderLayer {}) - render_queue = array_to_slice(ui.render_queue) - } + // TODO(Ed): Eventually put this into a sub-arena + entry, error := new(UI_RenderEntry) + (entry^) = UI_RenderEntry { + info = { + current.computed, + current.style, + current.text, + current.layout.font_size, + current.layout.border_width, + current.label, + false, + }, + layer_id = current.ancestors -1, + } - // else if layer.last == nil { - // layer.first.next = entry - // entry.prev = layer.first - // layer.last = entry - // } + if entry.layer_id >= i32(ui.render_queue.num) { + append( & ui.render_queue, UI_RenderLayer {}) + render_queue = array_to_slice(ui.render_queue) + } - // push_back to next layer - layer := & render_queue[entry.layer_id] - if layer.first == nil { - layer.first = entry - layer.last = entry - } - else { - layer.last.next = entry - entry.prev = layer.last - layer.last = entry - } - // dll_full_push_back( layer, entry, nil ) + // else if layer.last == nil { + // layer.first.next = entry + // entry.prev = layer.first + // layer.last = entry + // } - // If there is a parent entry, give it a reference to the child entry - parent_entry : ^UI_RenderEntry - if entry.layer_id > 0 { - parent_layer := & render_queue[entry.layer_id - 1] - parent_entry = parent_layer.last - entry.parent = parent_entry - - if parent_entry.first == nil { - parent_entry.first = entry - parent_entry.last = entry + // push_back to next layer + layer := & render_queue[entry.layer_id] + if layer.first == nil { + layer.first = entry + layer.last = entry } else { - parent_entry.last = entry + layer.last.next = entry + entry.prev = layer.last + layer.last = entry + } + // dll_full_push_back( layer, entry, nil ) + + // If there is a parent entry, give it a reference to the child entry + parent_entry : ^UI_RenderEntry + if entry.layer_id > 0 { + parent_layer := & render_queue[entry.layer_id - 1] + parent_entry = parent_layer.last + entry.parent = parent_entry + + if parent_entry.first == nil { + parent_entry.first = entry + parent_entry.last = entry + } + else { + parent_entry.last = entry + } + // dll_fl_append( parent_entry, entry ) } - // dll_fl_append( parent_entry, entry ) } } - // render_queue overlap corrections & render_list generation - render_queue = array_to_slice(ui.render_queue) - for layer_id : i32 = 0; layer_id < i32(ui.render_queue.num); layer_id += 1 + when UI_Render_Method == .Layers { - layer := & ui.render_queue.data[ layer_id ] - append( & ui.render_list, UI_RenderBoxInfo { layer_signal = true }) - - to_increment, error := make( Array(^UI_RenderEntry), 4 * Kilo ) - verify( error == .None, "Faied to make to_increment array.") - - to_inc_last_iterated : i32 = 0 - for entry := layer.first; entry != nil; entry = entry.next + // render_queue overlap corrections & render_list generation + render_queue = array_to_slice(ui.render_queue) + for layer_id : i32 = 0; layer_id < i32(ui.render_queue.num); layer_id += 1 { - for neighbor := entry.next; neighbor != nil; neighbor = neighbor.next + layer := & ui.render_queue.data[ layer_id ] + append( & ui.render_list, UI_RenderBoxInfo { layer_signal = true }) + + to_increment, error := make( Array(^UI_RenderEntry), 4 * Kilo ) + verify( error == .None, "Faied to make to_increment array.") + + to_inc_last_iterated : i32 = 0 + for entry := layer.first; entry != nil; entry = entry.next { - if ! overlap_range2( entry.info.computed.bounds, neighbor.info.computed.bounds) do continue - - - append( & to_increment, neighbor ) - } // for neighbor := entry.next; neighbor != nil; neighbor = neighbor.next - - if entry == to_increment.data[ to_inc_last_iterated ] { - to_inc_last_iterated += 1 - } - else { - // This entry stayed in this layer, we can append the value - array_append_value( & ui.render_list, entry.info ) - } - } // for entry := layer.first; entry != nil; entry = entry.next - - // Move overlaping entries & their children's by 1 layer - to_inc_slice := array_to_slice(to_increment) - for entry in to_inc_slice - { - pop_layer := render_queue[entry.layer_id] - entry.layer_id += 1 - if entry.layer_id >= i32(ui.render_queue.num) { - append( & ui.render_queue, UI_RenderLayer {} ) - render_queue = array_to_slice(ui.render_queue) - } - push_layer := render_queue[entry.layer_id] - - // pop entry from layer - prev := entry.prev - prev.next = entry.next - if entry == pop_layer.last { - pop_layer.last = prev - } - - // push entry to next layer - if push_layer.first == nil { - push_layer.first = entry - push_layer.last = entry - } - else { - push_layer.last.next = entry - entry.prev = push_layer.last - push_layer.last = entry - entry.next = nil - } - // else if push_layer.last == nil { - // push_layer.last = entry - // entry.prev = push_layer.first - // push_layer.first.next = entry - // entry.next = nil - // } - - // increment children's layers - if entry.first != nil - { - for child := entry.first; child != nil; child = ui_render_entry_tranverse( child ) + for neighbor := entry.next; neighbor != nil; neighbor = neighbor.next { - pop_layer := render_queue[child.layer_id] - child.layer_id += 1 + if ! overlap_range2( entry.info.computed.bounds, neighbor.info.computed.bounds) do continue + append( & to_increment, neighbor ) + } // for neighbor := entry.next; neighbor != nil; neighbor = neighbor.next - if child.layer_id >= i32(ui.render_queue.num) { - append( & ui.render_queue, UI_RenderLayer {}) - render_queue = array_to_slice(ui.render_queue) - } - push_layer := render_queue[child.layer_id] + if entry == to_increment.data[ to_inc_last_iterated ] { + to_inc_last_iterated += 1 + } + else { + // This entry stayed in this layer, we can append the value + array_append_value( & ui.render_list, entry.info ) + } + } // for entry := layer.first; entry != nil; entry = entry.next - // pop from current layer - if child == pop_layer.first { - pop_layer.first = nil - } - if child == pop_layer.last { - pop_layer.last = child.prev - } + // Move overlaping entries & their children's by 1 layer + to_inc_slice := array_to_slice(to_increment) + for entry in to_inc_slice + { + pop_layer := render_queue[entry.layer_id] + entry.layer_id += 1 + if entry.layer_id >= i32(ui.render_queue.num) { + append( & ui.render_queue, UI_RenderLayer {} ) + render_queue = array_to_slice(ui.render_queue) + } + push_layer := render_queue[entry.layer_id] - // push_back to next layer - if push_layer.first == nil { - push_layer.first = child - push_layer.last = child - } - else { - push_layer.last.next = child - child.prev = push_layer.last - push_layer.last = child - } + // pop entry from layer + prev := entry.prev + prev.next = entry.next + if entry == pop_layer.last { + pop_layer.last = prev + } - // else if push_layer.last == nil { - // push_layer.first.next = child - // child.prev = push_layer.first - // push_layer.last = child - // } + // push entry to next layer + if push_layer.first == nil { + push_layer.first = entry + push_layer.last = entry + } + else { + push_layer.last.next = entry + entry.prev = push_layer.last + push_layer.last = entry + entry.next = nil + } + // else if push_layer.last == nil { + // push_layer.last = entry + // entry.prev = push_layer.first + // push_layer.first.next = entry + // entry.next = nil + // } - } // for child := neighbor.first; child != nil; child = ui_render_entry_traverse_depth( child ) - } // if entry.first != nil - } // for entry in to_inc_slice - } // for & layer in render_queue + // increment children's layers + if entry.first != nil + { + for child := entry.first; child != nil; child = ui_render_entry_tranverse( child ) + { + pop_layer := render_queue[child.layer_id] + child.layer_id += 1 + + if child.layer_id >= i32(ui.render_queue.num) { + append( & ui.render_queue, UI_RenderLayer {}) + render_queue = array_to_slice(ui.render_queue) + } + push_layer := render_queue[child.layer_id] + + // pop from current layer + if child == pop_layer.first { + pop_layer.first = nil + } + if child == pop_layer.last { + pop_layer.last = child.prev + } + + // push_back to next layer + if push_layer.first == nil { + push_layer.first = child + push_layer.last = child + } + else { + push_layer.last.next = child + child.prev = push_layer.last + push_layer.last = child + } + + // else if push_layer.last == nil { + // push_layer.first.next = child + // child.prev = push_layer.first + // push_layer.last = child + // } + + } // for child := neighbor.first; child != nil; child = ui_render_entry_traverse_depth( child ) + } // if entry.first != nil + } // for entry in to_inc_slice + } // for & layer in render_queue + } + + render_list := array_to_slice(ui.render_list) } - render_queue := array_to_slice(ui.render_queue) - render_list := array_to_slice(ui.render_list) - get_state().ui_context = nil } diff --git a/code/sectr/ui/core/box.odin b/code/sectr/ui/core/box.odin index 9fc935b..c2b023f 100644 --- a/code/sectr/ui/core/box.odin +++ b/code/sectr/ui/core/box.odin @@ -118,7 +118,7 @@ ui_prev_cached_box :: #force_inline proc( box : ^UI_Box ) -> ^UI_Box { return hm // TODO(Ed): Rename to ui_box_tranverse_view_next // Traveral pritorizes immeidate children -ui_box_tranverse_next :: proc "contextless" ( box : ^ UI_Box, bypass_intersection_test := false ) -> (^ UI_Box) +ui_box_tranverse_next_depth_based :: proc "contextless" ( box : ^ UI_Box, bypass_intersection_test := false ) -> (^ UI_Box) { using state := get_state() // If current has children, do them first diff --git a/code/sectr/ui/core/layout_compute.odin b/code/sectr/ui/core/layout_compute.odin index 15883ad..86ebd69 100644 --- a/code/sectr/ui/core/layout_compute.odin +++ b/code/sectr/ui/core/layout_compute.odin @@ -181,7 +181,7 @@ ui_compute_children_bounding_area :: proc ( box : ^UI_Box ) ui_box_compute_layout_children :: proc( box : ^UI_Box ) { - for current := box.first; current != nil && current.prev != box; current = ui_box_tranverse_next( current ) + for current := box.first; current != nil && current.prev != box; current = ui_box_tranverse_next_depth_based( current ) { if current == box do return if current.computed.fresh do continue diff --git a/examples/Lorem Ipsum.txt b/examples/Lorem Ipsum (1022).txt similarity index 100% rename from examples/Lorem Ipsum.txt rename to examples/Lorem Ipsum (1022).txt diff --git a/examples/temp.txt b/examples/Lorem_Ispsum (197).txt similarity index 100% rename from examples/temp.txt rename to examples/Lorem_Ispsum (197).txt