diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d7e67f8..672f4ea 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,7 +2,6 @@ add_library(fever_engine main.cpp Controller.cpp Window.cpp - Light.cpp scene.cpp FrameBuffer.cpp Helper.cpp @@ -13,6 +12,8 @@ add_library(fever_engine material.cpp camera.cpp shader.cpp + transform.cpp + light.cpp ) target_compile_features(fever_engine PUBLIC cxx_std_20) diff --git a/src/Controller.cpp b/src/Controller.cpp index 439175f..ef3efac 100644 --- a/src/Controller.cpp +++ b/src/Controller.cpp @@ -1,9 +1,9 @@ #include "Controller.h" #include "FrameBuffer.h" #include "Helper.h" -#include "Light.h" #include "Window.h" #include "gltf_loader.h" +#include "light.h" #include "render.h" #include "shader.h" #include "util/Log.h" @@ -54,16 +54,6 @@ void Controller::run() auto standard_material_shader = m_shader_cache.load(shader_hash, Material::SHADER_NAME).first->second; - DirectionalLight directional_light( - DirectionalLight::Prototype("", glm::vec3(-0.2, -1.0, -0.3), glm::vec3(1.0f), 5.f), - standard_material_shader.handle().get()); - directional_light.setActive(true); - - PointLight point_light( - PointLight::Prototype("", "", glm::vec3(4.0, 1.0, 6.0), glm::vec3(1.0F), 3.0F), - standard_material_shader.handle().get()); - point_light.setActive(true); - Log::logger().info("Startup complete. Enter game loop."); // This is the game loop @@ -92,6 +82,7 @@ void Controller::run() m_postProcessFrameBuffer.bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + Light::update_lights(m_scene->registry(), standard_material_shader); Render::render(m_scene->registry()); Framebuffer::unbind(); diff --git a/src/Light.cpp b/src/Light.cpp deleted file mode 100644 index 5fc8fa1..0000000 --- a/src/Light.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include "Light.h" -#include "shader.h" - -#include - -uint32_t Light::s_idCounter = 0; - -Light::Light(const std::string &name, glm::vec3 color, float intensity, Shader *shader) - : m_shader(shader), m_intensity(intensity), m_lightColor(color * intensity) -{ - m_id = s_idCounter++; -} - -Light::~Light() = default; - -glm::vec3 Light::getColor() -{ - return m_lightColor; -} - -void Light::setShader(Shader *shader) -{ - this->m_shader = shader; - update(); -} - -void Light::setColor(glm::vec3 color) -{ - m_lightColor = color * m_intensity; - update(); -} - -void Light::setIntensity(float intensity) -{ - this->m_intensity = intensity; -} - -void Light::setActive(bool active) -{ - m_isActive = active; - update(); -} - -PointLight::PointLight(Prototype prototype, Shader *shader) - : Light(prototype.name, prototype.color, prototype.intensity, shader), m_position(prototype.position) -{ - update(); -} - -void PointLight::update() -{ - m_shader->bind(); - - m_shader->set_uniform((getStructMemberName() + "isActive").c_str(), m_isActive); - m_shader->set_uniform((getStructMemberName() + "position").c_str(), m_position); - m_shader->set_uniform((getStructMemberName() + "color").c_str(), m_lightColor); - - m_shader->unbind(); -} - -std::string PointLight::getStructMemberName() -{ - // id - 1 because id 0 is always the DirectionalLight! - std::string temp = "u_pointLight[" + std::to_string(m_id - 1) + "]."; - return temp; -} - -glm::vec3 PointLight::getPosition() -{ - return m_position; -} - -void PointLight::setPosition(glm::vec3 position) -{ - this->m_position = position; - update(); -} - -DirectionalLight::DirectionalLight(Prototype prototype, Shader *shader) - : Light(prototype.name, prototype.color, prototype.intensity, shader), m_direction(prototype.direction) -{ - update(); -} - -void DirectionalLight::update() -{ - m_shader->bind(); - - m_shader->set_uniform("u_directionalLight.isActive", m_isActive); - m_shader->set_uniform("u_directionalLight.direction", m_direction); - m_shader->set_uniform("u_directionalLight.color", m_lightColor); - - m_shader->unbind(); -} - -void DirectionalLight::setDirection(glm::vec3 direction) -{ - this->m_direction = direction; - update(); -} - -glm::vec3 DirectionalLight::getDirection() -{ - return m_direction; -} diff --git a/src/Light.h b/src/Light.h deleted file mode 100644 index e1eba72..0000000 --- a/src/Light.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#include -#include - -class Shader; - -class Light -{ -public: - struct Prototype - { - Prototype(const std::string &_name, const std::string &_parent, glm::vec3 _color, float _intensity) - : name(_name), parent(_parent), color(_color), intensity(_intensity) - {} - virtual ~Prototype() = default; - - std::string name; - std::string parent; - glm::vec3 color; - float intensity; - }; - - Light(const std::string &name, glm::vec3 color, float intensity, Shader *shader); - virtual ~Light() = 0; - - virtual void update() = 0; - - void setActive(bool active); - void setColor(glm::vec3 color); - void setIntensity(float intensity); - void setShader(Shader *shader); - - glm::vec3 getColor(); - -protected: - Shader *m_shader; - - uint32_t m_id; - static uint32_t s_idCounter; - - bool m_isActive = true; - - float m_intensity; - - // Color - glm::vec3 m_lightColor; -}; - -class PointLight : public Light -{ -public: - struct Prototype : public Light::Prototype - { - Prototype(const std::string &_name, const std::string &_parent, glm::vec3 _position, glm::vec3 _color, - float _intensity) - : Light::Prototype(_name, _parent, _color, _intensity), position(_position) - {} - glm::vec3 position; - }; - - PointLight(Prototype prototype, Shader *shader); - ~PointLight() override = default; - - void setPosition(glm::vec3 position); - - glm::vec3 getPosition(); - -private: - void update() override; - std::string getStructMemberName(); - - glm::vec3 m_position = glm::vec3(0.0f, 0.0f, 0.0f); -}; - -class DirectionalLight : public Light -{ -public: - struct Prototype : public Light::Prototype - { - Prototype(const std::string &_name, glm::vec3 _direction, glm::vec3 _color, float _intensity) - : Light::Prototype(_name, "", _color, _intensity), direction(_direction) - {} - glm::vec3 direction; - }; - - DirectionalLight(Prototype prototype, Shader *shader); - ~DirectionalLight() override = default; - - void setDirection(glm::vec3 direction); - - glm::vec3 getDirection(); - -private: - void update() override; - - glm::vec3 m_direction; -}; diff --git a/src/color.h b/src/color.h index 90fd22c..c29b89d 100644 --- a/src/color.h +++ b/src/color.h @@ -1,12 +1,25 @@ #pragma once +#include + struct Color { double r{}, g{}, b{}; double a = 1.0; bool linear = false; + + [[nodiscard]] auto to_vec3() const -> glm::vec3 { return {r, g, b}; } }; +constexpr auto operator*(Color const &color, float value) -> Color +{ + Color new_color = color; + new_color.r *= value; + new_color.g *= value; + new_color.b *= value; + return new_color; +} + namespace ColorConstant { static constexpr Color WHITE{.r = 1.0, .g = 1.0, .b = 1.0}; static constexpr Color BLACK{.r = 0.0, .g = 0.0, .b = 0.0}; diff --git a/src/light.cpp b/src/light.cpp new file mode 100644 index 0000000..872614d --- /dev/null +++ b/src/light.cpp @@ -0,0 +1,53 @@ +#include "light.h" +#include "transform.h" + +static auto light_active(float illuminance) -> bool +{ + return std::abs(illuminance) >= std::numeric_limits::epsilon(); +} + +static auto point_light_uniform_base(std::size_t n) -> std::string +{ + return "u_pointLight[" + std::to_string(n) + "]."; +} + +void Light::update_lights(entt::registry ®istry, Shader &shader) +{ + shader.bind(); + + // Directional light + { + auto directional_lights_view = + registry.view(); + + entt::entity entity = directional_lights_view.front(); + auto [directional_light, global_transform] = directional_lights_view.get(entity); + + glm::vec4 unit_vector{1.0, 0.0, 0.0, 0.0}; + glm::vec3 direction = glm::vec3(global_transform.transform * unit_vector); + + shader.set_uniform("u_directionalLight.isActive", + light_active(directional_light.illuminance)); + shader.set_uniform("u_directionalLight.direction", direction); + shader.set_uniform("u_directionalLight.color", + (directional_light.color * directional_light.illuminance).to_vec3()); + } + + // Point lights + { + auto point_lights_view = registry.view(); + std::size_t num = 0; + for (auto [entity, point_light, global_transform] : point_lights_view.each()) { + shader.set_uniform((point_light_uniform_base(num) + "isActive").c_str(), + light_active(point_light.intensity)); + shader.set_uniform((point_light_uniform_base(num) + "position").c_str(), + global_transform.position()); + shader.set_uniform((point_light_uniform_base(num) + "color").c_str(), + (point_light.color * point_light.intensity).to_vec3()); + + num++; + } + } + + Shader::unbind(); +} diff --git a/src/light.h b/src/light.h new file mode 100644 index 0000000..9f7c4e4 --- /dev/null +++ b/src/light.h @@ -0,0 +1,29 @@ +#pragma once + +#include "color.h" +#include "shader.h" + +#include +#include + +struct PointLight +{ + static constexpr glm::vec3 DEFAULT_POSITION{4.0, 1.0, 6.0}; + static constexpr float DEFAULT_INTENSITY = 3.0; + + Color color = ColorConstant::WHITE; + float intensity{}; +}; + +struct DirectionalLight +{ + static constexpr glm::vec3 DEFAULT_DIRECTION{-0.2, -1.0, -0.3}; + static constexpr float DEFAULT_ILLUMINANCE = 5.0; + + Color color = ColorConstant::WHITE; + float illuminance{}; +}; + +namespace Light { +void update_lights(entt::registry ®istry, Shader &shader); +} diff --git a/src/scene.cpp b/src/scene.cpp index 7187a30..aa473da 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -1,5 +1,6 @@ #include "scene.h" #include "camera.h" +#include "light.h" #include "mesh.h" #include "name.h" #include "relationship.h" @@ -24,6 +25,25 @@ Scene::Scene(entt::registry registry) : m_registry(std::move(registry)) // Remove Material resource as it is no longer needed. m_registry.erase>(entity); } + + // Spawn default lights + auto directional_light = m_registry.create(); + m_registry.emplace(directional_light, "Directional Light"); + m_registry.emplace( + directional_light, + Transform{.orientation = glm::toQuat( + glm::lookAt({}, DirectionalLight::DEFAULT_DIRECTION, Camera::UP_VECTOR))}); + m_registry.emplace(directional_light, GlobalTransform{}); + m_registry.emplace( + directional_light, DirectionalLight{.illuminance = DirectionalLight::DEFAULT_ILLUMINANCE}); + + auto point_light = m_registry.create(); + m_registry.emplace(point_light, "Point Light"); + m_registry.emplace(point_light, + Transform{.translation = PointLight::DEFAULT_POSITION}); + m_registry.emplace(point_light, GlobalTransform{}); + m_registry.emplace(point_light, + PointLight{.intensity = PointLight::DEFAULT_INTENSITY}); } void Scene::update(std::chrono::duration delta, @@ -32,41 +52,7 @@ void Scene::update(std::chrono::duration delta, float aspect_ratio, bool cursor_catched) { - { - // Update GlobalTransform components - // TODO: Only do this when the Transform changed. - auto root_transform_view = - m_registry.view(entt::exclude); - auto transform_view = m_registry.view(); - - for (auto [entity, transform, global_transform] : root_transform_view.each()) { - global_transform = transform; - - auto parent_global_transform = global_transform; - if (auto *children = m_registry.try_get(entity)) { - for (auto child : children->children) { - std::function - transform_propagate = [this, &transform_propagate, &transform_view]( - entt::entity entity, - GlobalTransform parent_global_transform) { - auto [transform, global_transform, parent] = transform_view.get(entity); - global_transform.transform = parent_global_transform.transform * - GlobalTransform(transform).transform; - - if (auto *children = m_registry.try_get(entity)) { - for (auto child : children->children) { - transform_propagate(child, global_transform); - } - } - }; - - transform_propagate(child, parent_global_transform); - } - } - } - } - + GlobalTransform::update(m_registry); Camera::aspect_ratio_update(m_registry, aspect_ratio); Camera::keyboard_movement(m_registry, key_input, delta); if (cursor_catched) { diff --git a/src/transform.cpp b/src/transform.cpp new file mode 100644 index 0000000..87ba1bc --- /dev/null +++ b/src/transform.cpp @@ -0,0 +1,37 @@ +#include "transform.h" +#include "relationship.h" + +void GlobalTransform::update(entt::registry ®istry) +{ + // Update GlobalTransform components + // TODO: Only do this when the Transform changed. + auto root_transform_view = + registry.view(entt::exclude); + auto transform_view = registry.view(); + + for (auto [entity, transform, global_transform] : root_transform_view.each()) { + global_transform = transform; + + auto parent_global_transform = global_transform; + if (auto *children = registry.try_get(entity)) { + for (auto child : children->children) { + std::function + transform_propagate = + [®istry, &transform_propagate, &transform_view]( + entt::entity entity, GlobalTransform parent_global_transform) { + auto [transform, global_transform, parent] = transform_view.get(entity); + global_transform.transform = parent_global_transform.transform * + GlobalTransform(transform).transform; + + if (auto *children = registry.try_get(entity)) { + for (auto child : children->children) { + transform_propagate(child, global_transform); + } + } + }; + + transform_propagate(child, parent_global_transform); + } + } + } +} diff --git a/src/transform.h b/src/transform.h index f367ff5..a0d9ca6 100644 --- a/src/transform.h +++ b/src/transform.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -33,4 +34,6 @@ struct GlobalTransform glm::mat4 transform{}; [[nodiscard]] auto position() const -> glm::vec3 { return transform[3]; }; + + static void update(entt::registry ®istry); };