From: Brendan Hansen Date: Wed, 3 Nov 2021 15:07:15 +0000 (-0500) Subject: added graphics transform and state X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=f14b0293cb0373ecfd552313189ac3b910491456;p=heartbreak.git added graphics transform and state --- diff --git a/include/gfx.h b/include/gfx.h index 2585c1a..e561704 100644 --- a/include/gfx.h +++ b/include/gfx.h @@ -3,6 +3,7 @@ #include "bh.h" +#include "utils.h" #include #include @@ -43,6 +44,8 @@ typedef struct ImmediateRenderer { GLint vertex_buffer; Shader simple_shader; + + bh_arr(mat3) world_transforms; } ImmediateRenderer; void gfx_immediate_renderer_init(ImmediateRenderer *ir); @@ -55,4 +58,12 @@ void gfx_immediate_renderer_push_triangle(ImmediateRenderer *ir, f32 x3, f32 y3, f32 u3, f32 v3); void gfx_immediate_renderer_update_window_size(ImmediateRenderer *ir, f32 width, f32 height); +void gfx_immediate_renderer_push_state(ImmediateRenderer *ir); +void gfx_immediate_renderer_pop_state(ImmediateRenderer *ir); +void gfx_immediate_renderer_origin(ImmediateRenderer *ir); +void gfx_immediate_renderer_translate(ImmediateRenderer *ir, f32 dx, f32 dy); +void gfx_immediate_renderer_scale(ImmediateRenderer *ir, f32 sx, f32 sy); +void gfx_immediate_renderer_rotate(ImmediateRenderer *ir, f32 angle); +void gfx_immediate_renderer_shear(ImmediateRenderer *ir, f32 kx, f32 ky); + #endif \ No newline at end of file diff --git a/include/utils.h b/include/utils.h index c0734e1..2efe859 100644 --- a/include/utils.h +++ b/include/utils.h @@ -19,4 +19,12 @@ b32 wasm_name_equals_string(const wasm_name_t* name1, const char* name2); wasm_extern_t* wasm_extern_lookup_by_name(wasm_module_t* module, wasm_instance_t* instance, const char* name); +// Linear algebra +typedef struct mat3 { + f32 data[9]; +} mat3; + +void mat3_identity(mat3 *out); +void mat3_mul(mat3 *out, mat3 a, mat3 b); + #endif \ No newline at end of file diff --git a/misc/onyx/heartbreak_graphics.onyx b/misc/onyx/heartbreak_graphics.onyx index ea9de1c..2d9c647 100644 --- a/misc/onyx/heartbreak_graphics.onyx +++ b/misc/onyx/heartbreak_graphics.onyx @@ -1,13 +1,31 @@ package heartbreak.graphics +// State + setClearColor :: (r, g, b: f32, a: f32 = 1) -> void #foreign "heartbreak" "graphics_set_clear_color" --- -clear :: () -> void #foreign "heartbreak" "graphics_clear" --- +setColor :: (r, g, b: f32, a: f32 = 1) -> void #foreign "heartbreak" "graphics_set_color" --- + +// +// Drawing +// FillMode :: enum { Fill :: 0x01; Line :: 0x02; } -setColor :: (r, g, b: f32, a: f32 = 1) -> void #foreign "heartbreak" "graphics_set_color" --- +clear :: () -> void #foreign "heartbreak" "graphics_clear" --- rectangle :: (mode: FillMode, x, y, w, h: f32) -> void #foreign "heartbreak" "graphics_rectangle" --- -circle :: (mode: FillMode, x, y, r: f32, segments: i32 = 24) -> void #foreign "heartbreak" "graphics_circle" --- \ No newline at end of file +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" --- + +// +// 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" --- +rotate :: (angle: f32) -> void #foreign "heartbreak" "graphics_rotate" --- +push :: () -> void #foreign "heartbreak" "graphics_push" --- +pop :: () -> void #foreign "heartbreak" "graphics_pop" --- diff --git a/src/gfx.c b/src/gfx.c index f81ffa7..581b7b6 100644 --- a/src/gfx.c +++ b/src/gfx.c @@ -1,5 +1,5 @@ #include "gfx.h" -#include "utils.h" +#include static const char* SIMPLE_SHADER_VERTEX = "#version 300 es\n" "precision mediump float;\n" @@ -8,13 +8,13 @@ static const char* SIMPLE_SHADER_VERTEX = "#version 300 es\n" "layout(location = 2) in vec4 a_color;\n" "\n" "uniform mat4 u_view;\n" -"uniform mat4 u_world;\n" +"uniform mat3 u_world;\n" "\n" "out vec4 v_color;\n" "out vec2 v_texture;\n" "\n" "void main() {\n" -" gl_Position = u_view * u_world * vec4(a_vertex, 0, 1);\n" +" gl_Position = u_view * vec4((u_world * vec3(a_vertex, 1)).xy, 0, 1);\n" " v_color = a_color;\n" " v_texture = a_texture;\n" "}"; @@ -94,6 +94,10 @@ Shader gfx_shader_make_from_source(const char *vertex_src, const char *fragment_ return shader; } +static void gfx_immediate_renderer_update_world_transform(ImmediateRenderer *ir) { + glUniformMatrix3fv(ir->simple_shader.world_uniform, 1, 0, bh_arr_last(ir->world_transforms).data); +} + void gfx_immediate_renderer_init(ImmediateRenderer *ir) { ir->vertex_allocator = bh_heap_allocator(); ir->vertex_data = bh_alloc(ir->vertex_allocator, sizeof(ImmediateVertex) * VERTEX_DATA_MAX_COUNT); @@ -129,17 +133,26 @@ void gfx_immediate_renderer_init(ImmediateRenderer *ir) { }; glUniformMatrix4fv(ir->simple_shader.view_uniform, 1, 0, identity_matrix); - glUniformMatrix4fv(ir->simple_shader.world_uniform, 1, 0, identity_matrix); + + mat3 world_mat; + mat3_identity(&world_mat); + bh_arr_new(bh_heap_allocator(), ir->world_transforms, 4); + bh_arr_push(ir->world_transforms, world_mat); + gfx_immediate_renderer_update_world_transform(ir); ir->current_color = (ImmediateColor) { 1, 1, 1, 1 }; } void gfx_immediate_renderer_flush(ImmediateRenderer *ir) { + if (ir->vertex_count == 0) return; + glBindBuffer(GL_ARRAY_BUFFER, ir->vertex_buffer); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(ImmediateVertex) * ir->vertex_count, ir->vertex_data); glBindBuffer(GL_ARRAY_BUFFER, -1); glUseProgram(ir->simple_shader.program); + gfx_immediate_renderer_update_world_transform(ir); + glBindVertexArray(ir->vertex_array); glDrawArrays(GL_TRIANGLES, 0, ir->vertex_count); glBindVertexArray(-1); @@ -189,4 +202,88 @@ void gfx_immediate_renderer_update_window_size(ImmediateRenderer *ir, f32 width, glViewport(0, 0, width, height); glUniformMatrix4fv(ir->simple_shader.view_uniform, 1, false, camera_matrix); +} + +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); +} + +void gfx_immediate_renderer_pop_state(ImmediateRenderer *ir) { + gfx_immediate_renderer_flush(ir); + bh_arr_pop(ir->world_transforms); +} + +void gfx_immediate_renderer_origin(ImmediateRenderer *ir) { + gfx_immediate_renderer_flush(ir); + mat3* top = &bh_arr_last(ir->world_transforms); + mat3_identity(top); +} + +void gfx_immediate_renderer_translate(ImmediateRenderer *ir, f32 dx, f32 dy) { + gfx_immediate_renderer_flush(ir); + + mat3 trans = (mat3) { + { 1, 0, 0, + 0, 1, 0, + dx, dy, 1, + } + }; + + mat3 new_top; + mat3_mul(&new_top, bh_arr_last(ir->world_transforms), trans); + + bh_arr_last(ir->world_transforms) = new_top; +} + +void gfx_immediate_renderer_scale(ImmediateRenderer *ir, f32 sx, f32 sy) { + gfx_immediate_renderer_flush(ir); + + mat3 scale = (mat3) { + { sx, 0, 0, + 0, sy, 0, + 0, 0, 1, + } + }; + + mat3 new_top; + mat3_mul(&new_top, bh_arr_last(ir->world_transforms), scale); + + bh_arr_last(ir->world_transforms) = new_top; +} + +void gfx_immediate_renderer_rotate(ImmediateRenderer *ir, f32 angle) { + gfx_immediate_renderer_flush(ir); + + f32 cangle = cos(angle); + f32 sangle = sin(angle); + + mat3 rotate = (mat3) { + { cangle, -sangle, 0, + sangle, cangle, 0, + 0, 0, 1, + } + }; + + mat3 new_top; + mat3_mul(&new_top, bh_arr_last(ir->world_transforms), rotate); + + bh_arr_last(ir->world_transforms) = new_top; +} + +void gfx_immediate_renderer_shear(ImmediateRenderer *ir, f32 kx, f32 ky) { + gfx_immediate_renderer_flush(ir); + + mat3 shear = (mat3) { + { 1, ky, 0, + kx, 1, 0, + 0, 0, 1, + } + }; + + mat3 new_top; + mat3_mul(&new_top, bh_arr_last(ir->world_transforms), shear); + + bh_arr_last(ir->world_transforms) = new_top; } \ No newline at end of file diff --git a/src/heartbreak_graphics.c b/src/heartbreak_graphics.c index 449a666..5c327ad 100644 --- a/src/heartbreak_graphics.c +++ b/src/heartbreak_graphics.c @@ -82,12 +82,87 @@ HEARTBREAK_DEF(circle, (WASM_I32, WASM_F32, WASM_F32, WASM_F32, WASM_I32), ()) { return NULL; } +HEARTBREAK_DEF(ellipse, (WASM_I32, WASM_F32, WASM_F32, WASM_F32, WASM_F32, WASM_I32), ()) { + // @TODO: Use fill mode + + f32 x = params->data[1].of.f32; + f32 y = params->data[2].of.f32; + f32 r1 = params->data[3].of.f32; + f32 r2 = params->data[4].of.f32; + i32 segments = params->data[5].of.i32; + + f32 cx1 = x + r1; + f32 cy1 = y; + f32 cx2 = x + cos(PI * 2 / segments) * r1; + f32 cy2 = y + sin(PI * 2 / segments) * r2; + f32 cx3, cy3; + + fori (i, 2, segments) { + cx3 = x + cos(i * PI * 2 / segments) * r1; + cy3 = y + sin(i * PI * 2 / segments) * r2; + + gfx_immediate_renderer_push_triangle(&renderer, + cx1, cy1, 0, 0, + cx2, cy2, 0, 0, + cx3, cy3, 0, 0); + + cx2 = cx3; + cy2 = cy3; + } + + return NULL; +} + +HEARTBREAK_DEF(origin, (), ()) { + gfx_immediate_renderer_origin(&renderer); + return NULL; +} + +HEARTBREAK_DEF(translate, (WASM_F32, WASM_F32), ()) { + gfx_immediate_renderer_translate(&renderer, params->data[0].of.f32, params->data[1].of.f32); + return NULL; +} + +HEARTBREAK_DEF(scale, (WASM_F32, WASM_F32), ()) { + gfx_immediate_renderer_scale(&renderer, params->data[0].of.f32, params->data[1].of.f32); + return NULL; +} + +HEARTBREAK_DEF(shear, (WASM_F32, WASM_F32), ()) { + gfx_immediate_renderer_shear(&renderer, params->data[0].of.f32, params->data[1].of.f32); + return NULL; +} + +HEARTBREAK_DEF(rotate, (WASM_F32), ()) { + gfx_immediate_renderer_rotate(&renderer, params->data[0].of.f32); + return NULL; +} + +HEARTBREAK_DEF(push, (), ()) { + gfx_immediate_renderer_push_state(&renderer); + return NULL; +} + +HEARTBREAK_DEF(pop, (), ()) { + gfx_immediate_renderer_pop_state(&renderer); + return NULL; +} + HEARTBREAK_MODULE { HEARTBREAK_FUNC(set_clear_color) HEARTBREAK_FUNC(clear) HEARTBREAK_FUNC(set_color) HEARTBREAK_FUNC(rectangle) HEARTBREAK_FUNC(circle) + HEARTBREAK_FUNC(ellipse) + + HEARTBREAK_FUNC(origin) + HEARTBREAK_FUNC(translate) + HEARTBREAK_FUNC(scale) + HEARTBREAK_FUNC(shear) + HEARTBREAK_FUNC(rotate) + HEARTBREAK_FUNC(push) + HEARTBREAK_FUNC(pop) { NULL } }; \ No newline at end of file diff --git a/src/utils.c b/src/utils.c index 5647b48..b7b397d 100644 --- a/src/utils.c +++ b/src/utils.c @@ -39,3 +39,24 @@ wasm_extern_t* wasm_extern_lookup_by_name(wasm_module_t* module, wasm_instance_t return exports.data[idx]; } +// +// Linear Algebra +// +void mat3_identity(mat3 *out) { + fori (i, 0, 9) out->data[i] = 0; + out->data[0] = 1; + out->data[1 * 3 + 1] = 1; + out->data[2 * 3 + 2] = 1; +} + +void mat3_mul(mat3 *out, mat3 a, mat3 b) { + fori (r, 0, 3) { + fori (c, 0, 3) { + out->data[r * 3 + c] = 0; + + fori (k, 0, 3) { + out->data[r * 3 + c] += a.data[r * 3 + k] * b.data[k * 3 + c]; + } + } + } +} \ No newline at end of file diff --git a/tests/simp.onyx b/tests/simp.onyx index 9e9ea34..4d2894a 100644 --- a/tests/simp.onyx +++ b/tests/simp.onyx @@ -33,8 +33,13 @@ draw :: () { hb.graphics.rectangle(.Fill, 100, 100, 200, 100); mx, my := hb.input.mouseGetPos(); + hb.graphics.push(); + hb.graphics.origin(); + hb.graphics.rotate(t); + hb.graphics.translate(~~ mx, ~~ my); hb.graphics.setColor(1, 0, 0); - hb.graphics.circle(.Fill, ~~mx, ~~my, 25); + hb.graphics.ellipse(.Fill, 0, 0, 25, 15 + 10 * math.sin(t)); + hb.graphics.pop(); w := cast(f32) hb.window.getWidth(); h := cast(f32) hb.window.getHeight();