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