#include "light_impl.h" #include "llr_impl.h" #include "mesh_impl.h" #include "llr/material_impl.h" #include "scene/animation_impl.h" #include #include #include static const int IRRADIANCE_MAP_WIDTH = 1024; static const int IRRADIANCE_MAP_HEIGHT = 1024; static const int PREFILTERED_ENVIRONMENT_MAP_WIDTH = 128; static const int PREFILTERED_ENVIRONMENT_MAP_HEIGHT = 128; static const int BRDF_INTEGRATION_MAP_WIDTH = 512; static const int BRDF_INTEGRATION_MAP_HEIGHT = 512; /// Initialize renderer state for IBL. static bool init_ibl(LLR* renderer) { assert(renderer); assert(!renderer->ibl); assert(!renderer->brdf_integration_map); if (!((renderer->ibl = gfx_make_ibl(renderer->gfxcore)))) { return false; } if (!((renderer->brdf_integration_map = gfx_make_brdf_integration_map( renderer->ibl, renderer->gfxcore, BRDF_INTEGRATION_MAP_WIDTH, BRDF_INTEGRATION_MAP_HEIGHT)))) { return false; } return true; } // TODO: Why is this done lazily here? Do it when the environment light is // created. // /// Compute irradiance and prefiltered environment maps for the light if they /// have not been already computed. static bool set_up_environment_light(LLR* renderer, EnvironmentLight* light) { assert(renderer); assert(light); assert(renderer->ibl); assert(renderer->brdf_integration_map); if (light->irradiance_map) { assert(light->prefiltered_environment_map); return true; } // For convenience. GfxCore* gfxcore = renderer->gfxcore; Texture* irradiance_map = 0; Texture* prefiltered_environment_map = 0; if (!((irradiance_map = gfx_make_irradiance_map( renderer->ibl, gfxcore, light->environment_map, IRRADIANCE_MAP_WIDTH, IRRADIANCE_MAP_HEIGHT)))) { goto cleanup; } int max_mip_level = 0; if (!((prefiltered_environment_map = gfx_make_prefiltered_environment_map( renderer->ibl, gfxcore, light->environment_map, PREFILTERED_ENVIRONMENT_MAP_WIDTH, PREFILTERED_ENVIRONMENT_MAP_HEIGHT, &max_mip_level)))) { goto cleanup; } light->irradiance_map = irradiance_map; light->prefiltered_environment_map = prefiltered_environment_map; light->max_reflection_lod = max_mip_level; return true; cleanup: if (irradiance_map) { gfx_destroy_texture(gfxcore, &irradiance_map); } if (prefiltered_environment_map) { gfx_destroy_texture(gfxcore, &prefiltered_environment_map); } return false; } static void configure_light(LLR* renderer, Light* light) { assert(renderer); assert(light); // For convenience. ShaderProgram* const shader = renderer->shader; switch (light->type) { case EnvironmentLightType: { EnvironmentLight* env = &light->environment; const bool initialized = set_up_environment_light(renderer, env); ASSERT(initialized); assert(env->environment_map); assert(env->irradiance_map); assert(env->prefiltered_environment_map); assert(renderer->brdf_integration_map); gfx_set_texture_uniform( shader, "BRDFIntegrationMap", renderer->brdf_integration_map); gfx_set_texture_uniform(shader, "Sky", env->environment_map); gfx_set_texture_uniform(shader, "IrradianceMap", env->irradiance_map); gfx_set_texture_uniform( shader, "PrefilteredEnvironmentMap", env->prefiltered_environment_map); gfx_set_float_uniform( shader, "MaxReflectionLOD", (float)env->max_reflection_lod); break; } default: assert(false); // TODO: Implement other light types. break; } } static void configure_state(LLR* renderer) { assert(renderer); // Check if anything changed first so that we don't call gfx_apply_uniforms() // unnecessarily. const bool nothing_changed = (renderer->changed_flags == 0); if (nothing_changed) { return; } // Setting a null shader is also allowed, in which case there is nothing to // configure. if (renderer->shader == 0) { renderer->shader_changed = false; return; } // For convenience. ShaderProgram* const shader = renderer->shader; const mat4* const model = &renderer->matrix_stack[renderer->stack_pointer]; // TODO: Check to see which ones the shader actually uses and avoid // computing the unnecessary matrices. if (renderer->matrix_changed || renderer->shader_changed) { renderer->matrix_changed = false; gfx_set_mat4_uniform(shader, "Model", model); gfx_set_mat4_uniform(shader, "ModelMatrix", model); } // TODO: camera_changed is not set anywhere. Need to think how imm primitive // rendering and imm mesh rendering work together. We could treat imm // primitive calls like setting a new shader. if (renderer->camera_changed || renderer->shader_changed) { renderer->camera_changed = false; // Set all supported camera-related uniforms. Shaders can choose which ones // to use. const mat4 modelview = mat4_mul(renderer->view, *model); const mat4 view_proj = mat4_mul(renderer->projection, renderer->view); const mat4 mvp = mat4_mul(renderer->projection, modelview); gfx_set_mat4_uniform(shader, "Modelview", &modelview); gfx_set_mat4_uniform(shader, "View", &renderer->view); gfx_set_mat4_uniform(shader, "Projection", &renderer->projection); gfx_set_mat4_uniform(shader, "ViewProjection", &view_proj); gfx_set_mat4_uniform(shader, "MVP", &mvp); gfx_set_vec3_uniform(shader, "CameraPosition", renderer->camera_position); gfx_set_mat4_uniform(shader, "CameraRotation", &renderer->camera_rotation); gfx_set_float_uniform(shader, "Fovy", renderer->fovy); gfx_set_float_uniform(shader, "Aspect", renderer->aspect); } if (renderer->lights_changed || renderer->shader_changed) { renderer->lights_changed = false; // TODO: Could do better by only setting the lights that have actually // changed. // TODO: Will also need to pass the number of lights to the shader once the // other light types are implemented. for (int i = 0; i < renderer->num_lights; ++i) { configure_light(renderer, renderer->lights[i]); } } if (renderer->skeleton_changed || renderer->shader_changed) { renderer->skeleton_changed = false; if (renderer->num_joints > 0) { gfx_set_mat4_array_uniform( shader, "JointMatrices", renderer->joint_matrices, renderer->num_joints); } } if (renderer->shader_changed) { renderer->shader_changed = false; gfx_activate_shader_program(renderer->shader); } // Must be called after activating the program. gfx_apply_uniforms(renderer->shader); } bool gfx_llr_make(LLR* renderer, GfxCore* gfxcore) { assert(renderer); assert(gfxcore); renderer->gfxcore = gfxcore; if (!init_ibl(renderer)) { goto cleanup; } gfx_llr_load_identity(renderer); renderer->view = mat4_id(); renderer->projection = mat4_id(); renderer->camera_rotation = mat4_id(); return true; cleanup: gfx_llr_destroy(renderer); return false; } void gfx_llr_destroy(LLR* renderer) { assert(renderer); assert(renderer->gfxcore); if (renderer->brdf_integration_map) { gfx_destroy_texture(renderer->gfxcore, &renderer->brdf_integration_map); } // TODO: Do this once the IBL from the scene renderer is gone. if (renderer->ibl) { // gfx_destroy_ibl(renderer->gfxcore, &renderer->ibl); } } void gfx_llr_set_shader(LLR* renderer, ShaderProgram* shader) { assert(renderer); // null shader is allowed, so do not assert it. // It's important to not set shader_changed unnecessarily, since that would // re-trigger the setting of uniforms. if (renderer->shader != shader) { renderer->shader = shader; renderer->shader_changed = true; } } void gfx_llr_push_light(LLR* renderer, Light* light) { assert(renderer); assert(light); assert(renderer->num_lights >= 0); ASSERT(renderer->num_lights < GFX_LLR_MAX_NUM_LIGHTS); renderer->lights[renderer->num_lights++] = light; renderer->lights_changed = true; } void gfx_llr_pop_light(LLR* renderer) { assert(renderer); ASSERT(renderer->num_lights > 0); renderer->lights[--renderer->num_lights] = 0; renderer->lights_changed = true; } void gfx_llr_set_skeleton( LLR* renderer, const Anima* anima, const Skeleton* skeleton) { assert(renderer); assert(anima); assert(skeleton); assert(skeleton->num_joints <= GFX_MAX_NUM_JOINTS); for (size_t i = 0; i < skeleton->num_joints; ++i) { const joint_idx joint_index = skeleton->joints[i]; const Joint* joint = &anima->joints[joint_index]; renderer->joint_matrices[i] = joint->joint_matrix; } renderer->num_joints = skeleton->num_joints; renderer->skeleton_changed = true; } void gfx_llr_clear_skeleton(LLR* renderer) { assert(renderer); renderer->num_joints = 0; renderer->skeleton_changed = true; } void gfx_llr_set_camera(LLR* renderer, const Camera* camera) { assert(renderer); renderer->camera_position = camera->spatial.p; renderer->camera_rotation = mat4_rotation(spatial3_transform(&camera->spatial)); renderer->view = spatial3_inverse_transform(&camera->spatial); renderer->projection = camera->projection; // Assuming a perspective matrix. renderer->fovy = (R)atan(1.0 / (mat4_at(camera->projection, 1, 1))) * 2; renderer->camera_changed = true; } void gfx_llr_set_aspect(LLR* renderer, float aspect) { assert(renderer); renderer->aspect = aspect; renderer->camera_changed = true; } void gfx_llr_render_geometry(LLR* renderer, const Geometry* geometry) { assert(renderer); assert(geometry); configure_state(renderer); gfx_render_geometry(geometry); } void gfx_llr_render_mesh(LLR* renderer, const Mesh* mesh) { assert(renderer); assert(renderer->shader); assert(mesh); assert(mesh->geometry); assert(mesh->material); gfx_material_activate(renderer->shader, mesh->material); gfx_llr_render_geometry(renderer, mesh->geometry); } void gfx_llr_load_identity(LLR* renderer) { assert(renderer); renderer->matrix_stack[0] = mat4_id(); renderer->stack_pointer = 0; renderer->matrix_changed = true; } void gfx_llr_push_matrix(LLR* renderer, const mat4* matrix) { assert(renderer); assert(matrix); assert(renderer->stack_pointer >= 0); ASSERT(renderer->stack_pointer < GFX_LLR_MAX_NUM_MATRICES); renderer->stack_pointer += 1; renderer->matrix_stack[renderer->stack_pointer] = mat4_mul(*matrix, renderer->matrix_stack[renderer->stack_pointer - 1]); renderer->matrix_changed = true; } void gfx_llr_pop_matrix(LLR* renderer) { assert(renderer); ASSERT(renderer->stack_pointer > 0); // For debugging, zero out the matrix stack as matrices are popped out. memset( &renderer->matrix_stack[renderer->stack_pointer], 0, sizeof(renderer->matrix_stack[0])); renderer->stack_pointer -= 1; renderer->matrix_changed = true; } void gfx_llr_translate(LLR* renderer, vec3 offset) { assert(renderer); const mat4 mat = mat4_translate(offset); gfx_llr_push_matrix(renderer, &mat); } void gfx_llr_set_model_matrix(LLR* renderer, const mat4* model) { assert(renderer); assert(model); renderer->matrix_stack[0] = *model; renderer->stack_pointer = 0; renderer->matrix_changed = true; }