aboutsummaryrefslogtreecommitdiff
path: root/src/renderer
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-06-27 10:18:39 -0700
committer3gg <3gg@shellblade.net>2025-06-27 10:18:39 -0700
commitbd57f345ed9dbed1d81683e48199626de2ea9044 (patch)
tree4221f2f2a7ad2244d2e93052bd68187ec91b8ea9 /src/renderer
parent9a82ce0083437a4f9f58108b2c23b957d2249ad8 (diff)
Restructure projectHEADmain
Diffstat (limited to 'src/renderer')
-rw-r--r--src/renderer/imm_renderer.c260
-rw-r--r--src/renderer/imm_renderer_impl.h44
-rw-r--r--src/renderer/renderer.c396
-rw-r--r--src/renderer/renderer_impl.h27
4 files changed, 727 insertions, 0 deletions
diff --git a/src/renderer/imm_renderer.c b/src/renderer/imm_renderer.c
new file mode 100644
index 0000000..8cf3a10
--- /dev/null
+++ b/src/renderer/imm_renderer.c
@@ -0,0 +1,260 @@
1#include "imm_renderer_impl.h"
2
3#include <gfx/core.h>
4#include <gfx/util/shader.h>
5
6#include <math/aabb3.h>
7
8#include <assert.h>
9#include <string.h> // memcpy
10
11bool imm_renderer_make(ImmRenderer* renderer, GfxCore* gfxcore) {
12 assert(renderer);
13 assert(gfxcore);
14
15 const size_t num_triangle_verts = IMM_MAX_NUM_TRIANGLES * 3;
16
17 renderer->gfxcore = gfxcore;
18
19 renderer->triangles = gfx_make_geometry(
20 gfxcore, &(GeometryDesc){
21 .type = Triangles,
22 .buffer_usage = BufferDynamic,
23 .num_verts = num_triangle_verts,
24 .positions3d = (BufferView3d){
25 .size_bytes = num_triangle_verts * sizeof(vec3)}});
26 if (!renderer->triangles) {
27 goto cleanup;
28 }
29
30 renderer->shader = gfx_make_immediate_mode_shader(gfxcore);
31 if (!renderer->shader) {
32 goto cleanup;
33 }
34
35 renderer->matrix_stack[0] = mat4_id();
36 renderer->stack_pointer = 0;
37
38 gfx_imm_set_colour(renderer, vec4_make(0.0, 0.0, 0.0, 1.0));
39
40 return true;
41
42cleanup:
43 imm_renderer_destroy(renderer);
44 return false;
45}
46
47void imm_renderer_destroy(ImmRenderer* renderer) {
48 assert(renderer);
49 assert(renderer->gfxcore);
50
51 if (renderer->triangles) {
52 gfx_destroy_geometry(renderer->gfxcore, &renderer->triangles);
53 // TODO: Could also destroy the geometry's buffers here.
54 }
55
56 if (renderer->shader) {
57 gfx_destroy_shader_program(renderer->gfxcore, &renderer->shader);
58 }
59}
60
61void imm_renderer_flush(ImmRenderer* renderer) {
62 assert(renderer);
63
64 if (renderer->num_triangle_verts > 0) {
65 gfx_update_geometry(
66 renderer->triangles,
67 &(GeometryDesc){
68 .num_verts = renderer->num_triangle_verts,
69 .positions3d = (BufferView3d){
70 .data = renderer->triangle_verts,
71 .size_bytes = renderer->num_triangle_verts * sizeof(vec3)}
72 });
73
74 gfx_apply_uniforms(renderer->shader);
75 gfx_render_geometry(renderer->triangles);
76
77 renderer->num_triangle_verts = 0;
78 }
79}
80
81void gfx_imm_start(ImmRenderer* renderer) {
82 assert(renderer);
83 // Shader uniforms are applied lazily.
84 // TODO: In the event that gfx_activate_shader_program() activates uniforms
85 // automatically for convenience, call an overload here that doesn't do so.
86 ShaderProgram* shader = renderer->shader;
87 gfx_activate_shader_program(shader);
88}
89
90void gfx_imm_end(ImmRenderer* renderer) {
91 assert(renderer);
92 imm_renderer_flush(renderer);
93 gfx_deactivate_shader_program(renderer->shader);
94}
95
96void gfx_imm_draw_triangles(
97 ImmRenderer* renderer, const vec3 verts[], size_t num_triangles) {
98 assert(renderer);
99 assert(verts);
100 const size_t new_verts = num_triangles * 3;
101 assert(
102 renderer->num_triangle_verts + new_verts < (IMM_MAX_NUM_TRIANGLES * 3));
103
104 memcpy(
105 renderer->triangle_verts + renderer->num_triangle_verts, verts,
106 new_verts * sizeof(vec3));
107
108 renderer->num_triangle_verts += new_verts;
109}
110
111void gfx_imm_draw_triangle(ImmRenderer* renderer, const vec3 verts[3]) {
112 gfx_imm_draw_triangles(renderer, verts, 1);
113}
114
115void gfx_imm_draw_aabb2(ImmRenderer* renderer, aabb2 box) {
116 assert(renderer);
117
118 // clang-format off
119 const vec3 verts[4] = {
120 vec3_make(box.min.x, box.min.y, 0), // 3 ---- 2
121 vec3_make(box.max.x, box.min.y, 0), // | |
122 vec3_make(box.max.x, box.max.y, 0), // | |
123 vec3_make(box.min.x, box.max.y, 0)}; // 0 ---- 1
124 // clang-format on
125
126#define tri(i0, i1, i2) verts[i0], verts[i1], verts[i2]
127 const vec3 tris[6] = {tri(0, 1, 2), tri(0, 2, 3)};
128#undef tri
129
130 gfx_imm_draw_triangles(renderer, tris, 2);
131}
132
133void gfx_imm_draw_aabb3(ImmRenderer* renderer, aabb3 box) {
134 assert(renderer);
135
136 // clang-format off
137 const vec3 vertices[8] = {
138 vec3_make(box.min.x, box.min.y, box.max.z), // 7 ----- 6
139 vec3_make(box.max.x, box.min.y, box.max.z), // / /|
140 vec3_make(box.max.x, box.max.y, box.max.z), // 3 ----- 2 |
141 vec3_make(box.min.x, box.max.y, box.max.z), // | | |
142 vec3_make(box.min.x, box.min.y, box.min.z), // | 4 ----- 5
143 vec3_make(box.max.x, box.min.y, box.min.z), // |/ |/
144 vec3_make(box.max.x, box.max.y, box.min.z), // 0 ----- 1
145 vec3_make(box.min.x, box.max.y, box.min.z)};
146 // clang-format on
147
148 gfx_imm_draw_box3(renderer, vertices);
149}
150
151void gfx_imm_draw_box3(ImmRenderer* renderer, const vec3 vertices[8]) {
152 assert(renderer);
153 assert(vertices);
154
155 // 7 ----- 6
156 // / /|
157 // 3 ----- 2 |
158 // | | |
159 // | 4 ----- 5
160 // |/ |/
161 // 0 ----- 1
162
163#define tri(i0, i1, i2) vertices[i0], vertices[i1], vertices[i2]
164 const vec3 tris[36] = {// Front.
165 tri(0, 1, 2), tri(0, 2, 3),
166 // Right.
167 tri(1, 5, 6), tri(1, 6, 2),
168 // Back.
169 tri(5, 4, 7), tri(5, 7, 6),
170 // Left.
171 tri(4, 0, 03), tri(4, 3, 7),
172 // Top.
173 tri(3, 2, 6), tri(3, 6, 7),
174 // Bottom.
175 tri(0, 4, 5), tri(0, 5, 1)};
176
177 gfx_imm_draw_triangles(renderer, tris, 12);
178}
179
180// Load the top of the matrix stack into the shader.
181static void update_shader_model_matrix(ImmRenderer* renderer) {
182 assert(renderer);
183 imm_renderer_flush(renderer);
184 gfx_set_mat4_uniform(
185 renderer->shader, "Model",
186 &renderer->matrix_stack[renderer->stack_pointer]);
187}
188
189void gfx_imm_load_identity(ImmRenderer* renderer) {
190 assert(renderer);
191 renderer->matrix_stack[0] = mat4_id();
192 renderer->stack_pointer = 0;
193 update_shader_model_matrix(renderer);
194}
195
196void gfx_imm_push_matrix(ImmRenderer* renderer, const mat4* matrix) {
197 assert(renderer);
198 assert(matrix);
199 assert(renderer->stack_pointer >= 0);
200 assert(renderer->stack_pointer < IMM_MAX_NUM_MATRICES); // TODO: hard assert.
201
202 renderer->matrix_stack[renderer->stack_pointer + 1] =
203 mat4_mul(*matrix, renderer->matrix_stack[renderer->stack_pointer]);
204 renderer->stack_pointer += 1;
205
206 update_shader_model_matrix(renderer);
207}
208
209void gfx_imm_pop_matrix(ImmRenderer* renderer) {
210 assert(renderer);
211 assert(renderer->stack_pointer > 0); // TODO: hard assert.
212
213 // For debugging, zero out the matrix stack as matrices are popped out.
214 memset(
215 &renderer->matrix_stack[renderer->stack_pointer], 0,
216 sizeof(renderer->matrix_stack[0]));
217
218 renderer->stack_pointer -= 1;
219
220 update_shader_model_matrix(renderer);
221}
222
223void gfx_imm_translate(ImmRenderer* renderer, vec3 offset) {
224 assert(renderer);
225 const mat4 mat = mat4_translate(offset);
226 gfx_imm_push_matrix(renderer, &mat);
227}
228
229void gfx_imm_set_camera(ImmRenderer* renderer, const Camera* camera) {
230 assert(renderer);
231 assert(renderer->shader);
232 imm_renderer_flush(renderer);
233 const mat4 view = spatial3_inverse_transform(&camera->spatial);
234 const mat4 view_proj = mat4_mul(camera->projection, view);
235 gfx_imm_set_view_projection_matrix(renderer, &view_proj);
236}
237
238void gfx_imm_set_model_matrix(ImmRenderer* renderer, const mat4* model) {
239 assert(renderer);
240 assert(model);
241 imm_renderer_flush(renderer);
242 renderer->matrix_stack[0] = *model;
243 renderer->stack_pointer = 0;
244 update_shader_model_matrix(renderer);
245}
246
247void gfx_imm_set_view_projection_matrix(
248 ImmRenderer* renderer, const mat4* view_proj) {
249 assert(renderer);
250 assert(renderer->shader);
251 imm_renderer_flush(renderer);
252 gfx_set_mat4_uniform(renderer->shader, "ViewProjection", view_proj);
253}
254
255void gfx_imm_set_colour(ImmRenderer* renderer, vec4 colour) {
256 assert(renderer);
257 assert(renderer->shader);
258 imm_renderer_flush(renderer);
259 gfx_set_vec4_uniform(renderer->shader, "Colour", colour);
260}
diff --git a/src/renderer/imm_renderer_impl.h b/src/renderer/imm_renderer_impl.h
new file mode 100644
index 0000000..5ece354
--- /dev/null
+++ b/src/renderer/imm_renderer_impl.h
@@ -0,0 +1,44 @@
1#pragma once
2
3#include <gfx/renderer.h>
4#include <gfx/sizes.h>
5
6#include <math/mat4.h>
7#include <math/vec3.h>
8
9#include <stdbool.h>
10#include <stddef.h>
11
12typedef struct Geometry Geometry;
13typedef struct ShaderProgram ShaderProgram;
14
15/// Immediate mode renderer.
16///
17/// Currently, the immediate mode renderer can only draw up to a maximum number
18/// of primitives per frame. It does not adjust this number dynamically. Keeps
19/// things simple while the extra complexity is not needed.
20typedef struct ImmRenderer {
21 GfxCore* gfxcore;
22 ShaderProgram* shader;
23 Geometry* triangles;
24 size_t num_triangle_verts; // Number of triangle verts this frame.
25 // TODO: wireframe rendering.
26 struct {
27 bool wireframe : 1;
28 } flags;
29 vec3 triangle_verts[IMM_MAX_NUM_TRIANGLES * 3];
30 // Matrix stack contains pre-multiplied matrices.
31 // It is also never empty. The top of the stack is an identity matrix when the
32 // stack is "empty" from the user's perspective.
33 mat4 matrix_stack[IMM_MAX_NUM_MATRICES];
34 int stack_pointer;
35} ImmRenderer;
36
37/// Create a new immediate mode renderer.
38bool imm_renderer_make(ImmRenderer*, GfxCore*);
39
40/// Destroy the immediate mode renderer.
41void imm_renderer_destroy(ImmRenderer*);
42
43/// Flush draw commands.
44void imm_renderer_flush(ImmRenderer*);
diff --git a/src/renderer/renderer.c b/src/renderer/renderer.c
new file mode 100644
index 0000000..c2a7dda
--- /dev/null
+++ b/src/renderer/renderer.c
@@ -0,0 +1,396 @@
1#include "renderer_impl.h"
2
3#include "scene/animation_impl.h"
4#include "scene/camera_impl.h"
5#include "scene/light_impl.h"
6#include "scene/material_impl.h"
7#include "scene/mesh_impl.h"
8#include "scene/model_impl.h"
9#include "scene/node_impl.h"
10#include "scene/object_impl.h"
11#include "scene/scene_impl.h"
12#include "scene/scene_memory.h"
13
14#include <gfx/core.h>
15#include <gfx/util/ibl.h>
16#include <gfx/util/shader.h>
17
18#include <log/log.h>
19#include <math/mat4.h>
20#include <math/spatial3.h>
21
22#include <assert.h>
23
24// TODO: Move to a header like "constants.h".
25static const int IRRADIANCE_MAP_WIDTH = 1024;
26static const int IRRADIANCE_MAP_HEIGHT = 1024;
27static const int PREFILTERED_ENVIRONMENT_MAP_WIDTH = 128;
28static const int PREFILTERED_ENVIRONMENT_MAP_HEIGHT = 128;
29static const int BRDF_INTEGRATION_MAP_WIDTH = 512;
30static const int BRDF_INTEGRATION_MAP_HEIGHT = 512;
31
32bool renderer_make(Renderer* renderer, GfxCore* gfxcore) {
33 assert(renderer);
34 assert(gfxcore);
35
36 renderer->gfxcore = gfxcore;
37
38 return true;
39}
40
41void renderer_destroy(Renderer* renderer) {
42 if (!renderer) {
43 return;
44 }
45 assert(renderer->gfxcore);
46 GfxCore* gfxcore = renderer->gfxcore;
47 if (renderer->ibl) {
48 gfx_destroy_ibl(gfxcore, &renderer->ibl);
49 }
50 if (renderer->shaders.debug) {
51 gfx_destroy_shader_program(gfxcore, &renderer->shaders.debug);
52 }
53 if (renderer->shaders.normals) {
54 gfx_destroy_shader_program(gfxcore, &renderer->shaders.normals);
55 }
56 if (renderer->shaders.normal_mapped_normals) {
57 gfx_destroy_shader_program(
58 gfxcore, &renderer->shaders.normal_mapped_normals);
59 }
60 if (renderer->shaders.tangents) {
61 gfx_destroy_shader_program(gfxcore, &renderer->shaders.tangents);
62 }
63}
64
65/// Initialize renderer state for IBL if not already initialized.
66static bool init_ibl(Renderer* renderer) {
67 assert(renderer);
68
69 if (!renderer->ibl && !(renderer->ibl = gfx_make_ibl(renderer->gfxcore))) {
70 return false;
71 }
72
73 if (!renderer->brdf_integration_map &&
74 !(renderer->brdf_integration_map = gfx_make_brdf_integration_map(
75 renderer->ibl, renderer->gfxcore, BRDF_INTEGRATION_MAP_WIDTH,
76 BRDF_INTEGRATION_MAP_HEIGHT))) {
77 return false;
78 }
79
80 return true;
81}
82
83static ShaderProgram* load_shader(Renderer* renderer, RenderSceneMode mode) {
84 assert(renderer);
85
86#define LOAD_AND_RETURN(pShader, constructor) \
87 { \
88 if (!pShader) { \
89 pShader = constructor(renderer->gfxcore); \
90 } \
91 assert(pShader); \
92 return pShader; \
93 }
94
95 switch (mode) {
96 case RenderDefault:
97 return 0;
98 case RenderDebug:
99 LOAD_AND_RETURN(renderer->shaders.debug, gfx_make_debug3d_shader);
100 case RenderNormals:
101 LOAD_AND_RETURN(renderer->shaders.normals, gfx_make_view_normals_shader);
102 case RenderNormalMappedNormals:
103 LOAD_AND_RETURN(
104 renderer->shaders.normal_mapped_normals,
105 gfx_make_view_normal_mapped_normals_shader);
106 case RenderTangents:
107 LOAD_AND_RETURN(renderer->shaders.tangents, gfx_make_view_tangents_shader);
108 }
109 assert(false);
110 return 0;
111}
112
113// static void log_matrix(const mat4* m) {
114// for (int row = 0; row < 4; ++row) {
115// LOGI("[ %5.2f, %5.2f, %5.2f, %5.2f ]", m->val[0][row], m->val[1][row],
116// m->val[2][row], m->val[3][row]);
117// }
118// }
119
120/// Computes irradiance and prefiltered environment maps for the light if they
121/// have not been already computed.
122static bool setup_environment_light(
123 Renderer* renderer, GfxCore* gfxcore, EnvironmentLight* light) {
124 assert(renderer);
125 assert(light);
126
127 if (!init_ibl(renderer)) {
128 return false;
129 }
130
131 if (light->irradiance_map) {
132 assert(light->prefiltered_environment_map);
133 return true;
134 }
135
136 Texture* irradiance_map = 0;
137 Texture* prefiltered_environment_map = 0;
138
139 if (!(irradiance_map = gfx_make_irradiance_map(
140 renderer->ibl, gfxcore, light->environment_map,
141 IRRADIANCE_MAP_WIDTH, IRRADIANCE_MAP_HEIGHT))) {
142 goto cleanup;
143 }
144
145 int max_mip_level = 0;
146 if (!(prefiltered_environment_map = gfx_make_prefiltered_environment_map(
147 renderer->ibl, gfxcore, light->environment_map,
148 PREFILTERED_ENVIRONMENT_MAP_WIDTH,
149 PREFILTERED_ENVIRONMENT_MAP_HEIGHT, &max_mip_level))) {
150 goto cleanup;
151 }
152
153 light->irradiance_map = irradiance_map;
154 light->prefiltered_environment_map = prefiltered_environment_map;
155 light->max_reflection_lod = max_mip_level;
156
157 return true;
158
159cleanup:
160 if (irradiance_map) {
161 gfx_destroy_texture(gfxcore, &irradiance_map);
162 }
163 if (prefiltered_environment_map) {
164 gfx_destroy_texture(gfxcore, &prefiltered_environment_map);
165 }
166 return false;
167}
168
169typedef struct RenderState {
170 GfxCore* gfxcore;
171 Renderer* renderer;
172 ShaderProgram* shader; // Null to use scene shaders.
173 const Scene* scene;
174 const Camera* camera;
175 const mat4* camera_rotation; // From camera to world space, rotation only.
176 const mat4* view_matrix;
177 const mat4* projection;
178 const float fovy;
179 const float aspect;
180 Light* environment_light;
181 const Anima* anima;
182 size_t num_joints;
183 mat4 joint_matrices[GFX_MAX_NUM_JOINTS];
184} RenderState;
185
186/// Load joint matrices into the render state.
187static void load_skeleton(RenderState* state, skeleton_idx skeleton_index) {
188 assert(state);
189 assert(skeleton_index.val != 0);
190
191 const Skeleton* skeleton = mem_get_skeleton(skeleton_index);
192 assert(skeleton);
193 assert(skeleton->num_joints <= GFX_MAX_NUM_JOINTS);
194
195 state->num_joints = skeleton->num_joints;
196
197 for (size_t i = 0; i < skeleton->num_joints; ++i) {
198 const joint_idx joint_index = skeleton->joints[i];
199 const Joint* joint = &state->anima->joints[joint_index];
200 state->joint_matrices[i] = joint->joint_matrix;
201 }
202}
203
204/// Draw the scene recursively.
205static void draw_recursively(
206 RenderState* state, mat4 parent_transform, const SceneNode* node) {
207 assert(state);
208 const mat4 node_transform = mat4_mul(parent_transform, node->transform);
209
210 // Anima.
211 if (node->type == AnimaNode) {
212 state->anima = gfx_get_node_anima(node);
213 }
214 // Activate light.
215 else if (node->type == LightNode) {
216 Light* light = mem_get_light(node->light);
217 assert(light);
218
219 if (light->type == EnvironmentLightType) {
220 bool result = setup_environment_light(
221 state->renderer, state->gfxcore, &light->environment);
222 // TODO: Handle the result in a better way.
223 assert(result);
224 state->environment_light = light;
225 }
226 }
227 // Model.
228 else if (node->type == ModelNode) {
229 const Model* model = gfx_get_node_model(node);
230 const SceneNode* root = mem_get_node(model->root);
231 draw_recursively(state, parent_transform, root);
232 }
233 // Render object.
234 else if (node->type == ObjectNode) {
235 const SceneObject* object = mem_get_object(node->object);
236 assert(object);
237
238 // TODO: Here we would frustum-cull the object.
239
240 // TODO: Avoid computing matrices like Modelview or MVP if the shader does
241 // not use them.
242 const mat4 model_matrix = node_transform;
243 const mat4 modelview = mat4_mul(*state->view_matrix, model_matrix);
244 const mat4 mvp = mat4_mul(*state->projection, modelview);
245
246 if (object->skeleton.val) {
247 load_skeleton(state, object->skeleton);
248 }
249
250 for (mesh_link_idx mesh_link_index = object->mesh_link;
251 mesh_link_index.val;) {
252 const MeshLink* mesh_link = mem_get_mesh_link(mesh_link_index);
253 mesh_link_index = mesh_link->next;
254
255 const Mesh* mesh = mem_get_mesh(mesh_link->mesh);
256 if (!mesh) {
257 continue;
258 }
259 assert(mesh->geometry);
260 assert(mesh->material);
261
262 // TODO: Here we would frustum-cull the mesh. The AABB would have to be
263 // transformed by the model matrix. Rotation would make the AABB
264 // relatively large, but still, the culling would be conservative.
265
266 // Apply common shader uniforms not captured by materials.
267 ShaderProgram* shader = state->shader ? state->shader : mesh->shader;
268 gfx_set_mat4_uniform(shader, "ModelMatrix", &model_matrix);
269 gfx_set_mat4_uniform(shader, "Modelview", &modelview);
270 gfx_set_mat4_uniform(shader, "View", state->view_matrix);
271 gfx_set_mat4_uniform(shader, "Projection", state->projection);
272 gfx_set_mat4_uniform(shader, "MVP", &mvp);
273 gfx_set_mat4_uniform(shader, "CameraRotation", state->camera_rotation);
274 gfx_set_float_uniform(shader, "Fovy", state->fovy);
275 gfx_set_float_uniform(shader, "Aspect", state->aspect);
276 if (state->camera) {
277 gfx_set_vec3_uniform(
278 shader, "CameraPosition", state->camera->spatial.p);
279 }
280 if (state->num_joints > 0) {
281 gfx_set_mat4_array_uniform(
282 shader, "JointMatrices", state->joint_matrices, state->num_joints);
283 }
284 // Apply lights.
285 if (state->environment_light) {
286 const EnvironmentLight* light = &state->environment_light->environment;
287 assert(light->environment_map);
288 assert(light->irradiance_map);
289 assert(light->prefiltered_environment_map);
290 assert(state->renderer->brdf_integration_map);
291 gfx_set_texture_uniform(
292 shader, "BRDFIntegrationMap",
293 state->renderer->brdf_integration_map);
294 gfx_set_texture_uniform(shader, "Sky", light->environment_map);
295 gfx_set_texture_uniform(shader, "IrradianceMap", light->irradiance_map);
296 gfx_set_texture_uniform(
297 shader, "PrefilteredEnvironmentMap",
298 light->prefiltered_environment_map);
299 gfx_set_float_uniform(
300 shader, "MaxReflectionLOD", light->max_reflection_lod);
301 }
302 material_activate(shader, mesh->material);
303 gfx_activate_shader_program(shader);
304 gfx_apply_uniforms(shader);
305 gfx_render_geometry(mesh->geometry);
306 }
307
308 // Reset state for next object.
309 state->num_joints = 0;
310 }
311
312 // Render children recursively.
313 for (node_idx child_index = node->child; child_index.val;) {
314 const SceneNode* child = mem_get_node(child_index);
315 draw_recursively(state, node_transform, child);
316 child_index = child->next;
317 }
318}
319
320void gfx_render_scene(Renderer* renderer, const RenderSceneParams* params) {
321 assert(renderer);
322 assert(params);
323 assert(params->scene);
324
325 ShaderProgram* const shader = load_shader(renderer, params->mode);
326
327 const Scene* scene = params->scene;
328 const SceneCamera* camera = params->camera;
329
330 GfxCore* gfxcore = renderer->gfxcore;
331
332 mat4 projection, camera_rotation, view_matrix;
333 if (camera) {
334 projection = camera->camera.projection;
335 camera_rotation =
336 mat4_rotation(spatial3_transform(&camera->camera.spatial));
337 view_matrix = spatial3_inverse_transform(&camera->camera.spatial);
338 } else {
339 projection = mat4_id();
340 camera_rotation = mat4_id();
341 view_matrix = mat4_id();
342 }
343
344 int x, y, width, height;
345 gfx_get_viewport(gfxcore, &x, &y, &width, &height);
346 const float aspect = (float)width / (float)height;
347
348 RenderState state = {
349 .gfxcore = gfxcore,
350 .renderer = renderer,
351 .shader = shader,
352 .scene = scene,
353 .camera = &camera->camera,
354 .camera_rotation = &camera_rotation,
355 .view_matrix = &view_matrix,
356 .projection = &projection,
357 .environment_light = 0,
358 // Assuming a perspective matrix.
359 .fovy = atan(1.0 / (mat4_at(projection, 1, 1))) * 2,
360 .aspect = aspect};
361
362 draw_recursively(&state, mat4_id(), scene->root);
363}
364
365static void update_rec(SceneNode* node, const SceneCamera* camera, R t) {
366 assert(node);
367 assert(camera);
368
369 const NodeType node_type = gfx_get_node_type(node);
370
371 // TODO: Models do not need to be animated if they are not visible to the
372 // camera.
373 if (node_type == AnimaNode) {
374 Anima* anima = gfx_get_node_anima_mut(node);
375 gfx_update_animation(anima, (R)t);
376 } else if (node_type == ModelNode) {
377 Model* model = gfx_get_node_model_mut(node);
378 SceneNode* root = gfx_get_model_root_mut(model);
379 update_rec(root, camera, t);
380 }
381
382 // Children.
383 SceneNode* child = gfx_get_node_child_mut(node);
384 while (child) {
385 update_rec(child, camera, t);
386 child = gfx_get_node_sibling_mut(child);
387 }
388}
389
390void gfx_update(Scene* scene, const SceneCamera* camera, R t) {
391 assert(scene);
392 assert(camera);
393
394 SceneNode* node = gfx_get_scene_root(scene);
395 update_rec(node, camera, t);
396}
diff --git a/src/renderer/renderer_impl.h b/src/renderer/renderer_impl.h
new file mode 100644
index 0000000..fc14dcb
--- /dev/null
+++ b/src/renderer/renderer_impl.h
@@ -0,0 +1,27 @@
1#pragma once
2
3#include <gfx/renderer.h>
4
5#include <stdbool.h>
6
7typedef struct IBL IBL;
8typedef struct ShaderProgram ShaderProgram;
9typedef struct Texture Texture;
10
11typedef struct Renderer {
12 GfxCore* gfxcore;
13 IBL* ibl;
14 Texture* brdf_integration_map;
15 struct {
16 ShaderProgram* debug;
17 ShaderProgram* normals;
18 ShaderProgram* normal_mapped_normals;
19 ShaderProgram* tangents;
20 } shaders;
21} Renderer;
22
23/// Create a new renderer.
24bool renderer_make(Renderer*, GfxCore*);
25
26/// Destroy the renderer.
27void renderer_destroy(Renderer*);