world transformation matrix
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 7 Jul 2021 18:09:12 +0000 (13:09 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 7 Jul 2021 18:09:12 +0000 (13:09 -0500)
core/container/array.onyx
modules/immediate_mode/gl_utils.onyx
modules/immediate_mode/immediate_renderer.onyx
modules/immediate_mode/module.onyx
modules/immediate_mode/texture.onyx
modules/immediate_mode/transform.onyx [new file with mode: 0644]
modules/ui/ui.onyx

index 6dad7f082e5c2ec20ee21ed64b08b362c664016e..7ec75b8de2b6b6e7ed50ac3bd4d8cc9c3db2ed92 100644 (file)
@@ -145,6 +145,15 @@ get :: (arr: ^[..] $T, idx: i32) -> T {
     return arr.data[idx];
 }
 
+get_ptr :: (arr: ^[..] $T, idx: i32) -> ^T {
+    if arr.count == 0 do return null;
+
+    while idx < 0          do idx += arr.count;
+    while idx >= arr.count do idx -= arr.count;
+
+    return ^arr.data[idx];
+}
+
 set :: (arr: ^[..] $T, idx: i32, value: T) {
     if arr.count == 0 do return;
 
index 9abbcb499bad7eae511b81f018963583457c7b60..67185ebfa8b1d1ba07d1165a16143c7b6ed39ea1 100644 (file)
@@ -6,6 +6,9 @@ use package core
 // This Shader represents an OpenGL program, not a shader. The name
 // is confusing but conceptually for most people, shaders are a bundled
 // version of the vertex and fragment shader.
+
+@Todo // Rewrite this so members that start with "uniform_" are automatically detected and the uniform location
+// is automatically retrieved. Same thing with vertex attributes.
 Shader :: struct {
     program : gl.GLProgram;
 
index d31a40e284cca6f082f40863e92844be13ab44a9..8be8a5a6002d7e84f6eef5f0243ae8cf55430c91 100644 (file)
@@ -24,6 +24,9 @@ Immediate_Vertex :: struct {
 }
 
 Immediate_Renderer :: struct {
+    // Needs to be a multiple of 6!!
+    Default_Max_Verticies :: 1020;
+
     active_shader : ^Shader;
 
     simple_shader, textured_shader : Shader;
@@ -42,15 +45,15 @@ Immediate_Renderer :: struct {
     vertex_array  : gl.GLVertexArrayObject;
     vertex_buffer : gl.GLBuffer;
 
-    // Needs to be a multiple of 6!!
-    Default_Max_Verticies :: 1020;
-
     mode := Immediate_Mode.Triangles;
 
     canvas: ^Canvas = null;
 
     window_width, window_height: i32;
 
+    world_transform_stack : [..] Transform;
+    world_transform_dirty := false; // If the world transform matrix should be pushed to the shader uniform.
+
     make :: (max_verticies := Default_Max_Verticies) -> Immediate_Renderer {
         ir : Immediate_Renderer;
         __initialize(^ir);
@@ -78,7 +81,11 @@ Immediate_Renderer :: struct {
         vertex_buffer = gl.createBuffer();
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
         gl.bufferData(gl.ARRAY_BUFFER, cast(gl.GLsizei) (max_verticies * sizeof Immediate_Vertex), gl.STREAM_DRAW);
-        
+
+        world_transform_stack = array.make(Transform, capacity=1);
+        array.insert_empty(^world_transform_stack, 0);
+        transform_identity(array.get_ptr(^world_transform_stack, -1));
+    
         ir->init_shader_params(^simple_shader);
         ir->init_shader_params(^textured_shader);
         ir->init_shader_params(^alpha_shader);
@@ -111,12 +118,10 @@ Immediate_Renderer :: struct {
 
         gl.bindBuffer(gl.ARRAY_BUFFER, -1);
 
-        // TEMPORARY
-        gl.uniformMatrix4(shader.world_uniform, false,
-                          f32.[ 1, 0, 0, 0,
-                                0, 1, 0, 0,
-                                0, 0, 1, 0,
-                                0, 0, 0, 1 ]);
+        world_transform := array.get_ptr(^world_transform_stack, -1);
+        world_matrix := transform_to_matrix(world_transform);
+
+        gl.uniformMatrix4(shader.world_uniform, false, world_matrix);
     }
 
     free :: (use ir: ^Immediate_Renderer) {
@@ -131,6 +136,17 @@ Immediate_Renderer :: struct {
     flush :: (use ir: ^Immediate_Renderer) {
         if vertex_count == 0 do return;
 
+        if world_transform_dirty || true {
+            world_transform_dirty = false;
+            world_transform := array.get_ptr(^world_transform_stack, -1);
+            world_matrix := transform_to_matrix(world_transform);
+
+            for shader: (#type ^Shader).[ ^simple_shader, ^textured_shader, ^alpha_shader ] {
+                gl.useProgram(shader.program);
+                gl.uniformMatrix4(shader.world_uniform, false, world_matrix);
+            }
+        }
+
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
         gl.bufferSubData(gl.ARRAY_BUFFER, 0, .{ count = vertex_count * sizeof Immediate_Vertex, data = ~~verticies.data });
         gl.bindBuffer(gl.ARRAY_BUFFER, -1);
@@ -327,6 +343,29 @@ Immediate_Renderer :: struct {
             ir->use_ortho_projection(0, ~~window_width, 0, ~~window_height);
         }
     }
+
+    push_matrix :: (use ir: ^Immediate_Renderer) {
+        if vertex_count > 0 do flush(ir);
+        world_transform_dirty = true;
+
+        array.push(^world_transform_stack, world_transform_stack[world_transform_stack.count - 1]);
+        transform_identity(array.get_ptr(^world_transform_stack, -1));
+    }
+
+    pop_matrix :: (use ir: ^Immediate_Renderer) {
+        if vertex_count > 0 do flush(ir);
+        world_transform_dirty = true;
+
+        array.pop(^world_transform_stack);
+    }
+
+    identity :: (use ir: ^Immediate_Renderer) {
+        transform_identity(array.get_ptr(^world_transform_stack, -1));
+    }
+
+    get_transform :: (use ir: ^Immediate_Renderer) -> ^Transform {
+        return array.get_ptr(^world_transform_stack, -1);
+    }
 }
 
 
@@ -336,40 +375,39 @@ Immediate_Renderer :: struct {
 // that operate on a global immediate renderer. This is probably the most common way that 
 // it will be used.
 
-#private
-immediate_renderer : Immediate_Renderer;
+global_renderer : Immediate_Renderer;
 
 immediate_renderer_init :: () {
-    immediate_renderer = Immediate_Renderer.make();
+    global_renderer = Immediate_Renderer.make();
 }
 
 immediate_renderer_free :: () {
-    Immediate_Renderer.free(^immediate_renderer);
+    Immediate_Renderer.free(^global_renderer);
 }
 
 vertex :: #match {
-    (position: Vector2)                { immediate_renderer->push_vertex(position); },
-    (position: Vector2, color: Color4) { immediate_renderer->push_vertex(position, color); },
+    (position: Vector2)                { global_renderer->push_vertex(position); },
+    (position: Vector2, color: Color4) { global_renderer->push_vertex(position, color); },
 }
 
 rect :: (position: Vector2, size: Vector2, color: Color4 = .{1,1,1}) {
-    immediate_renderer->rect(position, size, color);
+    global_renderer->rect(position, size, color);
 }
 
 textured_rect :: (position: Vector2, size: Vector2, texture_position: Vector2, texture_size: Vector2, color: Color4 = .{1,1,1}) {
-    immediate_renderer->textured_rect(position, size, texture_position, texture_size, color);
+    global_renderer->textured_rect(position, size, texture_position, texture_size, color);
 }
 
 circle :: (center: Vector2, radius: f32, color: Color4 = .{1,1,1}, segments := 12) {
-    immediate_renderer->circle(center, radius, color, segments);
+    global_renderer->circle(center, radius, color, segments);
 }
 
-flush :: () do immediate_renderer->flush();
+flush :: () do global_renderer->flush();
 
 set_texture :: #match {
-    (texture_id: i32 = -1) { immediate_renderer->set_texture(texture_id); },
+    (texture_id: i32 = -1) { global_renderer->set_texture(texture_id); },
     (texture: ^Texture, texture_index := 0) {
-        immediate_renderer->set_texture(texture_index);
+        global_renderer->set_texture(texture_index);
 
         if texture_index >= 0 {
             gl.activeTexture(gl.TEXTURE0 + texture_index);
@@ -382,29 +420,33 @@ set_texture :: #match {
 }
 
 use_ortho_projection :: (left: f32, right: f32, top: f32, bottom: f32) {
-    immediate_renderer->use_ortho_projection(left, right, top, bottom);
+    global_renderer->use_ortho_projection(left, right, top, bottom);
 }
 
 use_alpha_shader :: (texture_id: i32 = -1) {
-    immediate_renderer->use_alpha_shader(texture_id);
+    global_renderer->use_alpha_shader(texture_id);
 }
 
 scissor :: (x: f32, y: f32, w: f32, h: f32) {
-    immediate_renderer->scissor(x, y, w, h);
+    global_renderer->scissor(x, y, w, h);
 }
 
 scissor_disable :: () {
-    immediate_renderer->scissor_disable();
+    global_renderer->scissor_disable();
 }
 
 set_mode :: (mode: Immediate_Mode) {
-    immediate_renderer->set_mode(mode);
+    global_renderer->set_mode(mode);
 }
 
 use_canvas :: (canvas: ^Canvas) {
-    immediate_renderer->use_canvas(canvas);
+    global_renderer->use_canvas(canvas);
 }
 
 set_window_size :: (width: i32, height: i32) {
-    immediate_renderer->set_window_size(width, height);
+    global_renderer->set_window_size(width, height);
 }
+
+push_matrix :: () do global_renderer->push_matrix();
+pop_matrix  :: () do global_renderer->pop_matrix();
+identity    :: () do global_renderer->identity();
index 3c72307f5e79dd14bcb05a2d33c12d0b161c1ce3..47fae0faed2695254047bd567eb5bba0f309f37c 100644 (file)
@@ -5,5 +5,6 @@ package immediate_mode
 #load "./gl_utils"
 #load "./canvas"
 #load "./texture"
+#load "./transform"
 
 #private gl :: package gl
index 9982e134a46602930ddcd983a3a13c3e7e00db47..c6825be28b5d97de7b014d54274d53df1859dcc0 100644 (file)
@@ -28,10 +28,10 @@ create_texture :: (width: i32, height: i32, format: gl.GLenum) -> Texture {
     };
 }
 
-load_texture :: (width: i32, height: i32, data: [] u8, format: gl.GLenum, internal_type := gl.UNSIGNED_BYTE) -> Texture {
+load_texture :: (width: i32, height: i32, data: [] u8, format: gl.GLenum, internal_format: gl.GLenum, internal_type := gl.UNSIGNED_BYTE) -> Texture {
     texture := gl.createTexture();
     gl.bindTexture(gl.TEXTURE_2D, texture);
-    gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, format, internal_type, data);
+    gl.texImage2D(gl.TEXTURE_2D, 0, internal_format, width, height, 0, format, internal_type, data);
 
     // Setting some reasonable defaults for the texture parameters
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
diff --git a/modules/immediate_mode/transform.onyx b/modules/immediate_mode/transform.onyx
new file mode 100644 (file)
index 0000000..28b54cc
--- /dev/null
@@ -0,0 +1,34 @@
+package immediate_mode
+
+
+Transform :: struct {
+    translation: Vector2;
+    scale:       Vector2;
+    rotation:    f32;    // In radians
+}
+
+transform_identity :: (use t: ^Transform) {
+    translation = .{ 0, 0 };
+    scale       = .{ 1, 1 };
+    rotation    = 0;
+}
+
+transform_to_matrix :: (use t: ^Transform) -> [16] f32 {
+    math :: package core.math
+
+    st := math.sin(rotation);
+    ct := math.cos(rotation);
+
+    sx := scale.x;
+    sy := scale.y;
+
+    tx := translation.x;
+    ty := translation.y;
+
+    return f32.[
+        sx * ct, -sy * st, 0, 0,
+        sx * st,  sy * ct, 0, 0,
+        0, 0, 1, 0,
+        tx, ty, 0, 1
+    ];
+}
\ No newline at end of file
index aef9b0fe762aea5d08fe5c0e3cb32a24e37dc717..dcb5027ade2ab606f643c1604f996e247b15656a 100644 (file)
@@ -285,7 +285,7 @@ get_text_width :: (text: str, size := DEFAULT_TEXT_SIZE) -> f32 {
 
     tex_width, tex_height := font.common.scale_width, font.common.scale_height;
 
-    font_texture = gfx.load_texture(tex_width, tex_height, texture_data, gl.ALPHA);
+    font_texture = gfx.load_texture(tex_width, tex_height, texture_data, gl.ALPHA, gl.ALPHA);
 }