annoying work on font rendering

This commit is contained in:
Tuomas Katajisto 2025-05-03 00:10:04 +03:00
parent b0045107cb
commit cf01001cc1
6 changed files with 261 additions and 43 deletions

View File

@ -6,16 +6,29 @@ Arb_Tri :: struct {
Arb_Draw_Command_Type :: enum { Arb_Draw_Command_Type :: enum {
INVALID; INVALID;
DRAW_TEXT;
FLUSH_TRI; FLUSH_TRI;
SET_TEXTURE; SET_TEXTURE;
PREPARE_TEXT;
FONT_BOUNDARY;
QUAD_OCCLUDER;
REMOVE_TEXTURE; REMOVE_TEXTURE;
} }
Arb_Draw_Command :: struct { Arb_Draw_Command :: struct {
type : Arb_Draw_Command_Type = .INVALID; type : Arb_Draw_Command_Type = .INVALID;
// for triangles
tri_offset : int = 0; tri_offset : int = 0;
tri_count : int = 0; tri_count : int = 0;
texture : *Ui_Texture; texture : *Ui_Texture;
// for text
font : *Ui_Font;
text : string;
text_color : Vector4;
x,y : s64;
} }
Arb_Tri_State :: struct { Arb_Tri_State :: struct {
@ -89,8 +102,16 @@ arb_tri_flush :: () {
flush_arb_commands(); flush_arb_commands();
} }
debug_arb_flush : bool : true;
is_in_pass : bool = false;
layer : s32 = 0;
flush_arb_commands :: () { flush_arb_commands :: () {
layer = 0;
if debug_arb_flush then print(" --- !BEGIN FLUSH! ---- \n");
for arbTriState.command_list { for arbTriState.command_list {
if debug_arb_flush then print("[command] %\n", it.type);
if it.type == { if it.type == {
case .SET_TEXTURE; case .SET_TEXTURE;
gPipelines.arbtri.bind.images[0] = it.texture.tex; gPipelines.arbtri.bind.images[0] = it.texture.tex;
@ -98,9 +119,61 @@ flush_arb_commands :: () {
case .REMOVE_TEXTURE; case .REMOVE_TEXTURE;
gCurrentTexture = null; gCurrentTexture = null;
case .FLUSH_TRI; 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_pipeline(gPipelines.arbtri.pipeline);
sg_apply_bindings(*gPipelines.arbtri.bind); sg_apply_bindings(*gPipelines.arbtri.bind);
sg_draw(xx (it.tri_offset * 3), xx (it.tri_count * 3), 1); 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();
} }
} }

72
src/editor/console.jai Normal file
View File

@ -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);
}

18
src/editor/editor.jai Normal file
View File

@ -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();
}

View File

@ -1,6 +1,10 @@
#import "Basic"; #import "Basic";
#import "Math"; #import "Math";
#import "Input";
stbi :: #import "stb_image";
#load "ui/ui.jai"; #load "ui/ui.jai";
#load "editor/editor.jai";
#load "pipelines.jai"; #load "pipelines.jai";
#load "time.jai"; #load "time.jai";
#load "arbtri.jai"; #load "arbtri.jai";
@ -8,27 +12,27 @@
#load "load.jai"; #load "load.jai";
#load "./shaders/jai/shader_triangle.jai"; #load "./shaders/jai/shader_triangle.jai";
#import "Input"; last_frame_time : float64;
delta\ _time : float64;
stbi :: #import "stb_image";
state: struct { state: struct {
pass_action: sg_pass_action; pass_action_clear : sg_pass_action;
dpi_scale: float; pass_action : sg_pass_action;
fons: *FONScontext; dpi_scale : float;
font_default: Ui_Font; fons : *FONScontext;
font_default : Ui_Font;
}; };
Window_Info :: struct { Window_Info :: struct {
width: s32; width : s32;
height: s32; height : s32;
title: *u8; title : *u8;
}; };
get_window_info :: () -> Window_Info { get_window_info :: () -> Window_Info {
return Window_Info.{ return Window_Info.{
1200, 1920,
1200, 1080,
"trueno!" "trueno!"
}; };
} }
@ -63,7 +67,8 @@ init :: () {
state.font_default.fons_font = FONS_INVALID; state.font_default.fons_font = FONS_INVALID;
create_pipelines(); 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(); init_font_loads();
} }
@ -73,6 +78,9 @@ init_after_mandatory_loads :: () {
frame :: () { frame :: () {
delta_time = get_time() - last_frame_time;
last_frame_time = get_time();
sfetch_dowork(); sfetch_dowork();
if mandatory_loads_done() && !init_after_mandatory_done { if mandatory_loads_done() && !init_after_mandatory_done {
@ -81,6 +89,7 @@ frame :: () {
} }
if !mandatory_loads_done() then return; if !mandatory_loads_done() then return;
fonsClearState(state.fons);
dpis := state.dpi_scale; dpis := state.dpi_scale;
@ -94,12 +103,11 @@ frame :: () {
sgl_ortho(0.0, sapp_widthf(), sapp_heightf(), 0.0, -1.0, +1.0); sgl_ortho(0.0, sapp_widthf(), sapp_heightf(), 0.0, -1.0, +1.0);
tick_ui(); tick_ui();
sg_begin_pass(*(sg_pass.{ action = state.pass_action, swapchain = cast,force(sg_swapchain) sglue_swapchain() }));
render_ui(); render_ui();
sfons_flush(state.fons); is_in_pass = true;
sg_begin_pass(*(sg_pass.{ action = state.pass_action_clear, swapchain = cast,force(sg_swapchain) sglue_swapchain() }));
arb_tri_flush(); arb_tri_flush();
sgl_draw(); // sg_end_pass();
sg_end_pass();
sg_commit(); sg_commit();
input_per_frame_event_and_flag_update(); input_per_frame_event_and_flag_update();

View File

@ -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;
}

View File

@ -1,6 +1,37 @@
GR :: #import "GetRect_LeftHanded"()(Type_Indicator = Ui_Type_Indicator); GR :: #import "GetRect_LeftHanded"()(Type_Indicator = Ui_Type_Indicator);
Input :: #import "Input"; 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; default_texture : *Ui_Texture = null;
Ui_Font_Glyph :: struct { Ui_Font_Glyph :: struct {
@ -105,6 +136,7 @@ set_shader_for_color :: (enable_blend := false) {
type = .REMOVE_TEXTURE type = .REMOVE_TEXTURE
}); });
add_uvs = false; add_uvs = false;
immediate_flush();
} }
@ -114,12 +146,20 @@ set_shader_for_images :: (texture: *Ui_Texture) {
texture = texture texture = texture
}); });
add_uvs = true; add_uvs = true;
immediate_flush();
} }
gPreppedText: string; gPreppedText: string;
gPreppedTextWidth : s32; gPreppedTextWidth : s32;
prepare_text :: (font: *Ui_Type_Indicator.Font, text: string, effects: Ui_Type_Indicator.Font_Effects = 0) -> s64 { 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); fonsSetFont(state.fons, state.font_default.fons_font);
fonsSetSize(state.fons, xx font.character_height); fonsSetSize(state.fons, xx font.character_height);
w := fonsTextBounds(state.fons, 0.0, 0.0, text.data, text.data + text.count, null); 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; 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) { 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)); arb_tri_command_add(.{
fonsSetColor(state.fons, color); type = .DRAW_TEXT,
result := cast(*u8) temporary_alloc(gPreppedText.count + 1); // Add 1 for the zero. text_color = text_color,
memcpy(result, gPreppedText.data, gPreppedText.count); font = font,
result[gPreppedText.count] = 0; x = x,
fonsDrawText(state.fons, xx x, xx y, result, null); y = y
});
} }
get_mouse_pointer_position :: (window: Ui_Type_Indicator.Window_Type, right_handed: bool) -> (x: int, y: int, success: bool) { 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; return xx input_mouse_x, xx input_mouse_y, true;
@ -232,8 +273,12 @@ tick_ui :: () {
for ui_events { for ui_events {
GR.getrect_handle_event(it); GR.getrect_handle_event(it);
} }
vw = (cast(float) w)/100.0;
vh = (cast(float) h)/100.0;
array_reset_keeping_memory(*ui_events); array_reset_keeping_memory(*ui_events);
GR.ui_per_frame_update(1, xx w, xx h, get_time()); GR.ui_per_frame_update(1, xx w, xx h, get_time());
tick_editor_ui();
} }
checkboxTest : bool = false; checkboxTest : bool = false;
@ -247,29 +292,26 @@ idk : bool;
test_color : Vector3 = .{1.0, 0.0, 1.0}; 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 :: () { render_ui :: () {
proc := GR.default_theme_procs[0]; proc := GR.default_theme_procs[0];
my_theme := proc(); my_theme := proc();
GR.set_default_theme(my_theme); GR.set_default_theme(my_theme);
draw_editor_ui(*my_theme);
r := GR.get_rect(10, 10, 400, 30); r := GR.get_rect(100, 100, 200, 50);
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);
} }