diff --git a/CMakeLists.txt b/CMakeLists.txt index f4fccf2..7afaddf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,12 +4,24 @@ project(StoneEngine LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED YES) -set(USE_SYSTEM_GLM OFF CACHE BOOL "Uses glm from system libraries") -set(USE_SYSTEM_BOOST ON CACHE BOOL "Uses boost from system libraries") -set(SKIP_TESTS ON CACHE BOOL "Disables tests") -set(USE_SYSTEM_PAUSE OFF CACHE BOOL "Enables the use of Windows' pause after ending console only programs. (Windows only)") -set(FULL_CONFIGURE ON CACHE BOOL "Full configure project (may be used in pipeline to avoid the setup of all the dependencies)") -set(ENABLE_DOCS OFF CACHE BOOL "Builds documentation with doxygen") +option(USE_SYSTEM_GLM "Uses glm from system libraries" OFF) +option(USE_SYSTEM_BOOST "Uses boost from system libraries" ON) +option(SKIP_TESTS "Disables tests" ON) +option(USE_SYSTEM_PAUSE "Enables the use of Windows' pause after ending console only programs. (Windows only)" OFF) +option(FULL_CONFIGURE "Full configure project (may be used in pipeline to avoid the setup of all the dependencies)" ON) +option(ENABLE_DOCS "Builds documentation with doxygen" OFF) + +set(VALID_GRAPHICS_APIs "AUTO" "VULKAN" "OPENGL") +option(GRAPHICS_API "Configures the graphics API to use, can be AUTO, VULKAN, OPENGL" "AUTO") + +string(TOUPPER ${GRAPHICS_API} tmp) +set(GRAPHICS_API "${tmp}") +unset(tmp) + +if (NOT GRAPHICS_API IN_LIST VALID_GRAPHICS_APIs) + list(JOIN VALID_GRAPHICS_APIs ", " lst_str) + message(FATAL_ERROR "Expecting GRAPHICS_API value to be in the list [${lst_str}]") +endif () # TODO: dependencies are not mandatory set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") @@ -19,64 +31,72 @@ include(OSDetection) include(FetchContent) -if ( FULL_CONFIGURE ) - # Require Vulkan - find_package(Vulkan) - find_package(Python REQUIRED COMPONENTS Interpreter) - - if ( ${Python_VERSION_MAJOR} STRLESS 3 AND ${Python_VERSION_MINOR} STRLESS 10 ) - message(FATAL_ERROR "Got bad version of python: ${Python_VERSION}") - endif () - - file(DOWNLOAD https://raw.githubusercontent.com/nothings/stb/013ac3beddff3dbffafd5177e7972067cd2b5083/stb_image.h - ${CMAKE_BINARY_DIR}/include/stb_image.h - EXPECTED_HASH SHA256=594c2fe35d49488b4382dbfaec8f98366defca819d916ac95becf3e75f4200b3 - ) - - # Require ShaderC (google's shader compiler, compatible with GLSL or HLSL) - FetchContent_Declare( - shaderc - GIT_REPOSITORY https://github.com/google/shaderc - GIT_TAG 9a658e242ad4d1a4b3491383c1c58c780e3c01ff # tag/v2024.1 - ) - FetchContent_GetProperties(shaderc) - - if ( NOT shaderc_POPULATED ) - FetchContent_Populate(shaderc) - set($ENV{GIT_SYNC_DEPS_QUIET} 1) - execute_process( - COMMAND ${Python_EXECUTABLE} utils/git-sync-deps - WORKING_DIRECTORY ${shaderc_SOURCE_DIR} - COMMAND_ERROR_IS_FATAL ANY - OUTPUT_FILE git-sync-deps.out - ) - - set(SHADERC_SKIP_TESTS ON CACHE INTERNAL "Disables tests in ShaderC") - set(SHADERC_SKIP_EXAMPLES ON CACHE INTERNAL "Disables examples in ShaderC") - set(SHADERC_SKIP_INSTALL ON CACHE INTERNAL "Disables installation in ShaderC") - - add_subdirectory(${shaderc_SOURCE_DIR} ${shaderc_BINARY_DIR}) - endif () - # Fetches GLM - if ( USE_SYSTEM_GLM ) - find_package(glm) - - if ( NOT GLM_FOUND ) - message(STATUS "Couldn't find glm in system, will use online source instead") - endif () - else () - message(STATUS "Will fetch glm from online source") - endif () - - if ( NOT GLM_FOUND ) - FetchContent_Declare( - glm - GIT_REPOSITORY https://github.com/g-truc/glm.git - GIT_TAG 33b0eb9fa336ffd8551024b1d2690e418014553b # refs/tags/1.0.0 - ) +if (GRAPHICS_API STREQUAL "AUTO") + set(GRAPHICS_API "OPENGL") +endif () - set(GLM_ENABLE_CXX_17 ON CACHE INTERNAL "Forces C++17 in GLM") +if (FULL_CONFIGURE) + add_library(gfx_api INTERFACE) + if (GRAPHICS_API STREQUAL "VULKAN") + # Require Vulkan + message(STATUS "Setup gfx_api to use Vulkan rendering API") + find_package(Vulkan REQUIRED) + target_link_libraries(gfx_api INTERFACE ${Vulkan_LIBRARIES}) + target_include_directories(gfx_api INTERFACE ${Vulkan_INCLUDE_DIRS}) + elseif (GRAPHICS_API STREQUAL "OPENGL") + message(STATUS "Setup gfx_api to use OPENGL rendering API") + find_package(OpenGL REQUIRED) + find_package(GLEW REQUIRED) + if (APPLE) + target_link_libraries(gfx_api INTERFACE "-framework OpenGL" GLEW::GLEW) + else () + target_link_libraries(gfx_api INTERFACE OpenGL::GL GLEW::GLEW) + endif () + target_include_directories(gfx_api INTERFACE ${GLEW_INCLUDE_DIRS}) + else () + message(FATAL_ERROR "Unexpected value for GRAPHICS_API, unknown API ${GRAPHICS_API}") + endif() + + find_package(Python REQUIRED COMPONENTS Interpreter) + + if (${Python_VERSION_MAJOR} STRLESS 3 AND ${Python_VERSION_MINOR} STRLESS 10) + message(FATAL_ERROR "Got bad version of python: ${Python_VERSION}") + endif () + + file(DOWNLOAD https://raw.githubusercontent.com/nothings/stb/013ac3beddff3dbffafd5177e7972067cd2b5083/stb_image.h + ${CMAKE_BINARY_DIR}/include/stb_image.h + EXPECTED_HASH SHA256=594c2fe35d49488b4382dbfaec8f98366defca819d916ac95becf3e75f4200b3 + ) + + # Require ShaderC (google's shader compiler, compatible with GLSL or HLSL) + FetchContent_Declare( + shaderc + GIT_REPOSITORY https://github.com/google/shaderc + GIT_TAG 9a658e242ad4d1a4b3491383c1c58c780e3c01ff # tag/v2024.1 + ) + FetchContent_GetProperties(shaderc) + + if (NOT shaderc_POPULATED) + FetchContent_Populate(shaderc) + set($ENV{GIT_SYNC_DEPS_QUIET} 1) + execute_process( + COMMAND ${Python_EXECUTABLE} utils/git-sync-deps + WORKING_DIRECTORY ${shaderc_SOURCE_DIR} + COMMAND_ERROR_IS_FATAL ANY + OUTPUT_FILE git-sync-deps.out + ) + + set(SHADERC_SKIP_TESTS ON CACHE INTERNAL "Disables tests in ShaderC") + set(SHADERC_SKIP_EXAMPLES ON CACHE INTERNAL "Disables examples in ShaderC") + set(SHADERC_SKIP_INSTALL ON CACHE INTERNAL "Disables installation in ShaderC") + + add_subdirectory(${shaderc_SOURCE_DIR} ${shaderc_BINARY_DIR}) + endif () + + # Fetches GLM + if (USE_SYSTEM_GLM) + find_package(glm) FetchContent_MakeAvailable(glm) set(GLM_FOUND ON) @@ -88,102 +108,116 @@ if ( FULL_CONFIGURE ) ) endif () - # Fetches GoogleTest only if needed - if ( NOT SKIP_TESTS ) - FetchContent_Declare( - gtest - GIT_REPOSITORY https://github.com/google/googletest - GIT_TAG f8d7d77c06936315286eb55f8de22cd23c188571 - ) - - # For Windows: Prevent overriding the parent project's compiler/linker settings - set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - FetchContent_MakeAvailable(gtest) - set_target_properties(gtest PROPERTIES SYSTEM ON) - - if ( NOT SKIP_TESTS ) - enable_testing() - endif () - endif () - - # Setup Glfw - FetchContent_Declare( - glfw - GIT_REPOSITORY https://github.com/glfw/glfw - GIT_TAG 7b6aead9fb88b3623e3b3725ebb42670cbe4c579 # tag/3.4 - ) - FetchContent_GetProperties(glfw) - - if ( NOT glfw_POPULATED ) - FetchContent_Populate(glfw) - - set(GLFW_BUILD_EXAMPLES OFF CACHE INTERNAL "Build the GLFW example programs") - set(GLFW_BUILD_TESTS OFF CACHE INTERNAL "Build the GLFW test programs") - set(GLFW_BUILD_DOCS OFF CACHE INTERNAL "Build the GLFW documentation") - set(GLFW_INSTALL OFF CACHE INTERNAL "Generate installation target") - - if ( STONE_ENGINE_OS_NAME STREQUAL Linux ) - set(GLFW_BUILD_WAYLAND OFF CACHE INTERNAL "Enables the use of Wayland (Linux only)") - set(GLFW_BUILD_X11 ON CACHE INTERNAL "Enables the use of X11 (Linux only)") - endif () - - add_subdirectory(${glfw_SOURCE_DIR} ${glfw_BINARY_DIR}) - endif () - - set_target_properties(glfw PROPERTIES SYSTEM ON) - - # Setup Json - FetchContent_Declare( - json - GIT_REPOSITORY https://github.com/nlohmann/json - GIT_TAG 9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03 # tag/v3.11.3 - ) - FetchContent_MakeAvailable(json) - add_library(json ALIAS nlohmann_json) - - set_target_properties(nlohmann_json PROPERTIES SYSTEM ON) - - # Setup Assimp - FetchContent_Declare( - assimp - GIT_REPOSITORY https://github.com/assimp/assimp - GIT_TAG 8b9ed34eaa3e6ad24254cb7e058fb9150f66b865 # tag/v5.4.0 - ) - set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) - set(ASSIMP_BUILD_TESTS OFF CACHE BOOL "" FORCE) - set(ASSIMP_INJECT_DEBUG_POSTFIX OFF CACHE BOOL "" FORCE) - set(ASSIMP_INSTALL OFF CACHE BOOL "" FORCE) - FetchContent_MakeAvailable(assimp) - - set_target_properties(assimp PROPERTIES SYSTEM ON) - - if ( USE_SYSTEM_BOOST ) - find_package(Boost 1.74.0) - endif () - - if ( NOT USE_SYSTEM_BOOST OR NOT Boost_FOUND ) - set(BOOST_GIT_TAG a7090e8ce184501cfc9e80afa6cafb5bfd3b371c) # tag/boost-1.74.0 - FetchContent_Declare(Boost - GIT_REPOSITORY https://github.com/boostorg/boost - GIT_TAG ${BOOST_GIT_TAG} - ) - if ( NOT Boost_POPULATED ) - message(STATUS "Fetching Boost from git repository") - - FetchContent_Populate(Boost) - - message(STATUS "Configuring Boost") - add_subdirectory(${boost_SOURCE_DIR} ${boost_BINARY_DIR} EXCLUDE_FROM_ALL) - endif () - endif () + if (NOT GLM_FOUND) + FetchContent_Declare( + glm + GIT_REPOSITORY https://github.com/g-truc/glm.git + GIT_TAG 33b0eb9fa336ffd8551024b1d2690e418014553b # refs/tags/1.0.0 + ) + + set(GLM_ENABLE_CXX_17 ON CACHE INTERNAL "Forces C++17 in GLM") + + FetchContent_MakeAvailable(glm) + set(GLM_FOUND ON) + set_target_properties(glm PROPERTIES SYSTEM ON) + endif () + + # Fetches GoogleTest only if needed + if (NOT SKIP_TESTS) + FetchContent_Declare( + gtest + GIT_REPOSITORY https://github.com/google/googletest + GIT_TAG f8d7d77c06936315286eb55f8de22cd23c188571 + ) + + # For Windows: Prevent overriding the parent project's compiler/linker settings + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(gtest) + set_target_properties(gtest PROPERTIES SYSTEM ON) + + if (NOT SKIP_TESTS) + enable_testing() + endif () + endif () + + # Setup Glfw + FetchContent_Declare( + glfw + GIT_REPOSITORY https://github.com/glfw/glfw + GIT_TAG 7b6aead9fb88b3623e3b3725ebb42670cbe4c579 # tag/3.4 + ) + FetchContent_GetProperties(glfw) + + if (NOT glfw_POPULATED) + FetchContent_Populate(glfw) + + set(GLFW_BUILD_EXAMPLES OFF CACHE INTERNAL "Build the GLFW example programs") + set(GLFW_BUILD_TESTS OFF CACHE INTERNAL "Build the GLFW test programs") + set(GLFW_BUILD_DOCS OFF CACHE INTERNAL "Build the GLFW documentation") + set(GLFW_INSTALL OFF CACHE INTERNAL "Generate installation target") + + if (STONE_ENGINE_OS_NAME STREQUAL Linux) + set(GLFW_BUILD_WAYLAND OFF CACHE INTERNAL "Enables the use of Wayland (Linux only)") + set(GLFW_BUILD_X11 ON CACHE INTERNAL "Enables the use of X11 (Linux only)") + endif () + + add_subdirectory(${glfw_SOURCE_DIR} ${glfw_BINARY_DIR}) + endif () + + set_target_properties(glfw PROPERTIES SYSTEM ON) + + # Setup Json + FetchContent_Declare( + json + GIT_REPOSITORY https://github.com/nlohmann/json + GIT_TAG 9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03 # tag/v3.11.3 + ) + FetchContent_MakeAvailable(json) + add_library(json ALIAS nlohmann_json) + + set_target_properties(nlohmann_json PROPERTIES SYSTEM ON) + + # Setup Assimp + FetchContent_Declare( + assimp + GIT_REPOSITORY https://github.com/assimp/assimp + GIT_TAG 8b9ed34eaa3e6ad24254cb7e058fb9150f66b865 # tag/v5.4.0 + ) + set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) + set(ASSIMP_BUILD_TESTS OFF CACHE BOOL "" FORCE) + set(ASSIMP_INJECT_DEBUG_POSTFIX OFF CACHE BOOL "" FORCE) + set(ASSIMP_INSTALL OFF CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(assimp) + + set_target_properties(assimp PROPERTIES SYSTEM ON) + + if (USE_SYSTEM_BOOST) + find_package(Boost 1.74.0) + endif () + + if (NOT USE_SYSTEM_BOOST OR NOT Boost_FOUND) + set(BOOST_GIT_TAG a7090e8ce184501cfc9e80afa6cafb5bfd3b371c) # tag/boost-1.74.0 + FetchContent_Declare(Boost + GIT_REPOSITORY https://github.com/boostorg/boost + GIT_TAG ${BOOST_GIT_TAG} + ) + if (NOT Boost_POPULATED) + message(STATUS "Fetching Boost from git repository") + + FetchContent_Populate(Boost) + + message(STATUS "Configuring Boost") + add_subdirectory(${boost_SOURCE_DIR} ${boost_BINARY_DIR} EXCLUDE_FROM_ALL) + endif () + endif () endif () add_subdirectory(Engine) -if ( FULL_CONFIGURE ) - add_subdirectory(examples) +if (FULL_CONFIGURE) + add_subdirectory(examples) endif () -if ( ENABLE_DOCS ) - include(Doxygen) +if (ENABLE_DOCS) + include(Doxygen) endif () diff --git a/CMakePresets.json b/CMakePresets.json index 658eb54..1c6642c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -25,7 +25,8 @@ "SKIP_TESTS": { "type": "BOOL", "value": "OFF" - } + }, + "GRAPHICS_API": "AUTO" }, "condition": { "type": "notEquals", diff --git a/Engine/Core/include/Core/Image/ImageSource.hpp b/Engine/Core/include/Core/Image/ImageSource.hpp index 958f714..dac6e1c 100644 --- a/Engine/Core/include/Core/Image/ImageSource.hpp +++ b/Engine/Core/include/Core/Image/ImageSource.hpp @@ -33,7 +33,7 @@ class ImageSource : public Assets::Resource { [[nodiscard]] Size getSize() const; void unloadData(); - void loadData(bool force); + void loadData(bool force = false); [[nodiscard]] bool isLoaded() const; [[nodiscard]] std::shared_ptr getLoadedImage() const; diff --git a/Engine/Render/CMakeLists.txt b/Engine/Render/CMakeLists.txt index a934517..d90e96e 100644 --- a/Engine/Render/CMakeLists.txt +++ b/Engine/Render/CMakeLists.txt @@ -1,9 +1,21 @@ -setup_module( + +if (GRAPHICS_API STREQUAL "VULKAN") + setup_module( NAME render - TARGET_DEPS logging widgets utils + TARGET_DEPS logging widgets utils gfx_api VARIABLE_DEPS Vulkan_LIBRARIES Vulkan_INCLUDE_DIRS SPECIAL_HEADER_PATHS ${Vulkan_INCLUDE_DIRS} SPECIAL_LIBS ${Vulkan_LIBRARIES} - ENABLE_TESTS FATAL_ERROR -) + ) +elseif (GRAPHICS_API STREQUAL "OPENGL") + setup_module( + NAME render + TARGET_DEPS logging widgets utils gfx_api + VARIABLE_DEPS OpenGL::GL GLEW::GLEW + SPECIAL_LIBS OpenGL::GL GLEW::GLEW + FATAL_ERROR + ) +else () + message(FATAL_ERROR "Unexpected value for GRAPHICS_API, unknown API ${GRAPHICS_API}") +endif() diff --git a/Engine/Render/include/Render/OpenGL/OpenGLRenderer.hpp b/Engine/Render/include/Render/OpenGL/OpenGLRenderer.hpp new file mode 100644 index 0000000..628343a --- /dev/null +++ b/Engine/Render/include/Render/OpenGL/OpenGLRenderer.hpp @@ -0,0 +1,37 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "Render/OpenGL/RendererSettings.hpp" +#include "Render/Renderer.hpp" + + +namespace Stone::Render::OpenGL { + +class OpenGLDirector; +class OpenGLResources; + +class OpenGLRenderer : public Renderer { +public: + OpenGLRenderer() = default; + + ~OpenGLRenderer() override = default; + + void initialize(RendererSettings &settings); + + /** Renderer */ + + void updateRenderablesInNode(const std::shared_ptr &rootNode) override; + void renderWorld(const std::shared_ptr &world) override; + + void updateFrameSize(const std::pair &size) override; + + const std::shared_ptr &getDirector() const; + const std::shared_ptr &getResources() const; + +private: + std::shared_ptr _director; + std::shared_ptr _resources; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/include/Render/OpenGL/RendererSettings.hpp b/Engine/Render/include/Render/OpenGL/RendererSettings.hpp new file mode 100644 index 0000000..b669981 --- /dev/null +++ b/Engine/Render/include/Render/OpenGL/RendererSettings.hpp @@ -0,0 +1,21 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include +#include + + +namespace Stone::Render::OpenGL { + +enum class RenderingMethod { + Forward, + Deferred +}; + +struct RendererSettings { + std::pair frame_size = {}; + RenderingMethod rendering_method = RenderingMethod::Deferred; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/include/Render/OpenGL/Shader/ShaderGenerator.hpp b/Engine/Render/include/Render/OpenGL/Shader/ShaderGenerator.hpp new file mode 100644 index 0000000..4c89cab --- /dev/null +++ b/Engine/Render/include/Render/OpenGL/Shader/ShaderGenerator.hpp @@ -0,0 +1,28 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "Scene/Renderable/Material.hpp" + +namespace Stone::Scene { +class FragmentShader; +} + +namespace Stone::Render::OpenGL { + +class ShaderGenerator { + + +public: + ShaderGenerator() = default; + + ~ShaderGenerator() = default; + + void generateFragmentShaderTemplate(const Scene::MaterialInputSignature ¶ms, std::ostream &output); + + void generateOpenGlForwardFragmentShader(const Scene::MaterialInputSignature ¶ms, + const std::shared_ptr &shader, + std::ostream &output); +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/include/Render/Renderer.hpp b/Engine/Render/include/Render/Renderer.hpp index 70138de..4965f3e 100644 --- a/Engine/Render/include/Render/Renderer.hpp +++ b/Engine/Render/include/Render/Renderer.hpp @@ -2,7 +2,7 @@ #pragma once -#include "Scene/ISceneRenderer.hpp" +#include "Scene/Renderer/ISceneRenderer.hpp" #include @@ -15,7 +15,7 @@ class Renderer : public std::enable_shared_from_this, public Scene::IS virtual ~Renderer() = default; - virtual void updateFrameSize(std::pair size) = 0; + virtual void updateFrameSize(const std::pair &size) = 0; }; } // namespace Stone::Render diff --git a/Engine/Render/include/Render/Vulkan/VulkanRenderer.hpp b/Engine/Render/include/Render/Vulkan/VulkanRenderer.hpp index a43fffa..5c5f210 100644 --- a/Engine/Render/include/Render/Vulkan/VulkanRenderer.hpp +++ b/Engine/Render/include/Render/Vulkan/VulkanRenderer.hpp @@ -27,10 +27,10 @@ class VulkanRenderer : public Renderer { /** Renderer */ - void updateDataForWorld(const std::shared_ptr &world) override; + void updateRenderablesInNode(const std::shared_ptr &rootNode) override; void renderWorld(const std::shared_ptr &world) override; - void updateFrameSize(std::pair size) override; + void updateFrameSize(const std::pair &size) override; /** VulkanRenderer */ diff --git a/Engine/Render/src/Render/OpenGL/GlElements/GlFramebuffer.hpp b/Engine/Render/src/Render/OpenGL/GlElements/GlFramebuffer.hpp new file mode 100644 index 0000000..d0ef3d2 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/GlElements/GlFramebuffer.hpp @@ -0,0 +1,65 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include + +namespace Stone::Render::OpenGL { + +class GlFramebufferMesh { +public: + GlFramebufferMesh() { + float vertices[] = { + // pos | uv + -1.0f, -1.0f, 0.0f, 0.0f, // bottom left + 1.0f, -1.0f, 1.0f, 0.0f, // bottom right + -1.0f, 1.0f, 0.0f, 1.0f, // top left + 1.0f, 1.0f, 1.0f, 1.0f, // top right + }; + + unsigned int indices[] = { + 0, 3, 2, // top left + 0, 1, 3 // bottom right + }; + + glGenVertexArrays(1, &vao); + glGenBuffers(1, &vbo); + glGenBuffers(1, &ebo); + + glBindVertexArray(vao); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + + // position attribute + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0); + glEnableVertexAttribArray(0); + // texture coord attribute + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float))); + glEnableVertexAttribArray(1); + + glBindVertexArray(0); + } + + virtual ~GlFramebufferMesh() { + glDeleteVertexArrays(1, &vao); + glDeleteBuffers(1, &vbo); + glDeleteBuffers(1, &ebo); + } + + void draw() { + glBindVertexArray(vao); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + } + +private: + GLuint vbo; + GLuint vao; + GLuint ebo; +}; + + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/GlElements/GlGBuffer.hpp b/Engine/Render/src/Render/OpenGL/GlElements/GlGBuffer.hpp new file mode 100644 index 0000000..ca628d1 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/GlElements/GlGBuffer.hpp @@ -0,0 +1,109 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "../Renderable/Texture.hpp" +#include "GlFramebuffer.hpp" +#include "GlShaders.hpp" + +#include + +namespace Stone::Render::OpenGL { + +struct GlGBuffer { + + GlGBuffer(unsigned int width, unsigned int height) : width(width), height(height) { + glGenFramebuffers(1, &gBuffer); + glBindFramebuffer(GL_FRAMEBUFFER, gBuffer); + + gPosition = std::make_unique(width, height, GL_RGB16F, GL_RGB, GL_FLOAT, GL_CLAMP_TO_EDGE, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gPosition->getGlTexture(), 0); + + gNormal = std::make_unique(width, height, GL_RGB16F, GL_RGB, GL_FLOAT, GL_CLAMP_TO_EDGE, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, gNormal->getGlTexture(), 0); + + gAlbedoSpec = + std::make_unique(width, height, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, GL_CLAMP_TO_EDGE, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, gAlbedoSpec->getGlTexture(), 0); + + unsigned int attachments[3] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2}; + glDrawBuffers(3, attachments); + + GlVertexShader vertexShader(R"(#version 400 core + +layout (location = 0) in vec2 position; +layout (location = 1) in vec2 uv; + +out vec2 fragUV; + +void main() { + gl_Position = vec4(position, 0.0, 1.0); + fragUV = uv; +} + +)"); + + // TODO: Write fragment shader using all lights and pbr + GlFragmentShader fragmentShader(R"(#version 400 core + +in vec2 fragUV; + +uniform sampler2D u_position; +uniform sampler2D u_normal; +uniform sampler2D u_albedo_spec; + +out vec4 FragColor; + +void main() { + // FragColor = vec4(fragUV, 1.0, 1.0); + FragColor = vec4(vec3(texture(u_albedo_spec, fragUV)), 1.0); +} + +)"); + + program = std::make_shared(vertexShader, fragmentShader); + + frameMesh = std::make_shared(); + } + + virtual ~GlGBuffer() { + glDeleteFramebuffers(1, &gBuffer); + } + + void bind() { + glBindFramebuffer(GL_FRAMEBUFFER, gBuffer); + } + + void unbind() { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + void render() { + program->use(); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_STENCIL_TEST); + + program->setUniformTexture("u_position", *gPosition, 0); + program->setUniformTexture("u_normal", *gNormal, 1); + program->setUniformTexture("u_albedo_spec", *gAlbedoSpec, 2); + + frameMesh->draw(); + } + + unsigned int width; + unsigned int height; + + GLuint gBuffer; + + std::unique_ptr gPosition; + std::unique_ptr gNormal; + std::unique_ptr gAlbedoSpec; + + + std::shared_ptr frameMesh; + + std::shared_ptr program; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/GlElements/GlShaders.cpp b/Engine/Render/src/Render/OpenGL/GlElements/GlShaders.cpp new file mode 100644 index 0000000..ac7d1a1 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/GlElements/GlShaders.cpp @@ -0,0 +1,254 @@ +// Copyright 2024 Stone-Engine + +#include "GlShaders.hpp" + +#include "Render/OpenGL/Shader/ShaderGenerator.hpp" + +#include +#include + +namespace Stone::Render::OpenGL { + +// MARK: Compilation + +GLuint compileSource(const std::string &source, GLenum type) { + GLuint shaderId = glCreateShader(type); + if (shaderId == 0) { + throw std::runtime_error("Failed to create shader."); + } + + const char *source_c = source.c_str(); + glShaderSource(shaderId, 1, &source_c, nullptr); + glCompileShader(shaderId); + + GLint compiled = 0; + glGetShaderiv(shaderId, GL_COMPILE_STATUS, &compiled); + if (!compiled) { + GLint log_length = 0; + glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &log_length); + + std::vector log(log_length); + glGetShaderInfoLog(shaderId, log_length, &log_length, log.data()); + + glDeleteShader(shaderId); + + throw std::runtime_error("Failed to compile shader: " + std::string(log.data())); + } + + return shaderId; +} + +GLuint loadSpirv(const char *spirv_content, GLsizei spirv_length, GLenum type) { + (void)type; + + GLuint shaderId = glCreateShader(GL_VERTEX_SHADER); + if (shaderId == 0) { + throw std::runtime_error("Failed to create shader."); + } + + glShaderBinary(1, &shaderId, GL_SHADER_BINARY_FORMAT_SPIR_V_ARB, spirv_content, spirv_length); + glSpecializeShader(shaderId, "main", 0, nullptr, nullptr); + + int compiled = 0; + glGetShaderiv(shaderId, GL_COMPILE_STATUS, &compiled); + + if (!compiled) { + GLint log_length = 0; + glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &log_length); + + std::vector log(log_length); + glGetShaderInfoLog(shaderId, log_length, &log_length, log.data()); + + glDeleteShader(shaderId); + + throw std::runtime_error("Failed to compile shader: " + std::string(log.data())); + } + + return shaderId; +} + +// MARK: Vertex + +const char *basicVertexShaderSource = R"shader( +#version 400 core + +layout (location = 0) in vec3 position; +layout (location = 1) in vec3 normal; +layout (location = 2) in vec3 tangent; +layout (location = 3) in vec3 bitangent; +layout (location = 4) in vec2 uv; + +uniform mat3 u_mat_normal; +uniform mat4 u_mat_projection; +uniform mat4 u_mat_view; +uniform mat4 u_mat_model; +uniform vec3 u_camera_position; + +out FRAG_DATA { + vec3 wposition; + vec2 uv; + vec3 wnormal; + vec3 wtangent; + vec3 wbitangent; +} vs_out; + +void main() +{ + vs_out.wposition = vec3(u_mat_model * vec4(position, 1.0)); + gl_Position = u_mat_projection * u_mat_view * vec4(vs_out.wposition, 1.0); + vs_out.wnormal = u_mat_normal * normal; + vs_out.wtangent = u_mat_normal * tangent; + vs_out.wbitangent = u_mat_normal * bitangent; + vs_out.uv = uv; +} + +)shader"; + +const char *basicSkinVertexShaderSource = R"shader( +#version 400 core + +layout(location = 0) in vec3 position; +layout(location = 1) in vec3 normal; +layout(location = 2) in vec3 tangent; +layout(location = 3) in vec3 bitangent; +layout(location = 4) in vec2 uv; +layout(location = 5) in vec4 boneWeights; +layout(location = 6) in ivec4 boneIDs; + +out vec3 fragNormal; +out vec2 fragUV; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; + +const int MAX_BONES = 100; +uniform mat4 bones[MAX_BONES]; + +void main() { + fragNormal = normal; + fragUV = uv; + + mat4 boneTransform = mat4(0.0); + for (int i = 0; i < 4; i++) { + boneTransform += boneWeights[i] * bones[boneIDs[i]]; + } + + gl_Position = projection * view * model * boneTransform * vec4(position, 1.0); +} + +)shader"; + +std::unique_ptr GlVertexShader::makeStandardMeshShader() { + auto glShader = std::make_unique(basicVertexShaderSource); + return glShader; +} + +std::unique_ptr GlVertexShader::makeStandardSkinMeshShader() { + auto glShader = std::make_unique(basicSkinVertexShaderSource); + return glShader; +} + +std::unique_ptr GlVertexShader::makeStandardInstancedMeshShader() { + throw std::runtime_error("Not implemented."); +} + +// MARK: Fragment + +std::unique_ptr +GlFragmentShader::makeStandardForwardShader(const Scene::MaterialInputSignature ¶ms) { + std::stringstream source; + + Render::OpenGL::ShaderGenerator generator; + generator.generateOpenGlForwardFragmentShader(params, nullptr, source); + + return std::make_unique(source.str().c_str()); +} + +std::unique_ptr +GlFragmentShader::makeStandardDeferredShader(const Scene::MaterialInputSignature ¶ms) { + (void)params; + throw std::runtime_error("deffered shader is not implemented."); +} + +// MARK: Program + +GlShaderProgram::GlShaderProgram(const GlVertexShader &vertexShader, const GlFragmentShader &fragmentShader) + : _gl_program(0) { + _gl_program = glCreateProgram(); + glAttachShader(_gl_program, vertexShader.getGLShader()); + glAttachShader(_gl_program, fragmentShader.getGLShader()); + glLinkProgram(_gl_program); + + GLint success; + glGetProgramiv(_gl_program, GL_LINK_STATUS, &success); + if (!success) { + GLint log_length = 0; + glGetProgramiv(_gl_program, GL_INFO_LOG_LENGTH, &log_length); + + std::vector log(log_length); + glGetProgramInfoLog(_gl_program, log_length, &log_length, log.data()); + + glDeleteProgram(_gl_program); + + throw std::runtime_error("Failed to link program: " + std::string(log.data())); + } +} + +GlShaderProgram::~GlShaderProgram() { + if (_gl_program != 0) + glDeleteProgram(_gl_program); +} + +GLuint GlShaderProgram::getGlProgram() const { + return _gl_program; +} + +void GlShaderProgram::use() const { + glUseProgram(_gl_program); +} + +GLint GlShaderProgram::getUniformLocation(const std::string &name) const { + return glGetUniformLocation(_gl_program, name.c_str()); +} + +GLint GlShaderProgram::getUniformLocation(const Scene::Material::Location &location) const { + if (std::holds_alternative(location)) { + const auto &name(std::get(location)); + return getUniformLocation(name); + } else if (std::holds_alternative(location)) { + return std::get(location); + } + return -1; +} + +void GlShaderProgram::setUniform(const Scene::Material::Location &location, int integer) const { + glUniform1i(getUniformLocation(location), integer); +} + +void GlShaderProgram::setUniform(const Scene::Material::Location &location, float scalar) const { + glUniform1f(getUniformLocation(location), scalar); +} + +void GlShaderProgram::setUniform(const Scene::Material::Location &location, const glm::vec3 &vec3) const { + glUniform3fv(getUniformLocation(location), 1, glm::value_ptr(vec3)); +} + +void GlShaderProgram::setUniform(const Scene::Material::Location &location, const glm::mat3 &mat3) const { + glUniformMatrix3fv(getUniformLocation(location), 1, GL_FALSE, glm::value_ptr(mat3)); +} + +void GlShaderProgram::setUniform(const Scene::Material::Location &location, const glm::mat4 &mat4) const { + glUniformMatrix4fv(getUniformLocation(location), 1, GL_FALSE, glm::value_ptr(mat4)); +} + +void GlShaderProgram::setUniformTexture(const Scene::Material::Location &location, const Texture &texture, + int textureIndex) const { + assert(textureIndex >= 0 && textureIndex < 32); + glActiveTexture(GL_TEXTURE0 + textureIndex); + glBindTexture(GL_TEXTURE_2D, texture.getGlTexture()); + glUniform1i(getUniformLocation(location), (GLint)texture.getGlTexture()); +} + + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/GlElements/GlShaders.hpp b/Engine/Render/src/Render/OpenGL/GlElements/GlShaders.hpp new file mode 100644 index 0000000..15cd4bf --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/GlElements/GlShaders.hpp @@ -0,0 +1,99 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "../Renderable/Texture.hpp" +#include "Scene/Renderable/Material.hpp" +#include "Scene/Renderable/Shader.hpp" +#include "Utils/FileSystem.hpp" + +#include + +namespace Stone::Render::OpenGL { + +GLuint compileSource(const std::string &source, GLenum type); + +GLuint loadSpirv(const char *spirv_content, GLsizei spirv_length, GLenum type); + +class GlShaderBase { +public: + GlShaderBase(const Scene::AShader &shader, GLenum type) { + + auto [contentType, content] = shader.getContent(); + + using ContentType = Scene::AShader::ContentType; + switch (contentType) { + case ContentType::SourceCode: _gl_shader = compileSource(content, type); break; + case ContentType::SourceFile: _gl_shader = compileSource(Utils::readTextFile(content), type); break; + case ContentType::CompiledCode: _gl_shader = loadSpirv(content.data(), content.size(), type); break; + case ContentType::CompiledFile: + auto fileContent = Utils::readBinaryFile(content); + _gl_shader = loadSpirv(fileContent.data(), fileContent.size(), type); + break; + } + } + + GlShaderBase(const char *source, GLenum type) { + _gl_shader = compileSource(source, type); + } + + virtual ~GlShaderBase() { + if (_gl_shader != 0) + glDeleteShader(_gl_shader); + } + + GLuint getGLShader() const { + return _gl_shader; + } + +protected: + GLuint _gl_shader = 0; +}; + +class GlVertexShader : public GlShaderBase { +public: + GlVertexShader(const char *source) : GlShaderBase(source, GL_VERTEX_SHADER) { + } + + static std::unique_ptr makeStandardMeshShader(); + static std::unique_ptr makeStandardSkinMeshShader(); + static std::unique_ptr makeStandardInstancedMeshShader(); +}; + +class GlFragmentShader : public GlShaderBase { +public: + GlFragmentShader(const Scene::AShader &shader) : GlShaderBase(shader, GL_FRAGMENT_SHADER) { + } + + GlFragmentShader(const char *source) : GlShaderBase(source, GL_FRAGMENT_SHADER) { + } + + static std::unique_ptr makeStandardDeferredShader(const Scene::MaterialInputSignature ¶ms); + static std::unique_ptr makeStandardForwardShader(const Scene::MaterialInputSignature ¶ms); +}; + +class GlShaderProgram { +public: + GlShaderProgram(const GlVertexShader &vertexShader, const GlFragmentShader &fragmentShader); + + virtual ~GlShaderProgram(); + + GLuint getGlProgram() const; + + void use() const; + + GLint getUniformLocation(const std::string &name) const; + GLint getUniformLocation(const Scene::Material::Location &location) const; + + void setUniform(const Scene::Material::Location &location, int integer) const; + void setUniform(const Scene::Material::Location &location, float scalar) const; + void setUniform(const Scene::Material::Location &location, const glm::vec3 &vec3) const; + void setUniform(const Scene::Material::Location &location, const glm::mat3 &mat3) const; + void setUniform(const Scene::Material::Location &location, const glm::mat4 &mat4) const; + void setUniformTexture(const Scene::Material::Location &location, const Texture &texture, int textureIndex) const; + +private: + GLuint _gl_program; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/GlElements/ShaderPrograms.cpp b/Engine/Render/src/Render/OpenGL/GlElements/ShaderPrograms.cpp new file mode 100644 index 0000000..b75c435 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/GlElements/ShaderPrograms.cpp @@ -0,0 +1,99 @@ +// Copyright 2024 Stone-Engine + +#include "ShaderPrograms.hpp" + +#include "../OpenGLDirector.hpp" +#include "../OpenGLResources.hpp" +#include "GlShaders.hpp" +#include "Render/OpenGL/OpenGLRenderer.hpp" + +namespace Stone::Render::OpenGL { + +ShaderPrograms::ShaderPrograms(Scene::MaterialInputSignature params, const std::shared_ptr &resources) + : _resources(resources) { + switch (resources->getRenderer().lock()->getDirector()->getRenderingMethod()) { + case RenderingMethod::Forward: _glFragmentShader = GlFragmentShader::makeStandardForwardShader(params); break; + case RenderingMethod::Deferred: _glFragmentShader = GlFragmentShader::makeStandardDeferredShader(params); break; + } +} + +ShaderPrograms::ShaderPrograms(Scene::FragmentShader &shader, const std::shared_ptr &resources) + : _resources(resources) { + _glFragmentShader = std::make_unique(shader); +} + +ShaderPrograms::ShaderPrograms(Scene::Material &material, const std::shared_ptr &resources) + : _resources(resources) { + Scene::MaterialInputSignature params(std::dynamic_pointer_cast(material.shared_from_this())); + switch (resources->getRenderer().lock()->getDirector()->getRenderingMethod()) { + case RenderingMethod::Forward: _glFragmentShader = GlFragmentShader::makeStandardForwardShader(params); break; + case RenderingMethod::Deferred: _glFragmentShader = GlFragmentShader::makeStandardDeferredShader(params); break; + } +} + +void ShaderPrograms::makeMeshProgram() { + if (_meshProgram != nullptr) + return; + + assert(_resources.expired() == false); + + auto &vertexShader = _resources.lock()->getMeshVertexShader(); + assert(vertexShader != nullptr); + + _meshProgram = std::make_unique(*vertexShader, *_glFragmentShader); +} + +void ShaderPrograms::makeSkinMeshProgram() { + if (_skinMeshProgram != nullptr) + return; + + assert(_resources.expired() == false); + + auto &vertexShader = _resources.lock()->getSkinMeshVertexShader(); + assert(vertexShader != nullptr); + + _skinMeshProgram = std::make_unique(*vertexShader, *_glFragmentShader); +} + +void ShaderPrograms::makeInstancedMeshProgram() { + if (_instancedMeshProgram != nullptr) + return; + + assert(_resources.expired() == false); + + auto &vertexShader = _resources.lock()->getInstancedMeshVertexShader(); + assert(vertexShader != nullptr); + + _instancedMeshProgram = std::make_unique(*vertexShader, *_glFragmentShader); +} + +void ShaderPrograms::makeProgram(Scene::MeshType meshType) { + switch (meshType) { + case Scene::MeshType::Standard: makeMeshProgram(); break; + case Scene::MeshType::Skin: makeSkinMeshProgram(); break; + case Scene::MeshType::Instanced: makeInstancedMeshProgram(); break; + } +} + +const std::unique_ptr &ShaderPrograms::getMeshProgram() const { + return _meshProgram; +} + +const std::unique_ptr &ShaderPrograms::getSkinMeshProgram() const { + return _skinMeshProgram; +} + +const std::unique_ptr &ShaderPrograms::getInstancedMeshProgram() const { + return _instancedMeshProgram; +} + +GlShaderProgram *ShaderPrograms::getProgram(Scene::MeshType meshType) const { + switch (meshType) { + case Scene::MeshType::Standard: return _meshProgram.get(); + case Scene::MeshType::Skin: return _skinMeshProgram.get(); + case Scene::MeshType::Instanced: return _instancedMeshProgram.get(); + } + return nullptr; +} + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/GlElements/ShaderPrograms.hpp b/Engine/Render/src/Render/OpenGL/GlElements/ShaderPrograms.hpp new file mode 100644 index 0000000..c402e62 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/GlElements/ShaderPrograms.hpp @@ -0,0 +1,40 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "GlShaders.hpp" +#include "Scene/Renderable/IMeshObject.hpp" +#include "Scene/Renderable/Material.hpp" +#include "Scene/Renderable/Shader.hpp" + +namespace Stone::Render::OpenGL { + +class OpenGLResources; + +class ShaderPrograms { +public: + ShaderPrograms(Scene::MaterialInputSignature params, const std::shared_ptr &resources); + ShaderPrograms(Scene::FragmentShader &shader, const std::shared_ptr &resources); + ShaderPrograms(Scene::Material &material, const std::shared_ptr &resources); + + void makeMeshProgram(); + void makeSkinMeshProgram(); + void makeInstancedMeshProgram(); + void makeProgram(Scene::MeshType meshType); + + const std::unique_ptr &getMeshProgram() const; + const std::unique_ptr &getSkinMeshProgram() const; + const std::unique_ptr &getInstancedMeshProgram() const; + GlShaderProgram *getProgram(Scene::MeshType meshType) const; + +protected: + std::weak_ptr _resources; + + std::unique_ptr _meshProgram = nullptr; + std::unique_ptr _skinMeshProgram = nullptr; + std::unique_ptr _instancedMeshProgram = nullptr; + + std::unique_ptr _glFragmentShader; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/OpenGLDirector.cpp b/Engine/Render/src/Render/OpenGL/OpenGLDirector.cpp new file mode 100644 index 0000000..7984678 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/OpenGLDirector.cpp @@ -0,0 +1,85 @@ +// Copyright 2024 Stone-Engine + +#include "OpenGLDirector.hpp" + +#include "Render/OpenGL/OpenGLRenderer.hpp" +#include "Render/OpenGL/RenderContext.hpp" +#include "Scene/Node/WorldNode.hpp" + + +namespace Stone::Render::OpenGL { + +static void initializeOpenGL() { + static bool initialized = false; + if (initialized) + return; + + glewExperimental = true; + if (glewInit() != GLEW_OK) { + throw std::runtime_error("Failed to initialize GLEW"); + } + initialized = true; +} + +OpenGLDirector::OpenGLDirector(const std::shared_ptr &renderer, RenderingMethod method) + : _method(method), _frameSize(0, 0), _renderer(renderer), _gBuffer() { +} + +void OpenGLDirector::initialize(const std::pair &frameSize) { + initializeOpenGL(); + updateFrameSize(frameSize); + std::cout << "OpenGL version: " << glGetString(GL_VERSION) << std::endl; +} + +void OpenGLDirector::renderWorld(const std::shared_ptr &world) { + switch (_method) { + case RenderingMethod::Forward: renderWorldForward(world); break; + case RenderingMethod::Deferred: renderWorldDeffered(world); break; + } +} + +void OpenGLDirector::updateFrameSize(const std::pair &frameSize) { + _frameSize = frameSize; + + if (_gBuffer) + _gBuffer.reset(); + + if (_method == RenderingMethod::Deferred) { + _gBuffer = std::make_unique(_frameSize.first, _frameSize.second); + } +} + +void OpenGLDirector::renderWorldForward(const std::shared_ptr &world) { + OpenGL::RenderContext context; + context.renderer = _renderer.lock(); + world->initializeRenderContext(context); + + _clearViewport(); + + world->render(context); +} + +void OpenGLDirector::renderWorldDeffered(const std::shared_ptr &world) { + OpenGL::RenderContext context; + context.renderer = _renderer.lock(); + context.gBuffer = _gBuffer.get(); + world->initializeRenderContext(context); + + _gBuffer->bind(); + _clearViewport(); + + world->render(context); + + _gBuffer->unbind(); + + _gBuffer->render(); +} + +void OpenGLDirector::_clearViewport() { + glViewport(0, 0, static_cast(_frameSize.first), static_cast(_frameSize.second)); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +} + + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/OpenGLDirector.hpp b/Engine/Render/src/Render/OpenGL/OpenGLDirector.hpp new file mode 100644 index 0000000..b9ddb47 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/OpenGLDirector.hpp @@ -0,0 +1,47 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "GlElements/GlGBuffer.hpp" +#include "Render/OpenGL/RendererSettings.hpp" + +namespace Stone::Scene { +class Node; +class WorldNode; +} // namespace Stone::Scene + +namespace Stone::Render::OpenGL { + +class OpenGLRenderer; +struct GlGBuffer; + +class OpenGLDirector { + +public: + OpenGLDirector(const std::shared_ptr &renderer, RenderingMethod method); + + virtual ~OpenGLDirector() = default; + + void initialize(const std::pair &frameSize); + + void updateFrameSize(const std::pair &frameSize); + + void renderWorld(const std::shared_ptr &world); + void renderWorldForward(const std::shared_ptr &world); + void renderWorldDeffered(const std::shared_ptr &world); + + [[nodiscard]] + RenderingMethod getRenderingMethod() const { + return _method; + } + +private: + void _clearViewport(); + + const RenderingMethod _method; + std::pair _frameSize; + std::weak_ptr _renderer; + std::unique_ptr _gBuffer; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/OpenGLRenderer.cpp b/Engine/Render/src/Render/OpenGL/OpenGLRenderer.cpp new file mode 100644 index 0000000..8a25894 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/OpenGLRenderer.cpp @@ -0,0 +1,42 @@ +// Copyright 2024 Stone-Engine + + +#include "Render/OpenGL/OpenGLRenderer.hpp" + +#include "OpenGLDirector.hpp" +#include "OpenGLResources.hpp" +#include "RendererObjectFactory.hpp" + + +namespace Stone::Render::OpenGL { + +void OpenGLRenderer::initialize(RendererSettings &settings) { + const auto this_shared = std::static_pointer_cast(shared_from_this()); + _resources = std::make_shared(this_shared); + _director = std::make_shared(this_shared, settings.rendering_method); + _director->initialize(settings.frame_size); +} + +void OpenGLRenderer::updateRenderablesInNode(const std::shared_ptr &rootNode) { + OpenGL::RendererObjectFactory factory(std::static_pointer_cast(shared_from_this())); + factory.updateRenderablesInNode(rootNode); +} + +void OpenGLRenderer::renderWorld(const std::shared_ptr &world) { + _director->renderWorld(world); +} + +void OpenGLRenderer::updateFrameSize(const std::pair &size) { + _director->updateFrameSize(size); +} + +const std::shared_ptr &OpenGLRenderer::getDirector() const { + return _director; +} + +const std::shared_ptr &OpenGLRenderer::getResources() const { + return _resources; +} + + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/OpenGLResources.cpp b/Engine/Render/src/Render/OpenGL/OpenGLResources.cpp new file mode 100644 index 0000000..44a47f3 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/OpenGLResources.cpp @@ -0,0 +1,77 @@ +// Copyright 2024 Stone-Engine + +#include "OpenGLResources.hpp" + +#include "GlElements/ShaderPrograms.hpp" +#include "OpenGLDirector.hpp" +#include "Render/OpenGL/OpenGLRenderer.hpp" +#include "Scene/Renderable/Material.hpp" + +namespace Stone::Render::OpenGL { + +OpenGLResources::OpenGLResources(const std::shared_ptr &renderer) + : std::enable_shared_from_this(), _renderer(renderer) { +} + +const std::weak_ptr &OpenGLResources::getRenderer() const { + return _renderer; +} + +// MARK: Vertex Shaders + +const std::unique_ptr &OpenGLResources::getMeshVertexShader() { + if (_meshVertexShader == nullptr) { + _meshVertexShader = GlVertexShader::makeStandardMeshShader(); + } + return _meshVertexShader; +} + +const std::unique_ptr &OpenGLResources::getSkinMeshVertexShader() { + if (_skinMeshVertexShader == nullptr) { + _skinMeshVertexShader = GlVertexShader::makeStandardSkinMeshShader(); + } + return _skinMeshVertexShader; +} + +const std::unique_ptr &OpenGLResources::getInstancedMeshVertexShader() { + if (_instancedMeshVertexShader == nullptr) { + _instancedMeshVertexShader = GlVertexShader::makeStandardInstancedMeshShader(); + } + return _instancedMeshVertexShader; +} + +const std::unique_ptr &OpenGLResources::getVertexShader(Scene::MeshType meshType) { + switch (meshType) { + case Scene::MeshType::Standard: return getMeshVertexShader(); + case Scene::MeshType::Skin: return getSkinMeshVertexShader(); + case Scene::MeshType::Instanced: return getInstancedMeshVertexShader(); + } + assert(false); +} + +const std::unique_ptr &OpenGLResources::getFragmentShader(Scene::MaterialInputSignature params) { + auto renderer = getRenderer().lock(); + assert(renderer != nullptr); + auto it = _fragmentShaders.find(params); + if (it != _fragmentShaders.end()) { + return it->second; + } + switch (renderer->getDirector()->getRenderingMethod()) { + case RenderingMethod::Deferred: + return (_fragmentShaders[params] = GlFragmentShader::makeStandardDeferredShader(params)); + case RenderingMethod::Forward: + return (_fragmentShaders[params] = GlFragmentShader::makeStandardForwardShader(params)); + } +} + +const std::shared_ptr & +OpenGLResources::getStandardShaderPrograms(Scene::MaterialInputSignature params) { + auto it = _standardsShaderPrograms.find(params); + if (it == _standardsShaderPrograms.end()) { + return (_standardsShaderPrograms[params] = std::make_shared(params, shared_from_this())); + } else { + return it->second; + } +} + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/OpenGLResources.hpp b/Engine/Render/src/Render/OpenGL/OpenGLResources.hpp new file mode 100644 index 0000000..9669fe3 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/OpenGLResources.hpp @@ -0,0 +1,50 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "GlElements/GlShaders.hpp" +#include "GlElements/ShaderPrograms.hpp" +#include "Scene/Renderable/Material.hpp" + +namespace Stone::Render::OpenGL { + +class OpenGLRenderer; + +class OpenGLResources : public std::enable_shared_from_this { +public: + OpenGLResources() = delete; + + OpenGLResources(const std::shared_ptr &renderer); + + virtual ~OpenGLResources() = default; + + const std::weak_ptr &getRenderer() const; + + // MARK: Vertex Shaders + + const std::unique_ptr &getMeshVertexShader(); + const std::unique_ptr &getSkinMeshVertexShader(); + const std::unique_ptr &getInstancedMeshVertexShader(); + const std::unique_ptr &getVertexShader(Scene::MeshType meshType); + + // MARK: Fragment Shaders + + const std::unique_ptr &getFragmentShader(Scene::MaterialInputSignature params); + + // MARK: Shader Programs + + const std::shared_ptr &getStandardShaderPrograms(Scene::MaterialInputSignature params); + +private: + std::weak_ptr _renderer; + + std::unique_ptr _meshVertexShader; + std::unique_ptr _skinMeshVertexShader; + std::unique_ptr _instancedMeshVertexShader; + + std::unordered_map> _fragmentShaders; + + std::unordered_map> _standardsShaderPrograms; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/RenderContext.hpp b/Engine/Render/src/Render/OpenGL/RenderContext.hpp new file mode 100644 index 0000000..50f8113 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/RenderContext.hpp @@ -0,0 +1,17 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "Scene/Renderer/RenderContext.hpp" + +#include + +namespace Stone::Render::OpenGL { + +struct GlGBuffer; + +struct RenderContext : public Scene::RenderContext { + GlGBuffer *gBuffer; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/Renderable/FragmentShader.hpp b/Engine/Render/src/Render/OpenGL/Renderable/FragmentShader.hpp new file mode 100644 index 0000000..b666d74 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/Renderable/FragmentShader.hpp @@ -0,0 +1,30 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "../GlElements/ShaderPrograms.hpp" +#include "IOpenGLRendererObject.hpp" +#include "Render/OpenGL/OpenGLRenderer.hpp" +#include "Scene/Renderable/Shader.hpp" + +namespace Stone::Render::OpenGL { + +class FragmentShader : public IOpenGLRendererObject { +public: + FragmentShader(Scene::FragmentShader &fragmentShader, const std::shared_ptr &renderer) + : IOpenGLRendererObject(renderer) { + _shaderPrograms = std::make_shared(fragmentShader, renderer->getResources()); + } + + void render(Scene::RenderContext &) override { + } + + const std::shared_ptr &getShaderPrograms() const { + return _shaderPrograms; + } + +private: + std::shared_ptr _shaderPrograms; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/Renderable/IOpenGLRendererObject.hpp b/Engine/Render/src/Render/OpenGL/Renderable/IOpenGLRendererObject.hpp new file mode 100644 index 0000000..f459272 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/Renderable/IOpenGLRendererObject.hpp @@ -0,0 +1,22 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "Scene/Renderable/IRenderable.hpp" + +namespace Stone::Render::OpenGL { + +class OpenGLRenderer; + +class IOpenGLRendererObject : public Scene::IRendererObject { +public: + IOpenGLRendererObject(const std::shared_ptr &renderer) : _renderer(renderer) { + } + + ~IOpenGLRendererObject() override = default; + +protected: + std::weak_ptr _renderer; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/Renderable/InstancedMeshNode.hpp b/Engine/Render/src/Render/OpenGL/Renderable/InstancedMeshNode.hpp new file mode 100644 index 0000000..8c872fc --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/Renderable/InstancedMeshNode.hpp @@ -0,0 +1,26 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "IOpenGLRendererObject.hpp" +#include "Scene/Node/InstancedMeshNode.hpp" + +namespace Stone::Render::OpenGL { + +class InstancedMeshNode : public IOpenGLRendererObject { +public: + InstancedMeshNode(Scene::InstancedMeshNode &instancedMeshNode, const std::shared_ptr &renderer) + : IOpenGLRendererObject(renderer), _instancedMeshNode(instancedMeshNode) { + } + + ~InstancedMeshNode() override { + } + + void render(Scene::RenderContext &) override { + } + +private: + Scene::InstancedMeshNode &_instancedMeshNode; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/Renderable/Material.cpp b/Engine/Render/src/Render/OpenGL/Renderable/Material.cpp new file mode 100644 index 0000000..2713d0c --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/Renderable/Material.cpp @@ -0,0 +1,58 @@ +// Copyright 2024 Stone-Engine + +#include "Material.hpp" + +#include "../OpenGLResources.hpp" +#include "FragmentShader.hpp" + +namespace Stone::Render::OpenGL { + +Material::Material(Scene::Material &material, const std::shared_ptr &renderer) + : IOpenGLRendererObject(renderer), _material(material) { + assert(renderer); + assert(renderer->getResources()); + if (_material.getFragmentShader() == nullptr) { + Scene::MaterialInputSignature inputs(std::dynamic_pointer_cast(material.shared_from_this())); + _shaderPrograms = renderer->getResources()->getStandardShaderPrograms(inputs); + } else { + _shaderPrograms = _material.getFragmentShader()->getRendererObject()->getShaderPrograms(); + } + assert(_shaderPrograms); + +#ifndef NDEBUG + int textureCount = 0; + material.forEachTextures([&textureCount](auto, auto) { ++textureCount; }); + if (textureCount >= 32) { + // TODO: Use log module + std::cerr << "Material " << material.getId() << " has more than 32 textures" << std::endl; + } +#endif +} + +void Material::setUniforms(Scene::MeshType meshType) { + GlShaderProgram *program = _shaderPrograms->getProgram(meshType); + assert(program != nullptr); + program->use(); + + _material.forEachVectors([program](const auto &loc, glm::vec3 vec) { program->setUniform(loc, vec); }); + _material.forEachScalars([program](const auto &loc, float scalar) { program->setUniform(loc, scalar); }); + + int textureIndex = 0; + _material.forEachTextures( + [program, &textureIndex](const auto &loc, const std::shared_ptr &texture) { + if (textureIndex >= 32) + return; + + assert(texture->isDirty() == false); + auto rendererTexture = texture->getRendererObject(); + program->setUniformTexture(loc, *rendererTexture, textureIndex); + ++textureIndex; + }); +} + +const std::shared_ptr &Material::getShaderPrograms() const { + return _shaderPrograms; +} + + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/Renderable/Material.hpp b/Engine/Render/src/Render/OpenGL/Renderable/Material.hpp new file mode 100644 index 0000000..5423855 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/Renderable/Material.hpp @@ -0,0 +1,32 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "IOpenGLRendererObject.hpp" +#include "Scene/Renderable/IMeshObject.hpp" +#include "Scene/Renderable/Material.hpp" + + +namespace Stone::Render::OpenGL { + +class ShaderPrograms; + +class Material : public IOpenGLRendererObject { +public: + Material(Scene::Material &material, const std::shared_ptr &renderer); + + ~Material() override = default; + + void render(Scene::RenderContext &) override { + } + + void setUniforms(Scene::MeshType meshType); + + const std::shared_ptr &getShaderPrograms() const; + +private: + Scene::Material &_material; + std::shared_ptr _shaderPrograms; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/Renderable/Mesh.hpp b/Engine/Render/src/Render/OpenGL/Renderable/Mesh.hpp new file mode 100644 index 0000000..9c8025d --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/Renderable/Mesh.hpp @@ -0,0 +1,133 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "IOpenGLRendererObject.hpp" +#include "Render/OpenGL/OpenGLRenderer.hpp" +#include "Scene/Renderable/Mesh.hpp" + +#include + +namespace Stone::Render::OpenGL { + +struct VRAMMesh { + + VRAMMesh(const std::shared_ptr &mesh) { + if (mesh == nullptr) { + return; + } + + glGenVertexArrays(1, &elementsBuffer); + if (elementsBuffer == 0) { + throw std::runtime_error("Failed to generate vertex array buffer"); + } + + glGenBuffers(1, &verticesBuffer); + if (verticesBuffer == 0) { + glDeleteVertexArrays(1, &elementsBuffer); + throw std::runtime_error("Failed to generate vertices buffer"); + } + + glGenBuffers(1, &indicesBuffer); + if (indicesBuffer == 0) { + glDeleteBuffers(1, &verticesBuffer); + glDeleteVertexArrays(1, &elementsBuffer); + throw std::runtime_error("Failed to generate indices buffer"); + } + + glBindVertexArray(elementsBuffer); + + glBindBuffer(GL_ARRAY_BUFFER, verticesBuffer); + glBufferData(GL_ARRAY_BUFFER, mesh->getVertices().size() * sizeof(Scene::Vertex), mesh->getVertices().data(), + GL_STATIC_DRAW); + + numIndices = mesh->getIndices().size(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, numIndices * sizeof(uint32_t), mesh->getIndices().data(), GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Scene::Vertex), + (void *)offsetof(Scene::Vertex, position)); + + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Scene::Vertex), (void *)offsetof(Scene::Vertex, normal)); + + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Scene::Vertex), + (void *)offsetof(Scene::Vertex, tangent)); + + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Scene::Vertex), + (void *)offsetof(Scene::Vertex, bitangent)); + + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(Scene::Vertex), (void *)offsetof(Scene::Vertex, uv)); + } + + ~VRAMMesh() { + if (verticesBuffer != 0) { + glDeleteBuffers(1, &verticesBuffer); + } + if (indicesBuffer != 0) { + glDeleteBuffers(1, &indicesBuffer); + } + if (elementsBuffer != 0) { + glDeleteVertexArrays(1, &elementsBuffer); + } + } + + GLuint verticesBuffer = 0; + GLuint indicesBuffer = 0; + GLuint elementsBuffer = 0; + GLsizei numIndices = 0; +}; + +class RendererMesh : public IOpenGLRendererObject { + +public: + RendererMesh(const std::shared_ptr &mesh, const std::shared_ptr &renderer) + : IOpenGLRendererObject(renderer), _vramMesh(mesh) { + } + + ~RendererMesh() override = default; + + const VRAMMesh &getVRAMMesh() const { + return _vramMesh; + } + +private: + VRAMMesh _vramMesh; +}; + + +class DynamicMesh : public RendererMesh { +public: + DynamicMesh(Scene::DynamicMesh &mesh, const std::shared_ptr &renderer) + : RendererMesh(std::static_pointer_cast(mesh.shared_from_this()), renderer), _mesh(mesh) { + } + + ~DynamicMesh() override = default; + + void render(Scene::RenderContext &) override { + } + +private: + Scene::DynamicMesh &_mesh; +}; + +class StaticMesh : public RendererMesh { +public: + StaticMesh(Scene::StaticMesh &mesh, const std::shared_ptr &renderer) + : RendererMesh(mesh.getSourceMesh(), renderer), _mesh(mesh) { + } + + ~StaticMesh() override = default; + + void render(Scene::RenderContext &) override { + } + +private: + Scene::StaticMesh &_mesh; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/Renderable/MeshNode.cpp b/Engine/Render/src/Render/OpenGL/Renderable/MeshNode.cpp new file mode 100644 index 0000000..1c94cbb --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/Renderable/MeshNode.cpp @@ -0,0 +1,68 @@ +// Copyright 2024 Stone-Engine + +#include "MeshNode.hpp" + +#include "../OpenGLResources.hpp" +#include "Material.hpp" +#include "Mesh.hpp" +#include "Render/OpenGL/OpenGLRenderer.hpp" +#include "Scene/Renderable/Mesh.hpp" + +#include + + +namespace Stone::Render::OpenGL { + +MeshNode::MeshNode(Scene::MeshNode &meshNode, const std::shared_ptr &renderer) + : IOpenGLRendererObject(renderer), _meshNode(meshNode), _material(nullptr), _shaderPrograms(nullptr) { + + auto usedMaterial = _meshNode.getUsedMaterial(); + if (usedMaterial) { + assert(usedMaterial->isDirty() == false); + _material = usedMaterial->getRendererObject(); + } + + if (_material) { + _shaderPrograms = _material->getShaderPrograms(); + } else { + _shaderPrograms = renderer->getResources()->getStandardShaderPrograms(Scene::MaterialInputSignature(nullptr)); + } + assert(_shaderPrograms); + + _shaderPrograms->makeMeshProgram(); +} + +void MeshNode::render(Scene::RenderContext &context) { + auto mesh = _meshNode.getMesh(); + if (mesh == nullptr) + return; + + auto rendererMesh = mesh->getRendererObject(); + if (rendererMesh == nullptr) { + return; + } + const VRAMMesh &vramMesh = rendererMesh->getVRAMMesh(); + + GlShaderProgram *program = _shaderPrograms->getMeshProgram().get(); + program->use(); + + if (_material) { + _material->setUniforms(Scene::MeshType::Standard); + } + + program->setUniform("u_lights_count", 0); + program->setUniform("u_mat_model", context.mvp.modelMatrix); + program->setUniform("u_mat_view", context.mvp.viewMatrix); + program->setUniform("u_mat_projection", context.mvp.projMatrix); + program->setUniform("u_mat_normal", glm::inverseTranspose(glm::mat3(context.mvp.modelMatrix))); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + + glBindVertexArray(vramMesh.elementsBuffer); + glDrawElements(GL_TRIANGLES, vramMesh.numIndices, GL_UNSIGNED_INT, NULL); +} + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/Renderable/MeshNode.hpp b/Engine/Render/src/Render/OpenGL/Renderable/MeshNode.hpp new file mode 100644 index 0000000..f6a01f8 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/Renderable/MeshNode.hpp @@ -0,0 +1,27 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "IOpenGLRendererObject.hpp" +#include "Scene/Node/MeshNode.hpp" + +namespace Stone::Render::OpenGL { + +class Material; +class ShaderPrograms; + +class MeshNode : public IOpenGLRendererObject { +public: + MeshNode(Scene::MeshNode &meshNode, const std::shared_ptr &renderer); + + ~MeshNode() override = default; + + void render(Scene::RenderContext &context) override; + +private: + Scene::MeshNode &_meshNode; + std::shared_ptr _material; + std::shared_ptr _shaderPrograms; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/Renderable/SkinMesh.hpp b/Engine/Render/src/Render/OpenGL/Renderable/SkinMesh.hpp new file mode 100644 index 0000000..9295026 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/Renderable/SkinMesh.hpp @@ -0,0 +1,144 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "IOpenGLRendererObject.hpp" +#include "Scene/Renderable/SkinMesh.hpp" + +#include + +namespace Stone::Render::OpenGL { + +struct VRAMSkinMesh { + + VRAMSkinMesh(const std::shared_ptr &skinMesh) { + if (skinMesh == nullptr) { + return; + } + + glGenVertexArrays(1, &elementsBuffer); + if (elementsBuffer == 0) { + throw std::runtime_error("Failed to generate vertex array buffer"); + } + + glGenBuffers(1, &verticesBuffer); + if (verticesBuffer == 0) { + glDeleteVertexArrays(1, &elementsBuffer); + throw std::runtime_error("Failed to generate vertices buffer"); + } + + glGenBuffers(1, &indicesBuffer); + if (indicesBuffer == 0) { + glDeleteVertexArrays(1, &elementsBuffer); + glDeleteBuffers(1, &verticesBuffer); + throw std::runtime_error("Failed to generate indices buffer"); + } + + glBindVertexArray(elementsBuffer); + + glBindBuffer(GL_ARRAY_BUFFER, verticesBuffer); + glBufferData(GL_ARRAY_BUFFER, skinMesh->getVertices().size() * sizeof(Scene::WeightVertex), + skinMesh->getVertices().data(), GL_STATIC_DRAW); + + numIndices = skinMesh->getIndices().size(); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, numIndices * sizeof(uint32_t), skinMesh->getIndices().data(), + GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Scene::WeightVertex), + (void *)offsetof(Scene::Vertex, position)); + + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Scene::WeightVertex), + (void *)offsetof(Scene::Vertex, normal)); + + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Scene::WeightVertex), + (void *)offsetof(Scene::Vertex, tangent)); + + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Scene::WeightVertex), + (void *)offsetof(Scene::Vertex, bitangent)); + + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(Scene::WeightVertex), + (void *)offsetof(Scene::Vertex, uv)); + + glEnableVertexAttribArray(5); + glVertexAttribPointer( + 5, 4, GL_FLOAT, GL_FALSE, sizeof(Scene::WeightVertex), + (void *)reinterpret_cast(&reinterpret_cast(0)->weights)); + + glEnableVertexAttribArray(6); + glVertexAttribIPointer(6, 4, GL_INT, sizeof(Scene::WeightVertex), + (void *)reinterpret_cast(&reinterpret_cast(0)->ids)); + } + + ~VRAMSkinMesh() { + if (verticesBuffer != 0) { + glDeleteBuffers(1, &verticesBuffer); + } + if (indicesBuffer != 0) { + glDeleteBuffers(1, &indicesBuffer); + } + if (elementsBuffer != 0) { + glDeleteVertexArrays(1, &elementsBuffer); + } + } + + GLuint verticesBuffer = 0; + GLuint indicesBuffer = 0; + GLuint elementsBuffer = 0; + GLsizei numIndices = 0; +}; + +class RendererSkinMesh : public IOpenGLRendererObject { +public: + RendererSkinMesh(const std::shared_ptr &skinMesh, + const std::shared_ptr &renderer) + : IOpenGLRendererObject(renderer), _vramMesh(skinMesh) { + } + + ~RendererSkinMesh() override = default; + + const VRAMSkinMesh &getVRAMSkinMesh() const { + return _vramMesh; + } + +private: + VRAMSkinMesh _vramMesh; +}; + +class DynamicSkinMesh : public RendererSkinMesh { +public: + DynamicSkinMesh(Scene::DynamicSkinMesh &skinMesh, const std::shared_ptr &renderer) + : RendererSkinMesh(std::static_pointer_cast(skinMesh.shared_from_this()), renderer), + _skinMesh(skinMesh) { + } + + ~DynamicSkinMesh() override = default; + + void render(Scene::RenderContext &) override { + } + +private: + Scene::DynamicSkinMesh &_skinMesh; +}; + +class StaticSkinMesh : public RendererSkinMesh { +public: + StaticSkinMesh(Scene::StaticSkinMesh &skinMesh, const std::shared_ptr &renderer) + : RendererSkinMesh(skinMesh.getSourceMesh(), renderer), _skinMesh(skinMesh) { + } + + ~StaticSkinMesh() override = default; + + void render(Scene::RenderContext &) override { + } + +private: + Scene::StaticSkinMesh &_skinMesh; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/Renderable/SkinMeshNode.hpp b/Engine/Render/src/Render/OpenGL/Renderable/SkinMeshNode.hpp new file mode 100644 index 0000000..34274ce --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/Renderable/SkinMeshNode.hpp @@ -0,0 +1,26 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "IOpenGLRendererObject.hpp" +#include "Scene/Node/SkinMeshNode.hpp" + +namespace Stone::Render::OpenGL { + +class SkinMeshNode : public IOpenGLRendererObject { +public: + SkinMeshNode(Scene::SkinMeshNode &skinMeshNode, const std::shared_ptr &renderer) + : IOpenGLRendererObject(renderer), _skinMeshNode(skinMeshNode) { + } + + ~SkinMeshNode() override = default; + + void render(Scene::RenderContext &context) override { + (void)context; + } + +private: + Scene::SkinMeshNode &_skinMeshNode; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/Renderable/Texture.cpp b/Engine/Render/src/Render/OpenGL/Renderable/Texture.cpp new file mode 100644 index 0000000..cbba8ce --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/Renderable/Texture.cpp @@ -0,0 +1,88 @@ +// Copyright 2024 Stone-Engine + +#include "Texture.hpp" + +#include "Core/Image/ImageData.hpp" +#include "Core/Image/ImageSource.hpp" + +namespace Stone::Render::OpenGL { + +GLint convert(Core::Image::Channel channel) { + switch (channel) { + case Core::Image::Channel::GREY: return GL_RED; + case Core::Image::Channel::DUAL: return GL_RG; + case Core::Image::Channel::RGB: return GL_RGB; + case Core::Image::Channel::RGBA: return GL_RGBA; + } + return GL_RGBA; +} + +GLint convert(Scene::TextureFilter filter, bool mipmap) { + switch (filter) { + case Scene::TextureFilter::Nearest: return mipmap ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST; + case Scene::TextureFilter::Linear: + case Scene::TextureFilter::Cubic: return mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR; + } + return mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR; +} + +GLint convert(Scene::TextureWrap wrap) { + switch (wrap) { + case Scene::TextureWrap::Repeat: return GL_REPEAT; + case Scene::TextureWrap::MirroredRepeat: return GL_MIRRORED_REPEAT; + case Scene::TextureWrap::ClampToEdge: return GL_CLAMP_TO_EDGE; + case Scene::TextureWrap::ClampToBorder: return GL_CLAMP_TO_BORDER; + } + return GL_REPEAT; +} + +Texture::Texture(Scene::Texture &texture, const std::shared_ptr &renderer) + : IOpenGLRendererObject(renderer), _gl_texture(0) { + const auto &imageSource = texture.getImage(); + if (imageSource == nullptr) { + return; + } + + imageSource->loadData(); + std::shared_ptr imageData = imageSource->getLoadedImage(); + + glGenTextures(1, &_gl_texture); + if (_gl_texture == 0) { + std::runtime_error("Failed to create texture buffer."); + } + glBindTexture(GL_TEXTURE_2D, _gl_texture); + GLint format = convert(imageData->getChannels()); + glTexImage2D(GL_TEXTURE_2D, 0, format, imageData->getSize().x, imageData->getSize().y, 0, format, GL_UNSIGNED_BYTE, + imageData->getData()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, convert(texture.getWrap())); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, convert(texture.getWrap())); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, convert(texture.getMinFilter(), true)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, convert(texture.getMagFilter(), false)); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); +} + +Texture::Texture(GLsizei width, GLsizei height, GLint internalFormat, GLenum format, GLenum type, GLint wrap, + GLint filter) + : IOpenGLRendererObject(nullptr), _gl_texture(0) { + glGenTextures(1, &_gl_texture); + if (_gl_texture == 0) { + std::runtime_error("Failed to create texture buffer."); + } + glBindTexture(GL_TEXTURE_2D, _gl_texture); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); +} + +Texture::~Texture() { + if (_gl_texture != 0) { + glDeleteTextures(1, &_gl_texture); + } +} + + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/Renderable/Texture.hpp b/Engine/Render/src/Render/OpenGL/Renderable/Texture.hpp new file mode 100644 index 0000000..c94e4ca --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/Renderable/Texture.hpp @@ -0,0 +1,32 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "IOpenGLRendererObject.hpp" +#include "Scene/Renderable/Texture.hpp" + +#include + +namespace Stone::Render::OpenGL { + + +class Texture : public IOpenGLRendererObject { +public: + Texture(Scene::Texture &texture, const std::shared_ptr &renderer); + + Texture(GLsizei width, GLsizei height, GLint internalFormat, GLenum format, GLenum type, GLint wrap, GLint filter); + + ~Texture() override; + + void render(Scene::RenderContext &) override { + } + + GLuint getGlTexture() const { + return _gl_texture; + } + +private: + GLuint _gl_texture = 0; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/RendererObjectFactory.cpp b/Engine/Render/src/Render/OpenGL/RendererObjectFactory.cpp new file mode 100644 index 0000000..08b8bc8 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/RendererObjectFactory.cpp @@ -0,0 +1,51 @@ +// Copyright 2024 Stone-Engine + +#include "RendererObjectFactory.hpp" + +#include "Renderable/FragmentShader.hpp" +#include "Renderable/InstancedMeshNode.hpp" +#include "Renderable/Material.hpp" +#include "Renderable/Mesh.hpp" +#include "Renderable/MeshNode.hpp" +#include "Renderable/SkinMesh.hpp" +#include "Renderable/SkinMeshNode.hpp" +#include "Renderable/Texture.hpp" + + +#define UPDATE_RENDERER_OBJECT(RenderableClass, object) \ + void RendererObjectFactory::update##RenderableClass(const std::shared_ptr &(object)) { \ + Scene::RendererObjectFactory::update##RenderableClass((object)); \ + \ + auto new##RenderableClass = std::make_shared(*(object), _renderer); \ + updateRendererObject(*(object), new##RenderableClass); \ + } + + +namespace Stone::Render::OpenGL { + +RendererObjectFactory::RendererObjectFactory(std::shared_ptr renderer) + : _renderer(std::move(renderer)) { +} + + +UPDATE_RENDERER_OBJECT(MeshNode, meshNode) + +UPDATE_RENDERER_OBJECT(InstancedMeshNode, instancedMeshNode) + +UPDATE_RENDERER_OBJECT(SkinMeshNode, skinMeshNode) + +UPDATE_RENDERER_OBJECT(Material, material) + +UPDATE_RENDERER_OBJECT(DynamicMesh, dynamicMesh) + +UPDATE_RENDERER_OBJECT(StaticMesh, staticMesh) + +UPDATE_RENDERER_OBJECT(DynamicSkinMesh, dynamicSkinMesh) + +UPDATE_RENDERER_OBJECT(StaticSkinMesh, staticSkinMesh) + +UPDATE_RENDERER_OBJECT(Texture, texture) + +UPDATE_RENDERER_OBJECT(FragmentShader, shader) + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/RendererObjectFactory.hpp b/Engine/Render/src/Render/OpenGL/RendererObjectFactory.hpp new file mode 100644 index 0000000..ef12b98 --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/RendererObjectFactory.hpp @@ -0,0 +1,34 @@ +// Copyright 2024 Stone-Engine + +#pragma once + +#include "Scene/Renderer/RendererObjectFactory.hpp" + +namespace Stone::Render::OpenGL { + +class OpenGLRenderer; + +class RendererObjectFactory : public Scene::RendererObjectFactory { + +public: + explicit RendererObjectFactory(std::shared_ptr renderer); + + ~RendererObjectFactory() override = default; + + void updateMeshNode(const std::shared_ptr &meshNode) override; + void updateInstancedMeshNode(const std::shared_ptr &instancedMeshNode) override; + void updateSkinMeshNode(const std::shared_ptr &skinMeshNode) override; + void updateMaterial(const std::shared_ptr &material) override; + void updateDynamicMesh(const std::shared_ptr &mesh) override; + void updateStaticMesh(const std::shared_ptr &mesh) override; + void updateDynamicSkinMesh(const std::shared_ptr &skinmesh) override; + void updateStaticSkinMesh(const std::shared_ptr &skinmesh) override; + void updateTexture(const std::shared_ptr &texture) override; + void updateFragmentShader(const std::shared_ptr &shader) override; + + +private: + std::shared_ptr _renderer; +}; + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/OpenGL/Shader/ShaderGenerator.cpp b/Engine/Render/src/Render/OpenGL/Shader/ShaderGenerator.cpp new file mode 100644 index 0000000..32c2d8f --- /dev/null +++ b/Engine/Render/src/Render/OpenGL/Shader/ShaderGenerator.cpp @@ -0,0 +1,351 @@ +// Copyright 2024 Stone-Engine + +#include "Render/OpenGL/Shader/ShaderGenerator.hpp" + +#include "Scene/Renderable/Shader.hpp" +#include "Utils/FileSystem.hpp" + +namespace Stone::Render::OpenGL { + +void ShaderGenerator::generateFragmentShaderTemplate(const Scene::MaterialInputSignature ¶ms, + std::ostream &output) { + + output << "// Stone shader template" << std::endl; + + auto to_glsl = [](Scene::MaterialInputSignature::Type type) { + switch (type) { + case Scene::MaterialInputSignature::Type::None: return "void"; + case Scene::MaterialInputSignature::Type::Scalar: return "float"; + case Scene::MaterialInputSignature::Type::Vector2: return "vec2"; + case Scene::MaterialInputSignature::Type::Vector3: return "vec3"; + case Scene::MaterialInputSignature::Type::Vector4: return "vec4"; + case Scene::MaterialInputSignature::Type::Texture: return "sampler2D"; + default: return ""; + } + }; + + auto add_uniform_param = [&output, &to_glsl](const char *name, Scene::MaterialInputSignature::Type type) { + if (type != Scene::MaterialInputSignature::Type::None) + output << "// " << name << ": " << to_glsl(type) << std::endl; + }; + +#define __ADD_UNIFORM_PARAM(PARAM) add_uniform_param(#PARAM, params.PARAM); + + FOR_EACH_SHADER_PARAMETERS(__ADD_UNIFORM_PARAM); + output << std::endl; + + output << "void customShader(inout Material material) {" << std::endl; + output << " material.diffuse = vec3(1, 0, 0);" << std::endl; + output << " // TODO: Implement shader generation logic here" << std::endl; + output << "}" << std::endl; +} + +void ShaderGenerator::generateOpenGlForwardFragmentShader(const Scene::MaterialInputSignature ¶ms, + const std::shared_ptr &shader, + std::ostream &output) { + + std::ostream &source(output); + + source << "#version 400 core" << std::endl; + + source << R"( +in FRAG_DATA { + vec3 wposition; + vec2 uv; + vec3 wnormal; + vec3 wtangent; + vec3 wbitangent; +} fs_in; +)"; + + source << R"( +struct Material { + vec3 diffuse; + vec3 specular; + float roughness; + float metalness; + vec3 occlusion; + float shininess; +}; +)"; + + // unused = 0; sunlight = 1; spotlight = 2; pointlight = 3; ambiantlight = 4; + // caster_index = -1 : no caster; + source << R"( +struct Light { + int type; + vec3 position; + vec3 color; + vec3 specular; + float intensity; + vec3 attenuation; + vec3 direction; + float angle; + float cone_attenuation; + int caster_index; +}; +)"; + + source << R"( +struct LightCaster { + mat4 vp; + sampler2D depth_map; +}; + +)"; + + source << "uniform Light u_lights[" << 16 << "];" << std::endl; + source << "uniform int u_lights_count;" << std::endl; + source << "uniform LightCaster u_light_casters[" << 4 << "];" << std::endl; + + source << R"( +uniform mat3 u_mat_normal; +uniform mat4 u_mat_projection; +uniform mat4 u_mat_view; +uniform mat4 u_mat_model; +uniform vec3 u_camera_position; + +)"; + + auto to_glsl = [](Scene::MaterialInputSignature::Type type) { + switch (type) { + case Scene::MaterialInputSignature::Type::None: return "void"; + case Scene::MaterialInputSignature::Type::Scalar: return "float"; + case Scene::MaterialInputSignature::Type::Vector2: return "vec2"; + case Scene::MaterialInputSignature::Type::Vector3: return "vec3"; + case Scene::MaterialInputSignature::Type::Vector4: return "vec4"; + case Scene::MaterialInputSignature::Type::Texture: return "sampler2D"; + default: return ""; + } + }; + + auto add_uniform_param = [&source, &to_glsl](const char *name, Scene::MaterialInputSignature::Type type) { + if (type != Scene::MaterialInputSignature::Type::None) + source << "uniform " << to_glsl(type) << ' ' << name << ";" << std::endl; + }; + +#define __ADD_UNIFORM_PARAM(PARAM) add_uniform_param(#PARAM, params.PARAM); + + FOR_EACH_SHADER_PARAMETERS(__ADD_UNIFORM_PARAM); + source << std::endl; + + source << "out vec4 FragColor;" << std::endl; + + source << R"( +float calculShadow(Light light) { + int caster_index = light.caster_index; + if (caster_index >= 0) { + vec4 fpos_light = u_light_casters[caster_index].vp * vec4(fs_in.wposition, 1.0); + vec3 proj_coords = fpos_light.xyz / fpos_light.w; + proj_coords = proj_coords * 0.5 + 0.5; + bool outOfLight = (proj_coords.x < 0 || proj_coords.x > 1 || proj_coords.y < 0 || proj_coords.y > 1); + if (outOfLight) + return 1.0; + float current_depth = proj_coords.z; + float bias = 0.00001; // max(0.05 * (1.0 - dot(normal_direction, -light.direction)), 0.005); + vec2 texel_size = 1.0 / textureSize(u_light_casters[caster_index].depth_map, 0); + float shadow = 0.0; + int total = 0; + for (int x = -2; x <= 2; x++) { + for (int y = -2; y <= 2; y++) { + float pcf_depth = texture(u_light_casters[caster_index].depth_map, proj_coords.xy + vec2(x, y) * texel_size).r; + shadow += current_depth - bias > pcf_depth ? 0.0 : 1.0; + total++; + } + } + return shadow / (total); + } + return 1.0; +} + +)"; + + source << R"( +vec3 calculDirectionalLight(Light light, Material fragMat, vec3 normal_direction, vec3 fcamera_position) { + vec3 color = vec3(0); + float shadow = calculShadow(light); + if (shadow <= 0.0) return color; + float d = distance(light.position, fs_in.wposition); + float attenuation = 1.0 / (light.attenuation.x + light.attenuation.y * d + light.attenuation.z * d * d); + vec3 light_direction = normalize(-light.direction); + float diffuse_intensity = clamp(dot(light_direction, normal_direction), 0.0, 1.0); + color += light.color * fragMat.diffuse * diffuse_intensity * attenuation * shadow * fragMat.occlusion; + if (diffuse_intensity > 0) { + vec3 reflection = reflect(-light_direction, normal_direction); + vec3 halfway_direction = normalize(fcamera_position + light_direction); + float specular_intensity = pow(clamp(dot(normal_direction, halfway_direction), 0.0, 1.0), fragMat.shininess); + color += diffuse_intensity * light.specular * fragMat.specular * specular_intensity * shadow; + } + return color; +} + +)"; + + source << R"( +vec3 calculSpotLight(Light light, Material fragMat, vec3 normal_direction, vec3 fcamera_position) { + vec3 color = vec3(0); + float shadow = calculShadow(light); + if (shadow <= 0.0) return color; + float d = distance(light.position, fs_in.wposition); + float attenuation = 1.0 / (light.attenuation.x + light.attenuation.y * d + light.attenuation.z * d * d); + vec3 light_direction = normalize(light.position - fs_in.wposition); + float cosangle = cos(light.angle); + float spot_result = dot(-light_direction, light.direction); + if (spot_result > cosangle) { + float cosat = cos(light.angle - light.cone_attenuation); + attenuation *= clamp((spot_result - cosangle) / (cosat - cosangle), 0.0, 1.0); + float diffuse_intensity = clamp(dot(light_direction, normal_direction), 0.0, 1.0); + color += light.color * fragMat.diffuse * diffuse_intensity * attenuation * shadow * fragMat.occlusion; + if (diffuse_intensity > 0) { + vec3 reflection = reflect(-light_direction, normal_direction); + vec3 halfway_direction = normalize(fcamera_position + light_direction); + float specular_intensity = pow(clamp(dot(normal_direction, halfway_direction), 0.0, 1.0), fragMat.shininess); + color += diffuse_intensity * light.specular * fragMat.specular * specular_intensity * shadow; + } + } + return color; +} + +)"; + + source << R"( +vec3 calculPointLight(Light light, Material fragMat, vec3 normal_direction, vec3 fcamera_position) { + vec3 color = vec3(0); + float shadow = calculShadow(light); + if (shadow <= 0.0) return color; + float d = distance(light.position, fs_in.wposition); + float attenuation = 1.0 / (light.attenuation.x + light.attenuation.y * d + light.attenuation.z * d * d); + vec3 light_direction = normalize(light.position - fs_in.wposition); + float diffuse_intensity = clamp(dot(light_direction, normal_direction), 0.0, 1.0); + color += light.color * fragMat.diffuse * diffuse_intensity * attenuation * shadow * fragMat.occlusion; + if (diffuse_intensity > 0) { + vec3 reflection = reflect(-light_direction, normal_direction); + vec3 halfway_direction = normalize(fcamera_position + light_direction); + float specular_intensity = pow(clamp(dot(normal_direction, halfway_direction), 0.0, 1.0), fragMat.shininess); + color += diffuse_intensity * light.specular * fragMat.specular * specular_intensity * shadow; + } + return color; +} + +)"; + + source << R"( +vec3 calculAmbiantLight(Light light, Material fragMat) { + return fragMat.diffuse * light.color * light.intensity * fragMat.occlusion; +} +)"; + + // directional = 1; spotlight = 2; pointlight = 3; ambiantlight = 4; + source << R"( +vec3 calculLight(Light light, Material fragMat, vec3 normal_direction, vec3 fcamera_position) { + switch (light.type) { + case 1: // directional + return calculDirectionalLight(light, fragMat, normal_direction, fcamera_position); + case 2: // spotlight + return calculSpotLight(light, fragMat, normal_direction, fcamera_position); + case 3: // pointlight + return calculPointLight(light, fragMat, normal_direction, fcamera_position); + case 4: // ambiant + return calculAmbiantLight(light, fragMat); + default: + return vec3(0); + } +} + +)"; + + if (shader) { + auto [contentType, content] = shader->getContent(); + + using ContentType = Scene::AShader::ContentType; + switch (contentType) { + case ContentType::SourceCode: source << content << std::endl; break; + case ContentType::SourceFile: source << Utils::readTextFile(content) << std::endl; break; + default: break; + } + } + + + source << "void main() {" << std::endl; + // if ((texturebitmask & 128) != 0) { + // if (texture(texture_mask, (mask_transform * vec3(fs_in.uv, 1.0)).xy).a == 0.0) + // discard; + // } + source << " Material fragMat;" << std::endl; + + // TODO: Handle default values + const auto assign_to_vec = [&source](Scene::MaterialInputSignature::Type type, const std::string &name) { + switch (type) { + case Scene::MaterialInputSignature::Type::None: break; + case Scene::MaterialInputSignature::Type::Scalar: + source << " fragMat." << name << " = vec3(" << name << ", 0, 0);" << std::endl; + break; + case Scene::MaterialInputSignature::Type::Vector2: + source << " fragMat." << name << " = vec3(" << name << ", 0);" << std::endl; + break; + case Scene::MaterialInputSignature::Type::Vector3: + source << " fragMat." << name << " = " << name << ";" << std::endl; // + break; + case Scene::MaterialInputSignature::Type::Vector4: + source << " fragMat." << name << " = " << name << ".xyz;" << std::endl; + break; + case Scene::MaterialInputSignature::Type::Texture: + source << " fragMat." << name << " = texture(" << name << ", fs_in.uv).xyz;" << std::endl; + break; + } + }; + + const auto assign_to_float = [&source](Scene::MaterialInputSignature::Type type, const std::string &name, char x) { + switch (type) { + case Scene::MaterialInputSignature::Type::None: break; + case Scene::MaterialInputSignature::Type::Scalar: + source << " fragMat." << name << " = " << name << ";" << std::endl; // + break; + case Scene::MaterialInputSignature::Type::Vector2: + case Scene::MaterialInputSignature::Type::Vector3: + case Scene::MaterialInputSignature::Type::Vector4: + source << " fragMat." << name << " = " << name << "." << x << ";" << std::endl; + break; + case Scene::MaterialInputSignature::Type::Texture: + source << " fragMat." << name << " = texture(" << name << ", fs_in.uv)." << x << ";" << std::endl; + break; + } + }; + + assign_to_vec(params.diffuse, "diffuse"); + assign_to_vec(params.specular, "specular"); + assign_to_float(params.roughness, "roughness", 'x'); + assign_to_float(params.metallic, "metallic", 'x'); + assign_to_vec(params.occlusion, "occlusion"); + assign_to_float(params.shininess, "shininess", 'x'); + + if (shader) { + source << " " << shader->getFunction() << "(fragMat);" << std::endl; + } + + if (params.normal == Scene::MaterialInputSignature::Type::Texture) { + source << " vec3 normal_value = normalize(texture(normal, fs_in.uv).xyz * 2 - 1);" << std::endl; + } else { + source << " vec3 normal_value = vec3(0, 0, 1);" << std::endl; + } + + source << R"( + vec3 normal_direction = fs_in.wnormal * normal_value.z + + fs_in.wtangent * normal_value.x + + fs_in.wbitangent * normal_value.y; + normal_direction = normalize(normal_direction); + vec3 fcamera_position = normalize(u_camera_position - fs_in.wposition); + + vec3 color = vec3(0); + + // for (int i = 0; i < u_lights_count; i++) { + // color += calculLight(u_lights[i], fragMat, normal_direction, fcamera_position); + // } + color = vec3(1.0, 0.0, 0.0); + + FragColor = vec4(color, 1.0); +} +)"; +} + +} // namespace Stone::Render::OpenGL diff --git a/Engine/Render/src/Render/Renderer.cpp b/Engine/Render/src/Render/Renderer.cpp deleted file mode 100644 index b33b913..0000000 --- a/Engine/Render/src/Render/Renderer.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright 2024 Stone-Engine - -#include "Render/Renderer.hpp" diff --git a/Engine/Render/src/Render/Vulkan/Device/VulkanDevice.cpp b/Engine/Render/src/Render/Vulkan/Device/VulkanDevice.cpp index ed67e23..5e19d31 100644 --- a/Engine/Render/src/Render/Vulkan/Device/VulkanDevice.cpp +++ b/Engine/Render/src/Render/Vulkan/Device/VulkanDevice.cpp @@ -14,6 +14,7 @@ VulkanDevice::VulkanDevice(std::shared_ptr core) : _core(std::move(c } VulkanDevice::~VulkanDevice() { + _core.reset(); } void VulkanDevice::waitIdle() const { diff --git a/Engine/Render/src/Render/Vulkan/RenderContext.hpp b/Engine/Render/src/Render/Vulkan/RenderContext.hpp index 9e06f86..a3611d8 100644 --- a/Engine/Render/src/Render/Vulkan/RenderContext.hpp +++ b/Engine/Render/src/Render/Vulkan/RenderContext.hpp @@ -1,8 +1,8 @@ -// Copyright 2024 StoneEngine +// Copyright 2024 Stone-Engine #pragma once -#include "Scene/RenderContext.hpp" +#include "Scene/Renderer/RenderContext.hpp" #include diff --git a/Engine/Render/src/Render/Vulkan/RendererObjectManager.cpp b/Engine/Render/src/Render/Vulkan/RendererObjectFactory.cpp similarity index 58% rename from Engine/Render/src/Render/Vulkan/RendererObjectManager.cpp rename to Engine/Render/src/Render/Vulkan/RendererObjectFactory.cpp index 93c7cb5..f7a8054 100644 --- a/Engine/Render/src/Render/Vulkan/RendererObjectManager.cpp +++ b/Engine/Render/src/Render/Vulkan/RendererObjectFactory.cpp @@ -1,6 +1,6 @@ // Copyright 2024 Stone-Engine -#include "RendererObjectManager.hpp" +#include "RendererObjectFactory.hpp" #include "Device.hpp" #include "Render/Vulkan/VulkanRenderer.hpp" @@ -17,63 +17,63 @@ namespace Stone::Render::Vulkan { -RendererObjectManager::RendererObjectManager(const std::shared_ptr &renderer) - : Scene::RendererObjectManager(), _renderer(renderer) { +RendererObjectFactory::RendererObjectFactory(const std::shared_ptr &renderer) + : Scene::RendererObjectFactory(), _renderer(renderer) { } -void RendererObjectManager::updateMeshNode(const std::shared_ptr &meshNode) { - Scene::RendererObjectManager::updateMeshNode(meshNode); +void RendererObjectFactory::updateMeshNode(const std::shared_ptr &meshNode) { + Scene::RendererObjectFactory::updateMeshNode(meshNode); if (meshNode->getRendererObject()) { return; } auto newMeshNode = std::make_shared(meshNode, _renderer); - setRendererObjectTo(meshNode.get(), newMeshNode); + updateRendererObject(*meshNode, newMeshNode); } -void RendererObjectManager::updateMaterial(const std::shared_ptr &material) { - Scene::RendererObjectManager::updateMaterial(material); +void RendererObjectFactory::updateMaterial(const std::shared_ptr &material) { + Scene::RendererObjectFactory::updateMaterial(material); if (material->getRendererObject()) { return; } auto newMaterial = std::make_shared(material, _renderer); - setRendererObjectTo(material.get(), newMaterial); + updateRendererObject(*material, newMaterial); } -void RendererObjectManager::updateDynamicMesh(const std::shared_ptr &mesh) { - Scene::RendererObjectManager::updateDynamicMesh(mesh); +void RendererObjectFactory::updateDynamicMesh(const std::shared_ptr &mesh) { + Scene::RendererObjectFactory::updateDynamicMesh(mesh); if (mesh->getRendererObject()) { return; } auto newMesh = std::make_shared(mesh, _renderer); - setRendererObjectTo(mesh.get(), newMesh); + updateRendererObject(*mesh, newMesh); } -void RendererObjectManager::updateTexture(const std::shared_ptr &texture) { - Scene::RendererObjectManager::updateTexture(texture); +void RendererObjectFactory::updateTexture(const std::shared_ptr &texture) { + Scene::RendererObjectFactory::updateTexture(texture); if (texture->getRendererObject()) { return; } auto newTexture = std::make_shared(texture, _renderer); - setRendererObjectTo(texture.get(), newTexture); + updateRendererObject(*texture, newTexture); } -void RendererObjectManager::updateShader(const std::shared_ptr &shader) { - Scene::RendererObjectManager::updateShader(shader); +void RendererObjectFactory::updateFragmentShader(const std::shared_ptr &shader) { + Scene::RendererObjectFactory::updateFragmentShader(shader); if (shader->getRendererObject()) { return; } auto newShader = std::make_shared(shader, _renderer); - setRendererObjectTo(shader.get(), newShader); + updateRendererObject(*shader, newShader); } } // namespace Stone::Render::Vulkan diff --git a/Engine/Render/src/Render/Vulkan/RendererObjectManager.hpp b/Engine/Render/src/Render/Vulkan/RendererObjectFactory.hpp similarity index 72% rename from Engine/Render/src/Render/Vulkan/RendererObjectManager.hpp rename to Engine/Render/src/Render/Vulkan/RendererObjectFactory.hpp index a0c7c5c..9bf35f5 100644 --- a/Engine/Render/src/Render/Vulkan/RendererObjectManager.hpp +++ b/Engine/Render/src/Render/Vulkan/RendererObjectFactory.hpp @@ -2,7 +2,7 @@ #pragma once -#include "Scene/RendererObjectManager.hpp" +#include "Scene/Renderer/RendererObjectFactory.hpp" #include @@ -10,12 +10,12 @@ namespace Stone::Render::Vulkan { class VulkanRenderer; -class RendererObjectManager : public Scene::RendererObjectManager { +class RendererObjectFactory : public Scene::RendererObjectFactory { public: - RendererObjectManager(const std::shared_ptr &renderer); - RendererObjectManager(const RendererObjectManager &other) = default; - ~RendererObjectManager() override = default; + RendererObjectFactory(const std::shared_ptr &renderer); + RendererObjectFactory(const RendererObjectFactory &other) = default; + ~RendererObjectFactory() override = default; void updateMeshNode(const std::shared_ptr &meshNode) override; @@ -32,7 +32,7 @@ class RendererObjectManager : public Scene::RendererObjectManager { void updateTexture(const std::shared_ptr &texture) override; - void updateShader(const std::shared_ptr &shader) override; + void updateFragmentShader(const std::shared_ptr &shader) override; private: std::shared_ptr _renderer; diff --git a/Engine/Render/src/Render/Vulkan/VulkanRenderable/Material.cpp b/Engine/Render/src/Render/Vulkan/VulkanRenderable/Material.cpp index 656a8ed..2dc58a8 100644 --- a/Engine/Render/src/Render/Vulkan/VulkanRenderable/Material.cpp +++ b/Engine/Render/src/Render/Vulkan/VulkanRenderable/Material.cpp @@ -9,9 +9,6 @@ Material::Material(const std::shared_ptr &material, const std:: (void)renderer; } -Material::~Material() { -} - void Material::render(Scene::RenderContext &context) { (void)context; } diff --git a/Engine/Render/src/Render/Vulkan/VulkanRenderable/Material.hpp b/Engine/Render/src/Render/Vulkan/VulkanRenderable/Material.hpp index 78b4399..a579256 100644 --- a/Engine/Render/src/Render/Vulkan/VulkanRenderable/Material.hpp +++ b/Engine/Render/src/Render/Vulkan/VulkanRenderable/Material.hpp @@ -23,7 +23,7 @@ class Material : public Scene::IRendererObject { public: Material(const std::shared_ptr &material, const std::shared_ptr &renderer); - ~Material() override; + ~Material() override = default; void render(Scene::RenderContext &context) override; }; diff --git a/Engine/Render/src/Render/Vulkan/VulkanRenderable/Mesh.cpp b/Engine/Render/src/Render/Vulkan/VulkanRenderable/Mesh.cpp index 19568b9..2ae7810 100644 --- a/Engine/Render/src/Render/Vulkan/VulkanRenderable/Mesh.cpp +++ b/Engine/Render/src/Render/Vulkan/VulkanRenderable/Mesh.cpp @@ -9,9 +9,6 @@ Mesh::Mesh(const std::shared_ptr &mesh, const std::shared_pt (void)renderer; } -Mesh::~Mesh() { -} - void Mesh::render(Scene::RenderContext &context) { assert(dynamic_cast(&context)); auto vulkanContext = reinterpret_cast(&context); diff --git a/Engine/Render/src/Render/Vulkan/VulkanRenderable/Mesh.hpp b/Engine/Render/src/Render/Vulkan/VulkanRenderable/Mesh.hpp index b081a5a..faeabbb 100644 --- a/Engine/Render/src/Render/Vulkan/VulkanRenderable/Mesh.hpp +++ b/Engine/Render/src/Render/Vulkan/VulkanRenderable/Mesh.hpp @@ -23,7 +23,7 @@ class Mesh : public Scene::IRendererObject { public: Mesh(const std::shared_ptr &mesh, const std::shared_ptr &renderer); - ~Mesh() override; + ~Mesh() override = default; void render(Scene::RenderContext &context) override; }; diff --git a/Engine/Render/src/Render/Vulkan/VulkanRenderable/MeshNode.cpp b/Engine/Render/src/Render/Vulkan/VulkanRenderable/MeshNode.cpp index 476a67c..6eb8aa1 100644 --- a/Engine/Render/src/Render/Vulkan/VulkanRenderable/MeshNode.cpp +++ b/Engine/Render/src/Render/Vulkan/VulkanRenderable/MeshNode.cpp @@ -13,7 +13,6 @@ #include "Scene/Renderable/Mesh.hpp" #include "Scene/Renderable/Shader.hpp" #include "Scene/Renderable/Texture.hpp" -#include "Scene/RenderContext.hpp" #include "Texture.hpp" #include "Utils/FileSystem.hpp" @@ -82,12 +81,25 @@ void MeshNode::_createDescriptorSetLayout() { auto material = _sceneMeshNode.lock()->getMaterial(); if (material) { - auto shader = material->getFragmentShader(); + auto shader = material->getFragmentShader(); // TODO: Take default fragment shader if none is found if (shader) { material->forEachTextures( - [&](const std::pair> &texture) { + [&](const Scene::Material::Location &loc, const std::shared_ptr &texture) { + (void)texture; + VkDescriptorSetLayoutBinding samplerLayoutBinding; - samplerLayoutBinding.binding = shader->getLocation(texture.first); + + // TODO: Factor this code in scene function + int location = -1; + if (std::holds_alternative(loc)) { + location = std::get(loc); + } else if (std::holds_alternative(loc)) { + location = shader->getLocation(std::get(loc)); + } + if (location == -1) { + return; + } + samplerLayoutBinding.binding = location; samplerLayoutBinding.descriptorCount = 1; samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; @@ -388,7 +400,9 @@ void MeshNode::_createDescriptorPool(const std::shared_ptr &swapChain auto shader = material->getFragmentShader(); if (shader) { material->forEachTextures( - [&](const std::pair> &texture) { + [&](const Scene::Material::Location &location, const std::shared_ptr &texture) { + (void)location; + (void)texture; VkDescriptorPoolSize poolSize = {}; poolSize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; poolSize.descriptorCount = swapChain->getImageCount(); @@ -450,8 +464,18 @@ void MeshNode::_createDescriptorSets(const std::shared_ptr &swapChain auto shader = material->getFragmentShader(); if (shader) { material->forEachTextures( - [&](const std::pair> &texture) { - auto textureObject = texture.second->getRendererObject(); + [&](const Scene::Material::Location &loc, const std::shared_ptr &texture) { + int location = -1; + if (std::holds_alternative(loc)) { + location = std::get(loc); + } else if (std::holds_alternative(loc)) { + location = shader->getLocation(std::get(loc)); + } + if (location == -1) { + return; + } + + auto textureObject = texture->getRendererObject(); imagesInfo.push_back({}); VkDescriptorImageInfo &imageInfo(imagesInfo.back()); @@ -462,7 +486,7 @@ void MeshNode::_createDescriptorSets(const std::shared_ptr &swapChain VkWriteDescriptorSet descriptorWrite = {}; descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrite.dstSet = _descriptorSets[i]; - descriptorWrite.dstBinding = shader->getLocation(texture.first); + descriptorWrite.dstBinding = location; descriptorWrite.dstArrayElement = 0; descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; descriptorWrite.descriptorCount = 1; diff --git a/Engine/Render/src/Render/Vulkan/VulkanRenderable/Shader.cpp b/Engine/Render/src/Render/Vulkan/VulkanRenderable/Shader.cpp index 0c1d8fb..f592382 100644 --- a/Engine/Render/src/Render/Vulkan/VulkanRenderable/Shader.cpp +++ b/Engine/Render/src/Render/Vulkan/VulkanRenderable/Shader.cpp @@ -4,14 +4,11 @@ namespace Stone::Render::Vulkan { -Shader::Shader(const std::shared_ptr &shader, const std::shared_ptr &renderer) { +Shader::Shader(const std::shared_ptr &shader, const std::shared_ptr &renderer) { (void)shader; (void)renderer; } -Shader::~Shader() { -} - void Shader::render(Scene::RenderContext &context) { (void)context; } diff --git a/Engine/Render/src/Render/Vulkan/VulkanRenderable/Shader.hpp b/Engine/Render/src/Render/Vulkan/VulkanRenderable/Shader.hpp index 307e3ff..fe4541c 100644 --- a/Engine/Render/src/Render/Vulkan/VulkanRenderable/Shader.hpp +++ b/Engine/Render/src/Render/Vulkan/VulkanRenderable/Shader.hpp @@ -7,7 +7,7 @@ #include namespace Stone::Scene { -class Shader; +class FragmentShader; } // namespace Stone::Scene namespace Stone::Render::Vulkan { @@ -16,8 +16,8 @@ class VulkanRenderer; class Shader : public Scene::IRendererObject { public: - Shader(const std::shared_ptr &shader, const std::shared_ptr &renderer); - ~Shader() override; + Shader(const std::shared_ptr &shader, const std::shared_ptr &renderer); + ~Shader() override = default; void render(Scene::RenderContext &context) override; }; diff --git a/Engine/Render/src/Render/Vulkan/VulkanRenderer.cpp b/Engine/Render/src/Render/Vulkan/VulkanRenderer.cpp index 4e9e940..d4801f7 100644 --- a/Engine/Render/src/Render/Vulkan/VulkanRenderer.cpp +++ b/Engine/Render/src/Render/Vulkan/VulkanRenderer.cpp @@ -7,6 +7,9 @@ #include "RenderPass.hpp" #include "SwapChain.hpp" +#include +#include + namespace Stone::Render::Vulkan { VulkanRenderer::VulkanRenderer(RendererSettings &settings) : Renderer() { @@ -35,7 +38,7 @@ VulkanRenderer::~VulkanRenderer() { std::cout << "VulkanRenderer destroyed" << std::endl; } -void VulkanRenderer::updateFrameSize(std::pair size) { +void VulkanRenderer::updateFrameSize(const std::pair &size) { _recreateSwapChain(size); } diff --git a/Engine/Render/src/Render/Vulkan/VulkanRenderer_ISceneRenderer.cpp b/Engine/Render/src/Render/Vulkan/VulkanRenderer_ISceneRenderer.cpp index 51f515a..1cf1e77 100644 --- a/Engine/Render/src/Render/Vulkan/VulkanRenderer_ISceneRenderer.cpp +++ b/Engine/Render/src/Render/Vulkan/VulkanRenderer_ISceneRenderer.cpp @@ -4,22 +4,16 @@ #include "FramesRenderer.hpp" #include "Render/Vulkan/VulkanRenderer.hpp" #include "RenderContext.hpp" -#include "RendererObjectManager.hpp" +#include "RendererObjectFactory.hpp" #include "RenderPass.hpp" #include "Scene.hpp" -#include "Scene/ISceneRenderer.hpp" #include "SwapChain.hpp" namespace Stone::Render::Vulkan { -void VulkanRenderer::updateDataForWorld(const std::shared_ptr &world) { - RendererObjectManager manager(std::static_pointer_cast(shared_from_this())); - world->traverseTopDown([&manager](const std::shared_ptr &node) { - auto renderElement = std::dynamic_pointer_cast(node); - if (renderElement && renderElement->isDirty()) { - manager.updateRenderable(node); - } - }); +void VulkanRenderer::updateRenderablesInNode(const std::shared_ptr &rootNode) { + RendererObjectFactory factory(std::static_pointer_cast(shared_from_this())); + factory.updateRenderablesInNode(rootNode); } void VulkanRenderer::renderWorld(const std::shared_ptr &world) { @@ -90,7 +84,9 @@ void VulkanRenderer::_recordCommandBuffer(VkCommandBuffer commandBuffer, ImageCo } std::array clearValues = {}; - clearValues[0].color = {0.0f, 0.0f, 0.0f, 1.0f}; + clearValues[0].color = { + {0.0f, 0.0f, 0.0f, 1.0f} + }; clearValues[1].depthStencil = {1.0f, 0}; VkRenderPassBeginInfo renderPassInfo = {}; diff --git a/Engine/Render/test/OpenGL/test_OpenGLRenderer.cpp b/Engine/Render/test/OpenGL/test_OpenGLRenderer.cpp new file mode 100644 index 0000000..7fd296d --- /dev/null +++ b/Engine/Render/test/OpenGL/test_OpenGLRenderer.cpp @@ -0,0 +1,25 @@ +/* +#include "Render/OpenGL/OpenGLRenderer.hpp" +#include "Render/OpenGL/RendererSettings.hpp" + +#include + +using namespace Stone::Render::OpenGL; +using namespace Stone::Scene; + +TEST(OpenGLRender, InstanciateCore) { + RendererSettings settings; + + try { + OpenGLRenderer renderer(settings); + } catch (const std::runtime_error &e) { + if (std::string(e.what()) == "Failed to create window surface !") { + SUCCEED(); + } else { + FAIL() << e.what(); + } + } catch (const std::exception &e) { + FAIL() << e.what(); + } +} +*/ diff --git a/Engine/Render/test/test_VulkanRenderer.cpp b/Engine/Render/test/Vulkan/test_VulkanRenderer.cpp similarity index 98% rename from Engine/Render/test/test_VulkanRenderer.cpp rename to Engine/Render/test/Vulkan/test_VulkanRenderer.cpp index 5fa067f..0df9598 100644 --- a/Engine/Render/test/test_VulkanRenderer.cpp +++ b/Engine/Render/test/Vulkan/test_VulkanRenderer.cpp @@ -1,3 +1,4 @@ +/* #include "Render/Vulkan/RendererSettings.hpp" #include "Render/Vulkan/VulkanRenderer.hpp" @@ -21,3 +22,4 @@ TEST(VulkanRender, InstanciateCore) { FAIL() << e.what(); } } +*/ diff --git a/Engine/Scene/include/Scene.hpp b/Engine/Scene/include/Scene.hpp index 17d723a..700acc4 100644 --- a/Engine/Scene/include/Scene.hpp +++ b/Engine/Scene/include/Scene.hpp @@ -10,12 +10,15 @@ #include "Scene/Node/PivotNode.hpp" #include "Scene/Node/SkeletonNode.hpp" #include "Scene/Node/SkinMeshNode.hpp" +#include "Scene/Node/WireframeShape.hpp" #include "Scene/Node/WorldNode.hpp" #include "Scene/Renderable/Material.hpp" #include "Scene/Renderable/Mesh.hpp" #include "Scene/Renderable/Shader.hpp" #include "Scene/Renderable/SkinMesh.hpp" #include "Scene/Renderable/Texture.hpp" -#include "Scene/RenderContext.hpp" +#include "Scene/Renderer/ISceneRenderer.hpp" +#include "Scene/Renderer/RenderContext.hpp" +#include "Scene/Renderer/RendererObjectFactory.hpp" #include "Scene/Transform.hpp" #include "Scene/Vertex.hpp" diff --git a/Engine/Scene/include/Scene/Node/MeshNode.hpp b/Engine/Scene/include/Scene/Node/MeshNode.hpp index 4c13783..617542c 100644 --- a/Engine/Scene/include/Scene/Node/MeshNode.hpp +++ b/Engine/Scene/include/Scene/Node/MeshNode.hpp @@ -26,6 +26,8 @@ class MeshNode : public RenderableNode { [[nodiscard]] std::shared_ptr getMaterial() const; void setMaterial(std::shared_ptr material); + [[nodiscard]] std::shared_ptr getUsedMaterial() const; + protected: std::shared_ptr _mesh; std::shared_ptr _material; diff --git a/Engine/Scene/include/Scene/Node/Node.hpp b/Engine/Scene/include/Scene/Node/Node.hpp index b2faa65..a6ced59 100644 --- a/Engine/Scene/include/Scene/Node/Node.hpp +++ b/Engine/Scene/include/Scene/Node/Node.hpp @@ -5,7 +5,7 @@ #include "Core/Object.hpp" #include "Logging/TermColor.hpp" #include "Scene/Node/NodeMacros.hpp" -#include "Scene/RenderContext.hpp" +#include "Scene/Renderer/RenderContext.hpp" #include #include @@ -22,7 +22,7 @@ class WorldNode; * the node hierarchy, updating and rendering nodes, and accessing node properties. */ class Node : public Core::Object { - STONE_NODE(Node); + STONE_OBJECT(Node); public: explicit Node(const std::string &name = "node"); @@ -39,6 +39,9 @@ class Node : public Core::Object { */ void writeToJson(Json::Object &json) const override; + const static std::string nodeClassName; + virtual const char *getNodeClassName() const; + /** * @brief Updates the node. * diff --git a/Engine/Scene/include/Scene/Node/NodeMacros.hpp b/Engine/Scene/include/Scene/Node/NodeMacros.hpp index 16f002e..d64b7f0 100644 --- a/Engine/Scene/include/Scene/Node/NodeMacros.hpp +++ b/Engine/Scene/include/Scene/Node/NodeMacros.hpp @@ -8,7 +8,7 @@ \ public: \ static const std::string nodeClassName; \ - virtual const char *getNodeClassName() const; + const char *getNodeClassName() const override; /** * @brief Macro to use inside an abstract node class to make the engine reconize this class as a node. diff --git a/Engine/Scene/include/Scene/Renderable/IMeshObject.hpp b/Engine/Scene/include/Scene/Renderable/IMeshObject.hpp index 51d708a..d7d727d 100644 --- a/Engine/Scene/include/Scene/Renderable/IMeshObject.hpp +++ b/Engine/Scene/include/Scene/Renderable/IMeshObject.hpp @@ -9,6 +9,12 @@ namespace Stone::Scene { class Material; +enum class MeshType : uint8_t { + Standard = 0, + Skin = 1, + Instanced = 2, +}; + /** * @brief Interface for a mesh object of any type. * diff --git a/Engine/Scene/include/Scene/Renderable/IRenderable.hpp b/Engine/Scene/include/Scene/Renderable/IRenderable.hpp index 4d0ecf5..12cafc0 100644 --- a/Engine/Scene/include/Scene/Renderable/IRenderable.hpp +++ b/Engine/Scene/include/Scene/Renderable/IRenderable.hpp @@ -2,7 +2,7 @@ #pragma once -#include "Scene/RenderContext.hpp" +#include "Scene/Renderer/RenderContext.hpp" #include "Utils/SigSlot.hpp" namespace Stone::Scene { @@ -31,7 +31,7 @@ class IRendererObject { virtual void render(RenderContext &context) = 0; }; -class RendererObjectManager; +class RendererObjectFactory; /** * @brief Interface for renderable elements @@ -77,11 +77,12 @@ class IRenderable { */ template [[nodiscard]] std::shared_ptr getRendererObject() const { - return std::dynamic_pointer_cast(_rendererObject); + assert(std::dynamic_pointer_cast(_rendererObject) != nullptr); + return std::static_pointer_cast(_rendererObject); } protected: - friend class RendererObjectManager; + friend class RendererObjectFactory; /** * @brief Set the renderer object diff --git a/Engine/Scene/include/Scene/Renderable/Material.hpp b/Engine/Scene/include/Scene/Renderable/Material.hpp index 30d7600..7181f63 100644 --- a/Engine/Scene/include/Scene/Renderable/Material.hpp +++ b/Engine/Scene/include/Scene/Renderable/Material.hpp @@ -11,7 +11,7 @@ namespace Stone::Scene { class Texture; -class Shader; +class FragmentShader; /** * @brief The Material class represents a material used for rendering objects in the scene. @@ -23,6 +23,8 @@ class Material : public Core::Object, public IRenderable { STONE_OBJECT(Material) public: + using Location = std::variant; + Material() = default; Material(const Material &other) = default; @@ -40,109 +42,148 @@ class Material : public Core::Object, public IRenderable { /** * @brief Set a texture parameter for the Material. * - * @param name The name of the texture parameter. + * @param location The location of the texture parameter. * @param texture The texture to set. */ - void setTextureParameter(const std::string &name, std::shared_ptr texture); + void setTextureParameter(const Location &location, std::shared_ptr texture); /** * @brief Get a texture parameter from the Material. * - * @param name The name of the texture parameter. + * @param location The location of the texture parameter. * @return The texture parameter as a shared pointer to Texture. */ - [[nodiscard]] std::shared_ptr getTextureParameter(const std::string &name) const; + [[nodiscard]] std::shared_ptr getTextureParameter(const Location &location) const; /** * @brief Set a vector parameter for the Material. * - * @param name The name of the vector parameter. + * @param location The location of the vector parameter. * @param vector The vector to set. */ - void setVectorParameter(const std::string &name, const glm::vec3 &vector); + void setVectorParameter(const Location &location, const glm::vec3 &vector); /** * @brief Get a vector parameter from the Material. * - * @param name The name of the vector parameter. + * @param location The location of the vector parameter. * @return The vector parameter as a glm::vec3. */ - [[nodiscard]] glm::vec3 getVectorParameter(const std::string &name) const; + [[nodiscard]] glm::vec3 getVectorParameter(const Location &location) const; /** * @brief Set a scalar parameter for the Material. * - * @param name The name of the scalar parameter. + * @param location The location of the scalar parameter. * @param scalar The scalar value to set. */ - void setScalarParameter(const std::string &name, float scalar); + void setScalarParameter(const Location &location, float scalar); /** * @brief Get a scalar parameter from the Material. * - * @param name The name of the scalar parameter. + * @param location The location of the scalar parameter. * @return The scalar parameter as a float. */ - [[nodiscard]] float getScalarParameter(const std::string &name) const; + [[nodiscard]] float getScalarParameter(const Location &location) const; /** * @brief Iterate over all texture parameters in the Material. * * @param lambda The lambda function to call for each texture parameter. */ - void forEachTextures(const std::function> &)> &lambda); + void forEachTextures(const std::function &)> &lambda) const; /** * @brief Iterate over all vector parameters in the Material. * * @param lambda The lambda function to call for each vector parameter. */ - void forEachVectors(const std::function &)> &lambda); + void forEachVectors(const std::function &lambda) const; /** * @brief Iterate over all scalar parameters in the Material. * * @param lambda The lambda function to call for each scalar parameter. */ - void forEachScalars(const std::function &)> &lambda); - - /** - * @brief Set the vertex shader used by the Material. - * - * @param vertexShader The vertex shader to set. - */ - void setVertexShader(std::shared_ptr vertexShader); - - /** - * @brief Get the vertex shader used by the Material. - * - * @return The vertex shader as a shared pointer to Shader. - */ - [[nodiscard]] std::shared_ptr getVertexShader() const; + void forEachScalars(const std::function &lambda) const; /** * @brief Set the fragment shader used by the Material. * * @param fragmentShader The fragment shader to set. */ - void setFragmentShader(std::shared_ptr fragmentShader); + void setFragmentShader(std::shared_ptr fragmentShader); /** * @brief Get the fragment shader used by the Material. * * @return The fragment shader as a shared pointer to Shader. */ - [[nodiscard]] std::shared_ptr getFragmentShader() const; + [[nodiscard]] const std::shared_ptr &getFragmentShader() const; protected: - std::unordered_map> _textures; /**< Map of texture parameters. */ - std::unordered_map _vectors; /**< Map of vector parameters. */ - std::unordered_map _scalars; /**< Map of scalar parameters. */ + std::unordered_map> _textures; /**< Map of texture parameters. */ + std::unordered_map _vectors; /**< Map of vector parameters. */ + std::unordered_map _scalars; /**< Map of scalar parameters. */ - std::shared_ptr - _vertexShader; /**< The vertex shader used by the material. nullptr means using the standard shader. */ - std::shared_ptr + std::shared_ptr _fragmentShader; /**< The fragment shader used by the material. nullptr means using the standard shader. */ }; +std::string location_to_string(const Material::Location &location); +Material::Location string_to_location(const std::string &str); + + +#define FOR_EACH_SHADER_PARAMETERS(__WithParam) \ + __WithParam(diffuse) __WithParam(specular) __WithParam(ambient) __WithParam(emissive) __WithParam(shininess) \ + __WithParam(opacity) __WithParam(roughness) __WithParam(metallic) __WithParam(normal) __WithParam(occlusion) \ + __WithParam(height) + +struct MaterialInputSignature { + + enum class Type : uint8_t { + None = 0, + Scalar, + Vector2, + Vector3, + Vector4, + Texture, + }; + + union { + struct { +#define __DECLARE_PARAM(param) Type param : 3; + FOR_EACH_SHADER_PARAMETERS(__DECLARE_PARAM) +#undef __DECLARE_PARAM + }; + uint64_t data; // sizeof() should be greater or equal to the struct size + }; + bool _; + + MaterialInputSignature(); + MaterialInputSignature(const MaterialInputSignature &other) = default; + MaterialInputSignature &operator=(const MaterialInputSignature &other) = default; + + MaterialInputSignature(const std::shared_ptr &material); + + void setParamWithName(const std::string &name, Type value); + + void setFromMaterial(const std::shared_ptr &material); + + bool operator==(const MaterialInputSignature &other) const { + return data == other.data; + } +}; + + } // namespace Stone::Scene + +namespace std { +template <> +struct hash { + std::size_t operator()(const Stone::Scene::MaterialInputSignature ¶ms) const noexcept { + return std::hash()(params.data); + } +}; +} // namespace std diff --git a/Engine/Scene/include/Scene/Renderable/Shader.hpp b/Engine/Scene/include/Scene/Renderable/Shader.hpp index 669c993..d06f4fa 100644 --- a/Engine/Scene/include/Scene/Renderable/Shader.hpp +++ b/Engine/Scene/include/Scene/Renderable/Shader.hpp @@ -10,8 +10,8 @@ namespace Stone::Scene { /** * @brief The Shader class represents a shader used in rendering. */ -class Shader : public Core::Object, public IRenderable { - STONE_OBJECT(Shader); +class AShader : public Core::Object, public IRenderable { + STONE_ABSTRACT_OBJECT(AShader); public: enum class ContentType { @@ -21,15 +21,15 @@ class Shader : public Core::Object, public IRenderable { CompiledFile, /** The content is a link to a file containing the compiled bytes. */ }; - Shader() = default; - explicit Shader(const std::string &content); - Shader(ContentType contentType, std::string content); - Shader(const Shader &other) = default; + AShader() = default; + explicit AShader(const std::string &content); + AShader(ContentType contentType, std::string content); + AShader(const AShader &other) = default; - ~Shader() override = default; + ~AShader() override = default; /** - * @brief Write the Shader object to an output stream. + * @brief Write the shader object to an output stream. * * @param stream The output stream to write to. * @param closing_bracer Flag indicating whether to write a closing brace after the object. @@ -86,12 +86,24 @@ class Shader : public Core::Object, public IRenderable { void setLocation(const std::string &name, int location); private: - ContentType _contentType = ContentType::SourceCode; /** The type of the content. */ - std::string _content = "#version 450 core\n"; /** The content of the shader. */ - std::string _function = "main"; /** The function to call in the shader. */ + ContentType _contentType = ContentType::SourceCode; /** The type of the content. */ + std::string _content = "void customShader(inout Material fragMat) {}"; /** The content of the shader. */ + std::string _function = "customShader"; /** The function to call in the shader. */ std::unordered_map _locations = {}; /** The binding locations of the variables in the shader. */ int _maxLocation = -1; /** The cached maximum value from the locations. */ }; +class FragmentShader : public AShader { + STONE_OBJECT(FragmentShader); + +public: + FragmentShader() = default; + explicit FragmentShader(const std::string &content); + FragmentShader(ContentType contentType, std::string content); + FragmentShader(const FragmentShader &other) = default; + + ~FragmentShader() override = default; +}; + } // namespace Stone::Scene diff --git a/Engine/Scene/include/Scene/ISceneRenderer.hpp b/Engine/Scene/include/Scene/Renderer/ISceneRenderer.hpp similarity index 80% rename from Engine/Scene/include/Scene/ISceneRenderer.hpp rename to Engine/Scene/include/Scene/Renderer/ISceneRenderer.hpp index 260bc92..7c73f5d 100644 --- a/Engine/Scene/include/Scene/ISceneRenderer.hpp +++ b/Engine/Scene/include/Scene/Renderer/ISceneRenderer.hpp @@ -2,12 +2,13 @@ #pragma once -#include "SceneTypes.hpp" - #include namespace Stone::Scene { +class Node; +class WorldNode; + /** * @brief Interface for a scene renderer. */ @@ -16,7 +17,7 @@ class ISceneRenderer { /** * @brief Request the renderer to update all rendering data in the world. */ - virtual void updateDataForWorld(const std::shared_ptr &world) = 0; + virtual void updateRenderablesInNode(const std::shared_ptr &rootNode) = 0; /** * @brief Request the renderer to render the world from the given world root node. diff --git a/Engine/Scene/include/Scene/RenderContext.hpp b/Engine/Scene/include/Scene/Renderer/RenderContext.hpp similarity index 100% rename from Engine/Scene/include/Scene/RenderContext.hpp rename to Engine/Scene/include/Scene/Renderer/RenderContext.hpp diff --git a/Engine/Scene/include/Scene/RendererObjectManager.hpp b/Engine/Scene/include/Scene/Renderer/RendererObjectFactory.hpp similarity index 80% rename from Engine/Scene/include/Scene/RendererObjectManager.hpp rename to Engine/Scene/include/Scene/Renderer/RendererObjectFactory.hpp index 0467c0f..7f37c83 100644 --- a/Engine/Scene/include/Scene/RendererObjectManager.hpp +++ b/Engine/Scene/include/Scene/Renderer/RendererObjectFactory.hpp @@ -12,17 +12,24 @@ class Object; namespace Stone::Scene { /** - * @class RendererObjectManager + * @class RendererObjectFactory * @brief Provide basic overridable methods to manage the renderables objects elements in the scene. * * This class provides basic methods to update the renderer data of classes implementing the IRenderable interface. * It will store an instance of IRendererObject in the IRenderable::_rendererObject proprety. * Each IRenderable objects will use the appropriate method in this interface to update itself. */ -class RendererObjectManager { +class RendererObjectFactory { public: - RendererObjectManager() = default; - virtual ~RendererObjectManager() = default; + RendererObjectFactory() = default; + virtual ~RendererObjectFactory() = default; + + /** + * @brief Recursively updates the renderables in the given node. + * + * @param rootNode The root of the node graph that requires a renderable update + */ + virtual void updateRenderablesInNode(const std::shared_ptr &rootNode); /** * Updates the renderable object with the given node. @@ -79,6 +86,12 @@ class RendererObjectManager { */ virtual void updateStaticSkinMesh(const std::shared_ptr &skinmesh); + /** + * @brief Updates the renderer data for a given wireframe shape. + * @param wireframe shape The wireframe shape to be updated. + */ + virtual void updateWireframeShape(const std::shared_ptr &shape); + /** * @brief Updates the renderer data for a given texture. * @param texture The texture to be updated. @@ -89,7 +102,7 @@ class RendererObjectManager { * @brief Updates the renderer data for a given shader. * @param shader The shader to be updated. */ - virtual void updateShader(const std::shared_ptr &shader); + virtual void updateFragmentShader(const std::shared_ptr &shader); protected: /** @@ -101,17 +114,7 @@ class RendererObjectManager { * @param element The render element to set the renderer object to. * @param rendererObject The renderer object to set. */ - static void setRendererObjectTo(IRenderable *element, const std::shared_ptr &rendererObject); - - /** - * @brief Marks the given element as undirty. - * - * This class is friend with IRenderable, so it can access the protected method markUndirty but its - * inheriting classes can't. - * - * @param element The render element to mark as undirty. - */ - static void markElementUndirty(IRenderable *element); + static void updateRendererObject(IRenderable &element, const std::shared_ptr &rendererObject); }; } // namespace Stone::Scene diff --git a/Engine/Scene/include/SceneTypes.hpp b/Engine/Scene/include/SceneTypes.hpp index 31e64a7..e0a5e18 100644 --- a/Engine/Scene/include/SceneTypes.hpp +++ b/Engine/Scene/include/SceneTypes.hpp @@ -2,7 +2,7 @@ #pragma once -#include "Scene/RenderContext.hpp" +#include "Scene/Renderer/RenderContext.hpp" #include "Scene/Transform.hpp" #include "Scene/Vertex.hpp" @@ -27,7 +27,8 @@ class WorldNode; /** Renderable */ class Texture; class Material; -class Shader; +class AShader; +class FragmentShader; class IMeshObject; class IMeshInterface; @@ -37,5 +38,6 @@ class Skeleton; class ISkinMeshInterface; class DynamicSkinMesh; class StaticSkinMesh; +class WireframeShape; } // namespace Stone::Scene diff --git a/Engine/Scene/src/Scene/Assets/AssetResource_AssimpLoad.cpp b/Engine/Scene/src/Scene/Assets/AssetResource_AssimpLoad.cpp index 0525e49..223daf5 100644 --- a/Engine/Scene/src/Scene/Assets/AssetResource_AssimpLoad.cpp +++ b/Engine/Scene/src/Scene/Assets/AssetResource_AssimpLoad.cpp @@ -214,16 +214,16 @@ void loadMaterial(AssetResource &assetResource, const aiMaterial *material) { addMaterialTexture(assetResource, material, newMaterial, aiTextureType_AMBIENT, "ambient"); addMaterialTexture(assetResource, material, newMaterial, aiTextureType_EMISSIVE, "emissive"); addMaterialTexture(assetResource, material, newMaterial, aiTextureType_HEIGHT, "height"); - addMaterialTexture(assetResource, material, newMaterial, aiTextureType_NORMALS, "normals"); + addMaterialTexture(assetResource, material, newMaterial, aiTextureType_NORMALS, "normal"); addMaterialTexture(assetResource, material, newMaterial, aiTextureType_SHININESS, "shininess"); addMaterialTexture(assetResource, material, newMaterial, aiTextureType_OPACITY, "opacity"); addMaterialTexture(assetResource, material, newMaterial, aiTextureType_DISPLACEMENT, "displacement"); addMaterialTexture(assetResource, material, newMaterial, aiTextureType_LIGHTMAP, "lightmap"); addMaterialTexture(assetResource, material, newMaterial, aiTextureType_REFLECTION, "reflection"); - addMaterialTexture(assetResource, material, newMaterial, aiTextureType_BASE_COLOR, "color"); - addMaterialTexture(assetResource, material, newMaterial, aiTextureType_NORMAL_CAMERA, "normal_camera"); - addMaterialTexture(assetResource, material, newMaterial, aiTextureType_EMISSION_COLOR, "emission_color"); - addMaterialTexture(assetResource, material, newMaterial, aiTextureType_METALNESS, "metalness"); + addMaterialTexture(assetResource, material, newMaterial, aiTextureType_BASE_COLOR, "diffuse"); + addMaterialTexture(assetResource, material, newMaterial, aiTextureType_NORMAL_CAMERA, "normal"); + addMaterialTexture(assetResource, material, newMaterial, aiTextureType_EMISSION_COLOR, "emissive"); + addMaterialTexture(assetResource, material, newMaterial, aiTextureType_METALNESS, "metallic"); addMaterialTexture(assetResource, material, newMaterial, aiTextureType_DIFFUSE_ROUGHNESS, "roughness"); addMaterialTexture(assetResource, material, newMaterial, aiTextureType_AMBIENT_OCCLUSION, "occlusion"); addMaterialTexture(assetResource, material, newMaterial, aiTextureType_SHEEN, "sheen"); diff --git a/Engine/Scene/src/Scene/Geometry.cpp b/Engine/Scene/src/Scene/Geometry.cpp index 32a1318..dc3e3db 100644 --- a/Engine/Scene/src/Scene/Geometry.cpp +++ b/Engine/Scene/src/Scene/Geometry.cpp @@ -19,8 +19,8 @@ std::pair, std::vector> generateGeometryMesh(co std::vector indices; std::vector vertices; - const float phiStep = M_PIf32 / (float)rings; - const float thetaStep = 2 * M_PIf32 / (float)rings; + const float phiStep = (float)M_PI / (float)rings; + const float thetaStep = 2.0f * (float)M_PI / (float)rings; for (int i = 0; i <= rings; i++) { const float phi = (float)i * phiStep; diff --git a/Engine/Scene/src/Scene/Node/InstancedMeshNode.cpp b/Engine/Scene/src/Scene/Node/InstancedMeshNode.cpp index 29d1420..151e944 100644 --- a/Engine/Scene/src/Scene/Node/InstancedMeshNode.cpp +++ b/Engine/Scene/src/Scene/Node/InstancedMeshNode.cpp @@ -2,8 +2,6 @@ #include "Scene/Node/InstancedMeshNode.hpp" -#include "Scene/RendererObjectManager.hpp" - namespace Stone::Scene { STONE_NODE_IMPLEMENTATION(InstancedMeshNode) diff --git a/Engine/Scene/src/Scene/Node/MeshNode.cpp b/Engine/Scene/src/Scene/Node/MeshNode.cpp index d1e0870..73f861a 100644 --- a/Engine/Scene/src/Scene/Node/MeshNode.cpp +++ b/Engine/Scene/src/Scene/Node/MeshNode.cpp @@ -4,7 +4,6 @@ #include "Scene/Renderable/Material.hpp" #include "Scene/Renderable/Mesh.hpp" -#include "Scene/RendererObjectManager.hpp" namespace Stone::Scene { @@ -38,6 +37,14 @@ void MeshNode::setMaterial(std::shared_ptr material) { markDirty(); } +std::shared_ptr MeshNode::getUsedMaterial() const { + if (_material) + return _material; + if (_mesh) + return _mesh->getDefaultMaterial(); + return nullptr; +} + const char *MeshNode::_termClassColor() const { return TERM_COLOR_BOLD TERM_COLOR_GREEN; } diff --git a/Engine/Scene/src/Scene/Node/Node.cpp b/Engine/Scene/src/Scene/Node/Node.cpp index 904991e..918429b 100644 --- a/Engine/Scene/src/Scene/Node/Node.cpp +++ b/Engine/Scene/src/Scene/Node/Node.cpp @@ -6,7 +6,6 @@ #include #include -#include namespace Stone::Scene { diff --git a/Engine/Scene/src/Scene/Node/PivotNode.cpp b/Engine/Scene/src/Scene/Node/PivotNode.cpp index 49f668e..c92067f 100644 --- a/Engine/Scene/src/Scene/Node/PivotNode.cpp +++ b/Engine/Scene/src/Scene/Node/PivotNode.cpp @@ -2,8 +2,6 @@ #include "Scene/Node/PivotNode.hpp" -#include - namespace Stone::Scene { STONE_NODE_IMPLEMENTATION(PivotNode) diff --git a/Engine/Scene/src/Scene/Node/SkinMeshNode.cpp b/Engine/Scene/src/Scene/Node/SkinMeshNode.cpp index ad0fb56..1b6bdc6 100644 --- a/Engine/Scene/src/Scene/Node/SkinMeshNode.cpp +++ b/Engine/Scene/src/Scene/Node/SkinMeshNode.cpp @@ -5,7 +5,6 @@ #include "Scene/Node/SkeletonNode.hpp" #include "Scene/Renderable/Material.hpp" #include "Scene/Renderable/SkinMesh.hpp" -#include "Scene/RendererObjectManager.hpp" namespace Stone::Scene { diff --git a/Engine/Scene/src/Scene/RenderContext.cpp b/Engine/Scene/src/Scene/RenderContext.cpp deleted file mode 100644 index cb8c179..0000000 --- a/Engine/Scene/src/Scene/RenderContext.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright 2024 Stone-Engine - -#include "Scene/RenderContext.hpp" diff --git a/Engine/Scene/src/Scene/Renderable/Material.cpp b/Engine/Scene/src/Scene/Renderable/Material.cpp index 2600c6d..edd3e77 100644 --- a/Engine/Scene/src/Scene/Renderable/Material.cpp +++ b/Engine/Scene/src/Scene/Renderable/Material.cpp @@ -3,7 +3,7 @@ #include "Scene/Renderable/Material.hpp" #include "Scene/Renderable/Texture.hpp" -#include "Scene/RendererObjectManager.hpp" +#include "Scene/Renderer/RendererObjectFactory.hpp" #include "Utils/Glm.hpp" namespace Stone::Scene { @@ -13,50 +13,50 @@ void Material::writeToJson(Json::Object &json) const { auto &textures((json["textures"] = Json::object()).get()); for (auto &it : _textures) - textures[it.first] = it.second ? Json::number(it.second->getId()) : Json::null(); + textures[location_to_string(it.first)] = it.second ? Json::number(it.second->getId()) : Json::null(); auto &vectors((json["vectors"] = Json::object()).get()); for (auto &it : _vectors) - vectors[it.first] = to_json(it.second); + vectors[location_to_string(it.first)] = to_json(it.second); auto &scalars((json["scalars"] = Json::object()).get()); for (auto &it : _scalars) - scalars[it.first] = Json::number(it.second); + scalars[location_to_string(it.first)] = Json::number(it.second); } -void Material::setTextureParameter(const std::string &name, std::shared_ptr texture) { - _textures[name] = std::move(texture); +void Material::setTextureParameter(const Location &location, std::shared_ptr texture) { + _textures[location] = std::move(texture); markDirty(); } -std::shared_ptr Material::getTextureParameter(const std::string &name) const { - auto it = _textures.find(name); +std::shared_ptr Material::getTextureParameter(const Location &location) const { + auto it = _textures.find(location); if (it != _textures.end()) { return it->second; } return nullptr; } -void Material::setVectorParameter(const std::string &name, const glm::vec3 &vector) { - _vectors[name] = vector; +void Material::setVectorParameter(const Location &location, const glm::vec3 &vector) { + _vectors[location] = vector; markDirty(); } -glm::vec3 Material::getVectorParameter(const std::string &name) const { - auto it = _vectors.find(name); +glm::vec3 Material::getVectorParameter(const Location &location) const { + auto it = _vectors.find(location); if (it != _vectors.end()) { return it->second; } return glm::vec3(0.0f); } -void Material::setScalarParameter(const std::string &name, float scalar) { - _scalars[name] = scalar; +void Material::setScalarParameter(const Location &location, float scalar) { + _scalars[location] = scalar; markDirty(); } -float Material::getScalarParameter(const std::string &name) const { - auto it = _scalars.find(name); +float Material::getScalarParameter(const Location &location) const { + auto it = _scalars.find(location); if (it != _scalars.end()) { return it->second; } @@ -64,41 +64,106 @@ float Material::getScalarParameter(const std::string &name) const { } void Material::forEachTextures( - const std::function> &)> &lambda) { - for (auto &it : _textures) { - lambda(it); + const std::function &)> &lambda) const { + for (const auto &it : _textures) { + lambda(it.first, it.second); } } -void Material::forEachVectors(const std::function &)> &lambda) { - for (auto &it : _vectors) { - lambda(it); +void Material::forEachVectors(const std::function &lambda) const { + for (const auto &it : _vectors) { + lambda(it.first, it.second); } } -void Material::forEachScalars(const std::function &)> &lambda) { - for (auto &it : _scalars) { - it.second += 1; - lambda(it); +void Material::forEachScalars(const std::function &lambda) const { + for (const auto &it : _scalars) { + lambda(it.first, it.second); } } -void Material::setVertexShader(std::shared_ptr vertexShader) { - _vertexShader = std::move(vertexShader); +void Material::setFragmentShader(std::shared_ptr fragmentShader) { + _fragmentShader = std::move(fragmentShader); markDirty(); } -std::shared_ptr Material::getVertexShader() const { - return _vertexShader; +const std::shared_ptr &Material::getFragmentShader() const { + return _fragmentShader; } -void Material::setFragmentShader(std::shared_ptr fragmentShader) { - _fragmentShader = std::move(fragmentShader); - markDirty(); +std::string location_to_string(const Material::Location &location) { + if (std::holds_alternative(location)) { + return std::get(location); + } else { + return std::to_string(std::get(location)); + } } -std::shared_ptr Material::getFragmentShader() const { - return _fragmentShader; +Material::Location string_to_location(const std::string &str) { + if (str.empty()) { + return 0; + } + if (str[0] >= '0' && str[0] <= '9') { + return std::stoi(str); + } + return str; +} + +#define __INITIALIZE_PARAM(param) param(Type::None), +MaterialInputSignature::MaterialInputSignature() : FOR_EACH_SHADER_PARAMETERS(__INITIALIZE_PARAM) _() { +} + +MaterialInputSignature::MaterialInputSignature(const std::shared_ptr &material) : MaterialInputSignature() { + setFromMaterial(material); +} + +void MaterialInputSignature::setParamWithName(const std::string &name, Type value) { + using ParamSetter = std::function; + +#define __MAP_NAME_TO_PARAM(param) \ + { \ + #param, [](MaterialInputSignature &matParams, Type value) { \ + matParams.param = value; \ + } \ + } \ + , + + const static std::unordered_map paramSetters = { + FOR_EACH_SHADER_PARAMETERS(__MAP_NAME_TO_PARAM) // + }; + + auto it = paramSetters.find(name); + if (it != paramSetters.end()) { + it->second(*this, value); + } +} + +void MaterialInputSignature::setFromMaterial(const std::shared_ptr &material) { + if (material == nullptr) { + return; + } + + material->forEachScalars([this](const Material::Location &location, float value) { + (void)value; + if (std::holds_alternative(location)) { + const auto &name(std::get(location)); + setParamWithName(name, Type::Scalar); + } + }); + material->forEachVectors([this](const Material::Location &location, glm::vec3 value) { + (void)value; + if (std::holds_alternative(location)) { + const auto &name(std::get(location)); + setParamWithName(name, Type::Vector3); + } + }); + material->forEachTextures([this](const Material::Location &location, auto value) { + (void)value; + if (std::holds_alternative(location)) { + const auto &name(std::get(location)); + setParamWithName(name, Type::Texture); + } + }); } } // namespace Stone::Scene diff --git a/Engine/Scene/src/Scene/Renderable/Shader.cpp b/Engine/Scene/src/Scene/Renderable/Shader.cpp index cfa2d9f..3773bc5 100644 --- a/Engine/Scene/src/Scene/Renderable/Shader.cpp +++ b/Engine/Scene/src/Scene/Renderable/Shader.cpp @@ -6,7 +6,7 @@ namespace Stone::Scene { -Shader::Shader(const std::string &content) +AShader::AShader(const std::string &content) : Object(), IRenderable(), _contentType(ContentType::SourceFile), _content(content) { if (string_ends_with(content, ".glsl")) { _contentType = ContentType::SourceFile; @@ -19,11 +19,11 @@ Shader::Shader(const std::string &content) } } -Shader::Shader(ContentType contentType, std::string content) +AShader::AShader(ContentType contentType, std::string content) : Object(), IRenderable(), _contentType(contentType), _content(std::move(content)) { } -void Shader::writeToJson(Json::Object &json) const { +void AShader::writeToJson(Json::Object &json) const { Object::writeToJson(json); json["function"] = Json::string(_function); @@ -39,24 +39,24 @@ void Shader::writeToJson(Json::Object &json) const { } } -std::pair Shader::getContent() const { +std::pair AShader::getContent() const { return {_contentType, _content}; } -const std::string &Shader::getFunction() const { +const std::string &AShader::getFunction() const { return _function; } -void Shader::setFunction(const std::string &function) { +void AShader::setFunction(const std::string &function) { _function = function; markDirty(); } -int Shader::getMaxLocation() const { +int AShader::getMaxLocation() const { return _maxLocation; } -int Shader::getLocation(const std::string &name) const { +int AShader::getLocation(const std::string &name) const { auto it = _locations.find(name); if (it == _locations.end()) { return -1; @@ -64,13 +64,13 @@ int Shader::getLocation(const std::string &name) const { return it->second; } -void Shader::setContent(ContentType contentType, std::string content) { +void AShader::setContent(ContentType contentType, std::string content) { _contentType = contentType; _content = std::move(content); markDirty(); } -void Shader::setLocation(const std::string &name, int location) { +void AShader::setLocation(const std::string &name, int location) { _locations[name] = location; if (location > _maxLocation) { _maxLocation = location; @@ -78,4 +78,12 @@ void Shader::setLocation(const std::string &name, int location) { markDirty(); } +FragmentShader::FragmentShader(const std::string &content) : AShader(content) { +} + +FragmentShader::FragmentShader(ContentType contentType, std::string content) + : AShader(contentType, std::move(content)) { +} + + } // namespace Stone::Scene diff --git a/Engine/Scene/src/Scene/Renderable/Texture.cpp b/Engine/Scene/src/Scene/Renderable/Texture.cpp index a949349..e40e34d 100644 --- a/Engine/Scene/src/Scene/Renderable/Texture.cpp +++ b/Engine/Scene/src/Scene/Renderable/Texture.cpp @@ -3,7 +3,6 @@ #include "Scene/Renderable/Texture.hpp" #include "Core/Image/ImageSource.hpp" -#include "Scene/RendererObjectManager.hpp" namespace Stone::Scene { diff --git a/Engine/Scene/src/Scene/Renderer/RendererObjectFactory.cpp b/Engine/Scene/src/Scene/Renderer/RendererObjectFactory.cpp new file mode 100644 index 0000000..12c540e --- /dev/null +++ b/Engine/Scene/src/Scene/Renderer/RendererObjectFactory.cpp @@ -0,0 +1,113 @@ +// Copyright 2024 Stone-Engine + +#include "Scene/Renderer/RendererObjectFactory.hpp" + +#include "Scene.hpp" + +namespace Stone::Scene { + +using CastFunction = std::function &)>; + +#define CASTED_FUNCTION_MAP_ENTRY(ClassName) \ + {ClassName::StaticHashCode(), \ + [](RendererObjectFactory &factory, const std::shared_ptr &renderable) { \ + factory.update##ClassName(std::static_pointer_cast(renderable)); \ + }} + +const std::unordered_map updateCastedFunctions = { + CASTED_FUNCTION_MAP_ENTRY(MeshNode), CASTED_FUNCTION_MAP_ENTRY(InstancedMeshNode), + CASTED_FUNCTION_MAP_ENTRY(SkinMeshNode), CASTED_FUNCTION_MAP_ENTRY(Material), + CASTED_FUNCTION_MAP_ENTRY(DynamicMesh), CASTED_FUNCTION_MAP_ENTRY(StaticMesh), + CASTED_FUNCTION_MAP_ENTRY(DynamicSkinMesh), CASTED_FUNCTION_MAP_ENTRY(StaticSkinMesh), + CASTED_FUNCTION_MAP_ENTRY(WireframeShape), CASTED_FUNCTION_MAP_ENTRY(Texture), + CASTED_FUNCTION_MAP_ENTRY(FragmentShader), +}; + +void RendererObjectFactory::updateRenderablesInNode(const std::shared_ptr &rootNode) { + rootNode->traverseTopDown([this](const std::shared_ptr &node) { + auto renderElement = std::dynamic_pointer_cast(node); + if (renderElement && renderElement->isDirty()) { + updateRenderable(node); + } + }); +} + +void RendererObjectFactory::updateRenderable(const std::shared_ptr &renderable) { + auto it = updateCastedFunctions.find(renderable->getClassHashCode()); + if (it != updateCastedFunctions.end()) { + it->second(*this, renderable); + } +} + +void RendererObjectFactory::updateMeshNode(const std::shared_ptr &meshNode) { + if (meshNode->getMaterial() && meshNode->getMaterial()->isDirty()) + updateMaterial(meshNode->getMaterial()); + if (meshNode->getMesh() && meshNode->getMesh()->isDirty()) + updateRenderable(meshNode->getMesh()); +} + +void RendererObjectFactory::updateInstancedMeshNode(const std::shared_ptr &instancedMeshNode) { + if (instancedMeshNode->getMaterial() && instancedMeshNode->getMaterial()->isDirty()) + updateMaterial(instancedMeshNode->getMaterial()); + if (instancedMeshNode->getMesh() && instancedMeshNode->getMesh()->isDirty()) + updateRenderable(instancedMeshNode->getMesh()); +} + +void RendererObjectFactory::updateSkinMeshNode(const std::shared_ptr &skinMeshNode) { + if (skinMeshNode->getMaterial() && skinMeshNode->getMaterial()->isDirty()) + updateMaterial(skinMeshNode->getMaterial()); + if (skinMeshNode->getSkinMesh() && skinMeshNode->getSkinMesh()->isDirty()) + updateRenderable(skinMeshNode->getSkinMesh()); +} + +void RendererObjectFactory::updateMaterial(const std::shared_ptr &material) { + auto fragmentShader = material->getFragmentShader(); + if (fragmentShader && fragmentShader->isDirty()) { + updateFragmentShader(fragmentShader); + } + material->forEachTextures([this](const Material::Location &loc, const std::shared_ptr &texture) { + (void)loc; + if (texture->isDirty()) + updateTexture(texture); + }); +} + +void RendererObjectFactory::updateDynamicMesh(const std::shared_ptr &mesh) { + if (mesh->getDefaultMaterial() && mesh->getDefaultMaterial()->isDirty()) + updateMaterial(mesh->getDefaultMaterial()); +} + +void RendererObjectFactory::updateStaticMesh(const std::shared_ptr &mesh) { + if (mesh->getDefaultMaterial() && mesh->getDefaultMaterial()->isDirty()) + updateMaterial(mesh->getDefaultMaterial()); +} + +void RendererObjectFactory::updateDynamicSkinMesh(const std::shared_ptr &skinmesh) { + if (skinmesh->getDefaultMaterial() && skinmesh->getDefaultMaterial()->isDirty()) + updateMaterial(skinmesh->getDefaultMaterial()); +} + +void RendererObjectFactory::updateStaticSkinMesh(const std::shared_ptr &skinmesh) { + if (skinmesh->getDefaultMaterial() && skinmesh->getDefaultMaterial()->isDirty()) + updateMaterial(skinmesh->getDefaultMaterial()); +} + +void RendererObjectFactory::updateWireframeShape(const std::shared_ptr &shape) { + (void)shape; +} + +void RendererObjectFactory::updateTexture(const std::shared_ptr &texture) { + (void)texture; +} + +void RendererObjectFactory::updateFragmentShader(const std::shared_ptr &shader) { + (void)shader; +} + +void RendererObjectFactory::updateRendererObject(IRenderable &element, + const std::shared_ptr &rendererObject) { + element.setRendererObject(rendererObject); + element.markUndirty(); +} + +} // namespace Stone::Scene diff --git a/Engine/Scene/src/Scene/RendererObjectManager.cpp b/Engine/Scene/src/Scene/RendererObjectManager.cpp deleted file mode 100644 index 0161cf8..0000000 --- a/Engine/Scene/src/Scene/RendererObjectManager.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2024 Stone-Engine - -#include "Scene/RendererObjectManager.hpp" - -#include "Scene.hpp" - -namespace Stone::Scene { - -using CastFunction = std::function &)>; - -#define CASTED_FUNCTION_MAP_ENTRY(ClassName) \ - {ClassName::StaticHashCode(), \ - [](RendererObjectManager &manager, const std::shared_ptr &renderable) { \ - manager.update##ClassName(std::static_pointer_cast(renderable)); \ - }} - -const std::unordered_map updateCastedFunctions = { - CASTED_FUNCTION_MAP_ENTRY(MeshNode), CASTED_FUNCTION_MAP_ENTRY(InstancedMeshNode), - CASTED_FUNCTION_MAP_ENTRY(SkinMeshNode), CASTED_FUNCTION_MAP_ENTRY(Material), - CASTED_FUNCTION_MAP_ENTRY(DynamicMesh), CASTED_FUNCTION_MAP_ENTRY(StaticMesh), - CASTED_FUNCTION_MAP_ENTRY(DynamicSkinMesh), CASTED_FUNCTION_MAP_ENTRY(StaticSkinMesh), - CASTED_FUNCTION_MAP_ENTRY(Texture), CASTED_FUNCTION_MAP_ENTRY(Shader), -}; - -void RendererObjectManager::updateRenderable(const std::shared_ptr &renderable) { - auto it = updateCastedFunctions.find(renderable->getClassHashCode()); - if (it != updateCastedFunctions.end()) { - it->second(*this, renderable); - } -} - -void RendererObjectManager::updateMeshNode(const std::shared_ptr &meshNode) { - if (meshNode->getMaterial() && meshNode->getMaterial()->isDirty()) - updateMaterial(meshNode->getMaterial()); - if (meshNode->getMesh() && meshNode->getMesh()->isDirty()) - updateRenderable(meshNode->getMesh()); - meshNode->markUndirty(); -} - -void RendererObjectManager::updateInstancedMeshNode(const std::shared_ptr &instancedMeshNode) { - if (instancedMeshNode->getMaterial() && instancedMeshNode->getMaterial()->isDirty()) - updateMaterial(instancedMeshNode->getMaterial()); - if (instancedMeshNode->getMesh() && instancedMeshNode->getMesh()->isDirty()) - updateRenderable(instancedMeshNode->getMesh()); - instancedMeshNode->markUndirty(); -} - -void RendererObjectManager::updateSkinMeshNode(const std::shared_ptr &skinMeshNode) { - if (skinMeshNode->getMaterial() && skinMeshNode->getMaterial()->isDirty()) - updateMaterial(skinMeshNode->getMaterial()); - if (skinMeshNode->getSkinMesh() && skinMeshNode->getSkinMesh()->isDirty()) - updateRenderable(skinMeshNode->getSkinMesh()); - skinMeshNode->markUndirty(); -} - -void RendererObjectManager::updateMaterial(const std::shared_ptr &material) { - auto vertexShader = material->getVertexShader(); - if (vertexShader && vertexShader->isDirty()) { - updateShader(vertexShader); - } - auto fragmentShader = material->getFragmentShader(); - if (fragmentShader && fragmentShader->isDirty()) { - updateShader(fragmentShader); - } - material->forEachTextures([this](std::pair> &it) { - if (it.second->isDirty()) - updateTexture(it.second); - }); - material->markUndirty(); -} - -void RendererObjectManager::updateDynamicMesh(const std::shared_ptr &mesh) { - mesh->markUndirty(); -} - -void RendererObjectManager::updateStaticMesh(const std::shared_ptr &mesh) { - mesh->markUndirty(); -} - -void RendererObjectManager::updateDynamicSkinMesh(const std::shared_ptr &skinmesh) { - skinmesh->markUndirty(); -} - -void RendererObjectManager::updateStaticSkinMesh(const std::shared_ptr &skinmesh) { - skinmesh->markUndirty(); -} - -void RendererObjectManager::updateTexture(const std::shared_ptr &texture) { - texture->markUndirty(); -} - -void RendererObjectManager::updateShader(const std::shared_ptr &shader) { - shader->markUndirty(); -} - -void RendererObjectManager::setRendererObjectTo(IRenderable *element, - const std::shared_ptr &rendererObject) { - element->setRendererObject(rendererObject); -} - -void RendererObjectManager::markElementUndirty(IRenderable *element) { - element->markUndirty(); -} - -} // namespace Stone::Scene diff --git a/Engine/Scene/test/test_AssetResource.cpp b/Engine/Scene/test/test_AssetResource.cpp new file mode 100644 index 0000000..75b8a13 --- /dev/null +++ b/Engine/Scene/test/test_AssetResource.cpp @@ -0,0 +1,16 @@ +#include "Core/Assets/Bundle.hpp" +#include "Scene/Assets/AssetResource.hpp" + +#include + +using namespace Stone::Scene; + +TEST(AssetResource, FindBundle) { + auto bundle = std::make_shared("test/"); + + auto resource = bundle->loadResource("test.stone"); + + // TODO: Implement tests that parse fbx, dae, obj, and other formats + // and check if the meshes, textures, materials, and other assets are loaded correctly + // check if metadatas are loaded correctly +} diff --git a/Engine/Utils/include/Utils/SigSlot.hpp b/Engine/Utils/include/Utils/SigSlot.hpp index b9065ef..c4e4654 100644 --- a/Engine/Utils/include/Utils/SigSlot.hpp +++ b/Engine/Utils/include/Utils/SigSlot.hpp @@ -23,6 +23,7 @@ struct Signal; template struct Slot { + Slot() = delete; Slot(const Slot &) = delete; Slot &operator=(const Slot &) = delete; diff --git a/Engine/Window/src/Window/App.cpp b/Engine/Window/src/Window/App.cpp index d803e3a..6ab4382 100644 --- a/Engine/Window/src/Window/App.cpp +++ b/Engine/Window/src/Window/App.cpp @@ -11,7 +11,9 @@ namespace Stone::Window { App::App() : std::enable_shared_from_this(), _windows() { - glfwInit(); + if (glfwInit() == GLFW_FALSE) { + throw std::runtime_error("Failed to initialize GLFW"); + } } App::~App() { diff --git a/Engine/Window/src/Window/GlfwWindow.cpp b/Engine/Window/src/Window/GlfwWindow.cpp index 9ea677f..85643e6 100644 --- a/Engine/Window/src/Window/GlfwWindow.cpp +++ b/Engine/Window/src/Window/GlfwWindow.cpp @@ -2,7 +2,14 @@ #include "Window/GlfwWindow.hpp" +#ifdef STONE_RENDERER_VULKAN #include "Render/Vulkan/VulkanRenderer.hpp" +#elif defined(STONE_RENDERER_OPENGL) +#include "Render/OpenGL/OpenGLRenderer.hpp" +#else +#include "Render/OpenGL/OpenGLRenderer.hpp" +#endif + #include "Scene/Node/WorldNode.hpp" #include @@ -12,8 +19,17 @@ namespace Stone::Window { GlfwWindow::GlfwWindow(const std::shared_ptr &app, const WindowSettings &settings) : Window(app, settings), _glfwWindow(nullptr) { - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, settings.resizable ? GLFW_TRUE : GLFW_FALSE); +#ifdef STONE_RENDERER_VULKAN + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); +#else // def STONE_RENDERER_OPENGL + glfwWindowHint(GLFW_SAMPLES, 0); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_DEPTH_BITS, 32); +#endif GLFWwindow *sharingContext = nullptr; if (!settings.shareContext.expired()) { @@ -30,6 +46,8 @@ GlfwWindow::GlfwWindow(const std::shared_ptr &app, const WindowSettings &se throw std::runtime_error("Failed to create GLFW window"); } + glfwSetWindowSizeLimits(_glfwWindow, 300, 200, GLFW_DONT_CARE, GLFW_DONT_CARE); + glfwMakeContextCurrent(_glfwWindow); _initializeWindowCallbacks(); @@ -39,9 +57,12 @@ GlfwWindow::GlfwWindow(const std::shared_ptr &app, const WindowSettings &se _elapsedTime = glfwGetTime(); if (!_renderer) { + + int frameBufferWidth, frameBufferHeight; + glfwGetFramebufferSize(_glfwWindow, &frameBufferWidth, &frameBufferHeight); +#ifdef STONE_RENDERER_VULKAN Render::Vulkan::RendererSettings rendererSettings; rendererSettings.app_name = settings.title; - uint32_t glfwExtensionCount = 0; const char **glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); rendererSettings.instanceExt = std::vector(glfwExtensions, glfwExtensions + glfwExtensionCount); @@ -49,13 +70,22 @@ GlfwWindow::GlfwWindow(const std::shared_ptr &app, const WindowSettings &se VkSurfaceKHR *surface) { return glfwCreateWindowSurface(instance, _glfwWindow, allocator, surface); }; - rendererSettings.frame_size = { - static_cast(settings.width), - static_cast(settings.height), + static_cast(frameBufferWidth), + static_cast(frameBufferHeight), }; - _renderer = std::make_shared(rendererSettings); +#else + Render::OpenGL::RendererSettings rendererSettings; + rendererSettings.frame_size = { + static_cast(frameBufferWidth), + static_cast(frameBufferHeight), + }; + rendererSettings.rendering_method = Render::OpenGL::RenderingMethod::Forward; + auto renderer = std::make_shared(); + renderer->initialize(rendererSettings); + _renderer = renderer; +#endif } _world->setRenderer(_renderer); diff --git a/Engine/Window/src/Window/Window.cpp b/Engine/Window/src/Window/Window.cpp index 59dbba3..0ee4b23 100644 --- a/Engine/Window/src/Window/Window.cpp +++ b/Engine/Window/src/Window/Window.cpp @@ -25,7 +25,7 @@ void Window::loopOnce() { [this](const std::shared_ptr &node) { node->update(static_cast(_deltaTime)); }); if (_renderer) { - _renderer->updateDataForWorld(_world); + _renderer->updateRenderablesInNode(_world); _renderer->renderWorld(_world); } } @@ -47,7 +47,7 @@ std::shared_ptr Window::getWorld() const { } void Window::_onMouseMoveCallback(double x, double y) { - std::cout << this << ":mouse move " << x << " " << y << std::endl; + // std::cout << this << ":mouse move " << x << " " << y << std::endl; } void Window::_onMouseButtonCallback(int button, int action, int mods) { diff --git a/Makefile b/Makefile index 471f7e6..2183174 100644 --- a/Makefile +++ b/Makefile @@ -50,4 +50,7 @@ ${ALL_EXAMPLES}: examples setup-tidy: @${CMAKE} --preset=setup-tidy -.PHONY: clean all test examples libs setup-tidy +format: + find Engine examples -name '*.cpp' -or -name '*.h' -or -name '*.hpp' | xargs clang-format -i -style=file + +.PHONY: clean all test examples libs setup-tidy format diff --git a/README.md b/README.md index a78d4e6..3dbdbf4 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,30 @@ A 42 student's game engine using Vulkan & C++ ! ## Build and install ```bash +# Linux debian based apt install build-essential cmake ninja-build apt install vulkan-tools libvulkan-dev vulkan-validationlayers-dev apt install libglfw3-dev libglm-dev apt install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev zlib1g-dev apt install libboost-all-dev +apt install glew-utils libglew-dev +``` + +```bash +# Mac OS +brew install cmake +ninja +glew +assimp +glm +glfw +boost +vulkan-extensionlayer +mesa +shaderc + +clang-format +brew install glslang ``` ```bash diff --git a/Resources/unit_tests_root/simple_json.json b/Resources/unit_tests_root/simple_json.json new file mode 100644 index 0000000..c8073cb --- /dev/null +++ b/Resources/unit_tests_root/simple_json.json @@ -0,0 +1,21 @@ +{ + "Name": "Stone", + "Function": "Engine", + "Renderers": [ + { + "Name": "OpenGL", + "Performance": "Medium", + "Ready": true + }, + { + "Name": "Vulkan", + "Performance": "High", + "Ready": true + }, + { + "Name": "Metal", + "Performance": "High", + "Ready": false + } + ] +} \ No newline at end of file diff --git a/examples/glsl_generator/CMakeLists.txt b/examples/glsl_generator/CMakeLists.txt new file mode 100644 index 0000000..03835fa --- /dev/null +++ b/examples/glsl_generator/CMakeLists.txt @@ -0,0 +1,5 @@ +set(NAME glsl_generator) + +add_executable(${NAME} EXCLUDE_FROM_ALL main.cpp) +target_include_directories(${NAME} PRIVATE ${PROJECT_BINARY_DIR}/include) +target_link_libraries(${NAME} PRIVATE scene render) diff --git a/examples/glsl_generator/main.cpp b/examples/glsl_generator/main.cpp new file mode 100644 index 0000000..b9ac3a2 --- /dev/null +++ b/examples/glsl_generator/main.cpp @@ -0,0 +1,134 @@ +#include "config.h" +#include "Render/OpenGL/Shader/ShaderGenerator.hpp" +#include "Scene/Renderable/Shader.hpp" +#include "Utils/FileSystem.hpp" +#include "Utils/Json.hpp" + +#include +#include +#include +#include +#include +#include + +static time_t getLastmodifiedTimeOfFile(const char *filename) { + struct stat result; + if (stat(filename, &result) == 0) { + auto mod_time = result.st_mtime; + return mod_time; + } else + throw std::runtime_error("File does not exist"); +} + +Stone::Scene::MaterialInputSignature parseMaterialInputSignature(const Json::Value &json) { + Stone::Scene::MaterialInputSignature params; + + auto ¶ms_obj = json.get(); + + for (auto [key, value] : params_obj) { + Stone::Scene::MaterialInputSignature::Type type; + + if (!value.is()) + throw std::runtime_error("Invalid type for key " + key); + + const std::string &type_str = value.get(); + if (type_str == "scalar") + type = Stone::Scene::MaterialInputSignature::Type::Scalar; + else if (type_str == "vector2") + type = Stone::Scene::MaterialInputSignature::Type::Vector2; + else if (type_str == "vector3") + type = Stone::Scene::MaterialInputSignature::Type::Vector3; + else if (type_str == "vector4") + type = Stone::Scene::MaterialInputSignature::Type::Vector4; + else if (type_str == "texture") + type = Stone::Scene::MaterialInputSignature::Type::Texture; + else + throw std::runtime_error("Invalid type " + type_str); + + params.setParamWithName(key, type); + } + + return params; +} + +std::string to_string(Stone::Scene::MaterialInputSignature::Type type) { + switch (type) { + case Stone::Scene::MaterialInputSignature::Type::None: return "none"; + case Stone::Scene::MaterialInputSignature::Type::Scalar: return "scalar"; + case Stone::Scene::MaterialInputSignature::Type::Vector2: return "vector2"; + case Stone::Scene::MaterialInputSignature::Type::Vector3: return "vector3"; + case Stone::Scene::MaterialInputSignature::Type::Vector4: return "vector4"; + case Stone::Scene::MaterialInputSignature::Type::Texture: return "texture"; + } + return ""; +} + +void generateShaderOutput(const char *input_file, const char *output_file) { + + Json::Value input_json; + Json::parseFile(input_file, input_json); + Json::Object &input_json_obj(input_json.get()); + + std::ofstream output_stream(output_file, std::ios::out | std::ios::trunc); + + std::shared_ptr shader = nullptr; + if (input_json_obj.find("shader") != input_json_obj.end()) { + shader = std::make_shared(); + shader->setContent(Stone::Scene::AShader::ContentType::SourceCode, + input_json_obj["shader"].get()); + input_json_obj.erase("shader"); + } + + Stone::Scene::MaterialInputSignature params = parseMaterialInputSignature(input_json); + + Stone::Render::OpenGL::ShaderGenerator generator; + std::cout << "Generating shader: {" << std::endl; +#define __PRINT_SHADER_PARAM(param) std::cout << " " << #param << " " << to_string(params.param) << std::endl; + FOR_EACH_SHADER_PARAMETERS(__PRINT_SHADER_PARAM) + std::cout << "}" << std::endl; + + generator.generateOpenGlForwardFragmentShader(params, shader, output_stream); +} + +std::string input; +std::string output; + +void generateShader() { + try { + generateShaderOutput(input.c_str(), output.c_str()); + } catch (const std::exception &e) { + std::cerr << "Error: " << e.what() << std::endl; + } catch (...) { + std::cerr << "Unknown error occurred" << std::endl; + } +} + +void printUsage() { + std::cout << "Usage: glsl_generator [-f]" << std::endl; + std::cout << "-f : Watch for file change" << std::endl; +} + +int main(int argc, const char *argv[]) { + if (argc < 3) { + printUsage(); + return 1; + } + + input = argv[1]; + output = argv[2]; + + generateShader(); + + if (argc >= 4 && std::string(argv[3]).find('f') != std::string::npos) { + while (true) { + time_t last_modified = getLastmodifiedTimeOfFile(input.c_str()); + while (last_modified == getLastmodifiedTimeOfFile(input.c_str())) { + // sleep for a short period + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + } + + generateShader(); + } + return 0; + } +} diff --git a/examples/glsl_generator/shader_params.json b/examples/glsl_generator/shader_params.json new file mode 100644 index 0000000..25ab05b --- /dev/null +++ b/examples/glsl_generator/shader_params.json @@ -0,0 +1,18 @@ +{ + "diffuse": "texture", + "specular": "texture", + "ambient": "scalar", + "emissive": "vector3", + "shininess": "scalar", + "opacity": "texture", + "roughness": "scalar", + "metallic": "vector3", + "normal": "vector3", + "occlusion": "scalar", + "height": "vector3", + "shader": " +void customShader(inout Material mat) { + mat.diffuse = vec3(1, 0, 0); +} +" +} \ No newline at end of file diff --git a/examples/scop/CMakeLists.txt b/examples/scop/CMakeLists.txt index 74e5ab3..f825f41 100644 --- a/examples/scop/CMakeLists.txt +++ b/examples/scop/CMakeLists.txt @@ -1,6 +1,6 @@ set(NAME scop) -add_executable(${NAME} EXCLUDE_FROM_ALL main.cpp) +add_executable(${NAME} EXCLUDE_FROM_ALL main.cpp RotatingNode.cpp) target_include_directories(${NAME} PRIVATE ${PROJECT_BINARY_DIR}/include) target_link_libraries(${NAME} PRIVATE scene diff --git a/examples/scop/RotatingNode.cpp b/examples/scop/RotatingNode.cpp new file mode 100644 index 0000000..974d083 --- /dev/null +++ b/examples/scop/RotatingNode.cpp @@ -0,0 +1,4 @@ + +#include "RotatingNode.hpp" + +STONE_NODE_IMPLEMENTATION(RotatingNode) diff --git a/examples/scop/RotatingNode.hpp b/examples/scop/RotatingNode.hpp new file mode 100644 index 0000000..c78536f --- /dev/null +++ b/examples/scop/RotatingNode.hpp @@ -0,0 +1,23 @@ + +#pragma once + +#include "Scene/Node/PivotNode.hpp" + +class RotatingNode : public Stone::Scene::PivotNode { + STONE_NODE(RotatingNode) + +public: + RotatingNode(const std::string &name = "rotating_node") : PivotNode(name) { + } + + void update(float deltaTime) override { + getTransform().rotate(deltaTime * rotationSpeeds); + } + + void setRotationSpeed(glm::vec3 speeds) { + rotationSpeeds = speeds; + } + +private: + glm::vec3 rotationSpeeds = {0.0f, 0.4f, 0.0f}; +}; diff --git a/examples/scop/ScopEnv.hpp b/examples/scop/ScopEnv.hpp new file mode 100644 index 0000000..75ef800 --- /dev/null +++ b/examples/scop/ScopEnv.hpp @@ -0,0 +1,53 @@ + +#pragma once + +#include "ScopSceneController.hpp" +#include "Window.hpp" + +#include + +namespace Scop { + +class Env { +public: + Env(std::optional path = std::nullopt) { + std::cout << "Starting in directory " << std::filesystem::current_path() << std::endl; + + app = std::make_shared(); + + _makeWindow(); + _makeSceneController(); + + if (path) + sceneController->loadAsset(*path); + } + + int run() { + return app->run(); + } + + ~Env() { + std::cout << "Bye!" << std::endl; + } + +private: + void _makeWindow() { + Stone::Window::WindowSettings win_settings; + win_settings.title = "Scop"; + win_settings.width = 1280; + win_settings.height = 720; + win_settings.resizable = true; + + window = app->createWindow(win_settings); + } + + void _makeSceneController() { + sceneController = std::make_shared(window->getWorld()); + } + + std::shared_ptr app; + std::shared_ptr window; + std::shared_ptr sceneController; +}; + +} // namespace Scop diff --git a/examples/scop/ScopSceneController.hpp b/examples/scop/ScopSceneController.hpp new file mode 100644 index 0000000..b401752 --- /dev/null +++ b/examples/scop/ScopSceneController.hpp @@ -0,0 +1,99 @@ + +#pragma once + +#include "Core/Assets/Bundle.hpp" +#include "Core/Image/ImageSource.hpp" +#include "RotatingNode.hpp" +#include "Scene.hpp" +#include "Scene/Assets/AssetResource.hpp" +#include "Scene/Node/WorldNode.hpp" + +namespace Scop { + +class SceneController { +public: + SceneController(std::shared_ptr world) : world(world) { + assetsBundle = std::make_shared(); + + // Generate a Mesh + auto mesh = makePlaneMesh(); + + // Create a MeshNode + auto meshNode = std::make_shared(); + world->addChild(meshNode); + meshNode->setMesh(mesh); + + // Create a Texture + auto stone_texture = std::make_shared(); + auto stone_image_source = assetsBundle->loadResource( + "docs/img/stone-engine.png", Stone::Core::Image::Channel::RGBA); + stone_texture->setImage(stone_image_source); + + // Create a Material using the texture + auto stone_material = std::make_shared(); + stone_material->setTextureParameter("diffuse", stone_texture); + meshNode->setMaterial(stone_material); + + // Create a shader from a file + // auto stone_shader = std::make_shared("shaders/frag.spv"); + // stone_shader->setLocation("diffuse", 1); + // stone_material->setFragmentShader(stone_shader); + + // Create a second MeshNode with the same mesh + auto meshRotatingNode = world->addChild(); + meshRotatingNode->getTransform().setPosition({0.0f, 0.0f, 0.0f}); + auto secondMeshNode = std::make_shared(); + meshRotatingNode->addChild(secondMeshNode); + meshRotatingNode->setRotationSpeed({0.0f, 0.4f, 0.0f}); + secondMeshNode->setMesh(mesh); + + // Create a blue material that takes no texture as everything is in the shader code + // auto blueShader = std::make_shared("shaders/frag-blue.glsl"); + // auto blueMaterial = std::make_shared(); + // blueMaterial->setFragmentShader(blueShader); + // secondMeshNode->setMaterial(stone_material /* blueMaterial */); + + // Create a camera moving around the scene + auto cameraRotator = world->addChild(); + auto cameraNode = cameraRotator->addChild(); + cameraNode->getTransform().setPosition({0.0f, 3.0f, 3.0f}); + cameraNode->getTransform().rotate({-0.6f, 0.0f, 0.0f}); + world->setActiveCamera(cameraNode); + } + + void loadAsset(std::string path) { + // Load the asset from the given path + assetsBundle = std::make_shared(); + auto asset = assetsBundle->loadResource(path); + auto node = asset->getRootNode(); + world->addChild(node); + std::cout << asset->getMetadatas() << std::endl; + node->writeHierarchy(std::cout); + } + +private: + std::shared_ptr makePlaneMesh() { + auto mesh = std::make_shared(); + mesh->withElementsRef([](auto vertices, auto indices) { + indices = {0, 1, 2, 0, 2, 3}; + vertices.emplace_back(); + vertices.back().position = {-0.5f, -0.5f, 0.0f}; + vertices.back().uv = {0.0f, 0.0f}; + vertices.emplace_back(); + vertices.back().position = {0.5f, -0.5f, 0.0f}; + vertices.back().uv = {1.0f, 0.0f}; + vertices.emplace_back(); + vertices.back().position = {0.5f, 0.5f, 0.0f}; + vertices.back().uv = {1.0f, 1.0f}; + vertices.emplace_back(); + vertices.back().position = {-0.5f, 0.5f, 0.0f}; + vertices.back().uv = {0.0f, 1.0f}; + }); + return mesh; + } + + std::shared_ptr world; + std::shared_ptr assetsBundle; +}; + +} // namespace Scop diff --git a/examples/scop/main.cpp b/examples/scop/main.cpp index eeb5539..541c317 100644 --- a/examples/scop/main.cpp +++ b/examples/scop/main.cpp @@ -4,128 +4,21 @@ #include #endif -#include "Core/Assets/Bundle.hpp" -#include "Core/Image/ImageSource.hpp" -#include "Scene.hpp" -#include "Scene/Assets/AssetResource.hpp" -#include "Window.hpp" - -#include - -class RotatingNode : public Stone::Scene::PivotNode { - STONE_NODE(RotatingNode) - -public: - RotatingNode(const std::string &name = "rotating_node") : PivotNode(name) { - } - - void update(float deltaTime) override { - getTransform().rotate(deltaTime * rotationSpeeds); - } - - void setRotationSpeed(glm::vec3 speeds) { - rotationSpeeds = speeds; - } - -private: - glm::vec3 rotationSpeeds = {0.0f, 0.4f, 0.0f}; -}; - -STONE_NODE_IMPLEMENTATION(RotatingNode) +#include "ScopEnv.hpp" int main(int argc, char **argv) { #ifdef _WIN32 SetConsoleOutputCP(CP_UTF8); #endif - std::cout << "Starting in directory " << std::filesystem::current_path() << std::endl; - int retCode; { - // Create an App - auto app = std::make_shared(); - - // Create a Window - Stone::Window::WindowSettings win_settings; - win_settings.title = "Scop"; - auto window = app->createWindow(win_settings); - - // Create the assets bundle - auto assetsBundle = std::make_shared(); - - // Generate a Mesh - auto mesh = std::make_shared(); - mesh->withElementsRef([](auto vertices, auto indices) { - indices = {0, 1, 2, 0, 2, 3}; - vertices.emplace_back(); - vertices.back().position = {-0.5f, -0.5f, 0.0f}; - vertices.back().uv = {0.0f, 0.0f}; - vertices.emplace_back(); - vertices.back().position = {0.5f, -0.5f, 0.0f}; - vertices.back().uv = {1.0f, 0.0f}; - vertices.emplace_back(); - vertices.back().position = {0.5f, 0.5f, 0.0f}; - vertices.back().uv = {1.0f, 1.0f}; - vertices.emplace_back(); - vertices.back().position = {-0.5f, 0.5f, 0.0f}; - vertices.back().uv = {0.0f, 1.0f}; - }); - - // Create a MeshNode - auto meshNode = window->getWorld()->addChild(); - meshNode->setMesh(mesh); - - // Create a Texture - auto stone_texture = std::make_shared(); - auto stone_image_source = assetsBundle->loadResource( - "docs/img/stone-engine.png", Stone::Core::Image::Channel::RGBA); - stone_texture->setImage(stone_image_source); - - // Create a Material using the texture - auto stone_material = std::make_shared(); - stone_material->setTextureParameter("diffuse", stone_texture); - meshNode->setMaterial(stone_material); - - // Create a shader from a file - auto stone_shader = std::make_shared("shaders/frag.spv"); - stone_shader->setLocation("diffuse", 1); - stone_material->setFragmentShader(stone_shader); - - // Create a second MeshNode with the same mesh - auto meshRotatingNode = window->getWorld()->addChild(); - meshRotatingNode->getTransform().setPosition({0.0f, 0.0f, 0.0f}); - auto secondMeshNode = meshRotatingNode->addChild(); - meshRotatingNode->setRotationSpeed({0.0f, 0.4f, 0.0f}); - secondMeshNode->setMesh(mesh); - - // Create a blue material that takes no texture as everything is in the shader code - auto blueShader = std::make_shared("shaders/frag-blue.glsl"); - auto blueMaterial = std::make_shared(); - blueMaterial->setFragmentShader(blueShader); - secondMeshNode->setMaterial(stone_material /* blueMaterial */); - - // Create a camera moving around the scene - auto cameraRotator = window->getWorld()->addChild(); - auto cameraNode = cameraRotator->addChild(); - cameraNode->getTransform().setPosition({0.0f, 3.0f, 3.0f}); - cameraNode->getTransform().rotate({-0.6f, 0.0f, 0.0f}); - window->getWorld()->setActiveCamera(cameraNode); - - // Load a node from a file - if (argc > 1) { - auto asset = assetsBundle->loadResource(argv[1]); - auto node = asset->getRootNode(); - window->getWorld()->addChild(node); - std::cout << asset->getMetadatas() << std::endl; - node->writeHierarchy(std::cout); - } - - // Run the App - retCode = app->run(); + std::optional path = std::nullopt; + if (argc >= 2) + path = std::string(argv[1]); + retCode = Scop::Env(path).run(); } - std::cout << "Bye!" << std::endl; - #if STONE_ENGINE_USE_SYSTEM_PAUSE system("pause"); #endif