aboutsummaryrefslogtreecommitdiff
path: root/src/llr/llr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/llr/llr.c')
-rw-r--r--src/llr/llr.c406
1 files changed, 406 insertions, 0 deletions
diff --git a/src/llr/llr.c b/src/llr/llr.c
new file mode 100644
index 0000000..fe02c0d
--- /dev/null
+++ b/src/llr/llr.c
@@ -0,0 +1,406 @@
1#include "light_impl.h"
2#include "llr_impl.h"
3#include "mesh_impl.h"
4
5#include "llr/material_impl.h"
6#include "scene/animation_impl.h"
7
8#include <gfx/core.h>
9#include <gfx/util/ibl.h>
10
11#include <cassert.h>
12
13static const int IRRADIANCE_MAP_WIDTH = 1024;
14static const int IRRADIANCE_MAP_HEIGHT = 1024;
15static const int PREFILTERED_ENVIRONMENT_MAP_WIDTH = 128;
16static const int PREFILTERED_ENVIRONMENT_MAP_HEIGHT = 128;
17static const int BRDF_INTEGRATION_MAP_WIDTH = 512;
18static const int BRDF_INTEGRATION_MAP_HEIGHT = 512;
19
20/// Initialize renderer state for IBL.
21static bool init_ibl(LLR* renderer) {
22 assert(renderer);
23 assert(!renderer->ibl);
24 assert(!renderer->brdf_integration_map);
25
26 if (!((renderer->ibl = gfx_make_ibl(renderer->gfxcore)))) {
27 return false;
28 }
29
30 if (!((renderer->brdf_integration_map = gfx_make_brdf_integration_map(
31 renderer->ibl, renderer->gfxcore, BRDF_INTEGRATION_MAP_WIDTH,
32 BRDF_INTEGRATION_MAP_HEIGHT)))) {
33 return false;
34 }
35
36 return true;
37}
38
39/// Compute irradiance and prefiltered environment maps for the light if they
40/// have not been already computed.
41///
42/// This is done lazily here, and not when the light is created, because we
43/// need an IBL instance to do this and it is more convenient for the public
44/// API to create lights without worrying about those details. It also makes the
45/// public API cheaper, since the maps are only computed when they are actually
46/// needed.
47static bool set_up_environment_light(LLR* renderer, EnvironmentLight* light) {
48 assert(renderer);
49 assert(light);
50 assert(renderer->ibl);
51 assert(renderer->brdf_integration_map);
52
53 if (light->irradiance_map) {
54 assert(light->prefiltered_environment_map);
55 return true;
56 }
57
58 // For convenience.
59 GfxCore* gfxcore = renderer->gfxcore;
60
61 Texture* irradiance_map = 0;
62 Texture* prefiltered_environment_map = 0;
63
64 if (!((irradiance_map = gfx_make_irradiance_map(
65 renderer->ibl, gfxcore, light->environment_map,
66 IRRADIANCE_MAP_WIDTH, IRRADIANCE_MAP_HEIGHT)))) {
67 goto cleanup;
68 }
69
70 int max_mip_level = 0;
71 if (!((prefiltered_environment_map = gfx_make_prefiltered_environment_map(
72 renderer->ibl, gfxcore, light->environment_map,
73 PREFILTERED_ENVIRONMENT_MAP_WIDTH,
74 PREFILTERED_ENVIRONMENT_MAP_HEIGHT, &max_mip_level)))) {
75 goto cleanup;
76 }
77
78 light->irradiance_map = irradiance_map;
79 light->prefiltered_environment_map = prefiltered_environment_map;
80 light->max_reflection_lod = max_mip_level;
81
82 return true;
83
84cleanup:
85 if (irradiance_map) {
86 gfx_destroy_texture(gfxcore, &irradiance_map);
87 }
88 if (prefiltered_environment_map) {
89 gfx_destroy_texture(gfxcore, &prefiltered_environment_map);
90 }
91 return false;
92}
93
94static void configure_light(LLR* renderer, Light* light) {
95 assert(renderer);
96 assert(light);
97
98 // For convenience.
99 ShaderProgram* const shader = renderer->shader;
100
101 switch (light->type) {
102 case EnvironmentLightType: {
103 EnvironmentLight* env = &light->environment;
104
105 const bool initialized = set_up_environment_light(renderer, env);
106 ASSERT(initialized);
107 assert(env->environment_map);
108 assert(env->irradiance_map);
109 assert(env->prefiltered_environment_map);
110 assert(renderer->brdf_integration_map);
111
112 gfx_set_texture_uniform(
113 shader, "BRDFIntegrationMap", renderer->brdf_integration_map);
114 gfx_set_texture_uniform(shader, "Sky", env->environment_map);
115 gfx_set_texture_uniform(shader, "IrradianceMap", env->irradiance_map);
116 gfx_set_texture_uniform(
117 shader, "PrefilteredEnvironmentMap", env->prefiltered_environment_map);
118 gfx_set_float_uniform(
119 shader, "MaxReflectionLOD", (float)env->max_reflection_lod);
120
121 break;
122 }
123 default:
124 assert(false); // TODO: Implement other light types.
125 break;
126 }
127}
128
129static void configure_state(LLR* renderer) {
130 assert(renderer);
131
132 // Check if anything changed first so that we don't call gfx_apply_uniforms()
133 // unnecessarily.
134 const bool nothing_changed = (renderer->changed_flags == 0);
135 if (nothing_changed) {
136 return;
137 }
138 // Setting a null shader is also allowed, in which case there is nothing to
139 // configure.
140 if (renderer->shader == 0) {
141 renderer->shader_changed = false;
142 return;
143 }
144
145 // For convenience.
146 ShaderProgram* const shader = renderer->shader;
147 const mat4* const model = &renderer->matrix_stack[renderer->stack_pointer];
148
149 // TODO: Check to see which ones the shader actually uses and avoid
150 // computing the unnecessary matrices.
151
152 if (renderer->matrix_changed || renderer->shader_changed) {
153 renderer->matrix_changed = false;
154
155 gfx_set_mat4_uniform(shader, "Model", model);
156 gfx_set_mat4_uniform(shader, "ModelMatrix", model);
157 }
158
159 // TODO: camera_changed is not set anywhere. Need to think how imm primitive
160 // rendering and imm mesh rendering work together. We could treat imm
161 // primitive calls like setting a new shader.
162 if (renderer->camera_changed || renderer->shader_changed) {
163 renderer->camera_changed = false;
164
165 // Set all supported camera-related uniforms. Shaders can choose which ones
166 // to use.
167 const mat4 modelview = mat4_mul(renderer->view, *model);
168 const mat4 view_proj = mat4_mul(renderer->projection, renderer->view);
169 const mat4 mvp = mat4_mul(renderer->projection, modelview);
170
171 gfx_set_mat4_uniform(shader, "Modelview", &modelview);
172 gfx_set_mat4_uniform(shader, "View", &renderer->view);
173 gfx_set_mat4_uniform(shader, "Projection", &renderer->projection);
174 gfx_set_mat4_uniform(shader, "ViewProjection", &view_proj);
175 gfx_set_mat4_uniform(shader, "MVP", &mvp);
176 gfx_set_vec3_uniform(shader, "CameraPosition", renderer->camera_position);
177 gfx_set_mat4_uniform(shader, "CameraRotation", &renderer->camera_rotation);
178 gfx_set_float_uniform(shader, "Fovy", renderer->fovy);
179 gfx_set_float_uniform(shader, "Aspect", renderer->aspect);
180 }
181
182 if (renderer->lights_changed || renderer->shader_changed) {
183 renderer->lights_changed = false;
184
185 // TODO: Could do better by only setting the lights that have actually
186 // changed.
187 // TODO: Will also need to pass the number of lights to the shader once the
188 // other light types are implemented.
189 for (int i = 0; i < renderer->num_lights; ++i) {
190 configure_light(renderer, renderer->lights[i]);
191 }
192 }
193
194 if (renderer->skeleton_changed || renderer->shader_changed) {
195 renderer->skeleton_changed = false;
196
197 if (renderer->num_joints > 0) {
198 gfx_set_mat4_array_uniform(
199 shader, "JointMatrices", renderer->joint_matrices,
200 renderer->num_joints);
201 }
202 }
203
204 if (renderer->material_changed || renderer->shader_changed) {
205 renderer->material_changed = false;
206
207 gfx_material_activate(renderer->shader, renderer->material);
208 }
209
210 if (renderer->shader_changed) {
211 renderer->shader_changed = false;
212 gfx_activate_shader_program(renderer->shader);
213 }
214
215 // TODO: At present, this results in many redundant calls to
216 // glGetUniformLocation() and glUniformXyz(). Look at the trace.
217 //
218 // TODO: Could add to qapitrace functionality to detect redundant calls and
219 // other inefficiencies. Maybe ask in the Github first if there would be
220 // interest in this.
221 //
222 // Must be called after activating the program.
223 gfx_apply_uniforms(renderer->shader);
224}
225
226bool gfx_llr_make(LLR* renderer, GfxCore* gfxcore) {
227 assert(renderer);
228 assert(gfxcore);
229
230 renderer->gfxcore = gfxcore;
231 if (!init_ibl(renderer)) {
232 goto cleanup;
233 }
234 gfx_llr_load_identity(renderer);
235 renderer->view = mat4_id();
236 renderer->projection = mat4_id();
237 renderer->camera_rotation = mat4_id();
238 return true;
239
240cleanup:
241 gfx_llr_destroy(renderer);
242 return false;
243}
244
245void gfx_llr_destroy(LLR* renderer) {
246 assert(renderer);
247 assert(renderer->gfxcore);
248
249 if (renderer->brdf_integration_map) {
250 gfx_destroy_texture(renderer->gfxcore, &renderer->brdf_integration_map);
251 }
252
253 // TODO: Do this once the IBL from the scene renderer is gone.
254 if (renderer->ibl) {
255 // gfx_destroy_ibl(renderer->gfxcore, &renderer->ibl);
256 }
257}
258
259void gfx_llr_set_shader(LLR* renderer, ShaderProgram* shader) {
260 assert(renderer);
261 // null shader is allowed, so do not assert it.
262
263 // It's important to not set shader_changed unnecessarily, since that would
264 // re-trigger the setting of uniforms.
265 if (renderer->shader != shader) {
266 renderer->shader = shader;
267 renderer->shader_changed = true;
268 }
269}
270
271void gfx_llr_push_light(LLR* renderer, Light* light) {
272 assert(renderer);
273 assert(light);
274 assert(renderer->num_lights >= 0);
275 ASSERT(renderer->num_lights < GFX_LLR_MAX_NUM_LIGHTS);
276
277 renderer->lights[renderer->num_lights++] = light;
278 renderer->lights_changed = true;
279}
280
281void gfx_llr_pop_light(LLR* renderer) {
282 assert(renderer);
283 ASSERT(renderer->num_lights > 0);
284
285 renderer->lights[--renderer->num_lights] = 0;
286 renderer->lights_changed = true;
287}
288
289void gfx_llr_set_skeleton(
290 LLR* renderer, const Anima* anima, const Skeleton* skeleton) {
291 assert(renderer);
292 assert(anima);
293 assert(skeleton);
294 assert(skeleton->num_joints <= GFX_MAX_NUM_JOINTS);
295
296 for (size_t i = 0; i < skeleton->num_joints; ++i) {
297 const joint_idx joint_index = skeleton->joints[i];
298 const Joint* joint = &anima->joints[joint_index];
299 renderer->joint_matrices[i] = joint->joint_matrix;
300 }
301 renderer->num_joints = skeleton->num_joints;
302 renderer->skeleton_changed = true;
303}
304
305void gfx_llr_clear_skeleton(LLR* renderer) {
306 assert(renderer);
307
308 renderer->num_joints = 0;
309 renderer->skeleton_changed = true;
310}
311
312void gfx_llr_set_material(LLR* renderer, const Material* material) {
313 assert(renderer);
314 assert(material);
315
316 renderer->material = material;
317 renderer->material_changed = true;
318}
319
320void gfx_llr_set_camera(LLR* renderer, const Camera* camera) {
321 assert(renderer);
322
323 renderer->camera_position = camera->spatial.p;
324 renderer->camera_rotation =
325 mat4_rotation(spatial3_transform(&camera->spatial));
326 renderer->view = spatial3_inverse_transform(&camera->spatial);
327 renderer->projection = camera->projection;
328 // Assuming a perspective matrix.
329 renderer->fovy = (R)atan(1.0 / (mat4_at(camera->projection, 1, 1))) * 2;
330 renderer->camera_changed = true;
331}
332
333void gfx_llr_set_aspect(LLR* renderer, float aspect) {
334 assert(renderer);
335
336 renderer->aspect = aspect;
337 renderer->camera_changed = true;
338}
339
340void gfx_llr_render_geometry(LLR* renderer, const Geometry* geometry) {
341 assert(renderer);
342 assert(geometry);
343
344 configure_state(renderer);
345 gfx_render_geometry(geometry);
346}
347
348void gfx_llr_render_mesh(LLR* renderer, const Mesh* mesh) {
349 assert(renderer);
350 assert(mesh);
351 assert(mesh->geometry);
352 assert(mesh->material);
353 assert(mesh->shader);
354
355 gfx_llr_set_material(renderer, mesh->material);
356 gfx_llr_set_shader(renderer, mesh->shader);
357 gfx_llr_render_geometry(renderer, mesh->geometry);
358}
359
360void gfx_llr_load_identity(LLR* renderer) {
361 assert(renderer);
362
363 renderer->matrix_stack[0] = mat4_id();
364 renderer->stack_pointer = 0;
365 renderer->matrix_changed = true;
366}
367
368void gfx_llr_push_matrix(LLR* renderer, const mat4* matrix) {
369 assert(renderer);
370 assert(matrix);
371 assert(renderer->stack_pointer >= 0);
372 ASSERT(renderer->stack_pointer < GFX_LLR_MAX_NUM_MATRICES);
373
374 renderer->stack_pointer += 1;
375 renderer->matrix_stack[renderer->stack_pointer] =
376 mat4_mul(*matrix, renderer->matrix_stack[renderer->stack_pointer - 1]);
377 renderer->matrix_changed = true;
378}
379
380void gfx_llr_pop_matrix(LLR* renderer) {
381 assert(renderer);
382 ASSERT(renderer->stack_pointer > 0);
383
384 // For debugging, zero out the matrix stack as matrices are popped out.
385 memset(
386 &renderer->matrix_stack[renderer->stack_pointer], 0,
387 sizeof(renderer->matrix_stack[0]));
388 renderer->stack_pointer -= 1;
389 renderer->matrix_changed = true;
390}
391
392void gfx_llr_translate(LLR* renderer, vec3 offset) {
393 assert(renderer);
394
395 const mat4 mat = mat4_translate(offset);
396 gfx_llr_push_matrix(renderer, &mat);
397}
398
399void gfx_llr_set_model_matrix(LLR* renderer, const mat4* model) {
400 assert(renderer);
401 assert(model);
402
403 renderer->matrix_stack[0] = *model;
404 renderer->stack_pointer = 0;
405 renderer->matrix_changed = true;
406}