#include "bh.h"
+#include "utils.h"
#include <GLES3/gl3.h>
#include <GLFW/glfw3.h>
GLint vertex_buffer;
Shader simple_shader;
+
+ bh_arr(mat3) world_transforms;
} ImmediateRenderer;
void gfx_immediate_renderer_init(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
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
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" ---
#include "gfx.h"
-#include "utils.h"
+#include <math.h>
static const char* SIMPLE_SHADER_VERTEX = "#version 300 es\n"
"precision mediump float;\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"
"}";
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);
};
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);
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
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
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
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();