9#include "hellfire/core/Application.h"
10#include "hellfire/core/Time.h"
11#include "hellfire/ecs/CameraComponent.h"
12#include "hellfire/ecs/InstancedRenderableComponent.h"
13#include "hellfire/ecs/LightComponent.h"
14#include "hellfire/ecs/components/MeshComponent.h"
15#include "hellfire/graphics/renderer/SkyboxRenderer.h"
16#include "hellfire/scene/Scene.h"
22 context_ = std::make_unique<OGLRendererContext>();
23 context_->shader_handle = 0;
28 glEnable(GL_DEBUG_OUTPUT);
29 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
30 glDebugMessageCallback([](GLenum source, GLenum type, GLuint id,
31 GLenum severity, GLsizei length,
32 const GLchar *message,
const void *userParam) {
33 if (type == GL_DEBUG_TYPE_ERROR) {
34 std::cerr <<
"GL ERROR: " << message << std::endl;
41 shadow_material_ = MaterialBuilder::create_custom(
"Shadow Material",
"assets/shaders/shadow.vert",
42 "assets/shaders/shadow.frag");
48 const Entity *camera_entity = camera_override;
55 std::cerr <<
"No active camera in scene!" << std::endl;
59 const auto camera_comp = camera_entity->get_component<
CameraComponent>();
62 std::cerr <<
"Camera entity missing CameraComponent" << std::endl;
69 glClearColor(0.1f, 0.2f, 0.3f, 1.0f);
70 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
74 opaque_objects_.clear();
75 transparent_objects_.clear();
76 opaque_instanced_objects_.clear();
77 transparent_instanced_objects_.clear();
83 glEnable(GL_DEPTH_TEST);
95 if (!context_)
return;
98 std::vector<Entity *> directional_lights;
99 std::vector<Entity *> point_lights;
101 for (Entity *entity: light_entities) {
102 const auto *light = entity->get_component<LightComponent>();
103 if (!light)
continue;
106 switch (light->get_light_type()) {
107 case LightComponent::LightType::DIRECTIONAL:
108 if (directional_lights.size() < 4) {
109 directional_lights.push_back(entity);
112 case LightComponent::LightType::POINT:
113 if (point_lights.size() < 8) {
114 point_lights.push_back(entity);
117 case LightComponent::LightType::SPOT:
124 context_->num_directional_lights =
static_cast<
int>(directional_lights.size());
125 context_->num_point_lights =
static_cast<
int>(point_lights.size());
128 for (
int i = 0; i < context_->num_directional_lights; i++) {
129 context_->directional_light_entities[i] = directional_lights[i];
132 for (
int i = 0; i < context_->num_point_lights; i++) {
133 context_->point_light_entities[i] = point_lights[i];
137 context_->camera_component = &camera;
141 for (
const EntityID root_id: scene.get_root_entities()) {
142 collect_render_commands_recursive(root_id, camera_pos);
151 const auto *renderable = entity->get_component<RenderableComponent>();
152 const auto *mesh_comp = entity->get_component<MeshComponent>();
156 if (renderable && mesh_comp && transform) {
157 const auto mesh = mesh_comp->get_mesh();
158 const auto material = renderable->get_material();
160 if (mesh && material) {
161 const glm::vec3 object_pos = transform->get_world_position();
162 const float distance = glm::length(camera_pos - object_pos);
163 const bool is_transparent = material->is_transparent();
165 const RenderCommand cmd = {entity_id, mesh, material, distance, is_transparent};
167 if (is_transparent) {
168 transparent_objects_.push_back(cmd);
170 opaque_objects_.push_back(cmd);
176 if (
auto *instanced = entity->get_component<InstancedRenderableComponent>()) {
177 if (transform && instanced->has_mesh() && instanced->get_instance_count() > 0) {
178 if (
const auto material = instanced->get_material()) {
179 const glm::vec3 object_pos = transform->get_world_position();
180 const float distance = glm::length(camera_pos - object_pos);
181 const bool is_transparent = material->is_transparent();
185 if (is_transparent) {
186 transparent_instanced_objects_.push_back(cmd);
188 opaque_instanced_objects_.push_back(cmd);
195 for (
const EntityID child_id: scene_->get_children(entity_id)) {
196 collect_render_commands_recursive(child_id, camera_pos);
201 if (!shadow_maps_.contains(light_entity)) {
207 settings.min_filter = GL_NEAREST;
208 settings.mag_filter = GL_NEAREST;
209 settings.wrap_s = GL_CLAMP_TO_BORDER;
210 settings.wrap_t = GL_CLAMP_TO_BORDER;
211 shadow_map->attach_depth_texture(settings);
213 glBindTexture(GL_TEXTURE_2D, shadow_map->get_depth_attachment());
214 const float border_color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
215 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color);
216 glBindTexture(GL_TEXTURE_2D, 0);
217 shadow_maps_[light_entity] = {std::move(shadow_map), glm::mat4(1.0f)};
226 if (!transform)
return;
233 RenderingUtils::upload_lights_to_shader(shader, *context_);
236 for (
int i = 0; i < context_->num_directional_lights; i++) {
237 Entity* light_entity = context_->directional_light_entities[i];
239 if (shadow_maps_.contains(light_entity)) {
240 auto& shadow_data = shadow_maps_[light_entity];
243 const int texture_unit = 10 + i;
244 glActiveTexture(GL_TEXTURE0 + texture_unit);
245 glBindTexture(GL_TEXTURE_2D, shadow_data.framebuffer->get_depth_attachment());
248 shader
.set_int("uShadowMap[" + std::to_string(i) +
"]", texture_unit
);
249 shader.set_mat4(
"uLightSpaceMatrix[" + std::to_string(i) +
"]", shadow_data.light_view_proj);
260 RenderingUtils::set_standard_uniforms(shader, transform->get_world_matrix(), view, projection);
263 cmd.material->bind();
265 cmd.material->unbind();
269 const glm::mat4 &projection) {
277 RenderingUtils::upload_lights_to_shader(shader, *context_);
281 RenderingUtils::set_standard_uniforms(shader, glm::mat4(1.0f), view, projection, Time::current_time);
287 cmd.material->bind();
297 cmd.material->unbind();
304 glDisable(GL_CULL_FACE);
313 const std::vector<EntityID> light_entity_ids = scene.find_entities_with_component<LightComponent>();
314 std::vector<Entity *> light_entities;
315 for (
const EntityID id: light_entity_ids) {
316 if (Entity *e = scene.get_entity(id)) {
317 light_entities.push_back(e);
320 store_lights_in_context(light_entities, camera);
330 collect_geometry_from_scene(scene, camera.get_owner().transform()->get_position());
333 const glm::mat4 view = camera.get_view_matrix();
334 const glm::mat4 projection = camera.get_projection_matrix();
336 execute_geometry_pass(view, projection);
337 execute_skybox_pass(&scene, view, projection, &camera);
338 execute_transparency_pass(view, projection);
345 const std::vector<EntityID> light_entity_ids = scene.find_entities_with_component<LightComponent>();
347 std::vector<Entity*> shadow_casting_lights;
348 for (
const EntityID id : light_entity_ids) {
349 Entity* entity = scene.get_entity(id);
350 if (!entity)
continue;
352 const auto* light = entity->get_component<LightComponent>();
353 if (light && light->should_cast_shadows()) {
354 shadow_casting_lights.push_back(entity);
360 const glm::vec3 dummy_camera_pos(0.0f);
361 for (
const EntityID root_id : scene.get_root_entities()) {
362 collect_render_commands_recursive(root_id, dummy_camera_pos);
366 for (Entity* light_entity : shadow_casting_lights) {
367 auto* light = light_entity->get_component<LightComponent>();
368 if (!light)
continue;
370 ensure_shadow_map(light_entity, *light);
372 auto& shadow_data = shadow_maps_[light_entity];
373 shadow_data.framebuffer->bind();
375 glViewport(0, 0, 4096, 4096);
376 glClear(GL_DEPTH_BUFFER_BIT);
377 glEnable(GL_DEPTH_TEST);
378 glDepthFunc(GL_LESS);
380 glEnable(GL_CULL_FACE);
381 glCullFace(GL_FRONT);
384 shadow_data.light_view_proj = calculate_light_view_proj(light_entity, light, camera);
390 draw_shadow_geometry(shadow_data.light_view_proj);
392 shadow_data.framebuffer->unbind();
400 const Shader& shadow_shader = get_shader_for_material(shadow_material_);
402 shadow_shader.set_mat4(
"uLightViewProjMatrix", light_view_proj);
404 shadow_material_->bind();
406 for (
const auto &cmd : opaque_objects_) {
407 const Entity *entity = scene_->get_entity(cmd.entity_id);
408 if (!entity)
continue;
410 const auto* transform = entity->get_component<TransformComponent>();
411 if (!transform)
continue;
414 shadow_shader.set_mat4(
"uModelMatrix", transform->get_world_matrix());
419 shadow_material_->unbind();
423 const glm::vec3 light_dir = glm::normalize(light->get_direction());
426 const glm::vec3 camera_pos = camera.get_owner().transform()->get_position();
428 const float ortho_size = 100.0f;
429 const float texel_size = (ortho_size * 2.0f) / 4096.0f;
432 look_at.x = floor(camera_pos.x / texel_size) * texel_size;
434 look_at.z =
float(camera_pos.z / texel_size) * texel_size;
436 const glm::vec3 light_pos = look_at - light_dir * 30.0f;
438 glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
439 if (glm::abs(glm::dot(light_dir, up)) > 0.99f) {
440 up = glm::vec3(1.0f, 0.0f, 0.0f);
443 const glm::mat4 light_projection = glm::ortho(
444 -ortho_size, ortho_size,
445 -ortho_size, ortho_size,
448 const glm::mat4 light_view = glm::lookAt(light_pos, look_at, up);
450 return light_projection * light_view;
454 glEnable(GL_DEPTH_TEST);
455 glDepthMask(GL_TRUE);
456 glDepthFunc(GL_LESS);
460 glEnable(GL_CULL_FACE);
464 glEnable(GL_STENCIL_TEST);
465 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
467 std::ranges::sort(opaque_objects_,
468 [](
const RenderCommand &a,
const RenderCommand &b) {
469 return a.distance_to_camera < b.distance_to_camera;
472 for (
const auto &cmd: opaque_objects_) {
473 draw_render_command(cmd, view, proj);
476 for (
const auto &cmd: opaque_instanced_objects_) {
477 draw_instanced_command(cmd, view, proj);
483 glEnablei(GL_BLEND, 0);
484 glDisablei(GL_BLEND, 1);
485 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
489 std::ranges::sort(transparent_objects_,
490 [](
const RenderCommand &a,
const RenderCommand &b) {
491 return a.distance_to_camera > b.distance_to_camera;
495 glDisable(GL_CULL_FACE);
496 for (
const auto &cmd: transparent_objects_) {
498 glCullFace(GL_FRONT);
499 glDepthMask(GL_TRUE);
500 glEnable(GL_POLYGON_OFFSET_FILL);
501 glPolygonOffset(2.0f, 2.0f);
502 draw_render_command(cmd, view, proj);
503 glDisable(GL_POLYGON_OFFSET_FILL);
507 glDepthMask(GL_FALSE);
508 draw_render_command(cmd, view, proj);
512 for (
const auto &cmd: transparent_instanced_objects_) {
514 glCullFace(GL_FRONT);
515 glDepthMask(GL_TRUE);
516 draw_instanced_command(cmd, view, proj);
520 glDepthMask(GL_FALSE);
521 draw_instanced_command(cmd, view, proj);
525 glDepthMask(GL_TRUE);
539 object_id_attachment_settings.format = GL_RED_INTEGER;
540 object_id_attachment_settings.internal_format = GL_R32UI;
541 object_id_attachment_settings.type = GL_UNSIGNED_INT;
542 scene_framebuffers_[SCREEN_TEXTURE_1] = std::make_unique<Framebuffer>();
543 scene_framebuffers_[SCREEN_TEXTURE_1]->attach_color_texture(settings);
544 scene_framebuffers_[SCREEN_TEXTURE_1]->attach_color_texture(object_id_attachment_settings);
545 scene_framebuffers_[SCREEN_TEXTURE_1]->attach_depth_texture(settings);
547 scene_framebuffers_[SCREEN_TEXTURE_2] = std::make_unique<Framebuffer>();
548 scene_framebuffers_[SCREEN_TEXTURE_2]->attach_color_texture(settings);
549 scene_framebuffers_[SCREEN_TEXTURE_2]->attach_color_texture(object_id_attachment_settings);
550 scene_framebuffers_[SCREEN_TEXTURE_2]->attach_depth_texture(settings);
560 if (scene_framebuffers_[0]) {
561 scene_framebuffers_[current_fb_index_]->resize(width, height);
567 if (scene_framebuffers_[display_index]) {
568 return scene_framebuffers_[display_index]->get_color_attachment(0);
575 if (scene_framebuffers_[display_index]->get_color_attachment(1) != 0) {
576 return scene_framebuffers_[display_index]->get_color_attachment(1);
582 if (!scene_framebuffers_[SCREEN_TEXTURE_1]) {
588 if (scene_framebuffers_[display_index] &&
589 (scene_framebuffers_[display_index]->get_width() != framebuffer_width_ ||
590 scene_framebuffers_[display_index]->get_height() != framebuffer_height_)) {
591 scene_framebuffers_[display_index]->resize(framebuffer_width_, framebuffer_height_);
596 scene_framebuffers_[current_fb_index_]->bind();
600 constexpr GLuint clear_value = 0;
601 glClearBufferuiv(GL_COLOR, 1, &clear_value);
604 scene_framebuffers_[current_fb_index_]->unbind();
615 context_->shader_handle = fallback_shader.get_program_id();
625 if (
const uint32_t material_shader_id = material->get_compiled_shader_id(); material_shader_id != 0) {
627 if (
Shader *material_shader = shader_registry_.get_shader_from_id(material_shader_id)) {
628 return *material_shader;
633 if (material->has_custom_shader()) {
636 material->set_compiled_shader_id(compiled_id);
637 const auto shader = shader_registry_.get_shader_from_id(compiled_id);
647 if (!material || !material->has_custom_shader()) {
660 variant.defines = shader_info->defines;
663 shader_manager_.add_automatic_defines(*material, variant.defines);
666 return shader_manager_.load_shader(variant);
void set_float(const std::string &name, const float value) const
void set_int(const std::string &name, const int value) const
void set_vec3(const std::string &name, const float x, const float y, const float z) const
void set_uint(const std::string &name, const uint32_t value) const
void bind_instance_buffers()
size_t get_instance_count() const
void draw_render_command(const RenderCommand &cmd, const glm::mat4 &view, const glm::mat4 &projection)
void ensure_shadow_map(Entity *light_entity, const LightComponent &light)
Shader & get_shader_for_material(const std::shared_ptr< Material > &material)
SkyboxRenderer skybox_renderer_
ShadowSettings shadow_settings_
bool render_to_framebuffer_
void execute_skybox_pass(Scene *scene, const glm::mat4 &view, const glm::mat4 &projection, CameraComponent *camera_comp) const
uint32_t get_object_id_texture() const
glm::mat4 calculate_light_view_proj(Entity *light_entity, LightComponent *light, const CameraComponent &camera)
void draw_instanced_command(const InstancedRenderCommand &cmd, const glm::mat4 &view, const glm::mat4 &projection)
void render(Scene &scene, const Entity *camera_override)
void collect_geometry_from_scene(Scene &scene, const glm::vec3 camera_pos)
void execute_shadow_passes(Scene &scene, CameraComponent &camera)
Shader * fallback_shader_
uint32_t fallback_program_
uint32_t get_main_output_texture() const
void resize_main_framebuffer(uint32_t width, uint32_t height)
void execute_transparency_pass(const glm::mat4 &view, const glm::mat4 &proj)
void reset_framebuffer_data()
void execute_main_pass(Scene &scene, CameraComponent &camera)
uint32_t framebuffer_width_
void render_frame(Scene &scene, CameraComponent &camera)
void execute_geometry_pass(const glm::mat4 &view, const glm::mat4 &proj)
void collect_lights_from_scene(Scene &scene, CameraComponent &camera)
void store_lights_in_context(const std::vector< Entity * > &light_entities, CameraComponent &camera)
uint32_t framebuffer_height_
void draw_shadow_geometry(const glm::mat4 &light_view_proj)
void set_fallback_shader(Shader &fallback_shader)
void collect_render_commands_recursive(EntityID entity_id, const glm::vec3 &camera_pos)
void create_main_framebuffer(uint32_t width, uint32_t height)
uint32_t compile_material_shader(std::shared_ptr< Material > material)
Skybox * get_skybox() const
Manages a collection of entities and their hierarchical relationships.
Entity * get_entity(EntityID id)
Retrieves an entity by its ID.
EntityID get_default_camera_entity_id() const
SceneEnvironment * environment() const
void render(const Skybox &skybox, const CameraComponent *camera) const
InstancedRenderableComponent * instanced_renderable
std::string fragment_path
std::string fragment_path