217 lines
7.8 KiB
C++
217 lines
7.8 KiB
C++
#include <iostream>
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <glad/gl.h>
|
|
|
|
#include "renderer.hpp"
|
|
|
|
namespace Fan
|
|
{
|
|
|
|
Rc<UniformBuffer> Renderer::s_camera_ubo = nullptr;
|
|
Rc<UniformBuffer> 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<Shader*, std::unordered_map<Material*, std::vector<DrawCommand>>>;
|
|
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<Shader>& debug_shader,
|
|
const Rc<Mesh>& 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<int>(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<int>(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
|