From 43e25b262887544dd8931b2e3e6617a62fa355b4 Mon Sep 17 00:00:00 2001 From: Derek Christ Date: Sat, 22 Oct 2022 22:05:24 +0200 Subject: [PATCH] Refactor shaders --- ...stprocessing.frag => post_processing.frag} | 0 ...stprocessing.vert => post_processing.vert} | 0 .../{basic.frag => standard_material.frag} | 0 .../{basic.vert => standard_material.vert} | 0 src/CMakeLists.txt | 2 +- src/Controller.cpp | 50 +++--- src/Controller.h | 17 +- src/FrameBuffer.cpp | 17 +- src/FrameBuffer.h | 6 +- src/Light.cpp | 38 ++--- src/Light.h | 12 +- src/Scene.cpp | 49 ++---- src/Scene.h | 9 +- src/{ShaderProgram.cpp => ShaderProgram_.cpp} | 40 ++--- src/{ShaderProgram.h => ShaderProgram_.h} | 18 +-- src/gltf_loader.cpp | 16 +- src/gltf_loader.h | 1 + src/material.cpp | 10 +- src/material.h | 10 +- src/render.h | 43 ++++++ src/resources/CubeMap.cpp | 2 +- src/shader.cpp | 146 ++++++++++++++++++ src/shader.h | 50 ++++++ 23 files changed, 378 insertions(+), 158 deletions(-) rename data/shaders/{postprocessing.frag => post_processing.frag} (100%) rename data/shaders/{postprocessing.vert => post_processing.vert} (100%) rename data/shaders/{basic.frag => standard_material.frag} (100%) rename data/shaders/{basic.vert => standard_material.vert} (100%) rename src/{ShaderProgram.cpp => ShaderProgram_.cpp} (77%) rename src/{ShaderProgram.h => ShaderProgram_.h} (59%) create mode 100644 src/render.h create mode 100644 src/shader.cpp create mode 100644 src/shader.h diff --git a/data/shaders/postprocessing.frag b/data/shaders/post_processing.frag similarity index 100% rename from data/shaders/postprocessing.frag rename to data/shaders/post_processing.frag diff --git a/data/shaders/postprocessing.vert b/data/shaders/post_processing.vert similarity index 100% rename from data/shaders/postprocessing.vert rename to data/shaders/post_processing.vert diff --git a/data/shaders/basic.frag b/data/shaders/standard_material.frag similarity index 100% rename from data/shaders/basic.frag rename to data/shaders/standard_material.frag diff --git a/data/shaders/basic.vert b/data/shaders/standard_material.vert similarity index 100% rename from data/shaders/basic.vert rename to data/shaders/standard_material.vert diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fc223da..2a8beae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,7 +2,6 @@ add_library(fever_engine main.cpp Controller.cpp Window.cpp - ShaderProgram.cpp Light.cpp Scene.cpp FrameBuffer.cpp @@ -13,6 +12,7 @@ add_library(fever_engine gltf_loader.cpp material.cpp camera.cpp + shader.cpp ) target_compile_features(fever_engine PUBLIC cxx_std_20) diff --git a/src/Controller.cpp b/src/Controller.cpp index b51474f..df76872 100644 --- a/src/Controller.cpp +++ b/src/Controller.cpp @@ -2,9 +2,10 @@ #include "FrameBuffer.h" #include "Helper.h" #include "Light.h" -#include "ShaderProgram.h" #include "Window.h" #include "gltf_loader.h" +#include "render.h" +#include "shader.h" #include "util/Log.h" #include @@ -22,10 +23,11 @@ using namespace entt::literals; Controller::Controller() - : m_gameWindow(std::make_shared()), + : m_scene(m_shader_cache), + m_gameWindow(std::make_shared()), m_postProcessFrameBuffer(m_gameWindow->dimensions().first, m_gameWindow->dimensions().second, - postProcessingProgram) + post_processing_shader) { // if (!gltf.cameras.empty()) { // auto const &gltf_camera = gltf.cameras.at(0); @@ -41,18 +43,20 @@ Controller::Controller() void Controller::run() { - updateExposure(postProcessingProgram); + updateExposure(post_processing_shader); - // m_camera->translate(glm::vec3(0., .5, 2.)); + entt::hashed_string shader_hash (Material::SHADER_NAME.data()); + 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), - &defaultProgram); + 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), - &defaultProgram); + standard_material_shader.handle().get()); point_light.setActive(true); Log::logger().info("Startup complete. Enter game loop."); @@ -62,8 +66,8 @@ void Controller::run() // --- Timing --- limit_framerate(); - // --- Update game --- - m_gameWindow->clear_mouse_cursor_input(); // MOVE DOWN AGAIN! + // --- Check events, handle input --- + m_gameWindow->clear_mouse_cursor_input(); glfwPollEvents(); auto const &key_input = m_gameWindow->key_input(); @@ -72,7 +76,10 @@ void Controller::run() static constexpr auto MICROSECONDS_PER_SECOND = 1'000'000; m_scene.update( std::chrono::microseconds(static_cast(m_deltaTime * MICROSECONDS_PER_SECOND)), - key_input,mouse_cursor_input, m_gameWindow->aspectRatio()); + key_input, + mouse_cursor_input, + m_gameWindow->aspectRatio(), + m_gameWindow->cursor_catched()); // --- Render and buffer swap --- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -80,7 +87,7 @@ void Controller::run() m_postProcessFrameBuffer.bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - m_scene.draw(&defaultProgram); + Render::render(m_scene.registry()); Framebuffer::unbind(); m_postProcessFrameBuffer.drawOnEntireScreen(); @@ -92,19 +99,6 @@ void Controller::run() m_gameWindow->update_dimensions(); update_window_dimensions(); } - - // --- Check events, handle input --- - // m_gameWindow->clear_mouse_cursor_input(); - // glfwPollEvents(); - - // auto const &key_input = m_gameWindow->key_input(); - // auto const &mouse_cursor_input = m_gameWindow->mouse_cursor_input(); - - // m_camera->updatePositionFromKeyboardInput(key_input, (float)m_deltaTime); - - // if (m_gameWindow->cursor_catched()) { - // m_camera->updateDirectionFromMouseInput(mouse_cursor_input); - // } } } @@ -137,9 +131,9 @@ void Controller::update_window_dimensions() m_postProcessFrameBuffer.changeDimensions(dimensions.first, dimensions.second); } -void Controller::updateExposure(ShaderProgram &shaderProgram) const +void Controller::updateExposure(Shader &shader) const { - shaderProgram.bind(); - shaderProgram.setUniform("u_exposure", m_exposure); - ShaderProgram::unbind(); + shader.bind(); + shader.set_uniform("u_exposure", m_exposure); + Shader::unbind(); } diff --git a/src/Controller.h b/src/Controller.h index a4e3423..189ffda 100644 --- a/src/Controller.h +++ b/src/Controller.h @@ -1,13 +1,14 @@ #pragma once #include "FrameBuffer.h" -#include "ShaderProgram.h" #include "Scene.h" +#include "shader.h" #include #include -#include #include +#include +#include class Window; class Scene; @@ -21,21 +22,21 @@ public: void run(); - void updateExposure(ShaderProgram &shaderProgram) const; + void updateExposure(Shader &shader) const; private: void limit_framerate(); void update_window_dimensions(); std::shared_ptr m_gameWindow; - - ShaderProgram defaultProgram{{"defaultProgram", "data/shaders/basic.vert", "data/shaders/basic.frag"}}; - ShaderProgram skyboxProgram{{"skyboxProgram", "data/shaders/skybox.vert", "data/shaders/skybox.frag"}}; - ShaderProgram postProcessingProgram{ - {"postProcessingProgram", "data/shaders/postprocessing.vert", "data/shaders/postprocessing.frag"}}; + + Shader skybox_shader{"skybox", "data/shaders"}; + Shader post_processing_shader{"post_processing", "data/shaders"}; Framebuffer m_postProcessFrameBuffer; + entt::resource_cache m_shader_cache; + static constexpr unsigned MAX_FPS = 60; Scene m_scene; diff --git a/src/FrameBuffer.cpp b/src/FrameBuffer.cpp index 1e34378..0db83cc 100644 --- a/src/FrameBuffer.cpp +++ b/src/FrameBuffer.cpp @@ -1,5 +1,5 @@ #include "FrameBuffer.h" -#include "ShaderProgram.h" +#include "shader.h" #include "util/Log.h" #include @@ -19,7 +19,7 @@ GLuint Framebuffer::getFBO() const return m_FBO; } -Framebuffer::Framebuffer(uint32_t width, uint32_t height, ShaderProgram &shaderProgram) : m_shaderProgram(shaderProgram) +Framebuffer::Framebuffer(uint32_t width, uint32_t height, Shader &shader) : m_shader(shader) { glGenFramebuffers(1, &m_FBO); @@ -46,12 +46,11 @@ void Framebuffer::drawOnEntireScreen() const glGetIntegerv(GL_POLYGON_MODE, &wireframe); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - m_shaderProgram.bind(); + m_shader.bind(); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, getTextureId()); - GLint location = glGetUniformLocation(m_shaderProgram.getShaderProgramId(), "u_texture"); - glUniform1i(location, 0); + m_shader.set_uniform("u_texture", 0); // A VAO is necessary although no data is stored in it GLuint temp_vao; @@ -61,7 +60,7 @@ void Framebuffer::drawOnEntireScreen() const glBindVertexArray(0); glPolygonMode(GL_FRONT_AND_BACK, static_cast(wireframe)); - m_shaderProgram.unbind(); + m_shader.unbind(); } void Framebuffer::changeDimensions(uint32_t width, uint32_t height) @@ -108,7 +107,7 @@ void Framebuffer::generateTextures(uint32_t width, uint32_t height) void Framebuffer::setExposureCorrection(bool exposureCorrection) const { - m_shaderProgram.bind(); - m_shaderProgram.setUniform("u_exposureCorrection", exposureCorrection); - m_shaderProgram.unbind(); + m_shader.bind(); + m_shader.set_uniform("u_exposureCorrection", exposureCorrection); + m_shader.unbind(); } diff --git a/src/FrameBuffer.h b/src/FrameBuffer.h index 7ffd7d3..e0d7ae7 100644 --- a/src/FrameBuffer.h +++ b/src/FrameBuffer.h @@ -2,12 +2,12 @@ #include -class ShaderProgram; +class Shader; class Framebuffer { public: - Framebuffer(uint32_t width, uint32_t height, ShaderProgram &shaderProgram); + Framebuffer(uint32_t width, uint32_t height, Shader &shader); ~Framebuffer(); void bind() const; @@ -29,5 +29,5 @@ private: GLuint m_depthStencilBuffer; GLuint m_FBO; - ShaderProgram &m_shaderProgram; + Shader &m_shader; }; diff --git a/src/Light.cpp b/src/Light.cpp index 65d9893..5fc8fa1 100644 --- a/src/Light.cpp +++ b/src/Light.cpp @@ -1,12 +1,12 @@ #include "Light.h" -#include "ShaderProgram.h" +#include "shader.h" #include uint32_t Light::s_idCounter = 0; -Light::Light(const std::string &name, glm::vec3 color, float intensity, ShaderProgram *shaderProgram) - : m_shaderProgram(shaderProgram), m_intensity(intensity), m_lightColor(color * intensity) +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++; } @@ -18,9 +18,9 @@ glm::vec3 Light::getColor() return m_lightColor; } -void Light::setShaderProgram(ShaderProgram *shaderProgram) +void Light::setShader(Shader *shader) { - this->m_shaderProgram = shaderProgram; + this->m_shader = shader; update(); } @@ -41,21 +41,21 @@ void Light::setActive(bool active) update(); } -PointLight::PointLight(Prototype prototype, ShaderProgram *shaderProgram) - : Light(prototype.name, prototype.color, prototype.intensity, shaderProgram), m_position(prototype.position) +PointLight::PointLight(Prototype prototype, Shader *shader) + : Light(prototype.name, prototype.color, prototype.intensity, shader), m_position(prototype.position) { update(); } void PointLight::update() { - m_shaderProgram->bind(); + m_shader->bind(); - m_shaderProgram->setUniform((getStructMemberName() + "isActive").c_str(), m_isActive); - m_shaderProgram->setUniform((getStructMemberName() + "position").c_str(), m_position); - m_shaderProgram->setUniform((getStructMemberName() + "color").c_str(), m_lightColor); + 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_shaderProgram->unbind(); + m_shader->unbind(); } std::string PointLight::getStructMemberName() @@ -76,21 +76,21 @@ void PointLight::setPosition(glm::vec3 position) update(); } -DirectionalLight::DirectionalLight(Prototype prototype, ShaderProgram *shaderProgram) - : Light(prototype.name, prototype.color, prototype.intensity, shaderProgram), m_direction(prototype.direction) +DirectionalLight::DirectionalLight(Prototype prototype, Shader *shader) + : Light(prototype.name, prototype.color, prototype.intensity, shader), m_direction(prototype.direction) { update(); } void DirectionalLight::update() { - m_shaderProgram->bind(); + m_shader->bind(); - m_shaderProgram->setUniform("u_directionalLight.isActive", m_isActive); - m_shaderProgram->setUniform("u_directionalLight.direction", m_direction); - m_shaderProgram->setUniform("u_directionalLight.color", m_lightColor); + 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_shaderProgram->unbind(); + m_shader->unbind(); } void DirectionalLight::setDirection(glm::vec3 direction) diff --git a/src/Light.h b/src/Light.h index 1128e19..e1eba72 100644 --- a/src/Light.h +++ b/src/Light.h @@ -3,7 +3,7 @@ #include #include -class ShaderProgram; +class Shader; class Light { @@ -21,7 +21,7 @@ public: float intensity; }; - Light(const std::string &name, glm::vec3 color, float intensity, ShaderProgram *shaderProgram); + Light(const std::string &name, glm::vec3 color, float intensity, Shader *shader); virtual ~Light() = 0; virtual void update() = 0; @@ -29,12 +29,12 @@ public: void setActive(bool active); void setColor(glm::vec3 color); void setIntensity(float intensity); - void setShaderProgram(ShaderProgram *shaderProgram); + void setShader(Shader *shader); glm::vec3 getColor(); protected: - ShaderProgram *m_shaderProgram; + Shader *m_shader; uint32_t m_id; static uint32_t s_idCounter; @@ -59,7 +59,7 @@ public: glm::vec3 position; }; - PointLight(Prototype prototype, ShaderProgram *shaderProgram); + PointLight(Prototype prototype, Shader *shader); ~PointLight() override = default; void setPosition(glm::vec3 position); @@ -84,7 +84,7 @@ public: glm::vec3 direction; }; - DirectionalLight(Prototype prototype, ShaderProgram *shaderProgram); + DirectionalLight(Prototype prototype, Shader *shader); ~DirectionalLight() override = default; void setDirection(glm::vec3 direction); diff --git a/src/Scene.cpp b/src/Scene.cpp index 249c0ef..4155f10 100644 --- a/src/Scene.cpp +++ b/src/Scene.cpp @@ -1,20 +1,21 @@ #include "Scene.h" -#include "ShaderProgram.h" #include "camera.h" #include "mesh.h" #include "name.h" #include "relationship.h" +#include "shader.h" #include "transform.h" #include "util/Log.h" using namespace entt::literals; // TODO: make scene initialization part of gltf loader as seen in bevy -Scene::Scene() +Scene::Scene(entt::resource_cache &shader_cache) { GltfLoader loader{.image_cache = m_image_cache, .material_cache = m_material_cache, .mesh_cache = m_mesh_cache, + .shader_cache = shader_cache, .gltf_mesh_cache = m_gltf_mesh_cache, .gltf_node_cache = m_gltf_node_cache, .gltf_scene_cache = m_gltf_scene_cache}; @@ -92,16 +93,16 @@ Scene::Scene() // Spawn the camera auto entity = m_registry.create(); m_registry.emplace(entity, "Camera"); - m_registry.emplace(entity, Transform{.translation = glm::vec3(0.0, 0.5, -1.0)}); + m_registry.emplace(entity, Transform{.translation = glm::vec3(0.0, 0.25, -1.0)}); m_registry.emplace(entity, GlobalTransform{}); - m_registry.emplace(entity, - Camera{.projection = Camera::Perspective{}}); + m_registry.emplace(entity, Camera{.projection = Camera::Perspective{}}); } void Scene::update(std::chrono::duration delta, KeyInput const &key_input, MouseCursorInput const &mouse_cursor_input, - float aspect_ratio) + float aspect_ratio, + bool cursor_catched) { { // Update GlobalTransform components @@ -138,38 +139,10 @@ void Scene::update(std::chrono::duration delta, } } - Camera::keyboard_movement(m_registry, key_input, delta); - Camera::mouse_orientation(m_registry, mouse_cursor_input); Camera::aspect_ratio_update(m_registry, aspect_ratio); -} - -void Scene::draw(ShaderProgram *shaderprogram) const -{ - auto mesh_view = m_registry.view(); - auto camera_view = m_registry.view(); - auto camera_entity = camera_view.front(); - auto [camera, camera_transform] = camera_view.get(camera_entity); - glm::mat4 view_projection_matrix = - camera.projection_matrix() * Camera::view_matrix(camera_transform); - - for (auto [entity, mesh, material, transform] : mesh_view.each()) { - shaderprogram->bind(); - - // Bind textures - material.bind(*shaderprogram); - - // Bind modelview matrix uniform - { - glm::mat4 modelViewProj = view_projection_matrix * transform.transform; - shaderprogram->setUniform("u_modelViewProjMatrix", modelViewProj); - shaderprogram->setUniform("u_modelMatrix", transform.transform); - shaderprogram->setUniform("u_viewPosition", camera_transform.position()); - } - - glBindVertexArray(mesh.vao); - glDrawElements(GL_TRIANGLES, mesh.indices_count, mesh.indices_type, nullptr); - glBindVertexArray(0); - - ShaderProgram::unbind(); + Camera::keyboard_movement(m_registry, key_input, delta); + if (cursor_catched) { + Camera::mouse_orientation(m_registry, mouse_cursor_input); } + } diff --git a/src/Scene.h b/src/Scene.h index 20e4d6d..e5c1330 100644 --- a/src/Scene.h +++ b/src/Scene.h @@ -8,19 +8,20 @@ #include #include -class ShaderProgram; +class Shader; class Scene { public: - Scene(); + Scene(entt::resource_cache &shader_cache); void update(std::chrono::duration delta_time, KeyInput const &key_input, MouseCursorInput const &mouse_cursor_input, - float aspect_ratio); + float aspect_ratio, + bool cursor_catched); - void draw(ShaderProgram *shaderprogram) const; + auto registry() -> entt::registry & { return m_registry; } private: entt::registry m_registry; diff --git a/src/ShaderProgram.cpp b/src/ShaderProgram_.cpp similarity index 77% rename from src/ShaderProgram.cpp rename to src/ShaderProgram_.cpp index 6023160..dff1e18 100644 --- a/src/ShaderProgram.cpp +++ b/src/ShaderProgram_.cpp @@ -7,8 +7,8 @@ ShaderProgram::ShaderProgram(Prototype prototype) : m_shaderProgramId(glCreateProgram()) { - std::string vertexShaderSource = parse(prototype.vertexPath).value(); - std::string fragmentShaderSource = parse(prototype.fragmentPath).value(); + std::string vertexShaderSource = parse(prototype.vertexPath); + std::string fragmentShaderSource = parse(prototype.fragmentPath); GLuint vertexShader = compile(vertexShaderSource, GL_VERTEX_SHADER); GLuint fragmentShader = compile(fragmentShaderSource, GL_FRAGMENT_SHADER); @@ -21,7 +21,9 @@ ShaderProgram::ShaderProgram(Prototype prototype) : m_shaderProgramId(glCreatePr GLint isLinked = 0; glGetProgramiv(m_shaderProgramId, GL_LINK_STATUS, &isLinked); if (isLinked == 0) { - Log::logger().warn(R"(Failed to link shaderProgram "{}", "{}")", prototype.vertexPath, prototype.fragmentPath); + Log::logger().warn(R"(Failed to link shaderProgram "{}", "{}")", + prototype.vertexPath, + prototype.fragmentPath); } #ifdef NDEBUG @@ -50,19 +52,17 @@ void ShaderProgram::unbind() glUseProgram(0); } -auto ShaderProgram::parse(const std::filesystem::path &path) -> std::optional +auto ShaderProgram::parse(const std::filesystem::path &path) -> std::string { - std::fstream shaderfile; - shaderfile.open(path, std::ios::in); + std::fstream file; + file.open(path, std::ios::in); - if (!shaderfile.is_open()) { - Log::logger().warn("Shader \"{}\" not found", path.c_str()); - return {}; + if (!file.is_open()) { + Log::logger().critical(R"(Shader "{}" not found!)", path.string()); + std::terminate(); } - std::string contents((std::istreambuf_iterator(shaderfile)), (std::istreambuf_iterator())); - - return contents; + return {(std::istreambuf_iterator(file)), (std::istreambuf_iterator())}; } auto ShaderProgram::compile(const std::string &shaderSource, GLenum type) -> GLuint @@ -108,49 +108,49 @@ auto ShaderProgram::retrieveUniformLocation(std::string_view uniform_name) const return location; } -void ShaderProgram::setUniform(const std::string &name, bool value) const +void ShaderProgram::set_uniform(const std::string &name, bool value) const { GLint location = retrieveUniformLocation(name); glUniform1i(location, (int)value); } -void ShaderProgram::setUniform(const std::string &name, int value) const +void ShaderProgram::set_uniform(const std::string &name, int value) const { GLint location = retrieveUniformLocation(name); glUniform1i(location, value); } -void ShaderProgram::setUniform(const std::string &name, unsigned int value) const +void ShaderProgram::set_uniform(const std::string &name, unsigned int value) const { GLint location = retrieveUniformLocation(name); glUniform1ui(location, value); } -void ShaderProgram::setUniform(const std::string &name, float value) const +void ShaderProgram::set_uniform(const std::string &name, float value) const { GLint location = retrieveUniformLocation(name); glUniform1f(location, value); } -void ShaderProgram::setUniform(const std::string &name, glm::vec2 vector) const +void ShaderProgram::set_uniform(const std::string &name, glm::vec2 vector) const { GLint location = retrieveUniformLocation(name); glUniform2f(location, vector.x, vector.y); } -void ShaderProgram::setUniform(const std::string &name, glm::vec3 vector) const +void ShaderProgram::set_uniform(const std::string &name, glm::vec3 vector) const { GLint location = retrieveUniformLocation(name); glUniform3f(location, vector.x, vector.y, vector.z); } -void ShaderProgram::setUniform(const std::string &name, glm::mat3 matrix) const +void ShaderProgram::set_uniform(const std::string &name, glm::mat3 matrix) const { GLint location = retrieveUniformLocation(name); glUniformMatrix3fv(location, 1, GL_FALSE, glm::value_ptr(matrix)); } -void ShaderProgram::setUniform(const std::string &name, glm::mat4 matrix) const +void ShaderProgram::set_uniform(const std::string &name, glm::mat4 matrix) const { GLint location = retrieveUniformLocation(name); glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(matrix)); diff --git a/src/ShaderProgram.h b/src/ShaderProgram_.h similarity index 59% rename from src/ShaderProgram.h rename to src/ShaderProgram_.h index eb3177d..304beca 100644 --- a/src/ShaderProgram.h +++ b/src/ShaderProgram_.h @@ -26,19 +26,19 @@ public: auto retrieveUniformLocation(std::string_view uniform_name) const -> GLint; // May be rewritten... - void setUniform(const std::string &name, bool value) const; - void setUniform(const std::string &name, int value) const; - void setUniform(const std::string &name, unsigned int value) const; - void setUniform(const std::string &name, float value) const; - void setUniform(const std::string &name, glm::vec2 vector) const; - void setUniform(const std::string &name, glm::vec3 vector) const; - void setUniform(const std::string &name, glm::mat3 matrix) const; - void setUniform(const std::string &name, glm::mat4 matrix) const; + void set_uniform(const std::string &name, bool value) const; + void set_uniform(const std::string &name, int value) const; + void set_uniform(const std::string &name, unsigned int value) const; + void set_uniform(const std::string &name, float value) const; + void set_uniform(const std::string &name, glm::vec2 vector) const; + void set_uniform(const std::string &name, glm::vec3 vector) const; + void set_uniform(const std::string &name, glm::mat3 matrix) const; + void set_uniform(const std::string &name, glm::mat4 matrix) const; auto getShaderProgramId() const -> GLuint; private: - static auto parse(const std::filesystem::path &path) -> std::optional; + static auto parse(const std::filesystem::path &path) -> std::string; static auto compile(const std::string &shaderSource, GLenum type) -> GLuint; GLuint m_shaderProgramId; diff --git a/src/gltf_loader.cpp b/src/gltf_loader.cpp index 7aabd4b..7162276 100644 --- a/src/gltf_loader.cpp +++ b/src/gltf_loader.cpp @@ -1,6 +1,6 @@ #include "gltf_loader.h" -#include "util/Log.h" #include "definitions/attribute_locations.h" +#include "util/Log.h" #include @@ -73,7 +73,8 @@ static auto load_material(fx::gltf::Material const &material, fx::gltf::Document const &gltf, std::filesystem::path const &document_path, entt::resource_cache &material_cache, - entt::resource_cache &image_cache) -> entt::resource + entt::resource_cache &image_cache, + entt::resource_cache &shader_cache) -> entt::resource { auto base_color_texture_id = material.pbrMetallicRoughness.baseColorTexture.index; auto normal_texture_id = material.normalTexture.index; @@ -92,6 +93,10 @@ static auto load_material(fx::gltf::Material const &material, load_texture(normal_texture, gltf, document_path, Image::ColorFormat::RGB, image_cache); } + entt::hashed_string shader_hash(Material::SHADER_NAME.data()); + entt::resource shader = + shader_cache.load(shader_hash, Material::SHADER_NAME).first->second; + if (material.name.empty()) { Log::logger().warn("glTF material has no name."); } @@ -100,7 +105,8 @@ static auto load_material(fx::gltf::Material const &material, return material_cache .load(material_hash, Material{.base_color_texture = base_color_image, - .normal_map_texture = normal_map_image}) + .normal_map_texture = normal_map_image, + .shader = shader}) .first->second; } @@ -256,8 +262,8 @@ auto GltfLoader::operator()(std::filesystem::path const &document_path) -> resul // Load materials std::vector> materials; for (auto const &gltf_material : gltf.materials) { - entt::resource material = - load_material(gltf_material, gltf, document_path, material_cache, image_cache); + entt::resource material = load_material( + gltf_material, gltf, document_path, material_cache, image_cache, shader_cache); materials.push_back(material); } diff --git a/src/gltf_loader.h b/src/gltf_loader.h index 9756fc0..186d417 100644 --- a/src/gltf_loader.h +++ b/src/gltf_loader.h @@ -55,6 +55,7 @@ struct GltfLoader final entt::resource_cache &image_cache; entt::resource_cache &material_cache; entt::resource_cache &mesh_cache; + entt::resource_cache &shader_cache; entt::resource_cache &gltf_mesh_cache; entt::resource_cache &gltf_node_cache; diff --git a/src/material.cpp b/src/material.cpp index f0bf21b..d481952 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -1,7 +1,7 @@ #include "material.h" -#include "ShaderProgram.h" +#include "shader.h" -GpuMaterial::GpuMaterial(Material const &material) +GpuMaterial::GpuMaterial(Material const &material) : shader(material.shader) { int texture_unit_counter = 0; @@ -17,11 +17,11 @@ GpuMaterial::GpuMaterial(Material const &material) normal_map_texture = std::make_pair(GpuImage(material.normal_map_texture.value()), binding); } } -void GpuMaterial::bind(ShaderProgram const &shader_program) const +void GpuMaterial::bind() const { - auto bind_texture = [&shader_program](auto const &texture) { + auto bind_texture = [this](auto const &texture) { if (texture.has_value()) { - shader_program.setUniform(texture->second.uniform_name, texture->second.texture_unit); + shader->set_uniform(texture->second.uniform_name, texture->second.texture_unit); glActiveTexture(GL_TEXTURE0 + texture->second.texture_unit); glBindTexture(GL_TEXTURE_2D, texture->first.texture); } diff --git a/src/material.h b/src/material.h index 700f301..f7464c5 100644 --- a/src/material.h +++ b/src/material.h @@ -1,23 +1,27 @@ #pragma once #include "image.h" +#include "shader.h" #include #include -class ShaderProgram; +class Shader; struct Material { std::optional> base_color_texture; std::optional> normal_map_texture; + + static constexpr std::string_view SHADER_NAME{"standard_material"}; + entt::resource shader; }; struct GpuMaterial { GpuMaterial(Material const &material); - void bind(ShaderProgram const &shader_program) const; + void bind() const; struct Binding { @@ -27,4 +31,6 @@ struct GpuMaterial std::optional> base_color_texture; std::optional> normal_map_texture; + + entt::resource shader; }; diff --git a/src/render.h b/src/render.h new file mode 100644 index 0000000..fcf3ad2 --- /dev/null +++ b/src/render.h @@ -0,0 +1,43 @@ +#pragma once + +#include "camera.h" +#include "material.h" +#include "mesh.h" + +#include + +namespace Render { + +void render(entt::registry ®istry) +{ + auto mesh_view = registry.view(); + auto camera_view = registry.view(); + auto camera_entity = camera_view.front(); + auto [camera, camera_transform] = camera_view.get(camera_entity); + glm::mat4 view_projection_matrix = + camera.projection_matrix() * Camera::view_matrix(camera_transform); + + for (auto [entity, mesh, material, transform] : mesh_view.each()) { + auto shader = material.shader; + shader->bind(); + + // Bind textures + material.bind(); + + // Bind modelview matrix uniform + { + glm::mat4 modelViewProj = view_projection_matrix * transform.transform; + shader->set_uniform("u_modelViewProjMatrix", modelViewProj); + shader->set_uniform("u_modelMatrix", transform.transform); + shader->set_uniform("u_viewPosition", camera_transform.position()); + } + + glBindVertexArray(mesh.vao); + glDrawElements(GL_TRIANGLES, mesh.indices_count, mesh.indices_type, nullptr); + glBindVertexArray(0); + + Shader::unbind(); + } +} + +} // namespace Render diff --git a/src/resources/CubeMap.cpp b/src/resources/CubeMap.cpp index b33a007..e16e370 100644 --- a/src/resources/CubeMap.cpp +++ b/src/resources/CubeMap.cpp @@ -69,7 +69,7 @@ void AbstractCubeMap::bind(ShaderProgram *shaderProgram) const { std::string uniformName = "u_skybox"; - shaderProgram->setUniform(uniformName, 0); + shaderProgram->set_uniform(uniformName, 0); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_CUBE_MAP, m_glId); } diff --git a/src/shader.cpp b/src/shader.cpp new file mode 100644 index 0000000..ec2e66c --- /dev/null +++ b/src/shader.cpp @@ -0,0 +1,146 @@ +#include "shader.h" +#include "util/Log.h" + +#include +#include +#include + +Shader::Shader(std::string_view name, std::filesystem::path const &directory) + : program(glCreateProgram()) +{ + std::filesystem::path vertex_shader_path = directory / name; + vertex_shader_path.concat(".vert"); + + std::filesystem::path frag_shader_path = directory / name; + frag_shader_path.concat(".frag"); + + std::string vertex_shader_source = parse(vertex_shader_path); + std::string fragment_shader_source = parse(frag_shader_path); + + GLuint vertex_shader = compile(vertex_shader_source, GL_VERTEX_SHADER); + GLuint fragment_shader = compile(fragment_shader_source, GL_FRAGMENT_SHADER); + + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + + glLinkProgram(program); + + GLint linked = 0; + glGetProgramiv(program, GL_LINK_STATUS, &linked); + + if (linked == 0) { + Log::logger().warn(R"(Failed to link Shader "{}")", name); + } + +#ifdef NDEBUG + glDetachShader(m_ShaderId, vertexShader); + glDetachShader(m_ShaderId, fragmentShader); + + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); +#endif + + Log::logger().trace(R"(Loaded Shader "{}")", name); +} + +Shader::~Shader() +{ + glDeleteProgram(program); +} + +void Shader::bind() const +{ + glUseProgram(program); +} + +void Shader::unbind() +{ + glUseProgram(0); +} + +auto Shader::parse(const std::filesystem::path &path) -> std::string +{ + std::fstream file; + file.open(path, std::ios::in); + + if (!file.is_open()) { + Log::logger().critical(R"(Shader "{}" not found!)", path.string()); + std::terminate(); + } + + return {(std::istreambuf_iterator(file)), (std::istreambuf_iterator())}; +} + +auto Shader::compile(std::string_view source, GLenum type) -> GLuint +{ + GLuint program = glCreateShader(type); + auto const *src = source.data(); + glShaderSource(program, 1, &src, nullptr); + glCompileShader(program); + + int result{}; + glGetShaderiv(program, GL_COMPILE_STATUS, &result); + + if (result != GL_TRUE) { + int length{}; + glGetShaderiv(program, GL_INFO_LOG_LENGTH, &length); + + std::string message; + message.reserve(static_cast(length)); + + glGetShaderInfoLog(program, length, &length, message.data()); + Log::logger().error("Shader compilation failed: {}", message); + + return 0; + } + + return program; +} + +auto Shader::retrieveUniformLocation(std::string_view uniform_name) const -> GLint +{ + if (uniform_location_cache.find(uniform_name.data()) != uniform_location_cache.end()) { + return uniform_location_cache[uniform_name.data()]; + } + + GLint location = glGetUniformLocation(program, uniform_name.data()); + + if (location != -1) { + uniform_location_cache[uniform_name.data()] = location; + } else { + Log::logger().warn(R"(Uniform "{}" not found.)", uniform_name); + } + + return location; +} + +template +void Shader::set_uniform(std::string_view name, T value) const +{ + GLint location = retrieveUniformLocation(name); + + if constexpr (std::is_same_v || std::is_same_v) { + glUniform1i(location, (int)value); + } else if constexpr (std::is_same_v) { + glUniform1ui(location, value); + } else if constexpr (std::is_same_v) { + glUniform1f(location, value); + } else if constexpr (std::is_same_v) { + glUniform2f(location, value.x, value.y); //NOLINT(cppcoreguidelines-pro-type-union-access) + } else if constexpr (std::is_same_v) { + glUniform3f(location, value.x, value.y, value.z); //NOLINT(cppcoreguidelines-pro-type-union-access) + } else if constexpr (std::is_same_v) { + glUniformMatrix3fv(location, 1, GL_FALSE, glm::value_ptr(value)); + } else if constexpr (std::is_same_v) { + glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(value)); + } +} + +template void Shader::set_uniform(std::string_view, bool) const; +template void Shader::set_uniform(std::string_view, int) const; +template void Shader::set_uniform(std::string_view, unsigned) const; +template void Shader::set_uniform(std::string_view, float) const; +template void Shader::set_uniform(std::string_view, glm::vec2) const; +template void Shader::set_uniform(std::string_view, glm::vec3) const; +template void Shader::set_uniform(std::string_view, glm::mat3) const; +template void Shader::set_uniform(std::string_view, glm::mat4) const; diff --git a/src/shader.h b/src/shader.h new file mode 100644 index 0000000..64a67fb --- /dev/null +++ b/src/shader.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include +#include +#include + +struct Shader +{ + Shader(std::string_view name, std::filesystem::path const &directory); + + Shader(Shader const &) = delete; + auto operator=(Shader const &) -> Shader & = delete; + + Shader(Shader &&other) noexcept : program(other.program) { other.program = 0; } + auto operator=(Shader &&other) noexcept -> Shader & + { + program = other.program; + other.program = 0; + return *this; + }; + + ~Shader(); + + void bind() const; + static void unbind(); + + template + void set_uniform(std::string_view name, T value) const; + +private: + auto retrieveUniformLocation(std::string_view uniform_name) const -> GLint; + static auto parse(const std::filesystem::path &path) -> std::string; + static auto compile(std::string_view source, GLenum type) -> GLuint; + + GLuint program; + mutable std::unordered_map uniform_location_cache; +}; + +struct ShaderLoader +{ + using result_type = std::shared_ptr; + static constexpr std::string_view shader_directory{"data/shaders"}; + + auto operator()(std::string_view name) -> result_type + { + return std::make_shared(name, shader_directory); + } +};