From b37b5398a6afa940acd1138bde922a70838f33af Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Fri, 4 Jul 2025 10:37:01 -0700 Subject: Add the new low-level renderer, shared between the imm and scene graph renderer. LLR integration with the scene graph renderer not yet done. --- src/renderer/imm_renderer.c | 192 +++++++++++++++++++++++++++++++++++++++ src/renderer/imm_renderer_impl.h | 43 +++++++++ src/renderer/renderer.c | 46 ++++++---- src/renderer/renderer_impl.h | 6 +- 4 files changed, 269 insertions(+), 18 deletions(-) create mode 100644 src/renderer/imm_renderer.c create mode 100644 src/renderer/imm_renderer_impl.h (limited to 'src/renderer') diff --git a/src/renderer/imm_renderer.c b/src/renderer/imm_renderer.c new file mode 100644 index 0000000..01cc5bb --- /dev/null +++ b/src/renderer/imm_renderer.c @@ -0,0 +1,192 @@ +#include "imm_renderer_impl.h" + +#include +#include +#include +#include + +#include + +#include +#include // memcpy + +bool gfx_imm_make(ImmRenderer* renderer, GfxCore* gfxcore, LLR* llr) { + assert(renderer); + assert(gfxcore); + assert(llr); + + const size_t num_triangle_verts = GFX_IMM_MAX_NUM_TRIANGLES * 3; + + renderer->gfxcore = gfxcore; + renderer->llr = llr; + + renderer->triangles = gfx_make_geometry( + gfxcore, + &(GeometryDesc){.type = Triangles, + .buffer_usage = BufferDynamic, + .num_verts = num_triangle_verts, + .positions3d = (BufferView3d){ + .size_bytes = num_triangle_verts * sizeof(vec3)}}); + if (!renderer->triangles) { + goto cleanup; + } + + renderer->shader = gfx_make_immediate_mode_shader(gfxcore); + if (!renderer->shader) { + goto cleanup; + } + + gfx_imm_set_colour(renderer, vec4_make(0.0f, 0.0f, 0.0f, 1.0f)); + + return true; + +cleanup: + gfx_imm_destroy(renderer); + return false; +} + +void gfx_imm_destroy(ImmRenderer* renderer) { + assert(renderer); + assert(renderer->gfxcore); + + if (renderer->triangles) { + gfx_destroy_geometry(renderer->gfxcore, &renderer->triangles); + // TODO: Could also destroy the geometry's buffers here. + } + + if (renderer->shader) { + gfx_destroy_shader_program(renderer->gfxcore, &renderer->shader); + } +} + +void gfx_imm_flush(ImmRenderer* renderer) { + assert(renderer); + + if (renderer->num_triangle_verts > 0) { + gfx_update_geometry( + renderer->triangles, + &(GeometryDesc){ + .num_verts = renderer->num_triangle_verts, + .positions3d = (BufferView3d){ + .data = renderer->triangle_verts, + .size_bytes = renderer->num_triangle_verts * sizeof(vec3)} + }); + + gfx_llr_render_geometry(renderer->llr, renderer->triangles); + + renderer->num_triangle_verts = 0; + } +} + +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. + // gfx_activate_shader_program(renderer->shader); + gfx_llr_set_shader(renderer->llr, renderer->shader); +} + +void gfx_imm_end(ImmRenderer* renderer) { + assert(renderer); + + gfx_imm_flush(renderer); + // gfx_deactivate_shader_program(renderer->shader); + gfx_llr_set_shader(renderer->llr, 0); +} + +void gfx_imm_draw_triangles( + ImmRenderer* renderer, const vec3 verts[], size_t num_triangles) { + assert(renderer); + assert(verts); + const size_t new_verts = num_triangles * 3; + assert( + renderer->num_triangle_verts + new_verts < + (GFX_IMM_MAX_NUM_TRIANGLES * 3)); + + memcpy( + renderer->triangle_verts + renderer->num_triangle_verts, verts, + new_verts * sizeof(vec3)); + + renderer->num_triangle_verts += new_verts; +} + +void gfx_imm_draw_triangle(ImmRenderer* renderer, const vec3 verts[3]) { + gfx_imm_draw_triangles(renderer, verts, 1); +} + +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 vertices[8] = { + vec3_make(box.min.x, box.min.y, box.max.z), // 7 ----- 6 + vec3_make(box.max.x, box.min.y, box.max.z), // / /| + vec3_make(box.max.x, box.max.y, box.max.z), // 3 ----- 2 | + vec3_make(box.min.x, box.max.y, box.max.z), // | | | + vec3_make(box.min.x, box.min.y, box.min.z), // | 4 ----- 5 + vec3_make(box.max.x, box.min.y, box.min.z), // |/ |/ + vec3_make(box.max.x, box.max.y, box.min.z), // 0 ----- 1 + vec3_make(box.min.x, box.max.y, box.min.z)}; + // clang-format on + + gfx_imm_draw_box3(renderer, vertices); +} + +void gfx_imm_draw_box3(ImmRenderer* renderer, const vec3 vertices[8]) { + assert(renderer); + assert(vertices); + + // 7 ----- 6 + // / /| + // 3 ----- 2 | + // | | | + // | 4 ----- 5 + // |/ |/ + // 0 ----- 1 + +#define tri(i0, i1, i2) vertices[i0], vertices[i1], vertices[i2] + const vec3 tris[36] = { + // Front. + tri(0, 1, 2), tri(0, 2, 3), + // Right. + tri(1, 5, 6), tri(1, 6, 2), + // Back. + tri(5, 4, 7), tri(5, 7, 6), + // Left. + tri(4, 0, 03), tri(4, 3, 7), + // Top. + tri(3, 2, 6), tri(3, 6, 7), + // Bottom. + tri(0, 4, 5), tri(0, 5, 1)}; + + gfx_imm_draw_triangles(renderer, tris, 12); +} + +void gfx_imm_set_colour(ImmRenderer* renderer, vec4 colour) { + assert(renderer); + assert(renderer->shader); + + gfx_imm_flush(renderer); + + gfx_set_vec4_uniform(renderer->shader, "Colour", colour); +} diff --git a/src/renderer/imm_renderer_impl.h b/src/renderer/imm_renderer_impl.h new file mode 100644 index 0000000..61b49a7 --- /dev/null +++ b/src/renderer/imm_renderer_impl.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include + +#include +#include + +typedef struct Geometry Geometry; +typedef struct GfxCore GfxCore; +typedef struct IBL IBL; +typedef struct LLR LLR; +typedef struct Material Material; +typedef struct ShaderProgram ShaderProgram; +typedef struct Texture Texture; + +/// Immediate mode renderer. +/// +/// Currently, the immediate mode renderer can only draw up to a maximum number +/// of primitives per frame. It does not adjust this number dynamically. Keeps +/// things simple while the extra complexity is not needed. +/// TODO: Flush the buffer when it reaches its maximum size to remove this +/// constraint. +typedef struct ImmRenderer { + GfxCore* gfxcore; + LLR* llr; + + ShaderProgram* shader; // Immediate-mode shader program for primitives. + Geometry* triangles; + size_t num_triangle_verts; // Number of triangle verts this frame. + // TODO: wireframe rendering. + struct { + bool wireframe : 1; + } flags; + vec3 triangle_verts[GFX_IMM_MAX_NUM_TRIANGLES * 3]; +} ImmRenderer; + +/// Create a new immediate mode renderer. +bool gfx_imm_make(ImmRenderer*, GfxCore*, LLR*); + +/// Destroy the immediate mode renderer. +void gfx_imm_destroy(ImmRenderer*); diff --git a/src/renderer/renderer.c b/src/renderer/renderer.c index c2a7dda..0c1fe78 100644 --- a/src/renderer/renderer.c +++ b/src/renderer/renderer.c @@ -1,10 +1,10 @@ #include "renderer_impl.h" +#include "llr/light_impl.h" +#include "llr/material_impl.h" +#include "llr/mesh_impl.h" #include "scene/animation_impl.h" #include "scene/camera_impl.h" -#include "scene/light_impl.h" -#include "scene/material_impl.h" -#include "scene/mesh_impl.h" #include "scene/model_impl.h" #include "scene/node_impl.h" #include "scene/object_impl.h" @@ -12,16 +12,18 @@ #include "scene/scene_memory.h" #include +#include #include #include -#include +// #include +#include "gfx/gfx.h" + #include #include #include -// TODO: Move to a header like "constants.h". static const int IRRADIANCE_MAP_WIDTH = 1024; static const int IRRADIANCE_MAP_HEIGHT = 1024; static const int PREFILTERED_ENVIRONMENT_MAP_WIDTH = 128; @@ -29,16 +31,18 @@ static const int PREFILTERED_ENVIRONMENT_MAP_HEIGHT = 128; static const int BRDF_INTEGRATION_MAP_WIDTH = 512; static const int BRDF_INTEGRATION_MAP_HEIGHT = 512; -bool renderer_make(Renderer* renderer, GfxCore* gfxcore) { +bool gfx_renderer_make(Renderer* renderer, LLR* llr, GfxCore* gfxcore) { assert(renderer); + assert(llr); assert(gfxcore); renderer->gfxcore = gfxcore; + renderer->llr = llr; return true; } -void renderer_destroy(Renderer* renderer) { +void gfx_renderer_destroy(Renderer* renderer) { if (!renderer) { return; } @@ -117,9 +121,9 @@ static ShaderProgram* load_shader(Renderer* renderer, RenderSceneMode mode) { // } // } -/// Computes irradiance and prefiltered environment maps for the light if they +/// Compute irradiance and prefiltered environment maps for the light if they /// have not been already computed. -static bool setup_environment_light( +static bool set_up_environment_light( Renderer* renderer, GfxCore* gfxcore, EnvironmentLight* light) { assert(renderer); assert(light); @@ -168,6 +172,7 @@ cleanup: typedef struct RenderState { GfxCore* gfxcore; + LLR* llr; Renderer* renderer; ShaderProgram* shader; // Null to use scene shaders. const Scene* scene; @@ -209,6 +214,7 @@ static void draw_recursively( // Anima. if (node->type == AnimaNode) { + // Save the anima so that we can animate objects. state->anima = gfx_get_node_anima(node); } // Activate light. @@ -217,7 +223,7 @@ static void draw_recursively( assert(light); if (light->type == EnvironmentLightType) { - bool result = setup_environment_light( + bool result = set_up_environment_light( state->renderer, state->gfxcore, &light->environment); // TODO: Handle the result in a better way. assert(result); @@ -238,11 +244,13 @@ static void draw_recursively( // TODO: Here we would frustum-cull the object. // TODO: Avoid computing matrices like Modelview or MVP if the shader does - // not use them. + // not use them. const mat4 model_matrix = node_transform; const mat4 modelview = mat4_mul(*state->view_matrix, model_matrix); const mat4 mvp = mat4_mul(*state->projection, modelview); + // A model/anima can have many skeletons. We need to animate the given + // object using its skeleton, not just any skeleton of the anima. if (object->skeleton.val) { load_skeleton(state, object->skeleton); } @@ -260,9 +268,11 @@ static void draw_recursively( assert(mesh->material); // TODO: Here we would frustum-cull the mesh. The AABB would have to be - // transformed by the model matrix. Rotation would make the AABB - // relatively large, but still, the culling would be conservative. + // transformed by the model matrix. Rotation would make the AABB + // relatively large, but still, the culling would be conservative. + // TODO: Make sure we strictly set only the uniforms that are required by + // mesh rendering. See the other item below. // Apply common shader uniforms not captured by materials. ShaderProgram* shader = state->shader ? state->shader : mesh->shader; gfx_set_mat4_uniform(shader, "ModelMatrix", &model_matrix); @@ -270,7 +280,10 @@ static void draw_recursively( gfx_set_mat4_uniform(shader, "View", state->view_matrix); gfx_set_mat4_uniform(shader, "Projection", state->projection); gfx_set_mat4_uniform(shader, "MVP", &mvp); + // TODO: CameraRotation is only used by the skyquad and cubemap_filtering + // shaders, not mesh rendering. gfx_set_mat4_uniform(shader, "CameraRotation", state->camera_rotation); + // TODO: Fovy and Aspect are only used by the skyquad, not necessary here. gfx_set_float_uniform(shader, "Fovy", state->fovy); gfx_set_float_uniform(shader, "Aspect", state->aspect); if (state->camera) { @@ -297,9 +310,9 @@ static void draw_recursively( shader, "PrefilteredEnvironmentMap", light->prefiltered_environment_map); gfx_set_float_uniform( - shader, "MaxReflectionLOD", light->max_reflection_lod); + shader, "MaxReflectionLOD", (float)light->max_reflection_lod); } - material_activate(shader, mesh->material); + gfx_material_activate(shader, mesh->material); gfx_activate_shader_program(shader); gfx_apply_uniforms(shader); gfx_render_geometry(mesh->geometry); @@ -327,7 +340,7 @@ void gfx_render_scene(Renderer* renderer, const RenderSceneParams* params) { const Scene* scene = params->scene; const SceneCamera* camera = params->camera; - GfxCore* gfxcore = renderer->gfxcore; + GfxCore* const gfxcore = renderer->gfxcore; mat4 projection, camera_rotation, view_matrix; if (camera) { @@ -347,6 +360,7 @@ void gfx_render_scene(Renderer* renderer, const RenderSceneParams* params) { RenderState state = { .gfxcore = gfxcore, + .llr = renderer->llr, .renderer = renderer, .shader = shader, .scene = scene, diff --git a/src/renderer/renderer_impl.h b/src/renderer/renderer_impl.h index fc14dcb..6fd0c15 100644 --- a/src/renderer/renderer_impl.h +++ b/src/renderer/renderer_impl.h @@ -5,11 +5,13 @@ #include typedef struct IBL IBL; +typedef struct LLR LLR; typedef struct ShaderProgram ShaderProgram; typedef struct Texture Texture; typedef struct Renderer { GfxCore* gfxcore; + LLR* llr; IBL* ibl; Texture* brdf_integration_map; struct { @@ -21,7 +23,7 @@ typedef struct Renderer { } Renderer; /// Create a new renderer. -bool renderer_make(Renderer*, GfxCore*); +bool gfx_renderer_make(Renderer*, LLR*, GfxCore*); /// Destroy the renderer. -void renderer_destroy(Renderer*); +void gfx_renderer_destroy(Renderer*); -- cgit v1.2.3