From dda4b3e598c7c955a44e3874bf2c57bd16b1c3ea Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Thu, 8 Feb 2024 07:57:14 -0800 Subject: Add AABB2 and matrix stack to immediate-mode renderer. --- gfx/include/gfx/renderer.h | 23 ++++++- gfx/include/gfx/sizes.h | 3 + gfx/src/renderer/imm_renderer.c | 120 +++++++++++++++++++++++++++++------ gfx/src/renderer/imm_renderer_impl.h | 6 ++ 4 files changed, 131 insertions(+), 21 deletions(-) diff --git a/gfx/include/gfx/renderer.h b/gfx/include/gfx/renderer.h index 13e6c2c..c420889 100644 --- a/gfx/include/gfx/renderer.h +++ b/gfx/include/gfx/renderer.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -56,13 +57,31 @@ void gfx_imm_draw_triangles(ImmRenderer*, const vec3[], size_t num_triangles); void gfx_imm_draw_triangle(ImmRenderer*, const vec3[3]); /// Draw a bounding box. -void gfx_imm_draw_aabb(ImmRenderer*, aabb3); +void gfx_imm_draw_aabb2(ImmRenderer*, aabb2); + +/// Draw a bounding box. +void gfx_imm_draw_aabb3(ImmRenderer*, aabb3); /// Set the camera. void gfx_imm_set_camera(ImmRenderer*, const Camera*); -/// Set the model matrix. +/// Load an identity model matrix. Clears the matrix stack. +void gfx_imm_load_identity(ImmRenderer* renderer); + +/// Push the given matrix to the matrix stack. +void gfx_imm_push_matrix(ImmRenderer* renderer, const mat4* matrix); + +/// Pop the top of the matrix stack. +void gfx_imm_pop_matrix(ImmRenderer* renderer); + +/// Push a translation matrix to the matrix stack. +void gfx_imm_translate(ImmRenderer* renderer, vec3 offset); + +/// Set the model matrix. Clears the matrix stack. void gfx_imm_set_model_matrix(ImmRenderer*, const mat4*); +/// Set the view-projection matrix. +void gfx_imm_set_view_projection_matrix(ImmRenderer*, const mat4*); + /// Set the render colour. void gfx_imm_set_colour(ImmRenderer*, vec4 colour); diff --git a/gfx/include/gfx/sizes.h b/gfx/include/gfx/sizes.h index 9f13e30..f2ace8a 100644 --- a/gfx/include/gfx/sizes.h +++ b/gfx/include/gfx/sizes.h @@ -80,6 +80,9 @@ /// frame. #define IMM_MAX_NUM_TRIANGLES 1024 +/// Maximum number of matrices in the immediate-mode renderer's matrix stack. +#define IMM_MAX_NUM_MATRICES 32 + // Gfx. #define GFX_MAX_NUM_SCENES 4 diff --git a/gfx/src/renderer/imm_renderer.c b/gfx/src/renderer/imm_renderer.c index 12b642c..492c7af 100644 --- a/gfx/src/renderer/imm_renderer.c +++ b/gfx/src/renderer/imm_renderer.c @@ -33,6 +33,9 @@ bool imm_renderer_make(ImmRenderer* renderer, RenderBackend* render_backend) { goto cleanup; } + renderer->matrix_stack[0] = mat4_id(); + renderer->stack_pointer = 0; + gfx_imm_set_colour(renderer, vec4_make(0.0, 0.0, 0.0, 1.0)); return true; @@ -79,17 +82,16 @@ void imm_renderer_flush(ImmRenderer* renderer) { void gfx_imm_start(ImmRenderer* renderer) { assert(renderer); + // Shader uniforms are applied lazily. + // TODO: In the event that gfx_activate_shader_program() activates uniforms + // automatically for convenience, call an overload here that doesn't do so. ShaderProgram* shader = renderer->shader; gfx_activate_shader_program(shader); - // TODO: Having to apply manually is annoying. Maybe just apply implicitly - // when the program is activated? - // Note that then we'll need to be able to just gfx_apply_uniforms() in - // flush(). - // gfx_set_culling(renderer->render_backend, false); - gfx_apply_uniforms(shader); - gfx_set_blending(renderer->render_backend, true); - gfx_set_depth_mask(renderer->render_backend, false); - gfx_set_polygon_offset(renderer->render_backend, 0.5f, 0.5f); + + // TODO: Move this to the gltf_view program. + // gfx_set_blending(renderer->render_backend, true); + // gfx_set_depth_mask(renderer->render_backend, false); + // gfx_set_polygon_offset(renderer->render_backend, 0.5f, 0.5f); } void gfx_imm_end(ImmRenderer* renderer) { @@ -97,10 +99,11 @@ void gfx_imm_end(ImmRenderer* renderer) { imm_renderer_flush(renderer); - gfx_set_polygon_offset(renderer->render_backend, 0.0f, 0.0f); - gfx_set_depth_mask(renderer->render_backend, true); - gfx_set_blending(renderer->render_backend, false); - // gfx_set_culling(renderer->render_backend, true); + // TODO: Move this to the gltf_view program. + // gfx_set_polygon_offset(renderer->render_backend, 0.0f, 0.0f); + // gfx_set_depth_mask(renderer->render_backend, true); + // gfx_set_blending(renderer->render_backend, false); + gfx_deactivate_shader_program(renderer->shader); } @@ -123,9 +126,28 @@ void gfx_imm_draw_triangle(ImmRenderer* renderer, const vec3 verts[3]) { gfx_imm_draw_triangles(renderer, verts, 1); } -void gfx_imm_draw_aabb(ImmRenderer* renderer, aabb3 box) { +void gfx_imm_draw_aabb2(ImmRenderer* renderer, aabb2 box) { + assert(renderer); + + // clang-format off + const vec3 verts[4] = { + vec3_make(box.min.x, box.min.y, 0), // 3 ---- 2 + vec3_make(box.max.x, box.min.y, 0), // | | + vec3_make(box.max.x, box.max.y, 0), // | | + vec3_make(box.min.x, box.max.y, 0)}; // 0 ---- 1 + // clang-format on + +#define tri(i0, i1, i2) verts[i0], verts[i1], verts[i2] + const vec3 tris[6] = {tri(0, 1, 2), tri(0, 2, 3)}; +#undef tri + + gfx_imm_draw_triangles(renderer, tris, 2); +} + +void gfx_imm_draw_aabb3(ImmRenderer* renderer, aabb3 box) { assert(renderer); + // clang-format off const vec3 verts[8] = { box.min, // 2 ----- 6 vec3_make(box.min.x, box.min.y, box.max.z), // / /| @@ -135,9 +157,9 @@ void gfx_imm_draw_aabb(ImmRenderer* renderer, aabb3 box) { vec3_make(box.max.x, box.min.y, box.max.z), // |/ |/ vec3_make(box.max.x, box.max.y, box.min.z), // 1 ----- 5 box.max}; + // clang-format on #define tri(i0, i1, i2) verts[i0], verts[i1], verts[i2] - // TODO: Use vertex indices in Geometry. const vec3 tris[36] = {// Front. tri(1, 5, 7), tri(1, 7, 3), @@ -151,24 +173,84 @@ void gfx_imm_draw_aabb(ImmRenderer* renderer, aabb3 box) { tri(3, 7, 6), tri(3, 6, 2), // Bottom. tri(0, 4, 5), tri(0, 5, 1)}; +#undef tri gfx_imm_draw_triangles(renderer, tris, 12); } +// Load the top of the matrix stack into the shader. +static void update_shader_model_matrix(ImmRenderer* renderer) { + assert(renderer); + imm_renderer_flush(renderer); + gfx_set_mat4_uniform( + renderer->shader, "Model", + &renderer->matrix_stack[renderer->stack_pointer]); +} + +void gfx_imm_load_identity(ImmRenderer* renderer) { + assert(renderer); + renderer->matrix_stack[0] = mat4_id(); + renderer->stack_pointer = 0; + update_shader_model_matrix(renderer); +} + +void gfx_imm_push_matrix(ImmRenderer* renderer, const mat4* matrix) { + assert(renderer); + assert(matrix); + assert(renderer->stack_pointer >= 0); + assert(renderer->stack_pointer < IMM_MAX_NUM_MATRICES); // TODO: hard assert. + + renderer->matrix_stack[renderer->stack_pointer + 1] = + mat4_mul(*matrix, renderer->matrix_stack[renderer->stack_pointer]); + renderer->stack_pointer += 1; + + update_shader_model_matrix(renderer); +} + +void gfx_imm_pop_matrix(ImmRenderer* renderer) { + assert(renderer); + assert(renderer->stack_pointer > 0); // TODO: hard assert. + + // For debugging, zero out the matrix stack as matrices are popped out. + memset( + &renderer->matrix_stack[renderer->stack_pointer], 0, + sizeof(renderer->matrix_stack[0])); + + renderer->stack_pointer -= 1; + + update_shader_model_matrix(renderer); +} + +void gfx_imm_translate(ImmRenderer* renderer, vec3 offset) { + assert(renderer); + const mat4 mat = mat4_translate(offset); + gfx_imm_push_matrix(renderer, &mat); +} + void gfx_imm_set_camera(ImmRenderer* renderer, const Camera* camera) { assert(renderer); assert(renderer->shader); imm_renderer_flush(renderer); - const mat4 modelview = spatial3_inverse_transform(&camera->spatial); - const mat4 mvp = mat4_mul(camera->projection, modelview); - gfx_set_mat4_uniform(renderer->shader, "ViewProjection", &mvp); + const mat4 view = spatial3_inverse_transform(&camera->spatial); + const mat4 view_proj = mat4_mul(camera->projection, view); + gfx_imm_set_view_projection_matrix(renderer, &view_proj); } void gfx_imm_set_model_matrix(ImmRenderer* renderer, const mat4* model) { assert(renderer); assert(model); imm_renderer_flush(renderer); - gfx_set_mat4_uniform(renderer->shader, "Model", model); + renderer->matrix_stack[0] = mat4_id(); + renderer->stack_pointer = 0; + update_shader_model_matrix(renderer); +} + +void gfx_imm_set_view_projection_matrix( + ImmRenderer* renderer, const mat4* view_proj) { + assert(renderer); + assert(renderer->shader); + imm_renderer_flush(renderer); + gfx_set_mat4_uniform(renderer->shader, "ViewProjection", view_proj); } void gfx_imm_set_colour(ImmRenderer* renderer, vec4 colour) { diff --git a/gfx/src/renderer/imm_renderer_impl.h b/gfx/src/renderer/imm_renderer_impl.h index 6f4b818..7769855 100644 --- a/gfx/src/renderer/imm_renderer_impl.h +++ b/gfx/src/renderer/imm_renderer_impl.h @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -26,6 +27,11 @@ typedef struct ImmRenderer { bool wireframe : 1; } flags; vec3 triangle_verts[IMM_MAX_NUM_TRIANGLES * 3]; + // Matrix stack contains pre-multiplied matrices. + // It is also never empty. The top of the stack is an identity matrix when the + // stack is "empty" from the user's perspective. + mat4 matrix_stack[IMM_MAX_NUM_MATRICES]; + int stack_pointer; } ImmRenderer; /// Create a new immediate mode renderer. -- cgit v1.2.3