From 6ed67e8fcb584a1373738835d0ff0d8c2cf35aaf Mon Sep 17 00:00:00 2001 From: Daniel Bross Date: Mon, 30 Dec 2024 00:10:50 +0100 Subject: [PATCH] UI Windows slowly working --- core/math.jai | 12 ++ editor/editor_ui.jai | 149 +++++++++++------- input/input.jai | 4 +- renderer/dx11_renderer.jai | 45 ++++-- renderer/renderer.jai | 26 ++++ ui/ui.jai | 311 ++++++++++++++++++++++++++++++------- 6 files changed, 424 insertions(+), 123 deletions(-) diff --git a/core/math.jai b/core/math.jai index ec9d16b..5e7b766 100644 --- a/core/math.jai +++ b/core/math.jai @@ -79,6 +79,18 @@ aabb_add :: (aabb: *AABB, point: Vector3) { aabb.max.z = max(point.z, aabb.max.z); } +operator == :: inline (a: Vector2i, b: Vector2i) -> bool { + return a.x == b.x && a.y == b.y; +} + +operator + :: inline (a: Vector2i, b: Vector2i) -> Vector2i { + return .{a.x + b.x, a.y + b.y}; +} + +operator - :: inline (a: Vector2i, b: Vector2i) -> Vector2i { + return .{a.x - b.x, a.y - b.y}; +} + operator == :: inline (a: Vector3i, b: Vector3i) -> bool { return a.x == b.x && a.y == b.y && a.z == b.z; } diff --git a/editor/editor_ui.jai b/editor/editor_ui.jai index 7d287a3..239c0c9 100644 --- a/editor/editor_ui.jai +++ b/editor/editor_ui.jai @@ -32,8 +32,55 @@ pick_scene_view_at :: (camera: Camera, coordinates: Vector2) { } editor_ui :: () { - ui_window_begin("Entities"); - ui_label("TESTING ONE TWO", .{0,0.7,0,1}); + // Scene picking + if key_down(.MOUSE_LEFT) { + if engine.current_scene != null { + coords := engine.input.normalized_viewport_mouse_position; + pick_scene_view_at(engine.editor.camera, .{coords.x, 1.0-coords.y}); + } + } + + { + ui_window_begin("Create", 100, 5, 200, 200); + new_entity := editor_ui_entity_creation(); + if new_entity != null { + set_position(*new_entity.transform, engine.editor.camera.position + engine.editor.camera.forward * 20.0); + engine.editor.selected_entities.count = 0; + array_add(*engine.editor.selected_entities, new_entity); + } + + ui_window_end(); + } + + ui_window_begin("Entities", 1, 5, 200, 600); + if engine.current_scene != null { + for engine.current_scene.entities { + if it.flags & .DELETED continue; + + ui_set_next_padding(20); + clicked := false; + selected := array_find(engine.editor.selected_entities, it); + if it.name.count == 0 { + clicked = ui_clickable_label(tprint("%", it.type), selected, it.id); + } else { + clicked = ui_clickable_label(it.name, selected, it.id); + } + + if clicked { + if !key_pressed(.CTRL) { + engine.editor.selected_entities.count = 0; + array_add(*engine.editor.selected_entities, it); + } else { + if selected { + array_unordered_remove_by_value(*engine.editor.selected_entities, it); + } else { + array_add(*engine.editor.selected_entities, it); + } + } + } + //ui_space(0, 5); + } + } ui_window_end(); ui_full_size_background(); @@ -100,34 +147,34 @@ editor_ui :: () { ui_set_next_size_x(.PCT, 1.0); ui_tab_title_bar("ENTITIES"); - if engine.current_scene != null { - for engine.current_scene.entities { - if it.flags & .DELETED continue; + //if engine.current_scene != null { + // for engine.current_scene.entities { + // if it.flags & .DELETED continue; - ui_set_next_padding(20); - clicked := false; - selected := array_find(engine.editor.selected_entities, it); - if it.name.count == 0 { - clicked = ui_clickable_label(tprint("%", it.type), selected, it.id); - } else { - clicked = ui_clickable_label(it.name, selected, it.id); - } + // ui_set_next_padding(20); + // clicked := false; + // selected := array_find(engine.editor.selected_entities, it); + // if it.name.count == 0 { + // clicked = ui_clickable_label(tprint("%", it.type), selected, it.id); + // } else { + // clicked = ui_clickable_label(it.name, selected, it.id); + // } - if clicked { - if !key_pressed(.CTRL) { - engine.editor.selected_entities.count = 0; - array_add(*engine.editor.selected_entities, it); - } else { - if selected { - array_unordered_remove_by_value(*engine.editor.selected_entities, it); - } else { - array_add(*engine.editor.selected_entities, it); - } - } - } - //ui_space(0, 5); - } - } + // if clicked { + // if !key_pressed(.CTRL) { + // engine.editor.selected_entities.count = 0; + // array_add(*engine.editor.selected_entities, it); + // } else { + // if selected { + // array_unordered_remove_by_value(*engine.editor.selected_entities, it); + // } else { + // array_add(*engine.editor.selected_entities, it); + // } + // } + // } + // //ui_space(0, 5); + // } + //} } ui_pop_parent(); } @@ -174,32 +221,27 @@ editor_ui :: () { } ui_pop_parent(); - ui_set_next_size_x(.PCT, 1.0); - ui_set_next_size_y(.PCT, 0.65); + //ui_set_next_size_x(.PCT, 1.0); + //ui_set_next_size_y(.PCT, 0.65); - state := ui_interactable_texture(get_texture_from_pass("UI Blend Pass")); - engine.input.viewport_mouse_position = state.local_mouse_coordinates; - engine.input.normalized_viewport_mouse_position = state.normalized_local_mouse_coordinates; - if state.left_mouse_down { - if engine.current_scene != null { - pick_scene_view_at(engine.editor.camera, .{state.normalized_local_mouse_coordinates.x, 1.0-state.normalized_local_mouse_coordinates.y}); - } + //state := ui_interactable_texture(get_texture_from_pass("UI Blend Pass")); + //engine.input.viewport_mouse_position = state.local_mouse_coordinates; + //engine.input.normalized_viewport_mouse_position = state.normalized_local_mouse_coordinates; + - } + //engine.editor.mouse_viewport_state = state; - engine.editor.mouse_viewport_state = state; - - ui_set_next_size_x(.PCT, 1.0); - ui_set_next_size_y(.PCT, 0.25); - ui_tab_title_bar("Create entities"); - { - new_entity := editor_ui_entity_creation(); - if new_entity != null { - set_position(*new_entity.transform, engine.editor.camera.position + engine.editor.camera.forward * 20.0); - engine.editor.selected_entities.count = 0; - array_add(*engine.editor.selected_entities, new_entity); - } - } + //ui_set_next_size_x(.PCT, 1.0); + //ui_set_next_size_y(.PCT, 0.25); + //ui_tab_title_bar("Create entities"); + //{ + // new_entity := editor_ui_entity_creation(); + // if new_entity != null { + // set_position(*new_entity.transform, engine.editor.camera.position + engine.editor.camera.forward * 20.0); + // engine.editor.selected_entities.count = 0; + // array_add(*engine.editor.selected_entities, new_entity); + // } + //} } ui_pop_parent(); @@ -262,7 +304,10 @@ base_editor_update :: () { engine.editor.transform_gizmo.uniform_gizmo_scale = gizmo_scale; set_scale(*engine.editor.transform_gizmo.transform, .{gizmo_scale, gizmo_scale, gizmo_scale}); - coordinates := Vector2.{engine.editor.mouse_viewport_state.normalized_local_mouse_coordinates.x, 1.0 - engine.editor.mouse_viewport_state.normalized_local_mouse_coordinates.y}; + coordinates := engine.input.normalized_viewport_mouse_position; + coordinates.y = 1.0 - coordinates.y; + + //coordinates := Vector2.{engine.editor.mouse_viewport_state.normalized_local_mouse_coordinates.x, 1.0 - engine.editor.mouse_viewport_state.normalized_local_mouse_coordinates.y}; ray := normalized_screen_to_ray_v2(engine.editor.camera, coordinates); if update_transform_gizmo(ray, coordinates) { diff --git a/input/input.jai b/input/input.jai index 40b2b19..2167d9e 100644 --- a/input/input.jai +++ b/input/input.jai @@ -218,11 +218,11 @@ update_input :: () { update_gamepad_input(); - #if !EDITOR { + //#if EDITOR { engine.input.viewport_mouse_position.x = engine.input.mouse.x; engine.input.viewport_mouse_position.y = engine.input.mouse.y; engine.input.normalized_viewport_mouse_position = engine.input.viewport_mouse_position / Vector2.{xx engine.renderer.render_target_width, xx engine.renderer.render_target_height}; - } + //} } remove_all_temp_key_flags :: (using input_state: *Input_State) { diff --git a/renderer/dx11_renderer.jai b/renderer/dx11_renderer.jai index 09c5424..d9aed10 100644 --- a/renderer/dx11_renderer.jai +++ b/renderer/dx11_renderer.jai @@ -44,6 +44,7 @@ Backend_Shader :: struct { Rasterizer_State :: struct { fill: bool; cull_face: Cull_Face; + scissor_test: bool; state: *ID3D11RasterizerState; } @@ -64,6 +65,7 @@ D3D11_Backend :: struct { fill: bool = true; cull_face: Cull_Face = .BACK; + scissor_test: bool = false; } create_backend :: (w: *Window) -> *D3D11_Backend { @@ -253,10 +255,10 @@ init_device :: (hwnd: HWND, using renderer: *D3D11_Backend) -> bool { array_reserve(*renderer.rasterizer_states, 8); - create_rasterizer_state(renderer, true, .BACK); - create_rasterizer_state(renderer, false, .BACK); - create_rasterizer_state(renderer, true, .FRONT); - create_rasterizer_state(renderer, false, .FRONT); + create_rasterizer_state(renderer, true, .BACK, false); + create_rasterizer_state(renderer, false, .BACK, false); + create_rasterizer_state(renderer, true, .FRONT, false); + create_rasterizer_state(renderer, false, .FRONT, false); return true; } @@ -1266,13 +1268,13 @@ render :: (backend: *D3D11_Backend, command_buffer: *Render_Command_Buffer) { if it.set_draw_mode.draw_mode == { case .FILL; { - state := get_or_create_rasterizer_state(d3d_renderer, true, d3d_renderer.cull_face); + state := get_or_create_rasterizer_state(d3d_renderer, true, d3d_renderer.cull_face, d3d_renderer.scissor_test); d3d_renderer.fill = true; ID3D11DeviceContext_RSSetState(d3d_context, state.state); } case .WIREFRAME; { - state := get_or_create_rasterizer_state(d3d_renderer, false, d3d_renderer.cull_face); + state := get_or_create_rasterizer_state(d3d_renderer, false, d3d_renderer.cull_face, d3d_renderer.scissor_test); d3d_renderer.fill = false; ID3D11DeviceContext_RSSetState(d3d_context, state.state); } @@ -1282,19 +1284,19 @@ render :: (backend: *D3D11_Backend, command_buffer: *Render_Command_Buffer) { if it.set_cull_face.cull_face == { case .FRONT; { - state := get_or_create_rasterizer_state(d3d_renderer, d3d_renderer.fill, .FRONT); + state := get_or_create_rasterizer_state(d3d_renderer, d3d_renderer.fill, .FRONT, d3d_renderer.scissor_test); d3d_renderer.cull_face = .FRONT; ID3D11DeviceContext_RSSetState(d3d_context, state.state); } case .BACK; { - state := get_or_create_rasterizer_state(d3d_renderer, d3d_renderer.fill, .BACK); + state := get_or_create_rasterizer_state(d3d_renderer, d3d_renderer.fill, .BACK, d3d_renderer.scissor_test); d3d_renderer.cull_face = .BACK; ID3D11DeviceContext_RSSetState(d3d_context, state.state); } case .NONE; { - state := get_or_create_rasterizer_state(d3d_renderer, d3d_renderer.fill, .NONE); + state := get_or_create_rasterizer_state(d3d_renderer, d3d_renderer.fill, .NONE, d3d_renderer.scissor_test); d3d_renderer.cull_face = .NONE; ID3D11DeviceContext_RSSetState(d3d_context, state.state); } @@ -1370,6 +1372,20 @@ render :: (backend: *D3D11_Backend, command_buffer: *Render_Command_Buffer) { vp.TopLeftX = xx it.set_viewport.x; vp.TopLeftY = xx it.set_viewport.y; ID3D11DeviceContext_RSSetViewports(d3d_context, 1, *vp); + case .SET_SCISSOR; + rect : D3D11_RECT; + rect.left = xx it.set_scissor.x; + rect.right = xx (it.set_scissor.x + it.set_scissor.width); + rect.top = xx it.set_scissor.y; + rect.bottom = xx (it.set_scissor.y + it.set_scissor.height); + ID3D11DeviceContext_RSSetScissorRects(d3d_context, 1, *rect); + state := get_or_create_rasterizer_state(d3d_renderer, true, d3d_renderer.cull_face, true); + d3d_renderer.scissor_test = true; + ID3D11DeviceContext_RSSetState(d3d_context, state.state); + case .CLEAR_SCISSOR; + state := get_or_create_rasterizer_state(d3d_renderer, true, d3d_renderer.cull_face, false); + d3d_renderer.scissor_test = false; + ID3D11DeviceContext_RSSetState(d3d_context, state.state); case .DRAW; if it.draw.topology == { case .TRIANGLE_LIST; @@ -1426,6 +1442,7 @@ render :: (backend: *D3D11_Backend, command_buffer: *Render_Command_Buffer) { } } + d3d_renderer.scissor_test = false; now := seconds_since_init(); engine.renderer.last_render_time_cpu = cast(float)(now - time) * 1000.0; @@ -1446,17 +1463,17 @@ render :: (backend: *D3D11_Backend, command_buffer: *Render_Command_Buffer) { } #scope_file -get_or_create_rasterizer_state :: (backend: *D3D11_Backend, fill: bool, cull_face: Cull_Face) -> *Rasterizer_State { +get_or_create_rasterizer_state :: (backend: *D3D11_Backend, fill: bool, cull_face: Cull_Face, scissor_test: bool) -> *Rasterizer_State { for * backend.rasterizer_states { - if it.fill == fill && it.cull_face == cull_face { + if it.fill == fill && it.cull_face == cull_face && it.scissor_test == scissor_test { return it; } } - return create_rasterizer_state(backend, fill, cull_face); + return create_rasterizer_state(backend, fill, cull_face, scissor_test); } -create_rasterizer_state :: (backend: *D3D11_Backend, fill: bool, cull_face: Cull_Face) -> *Rasterizer_State { +create_rasterizer_state :: (backend: *D3D11_Backend, fill: bool, cull_face: Cull_Face, scissor_test: bool) -> *Rasterizer_State { raster_desc : D3D11_RASTERIZER_DESC; raster_desc.AntialiasedLineEnable = .FALSE; @@ -1481,7 +1498,7 @@ create_rasterizer_state :: (backend: *D3D11_Backend, fill: bool, cull_face: Cull raster_desc.FrontCounterClockwise = .FALSE; raster_desc.MultisampleEnable = .FALSE; - raster_desc.ScissorEnable = .FALSE; + raster_desc.ScissorEnable = ifx scissor_test then .TRUE else .FALSE; raster_desc.SlopeScaledDepthBias = 0.0; state : *ID3D11RasterizerState; diff --git a/renderer/renderer.jai b/renderer/renderer.jai index a58bee2..74099b9 100644 --- a/renderer/renderer.jai +++ b/renderer/renderer.jai @@ -422,6 +422,8 @@ Render_Command_Type :: enum { SET_BACKBUFFER; SET_SAMPLER; SET_VIEWPORT; + SET_SCISSOR; + CLEAR_SCISSOR; CLEAR_RENDER_TARGET; CLEAR_DEPTH_STENCIL; CLEAR_BACKBUFFER; @@ -498,6 +500,12 @@ Render_Command :: struct { min_depth: float; max_depth: float = 1.0; } + set_scissor : struct { + width: u32; + height: u32; + x: u32; + y: u32; + } clear_render_target : struct { rt: Backend_Render_Target; color : Vector4; @@ -1262,6 +1270,24 @@ push_cmd_set_viewport :: (using renderer: *Renderer, width: u32, height: u32, x: array_add(*engine.renderer.command_buffer.commands, command); } +push_cmd_set_scissor :: (using renderer: *Renderer, width: u32, height: u32, x: u32 = 0, y: u32 = 0) { + command : Render_Command; + command.type = .SET_SCISSOR; + command.set_scissor.width = width; + command.set_scissor.height = height; + command.set_scissor.x = x; + command.set_scissor.y = y; + + array_add(*engine.renderer.command_buffer.commands, command); +} + +push_cmd_clear_scissor :: (using renderer: *Renderer) { + command : Render_Command; + command.type = .CLEAR_SCISSOR; + + array_add(*engine.renderer.command_buffer.commands, command); +} + push_cmd_clear_render_target :: (using renderer: *Renderer, rt: Render_Target_Handle, color: Vector4) { command : Render_Command; command.type = .CLEAR_RENDER_TARGET; diff --git a/ui/ui.jai b/ui/ui.jai index c68acd4..73c5a7e 100644 --- a/ui/ui.jai +++ b/ui/ui.jai @@ -2,6 +2,11 @@ MAX_VERT_BUFFERS :: 64; MAX_BOXES :: 4096; MAX_WINDOWS :: 128; +SCROLL_SPEED :: 5; + +WINDOW_TITLE_BAR_HEIGHT :: 20.0; +WINDOW_BORDER_WIDTH :: 3.0; + UI_Box_Flags :: enum_flags u32 { NONE :: 0; CLICKABLE :: 1; @@ -133,11 +138,16 @@ Textured_Vert_Buffer :: struct { UI_Window :: struct { hash: u32; - root: *UI_Box; + boxes: Static_Array(*UI_Box, 4096); title: string; - position: Vector2; - size: Vector2; + position: Vector2i; + size: Vector2i; + + actual_position: Vector2i; + offset: Vector2i; + + scroll_offset: Vector2i; last_used_frame_index: u64; } @@ -151,6 +161,8 @@ UI_State :: struct { windows: Table(u32, UI_Window); current_window: *UI_Window; + currently_moving_window: *UI_Window; + last_box: *UI_Box; parent_stack: Stack(*UI_Box); @@ -178,28 +190,52 @@ UI_State :: struct { instanced_rects : [..] Rect_Instance_Data; instance_rect_sb : Buffer_Handle; + window_buffers: [MAX_WINDOWS] Instanced_Rects_Data; + num_used_window_buffers: s64; + texture_vert_buffers : [MAX_VERT_BUFFERS] Textured_Vert_Buffer; next_available_texture_buffer_index: s64; sampler: Sampler_Handle; } +Instanced_Rects_Data :: struct { + rects: Static_Array(Rect_Instance_Data, 4096); + buffer: Buffer_Handle; +} + ui_state : UI_State; -ui_window_make :: (title: string, hash: u32) -> *UI_Window { +ui_window_make :: (hash: u32) -> *UI_Window { window := get_ui_window_or_create_new(hash); return window; } -ui_window_begin :: (title: string, identifier: s64 = 0, loc := #caller_location) { +ui_window_begin :: (title: string, x: s32, y: s32, width: s32, height: s32, identifier: s64 = 0, loc := #caller_location) { assert(ui_state.current_window == null); - ui_state.current_window = ui_window_make(title, hash=get_hash(loc, identifier)); - ui_state.current_window.last_used_frame_index = ui_state.frame_index; + window := ui_window_make(hash=get_hash(loc, identifier)); + window.title = copy_temporary_string(title); + window.boxes.count = 0; + //window.boxes.allocator = temp; + window.last_used_frame_index = ui_state.frame_index; + window.position.x = x; + window.position.y = y; + window.size.x = width; + window.size.y = height; + + ui_state.current_window = window; + + ui_set_next_size_x(.PCT, 1.0); + ui_set_next_size_y(.CHILDREN_SUM); + ui_set_next_background_color(.{0.04, 0.04, 0.04, 1.0}); + background := ui_box(.DRAW_BACKGROUND); + ui_push_parent(background, alignment=.LEFT, axis=.VERTICAL); } ui_window_end :: () { ui_state.current_window = null; + ui_pop_parent(); } ui_box :: (flags: UI_Box_Flags, identifier: s64 = 0, loc := #caller_location) -> *UI_Box { @@ -214,8 +250,11 @@ ui_box_make :: (flags: UI_Box_Flags, hash: u32) -> *UI_Box { parent.num_children += 1; actual_hash += parent.hash * xx parent.num_children; //actual_hash += xx (parent.num_children*2); + } else if ui_state.current_window != null { + actual_hash += ui_state.current_window.hash + actual_hash; } + box := get_ui_box_or_create_new(actual_hash); box.last_used_frame_index = ui_state.frame_index; box.first_child = null; @@ -227,14 +266,11 @@ ui_box_make :: (flags: UI_Box_Flags, hash: u32) -> *UI_Box { box.parent = null; box.root_for_window = null; - if ui_state.current_window { - if ui_state.current_window.root == null { - ui_state.current_window.root = box; - box.root_for_window = ui_state.current_window; - } + if ui_state.current_window && parent == null { + array_add(*ui_state.current_window.boxes, box); + box.root_for_window = ui_state.current_window; } - // Set the links box.parent = parent; if box.parent != null { @@ -308,7 +344,8 @@ ui_init :: () { init(*ui_state.boxes, MAX_BOXES); ui_state.sampler = create_sampler(engine.renderer); - ui_state.fonts.regular = create_font(engine.renderer, "../assets/fonts/roboto/Roboto-Regular.ttf", 14); + //ui_state.fonts.regular = create_font(engine.renderer, "../assets/fonts/roboto/Roboto-Regular.ttf", 32); + ui_state.fonts.regular = create_font(engine.renderer, "../assets/fonts/Inconsolata-Regular.ttf", 18); ui_state.fonts.button = create_font(engine.renderer, "../assets/fonts/roboto/Roboto-Regular.ttf", 14); // ui_rect @@ -428,6 +465,13 @@ ui_init :: () { dynamic_buffer_size := size_of(Rect_Instance_Data) * ui_state.max_verts; ui_state.instance_rect_sb = create_structured_buffer(engine.renderer, null, dynamic_buffer_size, stride=size_of(Rect_Instance_Data), mappable=true); } + + { + for 0..MAX_WINDOWS-1 { + dynamic_buffer_size := size_of(Rect_Instance_Data) * ui_state.max_verts; + ui_state.window_buffers[it].buffer = create_structured_buffer(engine.renderer, null, dynamic_buffer_size, stride=size_of(Rect_Instance_Data), mappable=true); + } + } } // # BEGIN # LAYOUT ALGORITHM @@ -458,12 +502,22 @@ ui_figure_out_sizes :: () { // Upwards-dependent for *box : ui_state.boxes { if box.semantic_size[0].size_kind == .PCT { - assert(box.parent != null); - box.size.x = box.parent.size.x * box.semantic_size[0].value - (box.parent.padding_left + box.parent.padding_right); + if box.parent != null { + box.size.x = box.parent.size.x * box.semantic_size[0].value - (box.parent.padding_left + box.parent.padding_right); + } else if box.root_for_window != null { + box.size.x = cast(float)box.root_for_window.size.x * box.semantic_size[0].value - (WINDOW_BORDER_WIDTH); + } else { + assert(false); + } } if box.semantic_size[1].size_kind == .PCT { - assert(box.parent != null); - box.size.y = box.parent.size.y * box.semantic_size[1].value - (box.parent.padding_top + box.parent.padding_bottom); + if box.parent != null { + box.size.y = box.parent.size.y * box.semantic_size[1].value - (box.parent.padding_top + box.parent.padding_bottom); + } else if box.root_for_window != null { + box.size.y = cast(float)box.root_for_window.size.y * box.semantic_size[0].value - (WINDOW_BORDER_WIDTH); + } else { + assert(false); + } } } @@ -512,8 +566,14 @@ ui_figure_out_sizes :: () { // Find final positions for *box : ui_state.boxes { if box.parent == null { - box.rect.x = 0.0; - box.rect.y = 0.0; + if box.root_for_window { + window := box.root_for_window; + box.rect.x = xx (window.actual_position.x + window.scroll_offset.x) + WINDOW_BORDER_WIDTH; + box.rect.y = xx (window.actual_position.y + window.scroll_offset.y) + WINDOW_BORDER_WIDTH + WINDOW_TITLE_BAR_HEIGHT; + } else { + box.rect.x = 0.0; + box.rect.y = 0.0; + } box.rect.w = box.size.x; box.rect.h = box.size.y; ui_set_rect_recursively(box); @@ -600,9 +660,11 @@ ui_begin :: () { assert(!ui_state.begun); ui_state.frame_index += 1; ui_state.begun = true; + ui_state.current_window = null; ui_state.colored_verts.count = 0; ui_state.instanced_rects.count = 0; ui_state.next_available_texture_buffer_index = 0; + ui_state.num_used_window_buffers = 0; } ui_end :: () { @@ -689,55 +751,153 @@ ui_set_next_size_y :: (size: UI_Size_Kind, value: float = 0.0, strictness: float } // # END # BOX PROPERTY FUNCTIONS +get_rect_instance_buffer :: () -> *Instanced_Rects_Data { + //assert(ui_state.num_used_window_buffers <= MAX_WINDOWS); + next := *ui_state.window_buffers[ui_state.num_used_window_buffers]; + next.rects.count = 0; + ui_state.num_used_window_buffers += 1; + return next; +} + ui_render :: () { - // Render background - for * ui_state.boxes { - if it.parent == null { - ui_render_background_recursively(it); + // Draw each window separately + for window: ui_state.windows { + // Draw outer rect + border of window + title_bar_buffer_data := get_rect_instance_buffer(); + inv_w := 1.0 / cast(float)engine.renderer.render_target_width; + inv_h := 1.0 / cast(float)engine.renderer.render_target_height; + { + // Draw the window + + x : float = xx window.actual_position.x * inv_w * 2.0 - 1.0; + y : float = (cast(float)engine.renderer.render_target_height - xx window.actual_position.y) * inv_h * 2.0 - 1.0; + w : float = xx window.size.x * inv_w * 2.0; + h : float = xx window.size.y * inv_h * 2.0; + + color : Color = .{0.0,0.1,0.3,1}; + instance_data := instance_data_from_rect(x, y, w, h, 0.0, 0.0, 0.0, color); + array_add(*title_bar_buffer_data.rects, instance_data); + + { + border_color : Color = .{0.6,0.6,0.6,1}; + instance_data := instance_data_from_rect(x, y, w, h, 0.0, 0.0, WINDOW_BORDER_WIDTH, border_color); + array_add(*title_bar_buffer_data.rects, instance_data); + } } - } - if ui_state.colored_verts.count > 0 { - //upload_data_to_buffer(engine.renderer, ui_state.rect_vb, ui_state.colored_verts.data, cast(s32)ui_state.colored_verts.count * size_of(Colored_Vert)); + // Draw the title bar + { + x : float = (xx window.actual_position.x + WINDOW_BORDER_WIDTH) * inv_w * 2.0 - 1.0; + y : float = (cast(float)engine.renderer.render_target_height - (xx window.actual_position.y + WINDOW_BORDER_WIDTH)) * inv_h * 2.0 - 1.0; + w : float = (xx window.size.x - WINDOW_BORDER_WIDTH * 2.0) * inv_w * 2.0; + h : float = xx WINDOW_TITLE_BAR_HEIGHT * inv_h * 2.0; - //push_cmd_set_draw_mode(engine.renderer, .FILL); - //push_cmd_set_depth_write(engine.renderer, false); - //push_cmd_set_pipeline_state(engine.renderer, ui_state.shaders.ui_rect); + instance_data := instance_data_from_rect(x, y, w, h, 0.0, 0.0, 0.0, .{0.0, 0.2, 0.4, 1.0}); + array_add(*title_bar_buffer_data.rects, instance_data); + } - //push_cmd_set_vertex_buffer(engine.renderer, ui_state.rect_vb); - //push_cmd_draw(engine.renderer, ui_state.colored_verts.count); - } - // NEW DRAW TIME - if ui_state.instanced_rects.count > 0 { - upload_data_to_buffer(engine.renderer, ui_state.instance_rect_sb, ui_state.instanced_rects.data, cast(s32)ui_state.instanced_rects.count * size_of(Rect_Instance_Data)); + { + upload_data_to_buffer(engine.renderer, title_bar_buffer_data.buffer, title_bar_buffer_data.rects.data.data, cast(s32)title_bar_buffer_data.rects.count * size_of(Rect_Instance_Data)); + + push_cmd_set_draw_mode(engine.renderer, .FILL); + push_cmd_set_depth_write(engine.renderer, false); + push_cmd_set_pipeline_state(engine.renderer, ui_state.shaders.ui); + + push_cmd_set_constant_buffer(engine.renderer, 0, engine.screen_data_buffer, .PIXEL); + push_cmd_set_structured_buffer(engine.renderer, 0, title_bar_buffer_data.buffer, .VERTEX); + push_cmd_draw_instanced(engine.renderer, 4, title_bar_buffer_data.rects.count, topology=.TRIANGLE_STRIP); + + // Title bar + push_cmd_set_pipeline_state(engine.renderer, console.text_pipeline); + push_cmd_set_sampler(engine.renderer, 0, ui_state.sampler); + { + if window.title.count > 0 { + font_handle := ui_state.fonts.regular; + x : float = (xx window.actual_position.x + WINDOW_BORDER_WIDTH + 2); + y : float = (cast(float)engine.renderer.render_target_height - (window.actual_position.y + WINDOW_BORDER_WIDTH + 2.0)); + + text_size := get_text_size(engine.renderer, window.title, font_handle); + y -= text_size.y; + render_data := bake_text(engine.renderer, x, y, window.title, font_handle, .{1,1,1,1}); + font := *engine.renderer.fonts[font_handle - 1]; + + push_cmd_set_texture(engine.renderer, 0, font.texture); + push_cmd_set_vertex_buffer(engine.renderer, render_data.vb); + push_cmd_draw(engine.renderer, render_data.vert_count); + } + } + } + + push_cmd_set_scissor(engine.renderer, + xx window.size.x, + xx (window.size.y - WINDOW_TITLE_BAR_HEIGHT), + xx window.actual_position.x, + xx (window.actual_position.y + WINDOW_TITLE_BAR_HEIGHT)); + + buffer_data := get_rect_instance_buffer(); + + for box: window.boxes { + // Render background + ui_render_background_recursively(box, buffer_data); + } + + upload_data_to_buffer(engine.renderer, buffer_data.buffer, buffer_data.rects.data.data, cast(s32)buffer_data.rects.count * size_of(Rect_Instance_Data)); push_cmd_set_draw_mode(engine.renderer, .FILL); push_cmd_set_depth_write(engine.renderer, false); push_cmd_set_pipeline_state(engine.renderer, ui_state.shaders.ui); push_cmd_set_constant_buffer(engine.renderer, 0, engine.screen_data_buffer, .PIXEL); - push_cmd_set_structured_buffer(engine.renderer, 0, ui_state.instance_rect_sb, .VERTEX); - push_cmd_draw_instanced(engine.renderer, 4, ui_state.instanced_rects.count, topology=.TRIANGLE_STRIP); + push_cmd_set_structured_buffer(engine.renderer, 0, buffer_data.buffer, .VERTEX); + push_cmd_draw_instanced(engine.renderer, 4, buffer_data.rects.count, topology=.TRIANGLE_STRIP); + + // Text + push_cmd_set_pipeline_state(engine.renderer, console.text_pipeline); + push_cmd_set_sampler(engine.renderer, 0, ui_state.sampler); + + for box: window.boxes { + ui_render_text_recursively(box); + } + + push_cmd_clear_scissor(engine.renderer); + } + + // Render background + for * ui_state.boxes { + if it.parent == null { + //ui_render_background_recursively(it); + } + } + + // NEW DRAW TIME + if ui_state.instanced_rects.count > 0 { + //upload_data_to_buffer(engine.renderer, ui_state.instance_rect_sb, ui_state.instanced_rects.data, cast(s32)ui_state.instanced_rects.count * size_of(Rect_Instance_Data)); + + //push_cmd_set_draw_mode(engine.renderer, .FILL); + //push_cmd_set_depth_write(engine.renderer, false); + //push_cmd_set_pipeline_state(engine.renderer, ui_state.shaders.ui); + + //push_cmd_set_constant_buffer(engine.renderer, 0, engine.screen_data_buffer, .PIXEL); + //push_cmd_set_structured_buffer(engine.renderer, 0, ui_state.instance_rect_sb, .VERTEX); + //push_cmd_draw_instanced(engine.renderer, 4, ui_state.instanced_rects.count, topology=.TRIANGLE_STRIP); } // @Incomplete: this should be part of the same rendering somehow... Although it should happen on a per-texture basis // Render backgrounds with texture - for * ui_state.boxes { - if it.parent == null { - ui_render_texture_background_recursively(it); - } - } + //for * ui_state.boxes { + // if it.parent == null { + // ui_render_texture_background_recursively(it); + // } + //} - // Render text - push_cmd_set_pipeline_state(engine.renderer, ui_state.shaders.text); - for * ui_state.boxes { - if it.parent == null { - ui_render_text_recursively(it); - } - } + //for * ui_state.boxes { + // if it.parent == null { + // ui_render_text_recursively(it); + // } + //} } #scope_file @@ -833,7 +993,7 @@ instance_data_from_rect :: (x: float, y: float, w: float, h: float, corner_radiu return data; } -ui_render_background_recursively :: (box: *UI_Box) { +ui_render_background_recursively :: (box: *UI_Box, buffer_data: *Instanced_Rects_Data) { inv_w := 1.0 / cast(float)engine.renderer.render_target_width; inv_h := 1.0 / cast(float)engine.renderer.render_target_height; @@ -845,7 +1005,7 @@ ui_render_background_recursively :: (box: *UI_Box) { color : Color = box.style.background_color; instance_data := instance_data_from_rect(x, y, w, h, 0.0, 0.0, 0.0, color); - array_add(*ui_state.instanced_rects, instance_data); + array_add(*buffer_data.rects, instance_data); //array_add(*ui_state.colored_verts, make_vert(x + w, y - h, color)); //array_add(*ui_state.colored_verts, make_vert(x, y - h, color)); @@ -864,7 +1024,7 @@ ui_render_background_recursively :: (box: *UI_Box) { color : Color = box.style.border_color; instance_data := instance_data_from_rect(x, y, w, h, 0.0, 0.0, box.style.border_width, color); - array_add(*ui_state.instanced_rects, instance_data); + array_add(*buffer_data.rects, instance_data); } if should_draw_hover_animation(box) { @@ -875,13 +1035,13 @@ ui_render_background_recursively :: (box: *UI_Box) { color := Color.{1,1,1, box.hover_animation_t * 0.3}; instance_data := instance_data_from_rect(x, y, w, h, 0.0, 0.0, 0.0, color); - array_add(*ui_state.instanced_rects, instance_data); + array_add(*buffer_data.rects, instance_data); } child := box.first_child; while child != null { defer child = child.next; - ui_render_background_recursively(child); + ui_render_background_recursively(child, buffer_data); } } @@ -990,6 +1150,47 @@ ui_update_input :: () { mouse_x := engine.input.mouse.x; mouse_y := engine.input.mouse.y; + // Scrolling + for *window: ui_state.windows { + rect: Rect; + rect.x = xx window.actual_position.x; + rect.y = xx (window.actual_position.y+WINDOW_TITLE_BAR_HEIGHT); + rect.w = xx window.size.x; + rect.h = xx (window.size.y - WINDOW_TITLE_BAR_HEIGHT); + + if is_mouse_inside(mouse_x, mouse_y, rect) { + window.scroll_offset.y += cast(s32) engine.input.mouse.wheel * SCROLL_SPEED; + } + } + + if ui_state.currently_moving_window == null { + for *window: ui_state.windows { + rect: Rect; + rect.x = xx window.actual_position.x; + rect.y = xx window.actual_position.y; + rect.w = xx window.size.x; + rect.h = xx WINDOW_TITLE_BAR_HEIGHT; + + if is_mouse_inside(mouse_x, mouse_y, rect) { + if key_down(.MOUSE_LEFT) { + ui_state.currently_moving_window = window; + break; + } + } + } + } else { + ui_state.currently_moving_window.offset.x += xx engine.input.mouse.delta_x; + ui_state.currently_moving_window.offset.y += xx engine.input.mouse.delta_y; + ui_state.currently_moving_window.offset.x -= xx min(0, ui_state.currently_moving_window.position.x + ui_state.currently_moving_window.offset.x); + ui_state.currently_moving_window.offset.y -= xx min(0, ui_state.currently_moving_window.position.y + ui_state.currently_moving_window.offset.y); + + if !key_pressed(.MOUSE_LEFT) ui_state.currently_moving_window = null; + } + + for *window: ui_state.windows { + window.actual_position = window.position + window.offset; + } + for *box: ui_state.boxes { if engine.mode == .EDITING || box.flags & .PLAY_MODE_FOCUSABLE { if box.flags & .CLICKABLE {