#include #include #include #include #include #include "renderer.hpp" namespace Fan { Rc Renderer::s_camera_ubo = nullptr; Rc Renderer::s_light_ubo = nullptr; void Renderer::init() { glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glFrontFace(GL_CCW); using bp = Shader::BindingPoint; s_camera_ubo = UniformBuffer::create(sizeof(CameraData), bp::Camera); s_light_ubo = UniformBuffer::create(sizeof(LightEnvironmentData), bp::LightEnvironment); } void Renderer::set_viewport(int x, int y, int width, int height) { glViewport(x, y, width, height); } void Renderer::set_clear_color(const glm::vec4& color) { glClearColor(color.r, color.g, color.b, color.a); } void Renderer::set_wireframe(bool enabled) { glPolygonMode(GL_FRONT_AND_BACK, enabled ? GL_LINE : GL_FILL); } void Renderer::clear() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } void Renderer::draw_scene(const Scene& scene, const Camera& camera, int screen_width, int screen_height) { update_global_data(scene, camera, screen_width, screen_height); struct DrawCommand { const Transform* transform; const Mesh* mesh; GLuint index_offset; GLuint index_count; }; // Sort Entities: Shader -> Material -> SubMesh Draw Commands using RenderQueue = std::unordered_map>>; RenderQueue render_queue; for (const auto& entity : scene.get_entities()) { if (!entity.mesh) { continue; } // If Entity has an explicit material override if (entity.material) { if (!entity.material->shader) { continue; } Shader* shader_ptr = entity.material->shader.get(); Material* material_ptr = entity.material.get(); for (const auto& submesh : entity.mesh->get_submeshes()) { render_queue[shader_ptr][material_ptr].push_back({ &entity.transform, entity.mesh.get(), submesh.index_offset, submesh.index_count }); } // Otherwise use SubMesh's native material } else { for (const auto& submesh : entity.mesh->get_submeshes()) { if (!submesh.material || !submesh.material->shader) { continue; } Shader* shader_ptr = submesh.material->shader.get(); Material* material_ptr = submesh.material.get(); render_queue[shader_ptr][material_ptr].push_back({ &entity.transform, entity.mesh.get(), submesh.index_offset, submesh.index_count }); } } } // Execute sorted queue for minimal state switching for (const auto& [shader, materials] : render_queue) { shader->bind(); for (const auto& [material, draw_command] : materials) { material->bind(); for (const auto& cmd: draw_command) { shader->set_mat4("u_model", cmd.transform->get_model_matrix()); cmd.mesh->draw_submesh(cmd.index_offset, cmd.index_count); } } } } void Renderer::draw_debug_wireframes(const Scene& scene, const Camera& camera, int screen_width, int screen_height, const Rc& debug_shader, const Rc& default_mesh) { debug_shader->bind(); debug_shader->set_mat4("u_view_projection", camera.get_view_projection_matrix(screen_width, screen_height)); set_wireframe(true); // Draw all standard entities (green) debug_shader->set_vec3("u_color", glm::vec3(0.0f, 0.7f, 0.0f)); for (const Entity& entity : scene.get_entities()) { // If an entity has no mesh, use the default debug mesh auto mesh = entity.mesh ? entity.mesh : default_mesh; debug_shader->set_mat4("u_model", entity.transform.get_model_matrix()); mesh->draw(); } // Draw point lights (yellow) debug_shader->set_vec3("u_color", glm::vec3(0.7f, 0.7f, 0.0f)); for (const PointLight& light : scene.light_environment.point_lights) { glm::mat4 model = glm::translate(glm::mat4(1.0f), light.position); model = glm::scale(model, glm::vec3(0.2f)); debug_shader->set_mat4("u_model", model); default_mesh->draw(); } // Draw spot lights (cyan) debug_shader->set_vec3("u_color", glm::vec3(0.0f, 0.7f, 0.7f)); for (const SpotLight& light : scene.light_environment.spot_lights) { glm::mat4 model = glm::translate(glm::mat4(1.0f), light.position); model = glm::scale(model, glm::vec3(0.2f)); debug_shader->set_mat4("u_model", model); default_mesh->draw(); } // Restore state set_wireframe(false); } void Renderer::update_global_data(const Scene& scene, const Camera& camera, int screen_width, int screen_height) { // Update camera UBO CameraData cam_data{}; cam_data.view = camera.get_view_matrix(); cam_data.projection = camera.get_projection_matrix(screen_width, screen_height); s_camera_ubo->update(&cam_data, sizeof(CameraData)); // Update light UBO LightEnvironmentData light_data{}; const LightEnvironment& env = scene.light_environment; // Map DirectionalLight const auto& dir_src = env.directional_light; light_data.dir_light.direction = glm::vec4(dir_src.direction, 0.0f); light_data.dir_light.ambient = glm::vec4(dir_src.ambient, 0.0f); light_data.dir_light.diffuse = glm::vec4(dir_src.diffuse, 0.0f); light_data.dir_light.specular = glm::vec4(dir_src.specular, 0.0f); // Map PointLights if (light_data.active_point_lights > MAX_POINT_LIGHTS) { std::cerr << "WARNING: RENDERER: Maximum number of point lights reached" << " (" << MAX_POINT_LIGHTS << ")" << std::endl; } light_data.active_point_lights = std::min(static_cast(env.point_lights.size()), MAX_POINT_LIGHTS); for (int i = 0; i < light_data.active_point_lights; ++i) { const auto& src = env.point_lights[i]; light_data.point_lights[i].position = glm::vec4(src.position, 1.0f); light_data.point_lights[i].ambient = glm::vec4(src.ambient, 1.0f); light_data.point_lights[i].diffuse = glm::vec4(src.diffuse, 1.0f); light_data.point_lights[i].specular = glm::vec4(src.specular, 1.0f); light_data.point_lights[i].attenuation = glm::vec4(src.constant, src.linear, src.quadratic, 0.0f); } // Map SpotLights if (light_data.active_spot_lights > MAX_SPOT_LIGHTS) { std::cerr << "WARNING: RENDERER: Maximum number of spot lights reached" << " (" << MAX_SPOT_LIGHTS << ")" << std::endl; } light_data.active_spot_lights = std::min(static_cast(env.spot_lights.size()), MAX_SPOT_LIGHTS); for (int i = 0; i < light_data.active_spot_lights; ++i) { const auto& src = env.spot_lights[i]; light_data.spot_lights[i].position = glm::vec4(src.position, 1.0f); light_data.spot_lights[i].direction = glm::vec4(src.direction, 0.0f); light_data.spot_lights[i].ambient = glm::vec4(src.ambient, 1.0f); light_data.spot_lights[i].diffuse = glm::vec4(src.diffuse, 1.0f); light_data.spot_lights[i].specular = glm::vec4(src.specular, 1.0f); light_data.spot_lights[i].attenuation = glm::vec4(src.constant, src.linear, src.quadratic, 0.0f); light_data.spot_lights[i].cut_offs = glm::vec4(src.cut_off, src.outer_cut_off, 0.0f, 0.0f); } s_light_ubo->update(&light_data, sizeof(LightEnvironmentData)); } } // namespace Fan