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