font coloring and font wrapping at width
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 6 Nov 2021 21:43:02 +0000 (16:43 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 6 Nov 2021 21:43:02 +0000 (16:43 -0500)
Makefile
include/gfx.h
misc/onyx/heartbreak_graphics.onyx
src/gfx.c
src/heartbreak_graphics.c
tests/simp.onyx

index f8a10be33e50531c940e418856ea403f55158c0e..25d8e61a3b6200164cb28985c9831cf29c692cd9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -12,7 +12,7 @@ build/%.o: src/%.c
        $(CC) $(FLAGS) $(INCLUDES) -o $@ $(LIBS) -c $<
 
 tests/%.wasm: tests/%.onyx misc/onyx/*
-       onyx compile $< -o $@
+       onyx compile -V $< -o $@
 
 clean:
        rm -f $(OBJECT_FILES) 2>/dev/null
@@ -21,3 +21,5 @@ $(TARGET): $(OBJECT_FILES)
        $(CC) $(FLAGS) -o $@ $^ $(LIBS)
 
 all: $(TARGET) tests/simp.wasm
+run: all
+       ./bin/heartbreak tests/simp.wasm
index ac3f38e46fd879e22e5bf2ec8a931f256cff3f1b..5fe5c190dd99e1a1b41df38125fb358ad86a7994 100644 (file)
@@ -39,6 +39,7 @@ typedef struct Font {
     u64 magic_number;
 
     char *name;
+    f32  size;
 
     GLint texture;
 
@@ -78,7 +79,7 @@ typedef struct ImmediateRenderer {
     Font*        active_font;
 
     bh_arr(mat3) world_transforms;
-    i32          last_world_transforms_count;
+    b32          world_transform_dirty;
 
     GLuint matrix_block_buffer;
 } ImmediateRenderer;
@@ -91,7 +92,7 @@ void gfx_immediate_renderer_push_triangle(ImmediateRenderer *ir,
     f32 x1, f32 y1, f32 u1, f32 v1,
     f32 x2, f32 y2, f32 u2, f32 v2,
     f32 x3, f32 y3, f32 u3, f32 v3);
-void gfx_immediate_renderer_render_text(ImmediateRenderer *ir, f32 x, f32 y, char* msgptr, i32 msglen);
+void gfx_immediate_renderer_render_text(ImmediateRenderer *ir, f32 x, f32 y, char* msgptr, i32 msglen, f32 max_width);
 
 void gfx_immediate_renderer_update_window_size(ImmediateRenderer *ir, f32 width, f32 height);
 
index 20bca467e864878231b254d6e730422bce97f705..85bf8e0092696a3b700a05bc98fb8de2aff29b87 100644 (file)
@@ -19,18 +19,18 @@ rectangle :: (mode: FillMode, x, y, w, h: f32) -> void #foreign "heartbreak" "gr
 circle    :: (mode: FillMode, x, y, r: f32, segments: i32 = 24) -> void #foreign "heartbreak" "graphics_circle" ---
 ellipse   :: (mode: FillMode, x, y, r1, r2: f32, segments: i32 = 24) -> void #foreign "heartbreak" "graphics_ellipse" ---
 arc       :: (mode: FillMode, x, y, r, a1, a2: f32, segments: i32 = 24) -> void #foreign "heartbreak" "graphics_arc" ---
-print     :: (text: str, x, y: f32) -> void #foreign "heartbreak" "graphics_print" ---
+print     :: (text: str, x, y: f32, max_width: f32 = -1) -> void #foreign "heartbreak" "graphics_print" ---
 
 //
 // Transforms
 //
-origin    :: () -> void #foreign "heartbreak" "graphics_origin" ---
-translate :: (x, y: f32) -> void #foreign "heartbreak" "graphics_translate" ---
-scale     :: (x, y: f32) -> void #foreign "heartbreak" "graphics_scale" ---
-shear     :: (x, y: f32) -> void #foreign "heartbreak" "graphics_shear" ---
+origin    :: ()           -> void #foreign "heartbreak" "graphics_origin" ---
+translate :: (x, y: f32)  -> void #foreign "heartbreak" "graphics_translate" ---
+scale     :: (x, y: f32)  -> void #foreign "heartbreak" "graphics_scale" ---
+shear     :: (x, y: f32)  -> void #foreign "heartbreak" "graphics_shear" ---
 rotate    :: (angle: f32) -> void #foreign "heartbreak" "graphics_rotate" ---
-push      :: () -> void #foreign "heartbreak" "graphics_push" ---
-pop       :: () -> void #foreign "heartbreak" "graphics_pop" ---
+push      :: ()           -> void #foreign "heartbreak" "graphics_push" ---
+pop       :: ()           -> void #foreign "heartbreak" "graphics_pop" ---
 
 //
 // Objects
index a212170f1059011215bca98b944b93fba832e6c0..b27b7149ebff4dead27d81f1d4fa68f51491c3ce 100644 (file)
--- a/src/gfx.c
+++ b/src/gfx.c
@@ -18,7 +18,7 @@ static const char* SIMPLE_SHADER_VERTEX = "#version 300 es\n"
 "\n"
 "void main() {\n"
 "   gl_Position = u_view * vec4((u_world * vec3(a_vertex, 1)).xy, 0, 1);\n"
-"   v_color = a_color * u_color;\n"
+"   v_color = a_color;\n"
 "   v_texture = a_texture;\n"
 "}";
 
@@ -136,6 +136,8 @@ Shader gfx_shader_make_from_source(const char *vertex_src, const char *fragment_
 }
 
 static void gfx_immediate_renderer_update_world_transform(ImmediateRenderer *ir) {
+    if (!ir->world_transform_dirty) return;
+
     // TODO: Speed this process up either but changing mat3 to be a mat4x3 or something...
     f32* d = bh_arr_last(ir->world_transforms).data;
     f32 dumb_way_of_converting_a_matrix[] = {
@@ -147,6 +149,8 @@ static void gfx_immediate_renderer_update_world_transform(ImmediateRenderer *ir)
     glBindBuffer(GL_UNIFORM_BUFFER, ir->matrix_block_buffer);
     glBufferSubData(GL_UNIFORM_BUFFER, 16 * sizeof(f32), 12 * sizeof(f32), dumb_way_of_converting_a_matrix);
     glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+    ir->world_transform_dirty = 0;
 }
 
 void gfx_immediate_renderer_init(ImmediateRenderer *ir, bh_allocator allocator) {
@@ -253,7 +257,7 @@ void gfx_immediate_renderer_init(ImmediateRenderer *ir, bh_allocator allocator)
     gfx_image_unbind(ir);
 
     ir->current_color = (ImmediateColor) { 1, 1, 1, 1 };
-    ir->last_world_transforms_count = 0;
+    ir->world_transform_dirty = 1;
 
     ir->active_font = gfx_font_load(ir, "/usr/share/fonts/nerd-fonts-complete/TTF/mononoki-Regular Nerd Font Complete Mono.ttf", 32.0f);
 }
@@ -265,21 +269,22 @@ void gfx_immediate_renderer_flush(ImmediateRenderer *ir) {
     glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(ImmediateVertex) * ir->tris_vertex_count, ir->tris_vertex_data);
     glBindBuffer(GL_ARRAY_BUFFER, -1);
 
-    glUseProgram(ir->tris_shader.program);
-    if (bh_arr_length(ir->world_transforms) != ir->last_world_transforms_count) {
-        gfx_immediate_renderer_update_world_transform(ir);
-    }
+    gfx_immediate_renderer_update_world_transform(ir);
 
+    glUseProgram(ir->tris_shader.program);
     glBindVertexArray(ir->tris_vertex_array);
     glDrawArrays(GL_TRIANGLES, 0, ir->tris_vertex_count);
     glBindVertexArray(-1);
 
     ir->tris_vertex_count = 0;
-    ir->last_world_transforms_count = bh_arr_length(ir->world_transforms);
 }
 
 void gfx_immediate_renderer_set_color(ImmediateRenderer *ir, ImmediateColor color) {
     ir->current_color = color;
+
+    glBindBuffer(GL_UNIFORM_BUFFER, ir->matrix_block_buffer);
+    glBufferSubData(GL_UNIFORM_BUFFER, sizeof(f32) * (16 + 12), 4 * sizeof(f32), &color);
+    glBindBuffer(GL_UNIFORM_BUFFER, 0);
 }
 
 void gfx_immediate_renderer_push_vertex(ImmediateRenderer *ir, f32 x, f32 y, f32 u, f32 v) {
@@ -307,18 +312,30 @@ void gfx_immediate_renderer_push_triangle(ImmediateRenderer *ir,
     ir->tris_vertex_count += 3;
 }
 
-void gfx_immediate_renderer_render_text(ImmediateRenderer *ir, f32 x, f32 y, char* msgptr, i32 msglen) {
+void gfx_immediate_renderer_render_text(ImmediateRenderer *ir, f32 x, f32 y, char* msgptr, i32 msglen, f32 max_width) {
     if (ir->tris_vertex_count > 0) gfx_immediate_renderer_flush(ir);
 
     stbtt_aligned_quad* quads = (stbtt_aligned_quad *) alloca(msglen * sizeof(stbtt_aligned_quad));
     i32 quad_num = 0;
 
+    f32 origx = x;
+
     while (msglen--) {
         stbtt_GetPackedQuad(ir->active_font->char_data,
                             FONT_INTERNAL_IMAGE_SIZE, FONT_INTERNAL_IMAGE_SIZE,
                             *msgptr - ir->active_font->first_character,
                             &x, &y, &quads[quad_num], 0);
 
+        if (max_width > 0 && x - origx >= max_width) {
+            x = origx;
+            y += ir->active_font->size;
+
+            stbtt_GetPackedQuad(ir->active_font->char_data,
+                                FONT_INTERNAL_IMAGE_SIZE, FONT_INTERNAL_IMAGE_SIZE,
+                                *msgptr - ir->active_font->first_character,
+                                &x, &y, &quads[quad_num], 0);
+        }
+
         msgptr++, quad_num++;
     }
 
@@ -330,6 +347,8 @@ void gfx_immediate_renderer_render_text(ImmediateRenderer *ir, f32 x, f32 y, cha
     glBindTexture(GL_TEXTURE_2D, ir->active_font->texture);
     glUseProgram(ir->font_shader.program);
     glUniform1i(ir->font_shader.texture_uniform, 1);
+
+    gfx_immediate_renderer_update_world_transform(ir);
     glBindVertexArray(ir->font_vertex_array);
     glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (void *) 0, quad_num);
     glBindVertexArray(-1);
@@ -360,17 +379,23 @@ void gfx_immediate_renderer_push_state(ImmediateRenderer *ir) {
     gfx_immediate_renderer_flush(ir);
     mat3 new_top = bh_arr_last(ir->world_transforms);
     bh_arr_push(ir->world_transforms, new_top);
+
+    ir->world_transform_dirty = 1;
 }
 
 void gfx_immediate_renderer_pop_state(ImmediateRenderer *ir) {
     gfx_immediate_renderer_flush(ir);
     bh_arr_pop(ir->world_transforms);
+
+    ir->world_transform_dirty = 1;
 }
 
 void gfx_immediate_renderer_origin(ImmediateRenderer *ir) {
     gfx_immediate_renderer_flush(ir);
     mat3* top = &bh_arr_last(ir->world_transforms);
     mat3_identity(top);
+
+    ir->world_transform_dirty = 1;
 }
 
 void gfx_immediate_renderer_translate(ImmediateRenderer *ir, f32 dx, f32 dy) {
@@ -387,6 +412,7 @@ void gfx_immediate_renderer_translate(ImmediateRenderer *ir, f32 dx, f32 dy) {
     mat3_mul(&new_top, bh_arr_last(ir->world_transforms), trans);
 
     bh_arr_last(ir->world_transforms) = new_top;
+    ir->world_transform_dirty = 1;
 }
 
 void gfx_immediate_renderer_scale(ImmediateRenderer *ir, f32 sx, f32 sy) {
@@ -403,6 +429,7 @@ void gfx_immediate_renderer_scale(ImmediateRenderer *ir, f32 sx, f32 sy) {
     mat3_mul(&new_top, bh_arr_last(ir->world_transforms), scale);
 
     bh_arr_last(ir->world_transforms) = new_top;
+    ir->world_transform_dirty = 1;
 }
 
 void gfx_immediate_renderer_rotate(ImmediateRenderer *ir, f32 angle) {
@@ -422,6 +449,7 @@ void gfx_immediate_renderer_rotate(ImmediateRenderer *ir, f32 angle) {
     mat3_mul(&new_top, bh_arr_last(ir->world_transforms), rotate);
 
     bh_arr_last(ir->world_transforms) = new_top;
+    ir->world_transform_dirty = 1;
 }
 
 void gfx_immediate_renderer_shear(ImmediateRenderer *ir, f32 kx, f32 ky) {
@@ -438,6 +466,7 @@ void gfx_immediate_renderer_shear(ImmediateRenderer *ir, f32 kx, f32 ky) {
     mat3_mul(&new_top, bh_arr_last(ir->world_transforms), shear);
 
     bh_arr_last(ir->world_transforms) = new_top;
+    ir->world_transform_dirty = 1;
 }
 
 
@@ -530,6 +559,7 @@ Font* gfx_font_load(ImmediateRenderer *ir, char* font_path, i32 font_size) {
     font->first_character = FONT_FIRST_CHAR;
     font->last_character  = FONT_FIRST_CHAR + FONT_CHAR_COUNT - 1;
     font->char_data = font_char_data;
+    font->size = (f32) font_size;
 
     return font;
 }
index 506e886b422bdaace24e30ae0aed99f15796dd84..00c4656d5c5ffa5799876092493092579955b01a 100644 (file)
@@ -149,13 +149,14 @@ HEARTBREAK_DEF(arc, (WASM_I32, WASM_F32, WASM_F32, WASM_F32, WASM_F32, WASM_F32,
     return NULL;
 }
 
-HEARTBREAK_DEF(print, (WASM_I32, WASM_I32, WASM_F32, WASM_F32), ()) {
+HEARTBREAK_DEF(print, (WASM_I32, WASM_I32, WASM_F32, WASM_F32, WASM_F32), ()) {
     char* str_data = wasm_memory_data(wasm_memory) + params->data[0].of.i32;
     u32   str_size = params->data[1].of.i32;
 
     f32 x = params->data[2].of.f32;
     f32 y = params->data[3].of.f32;
-    gfx_immediate_renderer_render_text(&renderer, x, y, str_data, str_size);
+    f32 max_width = params->data[4].of.f32;
+    gfx_immediate_renderer_render_text(&renderer, x, y, str_data, str_size, max_width);
 
     return NULL;
 }
index e7387cc74d70140c3ed408e145c7caa5ab8c8ac1..ddec24900a6eeabb203d55d0557cd711b1d7c0b1 100644 (file)
@@ -82,5 +82,6 @@ draw :: () {
     hb.graphics.setColor(0, 0, 1);
     hb.graphics.arc(.Fill, 300, 300, 100, math.PI / 4, math.PI * 7 / 4);
 
-    hb.graphics.print("Hello, World!", 100, 100);
+    hb.graphics.setColor(0, 1, 1);
+    hb.graphics.print("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+`~[{}]\|<,>./?'\";:", 100, 100, 200 + math.sin(t) * 100);
 }