aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-07-04 10:27:06 -0700
committer3gg <3gg@shellblade.net>2025-07-04 10:27:06 -0700
commit1ec46bead3cf87971a2329f9ef4ddde5a0c48325 (patch)
tree3f4c404467c3ad9c94265295f4aa1b97a10a9eb3
parente386405ac636b7e4a41d5c03eb363e9c120ce919 (diff)
Clarify doc
-rw-r--r--include/gfx/core.h3
-rw-r--r--include/gfx/llr/light.h (renamed from include/gfx/scene/light.h)0
-rw-r--r--include/gfx/llr/llr.h117
-rw-r--r--include/gfx/llr/material.h (renamed from include/gfx/scene/material.h)0
-rw-r--r--include/gfx/llr/mesh.h (renamed from include/gfx/scene/mesh.h)0
-rw-r--r--src/llr/imm_renderer.c260
-rw-r--r--src/llr/imm_renderer_impl.h44
-rw-r--r--src/llr/light.c (renamed from src/scene/light.c)6
-rw-r--r--src/llr/light_impl.h (renamed from src/scene/light_impl.h)4
-rw-r--r--src/llr/llr.c554
-rw-r--r--src/llr/llr_impl.h99
-rw-r--r--src/llr/material.c (renamed from src/scene/material.c)2
-rw-r--r--src/llr/material_impl.h (renamed from src/scene/material_impl.h)2
-rw-r--r--src/llr/mesh.c (renamed from src/scene/mesh.c)4
-rw-r--r--src/llr/mesh_impl.h (renamed from src/scene/mesh_impl.h)2
15 files changed, 783 insertions, 314 deletions
diff --git a/include/gfx/core.h b/include/gfx/core.h
index 2a29003..0cf4465 100644
--- a/include/gfx/core.h
+++ b/include/gfx/core.h
@@ -474,6 +474,9 @@ void gfx_deactivate_shader_program(const ShaderProgram*);
474/// 474///
475/// This function should be called after setting all of the uniform variables 475/// This function should be called after setting all of the uniform variables
476/// and prior to issuing a draw call. 476/// and prior to issuing a draw call.
477///
478/// The given program must have been activated prior to this call with
479/// gfx_activate_shader_program().
477void gfx_apply_uniforms(const ShaderProgram*); 480void gfx_apply_uniforms(const ShaderProgram*);
478 481
479/// Set the texture uniform. 482/// Set the texture uniform.
diff --git a/include/gfx/scene/light.h b/include/gfx/llr/light.h
index 132e344..132e344 100644
--- a/include/gfx/scene/light.h
+++ b/include/gfx/llr/light.h
diff --git a/include/gfx/llr/llr.h b/include/gfx/llr/llr.h
new file mode 100644
index 0000000..49b7706
--- /dev/null
+++ b/include/gfx/llr/llr.h
@@ -0,0 +1,117 @@
1#pragma once
2
3#include <math/aabb2.h>
4#include <math/aabb3.h>
5#include <math/camera.h>
6#include <math/mat4.h>
7#include <math/vec3.h>
8#include <math/vec4.h>
9
10typedef struct Anima Anima;
11typedef struct Light Light;
12typedef struct Mesh Mesh;
13typedef struct Skeleton Skeleton;
14
15typedef struct ImmRenderer ImmRenderer;
16
17/// Prepare the graphics systems for immediate-mode rendering.
18///
19/// Call this before issuing any immediate-mode rendering draws.
20void gfx_imm_start(ImmRenderer*);
21
22/// End immediate mode rendering.
23///
24/// Call this after issuing immediate-mode rendering draws and before swapping
25/// buffers.
26void gfx_imm_end(ImmRenderer*);
27
28// -----------------------------------------------------------------------------
29// Immediate-mode rendering of scene elements.
30//
31// This renders models and meshes under lighting. It is up to the client to
32// determine visibility, sort the calls optimally, etc.
33
34/// Push a light into the lights stack.
35void gfx_imm_push_light(ImmRenderer*, Light*);
36
37/// Pop the last light from the lights stack.
38void gfx_imm_pop_light(ImmRenderer*);
39
40/// Load a skeleton.
41///
42/// If a skeleton is loaded, subsequent meshes are rendered with joint data
43/// passed to the shader. This has a cost, so if subsequent meshes are not
44/// animated, unload the skeleton prior to rendering them.
45void gfx_imm_set_skeleton(ImmRenderer*, const Anima*, const Skeleton*);
46
47/// Unload the loaded skeleton.
48void gfx_imm_unset_skeleton(ImmRenderer*);
49
50/// Render the mesh.
51void gfx_imm_render_mesh(ImmRenderer*, const Mesh*);
52
53// -----------------------------------------------------------------------------
54// Immediate-mode rendering of primitives.
55//
56// This is rather inefficient and should be reserved for debug rendering. It
57// also does not support lighting; the primitives are rendered with a constant
58// colour.
59
60/// Draw a set of triangles.
61void gfx_imm_draw_triangles(ImmRenderer*, const vec3[], size_t num_triangles);
62
63/// Draw a triangle.
64void gfx_imm_draw_triangle(ImmRenderer*, const vec3[3]);
65
66/// Draw a bounding box.
67void gfx_imm_draw_aabb2(ImmRenderer*, aabb2);
68
69/// Draw a bounding box.
70void gfx_imm_draw_aabb3(ImmRenderer*, aabb3);
71
72/// Draw a box.
73///
74/// The vertices must be given in the following order:
75///
76/// 7 ----- 6
77/// / /|
78/// 3 ----- 2 |
79/// | | |
80/// | 4 ----- 5
81/// |/ |/
82/// 0 ----- 1
83void gfx_imm_draw_box3(ImmRenderer* renderer, const vec3 vertices[8]);
84
85/// Set the render colour.
86void gfx_imm_set_colour(ImmRenderer*, vec4 colour);
87
88// -----------------------------------------------------------------------------
89// Matrix stack manipulation.
90//
91// Common to both scene and and primitive rendering.
92
93/// Load an identity model matrix. Clears the matrix stack.
94void gfx_imm_load_identity(ImmRenderer* renderer);
95
96/// Push the given matrix to the matrix stack.
97void gfx_imm_push_matrix(ImmRenderer* renderer, const mat4* matrix);
98
99/// Pop the top of the matrix stack.
100void gfx_imm_pop_matrix(ImmRenderer* renderer);
101
102/// Push a translation matrix to the matrix stack.
103void gfx_imm_translate(ImmRenderer* renderer, vec3 offset);
104
105/// Set the model matrix. Clears the matrix stack.
106void gfx_imm_set_model_matrix(ImmRenderer*, const mat4*);
107
108// -----------------------------------------------------------------------------
109// Camera
110//
111// Common to both scene and and primitive rendering.
112
113/// Set the camera.
114void gfx_imm_set_camera(ImmRenderer*, const Camera*);
115
116/// Set the view-projection matrix.
117// void gfx_imm_set_view_projection_matrix(ImmRenderer*, const mat4*);
diff --git a/include/gfx/scene/material.h b/include/gfx/llr/material.h
index bca664e..bca664e 100644
--- a/include/gfx/scene/material.h
+++ b/include/gfx/llr/material.h
diff --git a/include/gfx/scene/mesh.h b/include/gfx/llr/mesh.h
index 0d3b4d4..0d3b4d4 100644
--- a/include/gfx/scene/mesh.h
+++ b/include/gfx/llr/mesh.h
diff --git a/src/llr/imm_renderer.c b/src/llr/imm_renderer.c
deleted file mode 100644
index 4b856c9..0000000
--- a/src/llr/imm_renderer.c
+++ /dev/null
@@ -1,260 +0,0 @@
1#include "../renderer/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/llr/imm_renderer_impl.h b/src/llr/imm_renderer_impl.h
deleted file mode 100644
index 5ece354..0000000
--- a/src/llr/imm_renderer_impl.h
+++ /dev/null
@@ -1,44 +0,0 @@
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/scene/light.c b/src/llr/light.c
index adbec8d..168f16a 100644
--- a/src/scene/light.c
+++ b/src/llr/light.c
@@ -1,7 +1,7 @@
1#include "light_impl.h" 1#include "../scene/light_impl.h"
2 2
3#include "node_impl.h" 3#include "../scene/node_impl.h"
4#include "scene_memory.h" 4#include "../scene/scene_memory.h"
5 5
6#include <error.h> 6#include <error.h>
7 7
diff --git a/src/scene/light_impl.h b/src/llr/light_impl.h
index 1aa0bb4..32203c4 100644
--- a/src/scene/light_impl.h
+++ b/src/llr/light_impl.h
@@ -1,8 +1,8 @@
1#pragma once 1#pragma once
2 2
3#include <gfx/scene/light.h> 3#include <../../include/gfx/llr/light.h>
4 4
5#include "types.h" 5#include "../scene/types.h"
6 6
7typedef struct Texture Texture; 7typedef struct Texture Texture;
8 8
diff --git a/src/llr/llr.c b/src/llr/llr.c
new file mode 100644
index 0000000..62a7c30
--- /dev/null
+++ b/src/llr/llr.c
@@ -0,0 +1,554 @@
1#include "imm_renderer_impl.h"
2#include "light_impl.h"
3#include "mesh_impl.h"
4
5#include "scene/animation_impl.h"
6
7#include <gfx/core.h>
8#include <gfx/util/ibl.h>
9#include <gfx/util/shader.h>
10
11#include <math/aabb3.h>
12
13#include <cassert.h>
14#include <string.h> // memcpy
15
16static const int IRRADIANCE_MAP_WIDTH = 1024;
17static const int IRRADIANCE_MAP_HEIGHT = 1024;
18static const int PREFILTERED_ENVIRONMENT_MAP_WIDTH = 128;
19static const int PREFILTERED_ENVIRONMENT_MAP_HEIGHT = 128;
20static const int BRDF_INTEGRATION_MAP_WIDTH = 512;
21static const int BRDF_INTEGRATION_MAP_HEIGHT = 512;
22
23/// Initialize renderer state for IBL.
24static bool init_ibl(ImmRenderer* renderer) {
25 assert(renderer);
26 assert(!renderer->ibl);
27 assert(!renderer->brdf_integration_map);
28
29 if (!((renderer->ibl = gfx_make_ibl(renderer->gfxcore)))) {
30 return false;
31 }
32
33 if (!((renderer->brdf_integration_map = gfx_make_brdf_integration_map(
34 renderer->ibl, renderer->gfxcore, BRDF_INTEGRATION_MAP_WIDTH,
35 BRDF_INTEGRATION_MAP_HEIGHT)))) {
36 return false;
37 }
38
39 return true;
40}
41
42// TODO: Why is this done lazily here? Do it when the environment light is
43// created.
44//
45/// Compute irradiance and prefiltered environment maps for the light if they
46/// have not been already computed.
47static bool set_up_environment_light(
48 ImmRenderer* renderer, EnvironmentLight* light) {
49 assert(renderer);
50 assert(light);
51 assert(renderer->ibl);
52 assert(renderer->brdf_integration_map);
53
54 if (light->irradiance_map) {
55 assert(light->prefiltered_environment_map);
56 return true;
57 }
58
59 // For convenience.
60 GfxCore* gfxcore = renderer->gfxcore;
61
62 Texture* irradiance_map = 0;
63 Texture* prefiltered_environment_map = 0;
64
65 if (!((irradiance_map = gfx_make_irradiance_map(
66 renderer->ibl, gfxcore, light->environment_map,
67 IRRADIANCE_MAP_WIDTH, IRRADIANCE_MAP_HEIGHT)))) {
68 goto cleanup;
69 }
70
71 int max_mip_level = 0;
72 if (!((prefiltered_environment_map = gfx_make_prefiltered_environment_map(
73 renderer->ibl, gfxcore, light->environment_map,
74 PREFILTERED_ENVIRONMENT_MAP_WIDTH,
75 PREFILTERED_ENVIRONMENT_MAP_HEIGHT, &max_mip_level)))) {
76 goto cleanup;
77 }
78
79 light->irradiance_map = irradiance_map;
80 light->prefiltered_environment_map = prefiltered_environment_map;
81 light->max_reflection_lod = max_mip_level;
82
83 return true;
84
85cleanup:
86 if (irradiance_map) {
87 gfx_destroy_texture(gfxcore, &irradiance_map);
88 }
89 if (prefiltered_environment_map) {
90 gfx_destroy_texture(gfxcore, &prefiltered_environment_map);
91 }
92 return false;
93}
94
95static void configure_light(ImmRenderer* renderer, Light* light) {
96 assert(renderer);
97 assert(light);
98
99 // For convenience.
100 ShaderProgram* const shader = renderer->shader;
101
102 switch (light->type) {
103 case EnvironmentLightType: {
104 EnvironmentLight* env = &light->environment;
105
106 const bool initialized = set_up_environment_light(renderer, env);
107 ASSERT(initialized);
108 assert(env->environment_map);
109 assert(env->irradiance_map);
110 assert(env->prefiltered_environment_map);
111 assert(renderer->brdf_integration_map);
112
113 gfx_set_texture_uniform(
114 shader, "BRDFIntegrationMap", renderer->brdf_integration_map);
115 gfx_set_texture_uniform(shader, "Sky", env->environment_map);
116 gfx_set_texture_uniform(shader, "IrradianceMap", env->irradiance_map);
117 gfx_set_texture_uniform(
118 shader, "PrefilteredEnvironmentMap", env->prefiltered_environment_map);
119 gfx_set_float_uniform(
120 shader, "MaxReflectionLOD", (float)env->max_reflection_lod);
121
122 break;
123 }
124 default:
125 assert(false); // TODO: Implement other light types.
126 break;
127 }
128}
129
130static void configure_state(ImmRenderer* renderer) {
131 assert(renderer);
132
133 // Check if anything changed first so that we don't call gfx_apply_uniforms()
134 // unnecessarily.
135 const bool anything_changed =
136 renderer->camera_changed || renderer->lights_changed ||
137 renderer->skeleton_changed || renderer->shader_changed;
138 if (!anything_changed) {
139 return;
140 }
141
142 // For convenience.
143 ShaderProgram* const shader = renderer->shader;
144
145 // TODO: camera_changed is not set anywhere. Need to think how imm primitive
146 // rendering and imm mesh rendering work together. We could treat imm
147 // primitive calls like setting a new shader.
148 if (renderer->camera_changed || renderer->shader_changed) {
149 renderer->camera_changed = false;
150
151 // Set all supported camera-related uniforms. Shaders can choose which ones
152 // to use.
153 // TODO: Check to see which ones the shader actually uses and avoid
154 // computing the unnecessary matrices.
155 const mat4* const model = &renderer->matrix_stack[renderer->stack_pointer];
156 const mat4 modelview = mat4_mul(renderer->view, *model);
157 const mat4 view_proj = mat4_mul(renderer->projection, renderer->view);
158 const mat4 mvp = mat4_mul(renderer->projection, modelview);
159
160 gfx_set_mat4_uniform(shader, "ModelMatrix", model);
161 gfx_set_mat4_uniform(shader, "Modelview", &modelview);
162 gfx_set_mat4_uniform(shader, "View", &renderer->view);
163 gfx_set_mat4_uniform(shader, "Projection", &renderer->projection);
164 gfx_set_mat4_uniform(shader, "ViewProjection", &view_proj);
165 gfx_set_mat4_uniform(shader, "MVP", &mvp);
166 gfx_set_vec3_uniform(shader, "CameraPosition", renderer->camera_position);
167 }
168
169 if (renderer->lights_changed || renderer->shader_changed) {
170 renderer->lights_changed = false;
171
172 // TODO: Could do better by only setting the lights that have actually
173 // changed.
174 // TODO: Will also need to pass the number of lights to the shader once the
175 // other light types are implemented.
176 for (int i = 0; i < renderer->num_lights; ++i) {
177 configure_light(renderer, renderer->lights[i]);
178 }
179 }
180
181 if (renderer->skeleton_changed || renderer->shader_changed) {
182 renderer->skeleton_changed = false;
183
184 gfx_set_mat4_array_uniform(
185 shader, "JointMatrices", renderer->joint_matrices,
186 renderer->num_joints);
187 }
188
189 if (renderer->shader_changed) {
190 renderer->shader_changed = false;
191 gfx_activate_shader_program(renderer->shader);
192 }
193
194 // Must be called after activating the program.
195 gfx_apply_uniforms(renderer->shader);
196}
197
198bool gfx_imm_make(ImmRenderer* renderer, GfxCore* gfxcore) {
199 assert(renderer);
200 assert(gfxcore);
201
202 const size_t num_triangle_verts = IMM_MAX_NUM_TRIANGLES * 3;
203
204 renderer->gfxcore = gfxcore;
205
206 renderer->triangles = gfx_make_geometry(
207 gfxcore,
208 &(GeometryDesc){.type = Triangles,
209 .buffer_usage = BufferDynamic,
210 .num_verts = num_triangle_verts,
211 .positions3d = (BufferView3d){
212 .size_bytes = num_triangle_verts * sizeof(vec3)}});
213 if (!renderer->triangles) {
214 goto cleanup;
215 }
216
217 renderer->imm_shader = gfx_make_immediate_mode_shader(gfxcore);
218 if (!renderer->imm_shader) {
219 goto cleanup;
220 }
221 renderer->shader = renderer->imm_shader;
222
223 if (!init_ibl(renderer)) {
224 goto cleanup;
225 }
226
227 gfx_imm_load_identity(renderer);
228 gfx_imm_set_colour(renderer, vec4_make(0.0f, 0.0f, 0.0f, 1.0f));
229
230 return true;
231
232cleanup:
233 gfx_imm_destroy(renderer);
234 return false;
235}
236
237void gfx_imm_destroy(ImmRenderer* renderer) {
238 assert(renderer);
239 assert(renderer->gfxcore);
240
241 if (renderer->triangles) {
242 gfx_destroy_geometry(renderer->gfxcore, &renderer->triangles);
243 // TODO: Could also destroy the geometry's buffers here.
244 }
245
246 if (renderer->imm_shader) {
247 gfx_destroy_shader_program(renderer->gfxcore, &renderer->imm_shader);
248 }
249
250 if (renderer->brdf_integration_map) {
251 gfx_destroy_texture(renderer->gfxcore, &renderer->brdf_integration_map);
252 }
253
254 // TODO: Do this once the IBL from the scene renderer is gone.
255 if (renderer->ibl) {
256 // gfx_destroy_ibl(renderer->gfxcore, &renderer->ibl);
257 }
258}
259
260void gfx_imm_flush(ImmRenderer* renderer) {
261 assert(renderer);
262
263 if (renderer->num_triangle_verts > 0) {
264 configure_state(renderer);
265
266 gfx_update_geometry(
267 renderer->triangles,
268 &(GeometryDesc){
269 .num_verts = renderer->num_triangle_verts,
270 .positions3d = (BufferView3d){
271 .data = renderer->triangle_verts,
272 .size_bytes = renderer->num_triangle_verts * sizeof(vec3)}
273 });
274
275 gfx_apply_uniforms(renderer->shader);
276 gfx_render_geometry(renderer->triangles);
277
278 renderer->num_triangle_verts = 0;
279 }
280}
281
282void gfx_imm_set_shader(ImmRenderer* renderer, ShaderProgram* shader) {
283 assert(renderer);
284 assert(shader);
285
286 // TODO: It would probably be best to make the imm renderer work in terms of a
287 // new LLR renderer. Otherwise we need to constantly flush stuff everywhere
288 // "just in case". This would still allow the imm to render meshes with
289 // lighting etc. We just need to create an actual Mesh out of the 'triangles'
290 // Geometry that imm currently has. The change would greatly simplify the
291 // implementation of this otherwise coupled LLR-IMM renderer.
292 // Need to decide where to put the matrix stack manipulation. Might be good
293 // to move to the LLR. (Currently, manipulating the stack causes an imm
294 // flush, but that's because we have coupled imm with stack manipulation,
295 // which the new design seems like would address.)
296 gfx_imm_flush(renderer);
297
298 // It's important to not set shader_changed unnecessarily, since that would
299 // re-trigger the setting of uniforms.
300 if (renderer->shader != shader) {
301 renderer->shader = shader;
302 renderer->shader_changed = true;
303 }
304}
305
306void gfx_imm_start(ImmRenderer* renderer) {
307 assert(renderer);
308
309 // Shader uniforms are applied lazily.
310 // TODO: In the event that gfx_activate_shader_program() activates uniforms
311 // automatically for convenience, call an overload here that doesn't do so.
312 gfx_activate_shader_program(renderer->shader);
313}
314
315void gfx_imm_end(ImmRenderer* renderer) {
316 assert(renderer);
317
318 gfx_imm_flush(renderer);
319 gfx_deactivate_shader_program(renderer->shader);
320
321 // TODO: Should we clear all of the render state here as well? At least set
322 // the 'changed' variables to false, for example.
323}
324
325void gfx_imm_push_light(ImmRenderer* renderer, Light* light) {
326 assert(renderer);
327 assert(light);
328 assert(renderer->num_lights >= 0);
329 ASSERT(renderer->num_lights < IMM_MAX_NUM_LIGHTS);
330
331 renderer->lights[renderer->num_lights++] = light;
332 renderer->lights_changed = true;
333}
334
335void gfx_imm_pop_light(ImmRenderer* renderer) {
336 assert(renderer);
337 ASSERT(renderer->num_lights > 0);
338
339 renderer->lights[--renderer->num_lights] = 0;
340 renderer->lights_changed = true;
341}
342
343void gfx_imm_set_skeleton(
344 ImmRenderer* renderer, const Anima* anima, const Skeleton* skeleton) {
345 assert(renderer);
346 assert(anima);
347 assert(skeleton);
348 assert(skeleton->num_joints <= GFX_MAX_NUM_JOINTS);
349
350 for (size_t i = 0; i < skeleton->num_joints; ++i) {
351 const joint_idx joint_index = skeleton->joints[i];
352 const Joint* joint = &anima->joints[joint_index];
353 renderer->joint_matrices[i] = joint->joint_matrix;
354 }
355 renderer->num_joints = skeleton->num_joints;
356 renderer->skeleton_changed = true;
357}
358
359void gfx_imm_unset_skeleton(ImmRenderer* renderer) {
360 assert(renderer);
361
362 renderer->num_joints = 0;
363 renderer->skeleton_changed = true;
364}
365
366void gfx_imm_render_mesh(ImmRenderer* renderer, const Mesh* mesh) {
367 assert(renderer);
368 assert(mesh);
369 assert(mesh->geometry);
370 assert(mesh->material);
371
372 configure_state(renderer);
373 gfx_render_geometry(mesh->geometry);
374}
375
376void gfx_imm_draw_triangles(
377 ImmRenderer* renderer, const vec3 verts[], size_t num_triangles) {
378 assert(renderer);
379 assert(verts);
380 const size_t new_verts = num_triangles * 3;
381 assert(
382 renderer->num_triangle_verts + new_verts < (IMM_MAX_NUM_TRIANGLES * 3));
383
384 memcpy(
385 renderer->triangle_verts + renderer->num_triangle_verts, verts,
386 new_verts * sizeof(vec3));
387
388 renderer->num_triangle_verts += new_verts;
389}
390
391void gfx_imm_draw_triangle(ImmRenderer* renderer, const vec3 verts[3]) {
392 gfx_imm_draw_triangles(renderer, verts, 1);
393}
394
395void gfx_imm_draw_aabb2(ImmRenderer* renderer, aabb2 box) {
396 assert(renderer);
397
398 // clang-format off
399 const vec3 verts[4] = {
400 vec3_make(box.min.x, box.min.y, 0), // 3 ---- 2
401 vec3_make(box.max.x, box.min.y, 0), // | |
402 vec3_make(box.max.x, box.max.y, 0), // | |
403 vec3_make(box.min.x, box.max.y, 0)}; // 0 ---- 1
404 // clang-format on
405
406#define tri(i0, i1, i2) verts[i0], verts[i1], verts[i2]
407 const vec3 tris[6] = {tri(0, 1, 2), tri(0, 2, 3)};
408#undef tri
409
410 gfx_imm_draw_triangles(renderer, tris, 2);
411}
412
413void gfx_imm_draw_aabb3(ImmRenderer* renderer, aabb3 box) {
414 assert(renderer);
415
416 // clang-format off
417 const vec3 vertices[8] = {
418 vec3_make(box.min.x, box.min.y, box.max.z), // 7 ----- 6
419 vec3_make(box.max.x, box.min.y, box.max.z), // / /|
420 vec3_make(box.max.x, box.max.y, box.max.z), // 3 ----- 2 |
421 vec3_make(box.min.x, box.max.y, box.max.z), // | | |
422 vec3_make(box.min.x, box.min.y, box.min.z), // | 4 ----- 5
423 vec3_make(box.max.x, box.min.y, box.min.z), // |/ |/
424 vec3_make(box.max.x, box.max.y, box.min.z), // 0 ----- 1
425 vec3_make(box.min.x, box.max.y, box.min.z)};
426 // clang-format on
427
428 gfx_imm_draw_box3(renderer, vertices);
429}
430
431void gfx_imm_draw_box3(ImmRenderer* renderer, const vec3 vertices[8]) {
432 assert(renderer);
433 assert(vertices);
434
435 // 7 ----- 6
436 // / /|
437 // 3 ----- 2 |
438 // | | |
439 // | 4 ----- 5
440 // |/ |/
441 // 0 ----- 1
442
443#define tri(i0, i1, i2) vertices[i0], vertices[i1], vertices[i2]
444 const vec3 tris[36] = {
445 // Front.
446 tri(0, 1, 2), tri(0, 2, 3),
447 // Right.
448 tri(1, 5, 6), tri(1, 6, 2),
449 // Back.
450 tri(5, 4, 7), tri(5, 7, 6),
451 // Left.
452 tri(4, 0, 03), tri(4, 3, 7),
453 // Top.
454 tri(3, 2, 6), tri(3, 6, 7),
455 // Bottom.
456 tri(0, 4, 5), tri(0, 5, 1)};
457
458 gfx_imm_draw_triangles(renderer, tris, 12);
459}
460
461void gfx_imm_set_colour(ImmRenderer* renderer, vec4 colour) {
462 assert(renderer);
463 assert(renderer->shader);
464
465 gfx_imm_flush(renderer);
466
467 gfx_set_vec4_uniform(renderer->shader, "Colour", colour);
468}
469
470// Load the top of the matrix stack into the shader.
471static void update_shader_model_matrix(ImmRenderer* renderer) {
472 assert(renderer);
473
474 gfx_imm_flush(renderer);
475
476 gfx_set_mat4_uniform(
477 renderer->shader, "Model",
478 &renderer->matrix_stack[renderer->stack_pointer]);
479}
480
481void gfx_imm_load_identity(ImmRenderer* renderer) {
482 assert(renderer);
483
484 renderer->matrix_stack[0] = mat4_id();
485 renderer->stack_pointer = 0;
486 update_shader_model_matrix(renderer);
487}
488
489void gfx_imm_push_matrix(ImmRenderer* renderer, const mat4* matrix) {
490 assert(renderer);
491 assert(matrix);
492 assert(renderer->stack_pointer >= 0);
493 ASSERT(renderer->stack_pointer < IMM_MAX_NUM_MATRICES);
494
495 renderer->stack_pointer += 1;
496 renderer->matrix_stack[renderer->stack_pointer] =
497 mat4_mul(*matrix, renderer->matrix_stack[renderer->stack_pointer - 1]);
498
499 update_shader_model_matrix(renderer);
500}
501
502void gfx_imm_pop_matrix(ImmRenderer* renderer) {
503 assert(renderer);
504 ASSERT(renderer->stack_pointer > 0);
505
506 // For debugging, zero out the matrix stack as matrices are popped out.
507 memset(
508 &renderer->matrix_stack[renderer->stack_pointer], 0,
509 sizeof(renderer->matrix_stack[0]));
510 renderer->stack_pointer -= 1;
511
512 update_shader_model_matrix(renderer);
513}
514
515void gfx_imm_translate(ImmRenderer* renderer, vec3 offset) {
516 assert(renderer);
517
518 const mat4 mat = mat4_translate(offset);
519 gfx_imm_push_matrix(renderer, &mat);
520}
521
522void gfx_imm_set_model_matrix(ImmRenderer* renderer, const mat4* model) {
523 assert(renderer);
524 assert(model);
525
526 gfx_imm_flush(renderer);
527
528 renderer->matrix_stack[0] = *model;
529 renderer->stack_pointer = 0;
530 update_shader_model_matrix(renderer);
531}
532
533void gfx_imm_set_camera(ImmRenderer* renderer, const Camera* camera) {
534 assert(renderer);
535 assert(renderer->shader);
536
537 gfx_imm_flush(renderer);
538
539 const mat4 view = spatial3_inverse_transform(&camera->spatial);
540 // const mat4 view_proj = mat4_mul(camera->projection, view);
541 // gfx_imm_set_view_projection_matrix(renderer, &view_proj);
542 renderer->view = view;
543 renderer->projection = camera->projection;
544 renderer->camera_changed = true;
545}
546
547// void gfx_imm_set_view_projection_matrix(
548// ImmRenderer* renderer, const mat4* view_proj) {
549// assert(renderer);
550// assert(renderer->shader);
551//
552// gfx_imm_flush(renderer);
553// gfx_set_mat4_uniform(renderer->shader, "ViewProjection", view_proj);
554// }
diff --git a/src/llr/llr_impl.h b/src/llr/llr_impl.h
new file mode 100644
index 0000000..5ccabd1
--- /dev/null
+++ b/src/llr/llr_impl.h
@@ -0,0 +1,99 @@
1#pragma once
2
3#include <gfx/llr/llr.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 GfxCore GfxCore;
14typedef struct IBL IBL;
15typedef struct Material Material;
16typedef struct ShaderProgram ShaderProgram;
17typedef struct Texture Texture;
18
19/// Immediate mode renderer.
20///
21/// The renderer caches state changes in memory and only programs the underlying
22/// shader program when a draw call is issued and if anything has changed. This
23/// keeps the number of graphics API calls to a minimum, but requires tracking
24/// state changes. The 'changed' booleans below fulfill this purpose, and
25/// indicate whether a given state has changed since the last draw call.
26///
27/// The renderer must combine state changes accordingly. For example, if only
28/// the lights have changed, then it is sufficient to update light uniforms in
29/// the current shader program. On the other hand, if the shader program has
30/// changed, then the renderer must reconfigure it from scratch and set light
31/// uniforms, camera uniforms, etc.
32///
33/// Note that the shader program API has its own level of caching as well, so
34/// reconfiguration at the level of the renderer does not result in the
35/// worst-case set of graphics API calls.
36///
37/// Currently, the immediate mode renderer can only draw up to a maximum number
38/// of primitives per frame. It does not adjust this number dynamically. Keeps
39/// things simple while the extra complexity is not needed.
40/// TODO: Flush the buffer when it reaches its maximum size to remove this
41/// constraint.
42typedef struct ImmRenderer {
43 GfxCore* gfxcore;
44
45 vec3 camera_position;
46 mat4 view; // Camera view matrix.
47 mat4 projection; // Camera projection matrix.
48 bool camera_changed; // Whether the camera parameters have changed.
49
50 // -------------------------------------------
51 // Immediate-mode rendering of scene elements.
52
53 IBL* ibl;
54 Texture* brdf_integration_map;
55
56 ShaderProgram* shader; // Active shader. Not owned.
57 bool shader_changed; // Whether the shader has changed.
58
59 // Lights are not const because environment lights store lazily-computed
60 // irradiance maps.
61 Light* lights[IMM_MAX_NUM_LIGHTS]; // Lights stack.
62 int num_lights; // Number of lights enabled at a given point in time. It
63 // points to one past the top of the stack.
64 bool lights_changed; // Whether the lights have changed.
65
66 bool skeleton_changed;
67 size_t num_joints;
68 mat4 joint_matrices[GFX_MAX_NUM_JOINTS];
69
70 // ---------------------------------------
71 // Immediate-mode rendering of primitives.
72
73 ShaderProgram* imm_shader; // Immediate-mode shader program for primitives.
74 Geometry* triangles;
75 size_t num_triangle_verts; // Number of triangle verts this frame.
76 // TODO: wireframe rendering.
77 struct {
78 bool wireframe : 1;
79 } flags;
80 vec3 triangle_verts[IMM_MAX_NUM_TRIANGLES * 3];
81
82 // -------------
83 // Matrix stack.
84
85 // The matrix stack contains pre-multiplied matrices.
86 // It is also never empty. The top of the stack is an identity matrix when the
87 // stack is "empty" from the user's perspective.
88 mat4 matrix_stack[IMM_MAX_NUM_MATRICES];
89 int stack_pointer; // Points to the top of the stack.
90} ImmRenderer;
91
92/// Create a new immediate mode renderer.
93bool gfx_imm_make(ImmRenderer*, GfxCore*);
94
95/// Destroy the immediate mode renderer.
96void gfx_imm_destroy(ImmRenderer*);
97
98/// Flush draw commands.
99void gfx_imm_flush(ImmRenderer*);
diff --git a/src/scene/material.c b/src/llr/material.c
index 3248243..47cb45f 100644
--- a/src/scene/material.c
+++ b/src/llr/material.c
@@ -1,6 +1,6 @@
1#include "material_impl.h" 1#include "material_impl.h"
2 2
3#include "scene_memory.h" 3#include "../scene/scene_memory.h"
4 4
5#include <gfx/core.h> 5#include <gfx/core.h>
6 6
diff --git a/src/scene/material_impl.h b/src/llr/material_impl.h
index a6aa95b..f27bf4d 100644
--- a/src/scene/material_impl.h
+++ b/src/llr/material_impl.h
@@ -1,6 +1,6 @@
1#pragma once 1#pragma once
2 2
3#include <gfx/scene/material.h> 3#include <../../include/gfx/llr/material.h>
4 4
5typedef struct ShaderProgram ShaderProgram; 5typedef struct ShaderProgram ShaderProgram;
6 6
diff --git a/src/scene/mesh.c b/src/llr/mesh.c
index 1a93bed..ae75d8c 100644
--- a/src/scene/mesh.c
+++ b/src/llr/mesh.c
@@ -1,6 +1,6 @@
1#include "mesh_impl.h" 1#include "../scene/mesh_impl.h"
2 2
3#include "scene_memory.h" 3#include "../scene/scene_memory.h"
4 4
5#include <assert.h> 5#include <assert.h>
6 6
diff --git a/src/scene/mesh_impl.h b/src/llr/mesh_impl.h
index 560b77e..47ff525 100644
--- a/src/scene/mesh_impl.h
+++ b/src/llr/mesh_impl.h
@@ -1,6 +1,6 @@
1#pragma once 1#pragma once
2 2
3#include <gfx/scene/mesh.h> 3#include <../../include/gfx/llr/mesh.h>
4 4
5typedef struct Mesh { 5typedef struct Mesh {
6 const Geometry* geometry; 6 const Geometry* geometry;