From cf01001cc1e4d0007e5420c9060e0b299424e9ca Mon Sep 17 00:00:00 2001 From: Katajisto Date: Sat, 3 May 2025 00:10:04 +0300 Subject: [PATCH] annoying work on font rendering --- src/arbtri.jai | 73 +++++++++++++++++++++++++++++ src/editor/console.jai | 72 +++++++++++++++++++++++++++++ src/editor/editor.jai | 18 ++++++++ src/main.jai | 44 ++++++++++-------- src/ui/component_themes.jai | 5 ++ src/ui/ui.jai | 92 +++++++++++++++++++++++++++---------- 6 files changed, 261 insertions(+), 43 deletions(-) create mode 100644 src/editor/console.jai create mode 100644 src/editor/editor.jai create mode 100644 src/ui/component_themes.jai diff --git a/src/arbtri.jai b/src/arbtri.jai index 7d2a002..f55b7f4 100644 --- a/src/arbtri.jai +++ b/src/arbtri.jai @@ -6,16 +6,29 @@ Arb_Tri :: struct { Arb_Draw_Command_Type :: enum { INVALID; + DRAW_TEXT; FLUSH_TRI; SET_TEXTURE; + PREPARE_TEXT; + FONT_BOUNDARY; + QUAD_OCCLUDER; REMOVE_TEXTURE; } Arb_Draw_Command :: struct { type : Arb_Draw_Command_Type = .INVALID; + + // for triangles tri_offset : int = 0; tri_count : int = 0; texture : *Ui_Texture; + + // for text + font : *Ui_Font; + text : string; + text_color : Vector4; + x,y : s64; + } Arb_Tri_State :: struct { @@ -89,8 +102,16 @@ arb_tri_flush :: () { flush_arb_commands(); } +debug_arb_flush : bool : true; +is_in_pass : bool = false; + +layer : s32 = 0; + flush_arb_commands :: () { + layer = 0; + if debug_arb_flush then print(" --- !BEGIN FLUSH! ---- \n"); for arbTriState.command_list { + if debug_arb_flush then print("[command] %\n", it.type); if it.type == { case .SET_TEXTURE; gPipelines.arbtri.bind.images[0] = it.texture.tex; @@ -98,9 +119,61 @@ flush_arb_commands :: () { case .REMOVE_TEXTURE; gCurrentTexture = null; case .FLUSH_TRI; + if !is_in_pass { + sg_begin_pass(*(sg_pass.{ action = state.pass_action, swapchain = cast,force(sg_swapchain) sglue_swapchain() })); + is_in_pass = true; + } sg_apply_pipeline(gPipelines.arbtri.pipeline); sg_apply_bindings(*gPipelines.arbtri.bind); sg_draw(xx (it.tri_offset * 3), xx (it.tri_count * 3), 1); + case .PREPARE_TEXT; + if is_in_pass { + sg_end_pass(); + is_in_pass = false; + } + font := it.font; + text := it.text; + if text.count < 1 then continue; + print("prep at size: %\n", font.character_height); + fonsSetFont(state.fons, state.font_default.fons_font); + fonsSetSize(state.fons, xx font.character_height); + w := fonsTextBounds(state.fons, 0.0, 0.0, text.data, text.data + text.count, null); + gPreppedText = text; + gPreppedTextWidth = cast(s32) w; + + array_reset(*font.temporary_glyphs); + array_reset(*font.temporary_glyphs_byte_offsets); + + for 0..(text.count-1) { + glyph : Ui_Font_Glyph; + + glyph.advance = cast(u32) fonsTextBounds(state.fons, 0.0, 0.0, text.data + it, text.data + it + 1, null); + + array_add(*font.temporary_glyphs, glyph); + array_add(*font.temporary_glyphs_byte_offsets, cast(u32) it); + } + case .DRAW_TEXT; + if gPreppedText.count < 1 then continue; + text_color := it.text_color; + x := it.x; + y := it.y; + font := it.font; + + color := sfons_rgba(xx (255.0 * text_color.x), xx (255.0 * text_color.y), xx (255.0 * text_color.z), xx (255.0 * text_color.w)); + fonsSetColor(state.fons, color); + result := cast(*u8) temporary_alloc(gPreppedText.count + 1); // Add 1 for the zero. + memcpy(result, gPreppedText.data, gPreppedText.count); + result[gPreppedText.count] = 0; + sgl_layer(layer); + fonsDrawText(state.fons, xx x, xx y, result, null); + sg_begin_pass(*(sg_pass.{ action = state.pass_action, swapchain = cast,force(sg_swapchain) sglue_swapchain() })); + sfons_flush(state.fons); + sgl_draw_layer(layer); + sg_end_pass(); + layer += 1; } } + if is_in_pass { + sg_end_pass(); + } } diff --git a/src/editor/console.jai b/src/editor/console.jai new file mode 100644 index 0000000..3d9b759 --- /dev/null +++ b/src/editor/console.jai @@ -0,0 +1,72 @@ +Console_State :: enum { + CLOSED; + OPEN_HALF; + OPEN_FULL; +} + +console_state : Console_State = .CLOSED; + +console_h : float = 0.0; +console_move_speed : float = 400.0; + +get_console_h_target_from_state :: (state: Console_State) -> float { + if state == { + case .OPEN_HALF; + return 45 * vh; + case .OPEN_FULL; + return 95 * vh; + } + return 0; +} + +tick_console :: () { + if input_button_states[Key_Code.F1] & .START { + if console_state != .CLOSED { + if input_button_states[Key_Code.SHIFT] & .DOWN { + if console_state == .OPEN_FULL { + console_state = .OPEN_HALF; + } else { + console_state = .OPEN_FULL; + } + } else { + console_state = .CLOSED; + } + } else { + if input_button_states[Key_Code.SHIFT] & .DOWN { + console_state = .OPEN_FULL; + } else { + console_state = .OPEN_HALF; + } + } + } + + t := get_console_h_target_from_state(console_state); + if console_h < t { + console_h += cast(float) (console_move_speed * vh * delta_time); + if console_h > t { + console_h = t; + } + } + if console_h > t { + console_h -= cast(float) (console_move_speed * vh * delta_time); + if console_h < t { + console_h = t; + } + } +} + +console_input : string = "HELLO??"; + +draw_console :: (theme: *GR.Overall_Theme) { + if console_h < 1 then return; + r := GR.get_rect(0, 0, 100*vw, console_h); + draw_rectangle(r, theme.text_input_theme.button_theme.surface_color); + r.w = 102*vw; + r.x -= 1*vw; + r.y = r.h - ui_h(5, 4); + r.h = ui_h(5, 4); + GR.label(r, "HELLO?!! wTF=", *theme.label_theme); + // a, new, state := GR.text_input(r, console_input, *theme.text_input_theme); + + // if !state.active then GR.activate(state); +} diff --git a/src/editor/editor.jai b/src/editor/editor.jai new file mode 100644 index 0000000..a1e90b1 --- /dev/null +++ b/src/editor/editor.jai @@ -0,0 +1,18 @@ +#load "console.jai"; + +draw_editor_ui :: (theme: *GR.Overall_Theme) { + r := GR.get_rect(0,0,0,0); + r.x = 0; r.w = 100*vw; + r.y = 0; r.h = ui_h(5, 0); + draw_bg_rectangle(r, theme); + r.w = 15*vw; + GR.label(r, "Trueno!", *t_label_left(theme)); + r = GR.get_rect(0, ui_h(5,0), ui_w(20, 20), ui_h(95, 0)); + draw_bg_rectangle(r, theme); + + draw_console(theme); +} + +tick_editor_ui :: () { + tick_console(); +} diff --git a/src/main.jai b/src/main.jai index 4b7b4e3..0a4f3c3 100644 --- a/src/main.jai +++ b/src/main.jai @@ -1,6 +1,10 @@ #import "Basic"; #import "Math"; +#import "Input"; +stbi :: #import "stb_image"; + #load "ui/ui.jai"; +#load "editor/editor.jai"; #load "pipelines.jai"; #load "time.jai"; #load "arbtri.jai"; @@ -8,27 +12,27 @@ #load "load.jai"; #load "./shaders/jai/shader_triangle.jai"; -#import "Input"; - -stbi :: #import "stb_image"; +last_frame_time : float64; +delta\ _time : float64; state: struct { - pass_action: sg_pass_action; - dpi_scale: float; - fons: *FONScontext; - font_default: Ui_Font; + pass_action_clear : sg_pass_action; + pass_action : sg_pass_action; + dpi_scale : float; + fons : *FONScontext; + font_default : Ui_Font; }; Window_Info :: struct { - width: s32; - height: s32; - title: *u8; + width : s32; + height : s32; + title : *u8; }; get_window_info :: () -> Window_Info { return Window_Info.{ - 1200, - 1200, + 1920, + 1080, "trueno!" }; } @@ -63,7 +67,8 @@ init :: () { state.font_default.fons_font = FONS_INVALID; create_pipelines(); - state.pass_action.colors[0] = .{ load_action = .CLEAR, clear_value = .{ r = 0.5, g = 0.5, b = 0.9, a = 1 } }; + state.pass_action_clear.colors[0] = .{ load_action = .CLEAR, clear_value = .{ r = 0.5, g = 0.5, b = 0.9, a = 1 } }; + state.pass_action.colors[0] = .{ load_action = .LOAD }; init_font_loads(); } @@ -73,6 +78,9 @@ init_after_mandatory_loads :: () { frame :: () { + delta_time = get_time() - last_frame_time; + last_frame_time = get_time(); + sfetch_dowork(); if mandatory_loads_done() && !init_after_mandatory_done { @@ -81,6 +89,7 @@ frame :: () { } if !mandatory_loads_done() then return; + fonsClearState(state.fons); dpis := state.dpi_scale; @@ -94,12 +103,11 @@ frame :: () { sgl_ortho(0.0, sapp_widthf(), sapp_heightf(), 0.0, -1.0, +1.0); tick_ui(); - sg_begin_pass(*(sg_pass.{ action = state.pass_action, swapchain = cast,force(sg_swapchain) sglue_swapchain() })); - render_ui(); - sfons_flush(state.fons); + render_ui(); + is_in_pass = true; + sg_begin_pass(*(sg_pass.{ action = state.pass_action_clear, swapchain = cast,force(sg_swapchain) sglue_swapchain() })); arb_tri_flush(); - sgl_draw(); - sg_end_pass(); + // sg_end_pass(); sg_commit(); input_per_frame_event_and_flag_update(); diff --git a/src/ui/component_themes.jai b/src/ui/component_themes.jai new file mode 100644 index 0000000..7d94cba --- /dev/null +++ b/src/ui/component_themes.jai @@ -0,0 +1,5 @@ +t_label_left :: (theme: *GR.Overall_Theme) -> GR.Label_Theme { + t := theme.label_theme; + t.alignment = GR.Text_Alignment.Left; + return t; +} diff --git a/src/ui/ui.jai b/src/ui/ui.jai index 6b580fd..7e4f94e 100644 --- a/src/ui/ui.jai +++ b/src/ui/ui.jai @@ -1,6 +1,37 @@ GR :: #import "GetRect_LeftHanded"()(Type_Indicator = Ui_Type_Indicator); Input :: #import "Input"; +#load "component_themes.jai"; + +// vw is 1/100 of view width +vw : float; +// vh is 1/100 of view height +vh : float; + +// These are for making minimum widths and heights. +// nvh is 1/100 of 1080; +nvh : float : 1080 / 100; +// nvw is 1/100 of 1920; +nvw : float : 1920 / 100; + +ui_w :: (normal_w : int, min_w: int) -> float { + min_width := min_w * nvw; + normal_width := normal_w * vw; + + if min_width > normal_width then return min_width; + + return normal_width; +} + +ui_h :: (normal_h : int, min_h: int) -> float { + min_height := min_h * nvh; + normal_height := normal_h * vh; + + if min_height > normal_height then return min_height; + + return normal_height; +} + default_texture : *Ui_Texture = null; Ui_Font_Glyph :: struct { @@ -105,6 +136,7 @@ set_shader_for_color :: (enable_blend := false) { type = .REMOVE_TEXTURE }); add_uvs = false; + immediate_flush(); } @@ -114,12 +146,20 @@ set_shader_for_images :: (texture: *Ui_Texture) { texture = texture }); add_uvs = true; + immediate_flush(); } gPreppedText: string; gPreppedTextWidth : s32; prepare_text :: (font: *Ui_Type_Indicator.Font, text: string, effects: Ui_Type_Indicator.Font_Effects = 0) -> s64 { + if text.count < 1 then return 0; + + arb_tri_command_add(.{ + type = .PREPARE_TEXT, + font = font, + text = text + }); fonsSetFont(state.fons, state.font_default.fons_font); fonsSetSize(state.fons, xx font.character_height); w := fonsTextBounds(state.fons, 0.0, 0.0, text.data, text.data + text.count, null); @@ -141,12 +181,13 @@ prepare_text :: (font: *Ui_Type_Indicator.Font, text: string, effects: Ui_Type_I return cast(s64) w; } draw_prepared_text :: (font: *Ui_Type_Indicator.Font, x: s64, y: s64, text_color: Vector4, effects: Ui_Type_Indicator.Font_Effects = 0) { - color := sfons_rgba(xx (255.0 * text_color.x), xx (255.0 * text_color.y), xx (255.0 * text_color.z), xx (255.0 * text_color.w)); - fonsSetColor(state.fons, color); - result := cast(*u8) temporary_alloc(gPreppedText.count + 1); // Add 1 for the zero. - memcpy(result, gPreppedText.data, gPreppedText.count); - result[gPreppedText.count] = 0; - fonsDrawText(state.fons, xx x, xx y, result, null); + arb_tri_command_add(.{ + type = .DRAW_TEXT, + text_color = text_color, + font = font, + x = x, + y = y + }); } get_mouse_pointer_position :: (window: Ui_Type_Indicator.Window_Type, right_handed: bool) -> (x: int, y: int, success: bool) { return xx input_mouse_x, xx input_mouse_y, true; @@ -232,8 +273,12 @@ tick_ui :: () { for ui_events { GR.getrect_handle_event(it); } + vw = (cast(float) w)/100.0; + vh = (cast(float) h)/100.0; array_reset_keeping_memory(*ui_events); GR.ui_per_frame_update(1, xx w, xx h, get_time()); + + tick_editor_ui(); } checkboxTest : bool = false; @@ -247,29 +292,26 @@ idk : bool; test_color : Vector3 = .{1.0, 0.0, 1.0}; +draw_bg_rectangle :: (r: GR.Rect, theme: GR.Overall_Theme) { + draw_rectangle(r, theme.background_color); +} + +draw_rectangle :: (r: GR.Rect, color: Vector4) { + set_shader_for_color(); + immediate_quad(.{r.x, r.y}, .{r.x + r.w, r.y}, .{r.x + r.w, r.y + r.h}, .{r.x, r.y + r.h}, color); + immediate_flush(); +} + +font_boundary :: () { + arb_tri_command_add(.{type = .FONT_BOUNDARY}); +} + render_ui :: () { proc := GR.default_theme_procs[0]; my_theme := proc(); GR.set_default_theme(my_theme); - - r := GR.get_rect(10, 10, 400, 30); - pressed := GR.button(r, "GetRect render lfg!!", *my_theme.button_theme); - if pressed { - } - - r.y += 50; - - GR.text_input(r, "TTT", *my_theme.text_input_theme); - - r.y += 150; - if GR.base_checkbox(r, "CHECK!!!", idk, null) { - idk = !idk; - } - - r.y += 150; - r.h = 500; - - GR.color_picker(r, *test_color); + draw_editor_ui(*my_theme); + r := GR.get_rect(100, 100, 200, 50); }