diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..b3fa1d3 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,39 @@ +--- +Checks: "clang-diagnostic-*,clang-analyzer-*,cppcoreguidelines-*,modernize-*,-modernize-use-trailing-return-type" +WarningsAsErrors: true +HeaderFilterRegex: "" +AnalyzeTemporaryDtors: false +FormatStyle: google +CheckOptions: + - key: cert-dcl16-c.NewSuffixes + value: "L;LL;LU;LLU" + - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField + value: "0" + - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors + value: "1" + - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: "1" + - key: google-readability-braces-around-statements.ShortStatementLines + value: "1" + - key: google-readability-function-size.StatementThreshold + value: "800" + - key: google-readability-namespace-comments.ShortNamespaceLines + value: "10" + - key: google-readability-namespace-comments.SpacesBeforeComments + value: "2" + - key: modernize-loop-convert.MaxCopySize + value: "16" + - key: modernize-loop-convert.MinConfidence + value: reasonable + - key: modernize-loop-convert.NamingStyle + value: CamelCase + - key: modernize-pass-by-value.IncludeStyle + value: llvm + - key: modernize-replace-auto-ptr.IncludeStyle + value: llvm + - key: modernize-use-nullptr.NullMacros + value: "NULL" + - key: readability-magic-numbers + value: "0" +--- + diff --git a/CMake/Getassimp.cmake b/CMake/Getassimp.cmake new file mode 100644 index 0000000..7438198 --- /dev/null +++ b/CMake/Getassimp.cmake @@ -0,0 +1,13 @@ +include(ExternalProject) +ExternalProject_Add(external_assimp + GIT_REPOSITORY https://github.com/assimp/assimp.git + GIT_TAG v5.2.5 + GIT_SUBMODULES_RECURSE true + GIT_PROGRESS true + CMAKE_ARGS + -D CMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR} + -D CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -D ASSIMP_INSTALL=NO + -D ASSIMP_BUILD_TESTS=NO +) +message("Downloaded assimp library") \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index d30ac6b..82bb540 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ find_package(GLEW REQUIRED) find_package(glfw3 CONFIG REQUIRED) find_package(glm CONFIG REQUIRED) find_package(OpenGL REQUIRED) -find_package(assimp REQUIRED) +find_package(assimp CONFIG REQUIRED) # Link sources file(GLOB_RECURSE source_list src/*.cpp) @@ -45,7 +45,14 @@ target_link_libraries(simpleengine PUBLIC GLEW::GLEW) target_link_libraries(simpleengine PUBLIC glfw) target_link_libraries(simpleengine PUBLIC ${GLM_LIBRARIES}) target_link_libraries(simpleengine PUBLIC ${OPENGL_LIBRARIES}) -target_link_libraries(simpleengine PUBLIC assimp) +if(WIN32) + target_link_libraries(simpleengine PUBLIC assimp::assimp) +else() + target_link_libraries(simpleengine PUBLIC assimp) +endif() + +# Link resources +target_link_libraries(simpleengine PUBLIC cmrc::base) target_link_libraries(simpleengine PRIVATE simpleengine_resources) # Include some dependencies' include directories diff --git a/examples/dev_testing/CMakeLists.txt b/examples/dev_testing/CMakeLists.txt index 01a45e9..6253a89 100644 --- a/examples/dev_testing/CMakeLists.txt +++ b/examples/dev_testing/CMakeLists.txt @@ -9,17 +9,17 @@ target_sources(dev_testing PRIVATE ${source_list}) target_include_directories(dev_testing PUBLIC include) # Embed shaders -file(GLOB_RECURSE shaders_list resources/shaders/*.glsl) -cmrc_add_resource_library( - resource_shaders - WHENCE resources/shaders - PREFIX shaders - ${shaders_list} -) +#file(GLOB_RECURSE shaders_list resources/shaders/*.glsl) +#cmrc_add_resource_library( +# resource_shaders +# WHENCE resources/shaders +# PREFIX shaders +# ${shaders_list} +#) # Link simpleengine target_link_libraries(dev_testing PUBLIC simpleengine) -target_link_libraries(dev_testing PRIVATE resource_shaders) +#target_link_libraries(dev_testing PRIVATE resource_shaders) # Set standard to C++20 set_target_properties(dev_testing PROPERTIES CXX_STANDARD 20 CXX_EXTENSIONS OFF) \ No newline at end of file diff --git a/examples/dev_testing/resources/shaders/fragment_core.glsl b/examples/dev_testing/resources/shaders/fragment_core.glsl deleted file mode 100644 index d5fe2e6..0000000 --- a/examples/dev_testing/resources/shaders/fragment_core.glsl +++ /dev/null @@ -1,46 +0,0 @@ -#version 440 - -in vec3 vs_position; -in mat4 vs_transform; -in vec2 vs_texcoord; -in vec3 vs_normal; -in vec3 vs_to_light; -in vec3 vs_to_camera; - -uniform bool texture_is_set; -uniform sampler2D vs_texture; -uniform vec3 light_color; -uniform float shine_damper; -uniform float reflectivity; - -out vec4 fs_color; - -void main() { - // Lighting - vec3 unit_normal = normalize(vs_normal); - vec3 unit_light_vector = normalize(vs_to_light); - - float dot_prod = dot(unit_normal, unit_light_vector); - float brightness = max(dot_prod, 0.f); - vec3 diffuse = brightness * light_color; - - // Specular lighting - // only do all this math is reflectivity is > 0 - vec3 final_specular = vec3(0.f); - if (reflectivity > 0) { - vec3 unit_vector_to_camera = normalize(vs_to_camera); - vec3 light_direction = -unit_vector_to_camera; - vec3 reflected_light_dir = reflect(light_direction, unit_normal); - float specular_factor = dot(reflected_light_dir, unit_vector_to_camera); - specular_factor = max(specular_factor, 0.f); - float damped_specular = pow(specular_factor, shine_damper); - final_specular = damped_specular * reflectivity * light_color; - } - - if (texture_is_set) { - //fs_color = vec4(0.5 * unit_normal + vec3(0.5), 1.f); // Visualize normals - fs_color = vec4(diffuse, 1.f) * texture(vs_texture, vs_texcoord) + vec4(final_specular, 1.f); - } else { - fs_color = vec4(diffuse, 1.f) + vec4(final_specular, 1.f); - } -} \ No newline at end of file diff --git a/examples/dev_testing/resources/shaders/vertex_core.glsl b/examples/dev_testing/resources/shaders/vertex_core.glsl deleted file mode 100644 index 42b9be3..0000000 --- a/examples/dev_testing/resources/shaders/vertex_core.glsl +++ /dev/null @@ -1,31 +0,0 @@ -#version 440 - -layout (location = 0) in vec3 vertex_position; -layout (location = 1) in vec2 vertex_texcoord; -layout (location = 2) in vec3 vertex_normal; - -out vec3 vs_position; -out vec2 vs_texcoord; -out mat4 vs_transform; -out vec3 vs_normal; -out vec3 vs_to_light; -out vec3 vs_to_camera; - -uniform mat4 transform_matrix; -uniform mat4 view_matrix; -uniform mat4 projection_matrix; -uniform vec3 light_position; - -void main() { - vec4 world_pos = (transform_matrix * vec4(vertex_position, 1.f)); - - vs_position = world_pos.xyz; - vs_transform = transform_matrix; - vs_texcoord = vertex_texcoord; - - gl_Position = projection_matrix * view_matrix * world_pos; - - vs_normal = (transform_matrix * vec4(vertex_normal, 0.f)).xyz; - vs_to_light = light_position - world_pos.xyz; - vs_to_camera = (inverse(view_matrix) * vec4(0.f, 0.f, 0.f, 1.f)).xyz - world_pos.xyz; -} \ No newline at end of file diff --git a/examples/dev_testing/src/main.cpp b/examples/dev_testing/src/main.cpp index 269f46b..ed161eb 100644 --- a/examples/dev_testing/src/main.cpp +++ b/examples/dev_testing/src/main.cpp @@ -1,8 +1,6 @@ #include "simpleengine/camera.h" #include "simpleengine/ecs/component/mesh_component.h" -#include #include "simpleengine/ecs/component/transform_component.h" -#include #include "simpleengine/ecs/entity.h" #include "simpleengine/gfx/light.h" #include "simpleengine/gfx/material.h" @@ -11,29 +9,28 @@ #include "simpleengine/gfx/renderer.h" #include "simpleengine/gfx/texture.h" #include "simpleengine/vector.h" -#include -#include +#include +#include #include -#include #include -#include -#include #include +#include +#include +#include #include +#include +#include +#include #include #include -#include -#include #include #include +#include #include #include -#include -CMRC_DECLARE(resource_shaders); - namespace se = simpleengine; class FPSCounterEvent : public se::Event { @@ -46,14 +43,14 @@ public: frame_count = 0; } - virtual void update(const float& delta_time) { + virtual void update(const float &delta_time) { double current_time = glfwGetTime(); frame_count++; // Check if the last print was 1 second ago. if (current_time - last_frame_time >= 1.0) { - double ms_per_frame = 1000 / (double) frame_count; - + double ms_per_frame = 1000 / (double)frame_count; + printf("%d fps, %f ms/frame\n", frame_count, ms_per_frame); frame_count = 0; last_frame_time += 1.0; @@ -61,161 +58,55 @@ public: } }; -std::string read_resource_shader(const std::string& path) { - auto fs = cmrc::resource_shaders::get_filesystem(); - cmrc::file vertex_file = fs.open(path); - - return std::string(vertex_file.begin()); -} - int main(int argc, char *argv[]) { se::Game game(640, 480, "SimpleEngine 3D OpenGL - Developer Testing", GLFW_OPENGL_CORE_PROFILE, 4, 4, false); - /* se::gfx::Texture wall_texture("resources/wall.jpg"); - se::gfx::Texture crate_texture("resources/container.jpg", true, true); */ - // Load core shaders from SimpleEngine resources se::gfx::shaders::Core3dShader core_shader; - auto light = std::make_shared(core_shader, glm::vec3(0.f, 1.f, -10.f), glm::vec3(1.f, 1.f, 1.f)); - game.add_event(light); - - auto white_texture = se::gfx::Texture::white_texture(); - // white_texture.shine_damper = 10; - //white_texture.reflectivity = 1; - /* auto dragon = std::make_shared(game.get_window(), core_shader, white_texture, "examples/dev_testing/resources/dragon.obj"); - dragon->translate(0.f, -5.f, -15.f); - game.add_event(dragon); */ - - /* auto cube = std::make_shared(game.get_window(), core_shader, white_texture, "examples/dev_testing/resources/cube.obj"); - cube->translate(0.f, -5.f, -15.f); - game.add_event(cube); - - se::gfx::Texture grass("examples/dev_testing/resources/grass.png"); - auto terrain = std::make_shared(game.get_window(), core_shader, grass, 0, 0); - terrain->translate(0.f, -5.f, -15.f); - game.add_event(terrain); */ - - /* se::gfx::Texture stall_texture("examples/dev_testing/resources/stallTextureb.png"); - auto stall = std::make_shared(game.get_window(), core_shader, stall_texture, "examples/dev_testing/resources/stall.obj"); - stall->translate(10.f, -5.f, 0.f); - stall->rotate_y(90.f); - game.add_event(stall); */ - - /* std::vector square_vertices = { - { se::Vectorf(0.5f, 0.5f, -1.f), glm::vec3(1.f, 0.f, 0.f), glm::vec2(0.f, 0.f) }, // top right - { se::Vectorf(0.5f, -0.5f, -1.f), glm::vec3(0.f, 1.f, 0.f), glm::vec2(1.f, 0.f) }, // bottom right - { se::Vectorf(-0.5f, -0.5f, -1.f), glm::vec3(0.f, 0.f, 1.f), glm::vec2(0.5f, 1.0f) }, // bottom left - { se::Vectorf(-0.5f, 0.5f, -1.f), glm::vec3(.5f, 0.5f, 0.f), glm::vec2(0.5f, 1.0f) }, // top left - }; - - std::vector indicies = { - 0, 1, 3, - 1, 2, 3 - }; - - auto square = std::make_shared(game.get_window(), core_shader, square_vertices, indicies); - square->translate(1.25f, 0.f, -1.f); - //square->rotate_y(90.f); - //square->scale(.75f); - game.add_event(square); */ - - /* se::gfx::Texture white_texture("examples/dev_testing/resources/white_texture.png"); - auto cube = std::make_shared(game.get_window(), core_shader, white_texture, "examples/dev_testing/resources/cube.obj"); - cube->translate(-1.25f, 0.f, -1.f); - game.add_event(cube); */ - - /* std::vector tri_vertices = { - { se::Vectorf(-0.5f, -0.5f, -1.f), glm::vec3(1.f, 0.f, 0.f), glm::vec2(0.f, 0.f) }, // top right - { se::Vectorf(0.5f, -0.5f, -1.f), glm::vec3(0.f, 1.f, 0.f), glm::vec2(1.f, 0.f) }, // bottom right - { se::Vectorf(0.f, 0.5f, -1.f), glm::vec3(0.f, 0.f, 1.f), glm::vec2(0.5f, 1.0f) }, // bottom left - }; - - auto tri = std::make_shared(game.get_window(), core_shader, tri_vertices); - tri->translate(-1.25f, 0.f, -1.f); - tri->scale(.75f); - game.add_event(tri); */ - - std::vector cube_vertices = { - { se::Vectorf(-1.f, -1.f, -1.f), glm::vec3(1.f, 0.f, 0.f), glm::vec2(0.f, 0.f) }, - { se::Vectorf(1.f, -1.f, -1.f), glm::vec3(0.f, 1.f, 0.f), glm::vec2(1.f, 0.f) }, - { se::Vectorf(1.f, 1.f, -1.f), glm::vec3(0.f, 0.f, 1.f), glm::vec2(2.f, 0.f) }, - { se::Vectorf(-1.f, 1.f, -1.f), glm::vec3(1.f, 1.f, 0.f), glm::vec2(3.f, 0.f) }, - { se::Vectorf(-1, -1, -1), glm::vec3(1.f, 1.f, 0.f), glm::vec2(4.f, 0.f) }, - - { se::Vectorf(-1, -1, 1), glm::vec3(1.f, 0.f, 0.f), glm::vec2(0.f, 1.f) }, - { se::Vectorf(1, -1, 1), glm::vec3(0.f, 1.f, 0.f), glm::vec2(1.f, 1.f) }, - { se::Vectorf(1, 1, 1), glm::vec3(0.f, 0.f, 1.f), glm::vec2(2.f, 1.f) }, - { se::Vectorf(-1, 1, 1), glm::vec3(1.f, 1.f, 0.f), glm::vec2(3.f, 1.f) }, - { se::Vectorf(-1, -1, 1), glm::vec3(1.f, 1.f, 0.f), glm::vec2(4.f, 1.f) }, - - { se::Vectorf(-1, 1, -1), glm::vec3(1.f, 0.f, 0.f), glm::vec2(0.f, -1.f) }, - { se::Vectorf(1, 1, -1), glm::vec3(0.f, 1.f, 0.f), glm::vec2(1.f, -1.f) }, - - { se::Vectorf(-1, 1, 1), glm::vec3(1.f, 0.f, 0.f), glm::vec2(0.f, 2.f) }, - { se::Vectorf(1, 1, 1), glm::vec3(0.f, 1.f, 0.f), glm::vec2(1.f, 2.f) }, - }; - - std::vector cube_indicies = { - 0, 1, 5, 5, 1, 6, - 1, 2, 6, 6, 2, 7, - 2, 3, 7, 7, 3, 8, - 3, 4, 8, 8, 4, 9, - 10, 11, 0, 0, 11, 1, - 5, 6, 12, 12, 6, 13 - }; - - std::unordered_map>> textures; - textures.emplace(white_texture.type, std::vector>{ std::make_shared(white_texture) }); - se::gfx::Material white_material(textures, 1.f, 0.f, 0.f, 0.f, 0.f); + auto camera = std::make_shared(game.get_window(), core_shader, 70, glm::vec3(0, 0, 0)); + game.add_event(camera); // Create a renderer - auto renderer = std::make_shared(game.get_window(), core_shader); + auto renderer = std::make_shared(game.get_window(), core_shader, camera); + renderer->initialize(); game.add_renderable(renderer); // Create a Scene and give it the renderer auto scene = std::make_shared(renderer); game.add_event(scene); + se::ecs::Entity other_e = scene->create_entity(); + other_e.add_component("examples/dev_testing/resources/transparent_window.fbx", simpleengine::gfx::ModelProcessingFlags::MdlProcFlag_CALCULATE_TANGENT_SPACE | simpleengine::gfx::ModelProcessingFlags::MdlProcFlag_TRANSPARENT); + auto &other_transform = other_e.add_component(); + other_transform.translate(5.f, 0.5f, 1.f); + // Create an Entity in the Scene and add components to it. se::ecs::Entity entity = scene->create_entity(); - //entity.add_component("examples/dev_testing/resources/dragon.obj"); - //entity.add_component("examples/dev_testing/resources/stall.obj"); - - // Backpack model required vertically flipped texture coords. - auto& model_comp = entity.add_component("examples/dev_testing/resources/backpack/backpack.obj"); - model_comp.model.vertically_flip_tex_coords(); - //entity.add_component("examples/dev_testing/resources/scientist/scientist.fbx"); - //entity.add_component("examples/dev_testing/resources/paradigm/paradigm.fbx"); - //entity.add_component(); - auto& transform_comp = entity.add_component(); - transform_comp.translate(15.f, -8.f, 0.f); - /* transform_comp.scale(0.1f); - transform_comp.rotate_z(-90.f);*/ - transform_comp.rotate_x(-90.f); - + // entity.add_component("examples/dev_testing/resources/planks/planks.fbx", simpleengine::gfx::ModelProcessingFlags::MdlProcFlag_CALCULATE_TANGENT_SPACE); + //entity.add_component("examples/dev_testing/resources/bricks/bricks.fbx", simpleengine::gfx::ModelProcessingFlags::MdlProcFlag_CALCULATE_TANGENT_SPACE); + entity.add_component("examples/dev_testing/resources/transparent_window.fbx", simpleengine::gfx::ModelProcessingFlags::MdlProcFlag_CALCULATE_TANGENT_SPACE | simpleengine::gfx::ModelProcessingFlags::MdlProcFlag_TRANSPARENT); - // Create the entity and add the model component to it. - /* auto entity = std::make_shared(); - entity->add_component(cube_vertices, cube_indicies, white_material, true); - entity->translate(3.5f, 0.f, 0.f); */ + auto &transform_comp = entity.add_component(); + transform_comp.translate(4.f, 0.f, 0.f); - /* auto entity = std::make_shared(); - entity->add_component("examples/dev_testing/resources/dragon.obj"); - entity->translate(12.f, -4.f, 0.f); */ + se::ecs::Entity brick_e = scene->create_entity(); + brick_e.add_component("examples/dev_testing/resources/bricks/bricks.fbx", simpleengine::gfx::ModelProcessingFlags::MdlProcFlag_CALCULATE_TANGENT_SPACE); + auto &brick_transf = brick_e.add_component(); + brick_transf.translate(6.f, -0.5f, 1.f); - auto camera = std::make_shared(game.get_window(), core_shader, 70, glm::vec3(0, 0, 0)); - game.add_event(camera); + auto light = std::make_shared(core_shader, glm::vec3(0.f, 0.f, 0.f), glm::vec3(1.f, 1.f, 1.f)); + game.add_event(light); auto fps_counter = std::make_shared(); game.add_event(fps_counter); game.set_enable_vsync(true); - //game.set_fps_limit(120); + // game.set_fps_limit(120); int res = game.run(); renderer->destroy(); - + return res; } \ No newline at end of file diff --git a/include/simpleengine/gfx/light.h b/include/simpleengine/gfx/light.h index 252b044..86f362a 100644 --- a/include/simpleengine/gfx/light.h +++ b/include/simpleengine/gfx/light.h @@ -18,8 +18,8 @@ namespace simpleengine::gfx { virtual void update(const float& delta_time) override { shader.use(); - shader.set_uniform_float_vec3("light_position", position, false); - shader.set_uniform_float_vec3("light_color", color, false); + shader.set_uniform_float_vec3("u_light_position", position, false); + shader.set_uniform_float_vec3("u_light_color", color, false); shader.unuse(); } diff --git a/include/simpleengine/gfx/material.h b/include/simpleengine/gfx/material.h index c024e45..1acd12b 100644 --- a/include/simpleengine/gfx/material.h +++ b/include/simpleengine/gfx/material.h @@ -1,5 +1,6 @@ #pragma once +#include "rendering_type.h" #include "texture.h" #include @@ -10,17 +11,34 @@ namespace simpleengine::gfx { class Material { public: + RenderingType rendering_type; + + // TODO: Only one of each texture type. std::unordered_map>> textures; - float ambient_scalar; - float diffuse_scalar; - float specular_scalar; - float shine; - float reflectivity; + float ambient_strength; + float diffuse_strength; - Material(std::unordered_map>> textures, float shine = 1.f, float reflectivity = 0.f, float specular_scalar = 0.f, float ambient_scalar = 0.f, float diffuse_scalar = 0.f) : - textures(textures), ambient_scalar(ambient_scalar), diffuse_scalar(diffuse_scalar), specular_scalar(specular_scalar), - shine(shine), reflectivity(reflectivity) { + /** + * @brief This strengthens the brightness of a specular highlight. + * + */ + float specular_strength; + + /** + * @brief The shininess value of the highlight. (Radius of specular highlight?) + + * The higher the shininess value of an object, the more it properly reflects the light + * instead of scattering it all around and thus the smaller the highlight becomes. + * + * The shader multiplies this by 32 to get the specular highlight. + * + */ + float shine_factor; + + Material(std::unordered_map>> textures, RenderingType rendering_type, float shine = 1.f, + float specular_scalar = 1.f, float ambient_scalar = 1.f, float diffuse_scalar = 1.f) : textures(textures), rendering_type(rendering_type), + ambient_strength(ambient_scalar), diffuse_strength(diffuse_scalar), specular_strength(specular_scalar), shine_factor(shine) { } }; diff --git a/include/simpleengine/gfx/mesh.h b/include/simpleengine/gfx/mesh.h index e696053..abf9c2e 100644 --- a/include/simpleengine/gfx/mesh.h +++ b/include/simpleengine/gfx/mesh.h @@ -8,6 +8,7 @@ #include "../renderable.h" #include "../transformable.h" #include "material.h" +#include "../vector.h" #include #include @@ -23,6 +24,9 @@ namespace simpleengine::gfx { std::vector vertices; std::vector indicies; + std::vector tangents; + gfx::VBO tangent_vbo; + bool are_buffers_created = false; gfx::VBO ebo; gfx::VBO vbo; @@ -30,7 +34,7 @@ namespace simpleengine::gfx { Mesh(std::vector vertices, std::vector indicies, Material material); Mesh(std::vector vertices, std::vector indicies = std::vector(), std::optional material = std::nullopt); - Mesh(std::vector vertices, std::vector indicies, Material material, gfx::VBO ebo, gfx::VBO vbo, gfx::VAO vao); + Mesh(std::vector vertices, std::vector indicies, Material material, gfx::VBO ebo, gfx::VBO vbo, gfx::VAO vao, gfx::VBO tangent_vbo); virtual void destroy() override; @@ -45,5 +49,12 @@ namespace simpleengine::gfx { * */ void calculate_normals(); + + /** + * @brief Calculate tangents and bi-tangents (tangent space) for the model. + * + * @note This **will** overwrite the existing tangents and bi-tangents. + */ + void calculate_tangents(); }; } \ No newline at end of file diff --git a/include/simpleengine/gfx/model.h b/include/simpleengine/gfx/model.h index a49773d..4b3c899 100644 --- a/include/simpleengine/gfx/model.h +++ b/include/simpleengine/gfx/model.h @@ -2,6 +2,7 @@ #include "mesh.h" #include "simpleengine/gfx/texture.h" +#include "../vector.h" #include #include @@ -14,6 +15,8 @@ namespace simpleengine::gfx { MdlProcFlag_NONE = 0b00000000, MdlProcFlag_FLIP_TEX_COORDS_VERTICALLY = 0b00000001, MdlProcFlag_FLIP_TEX_COORDS_HORIZONTALLY = 0b00000010, + MdlProcFlag_CALCULATE_TANGENT_SPACE = 0b00000100, + MdlProcFlag_TRANSPARENT = 0b00001000, }; /** @@ -39,12 +42,12 @@ namespace simpleengine::gfx { std::unordered_map> load_all_textures(aiMaterial* material); std::vector> load_material_texture(std::unordered_map>>& processed_textures, aiMaterial* material, aiTextureType type); - protected: void post_process(); public: void vertically_flip_tex_coords(); void horizontally_flip_tex_coords(); + void calculate_tangents(); }; } \ No newline at end of file diff --git a/include/simpleengine/gfx/renderer.h b/include/simpleengine/gfx/renderer.h index 3cb5cce..684e7b7 100644 --- a/include/simpleengine/gfx/renderer.h +++ b/include/simpleengine/gfx/renderer.h @@ -1,36 +1,45 @@ #pragma once +#include "../camera.h" #include "shader.h" -#include "simpleengine/gfx/mesh.h" +#include "../renderable.h" +#include "rendering_type.h" #include #include #include +#include namespace simpleengine::gfx { + class Mesh; + class RenderingJob { public: - gfx::Mesh& rendering_mesh; + RenderingType rendering_type; + gfx::Mesh* rendering_mesh; glm::mat4 transform_mat; - RenderingJob(gfx::Mesh& mesh, glm::mat4 position) : rendering_mesh(mesh), transform_mat(position) { - - } + RenderingJob(RenderingType rendering_type, gfx::Mesh& mesh, glm::mat4 position); }; class Renderer : public simpleengine::Renderable { private: GLFWwindow* window; + protected: public: + std::queue transparent_render_queue; + std::queue other_render_queue; - std::queue rendering_queue; gfx::Shader shader; + std::shared_ptr camera; - Renderer(GLFWwindow* window, gfx::Shader shader); - Renderer(GLFWwindow* window, GLuint shader_program); + Renderer(GLFWwindow* window, gfx::Shader shader, std::shared_ptr camera); + Renderer(GLFWwindow* window, GLuint shader_program, std::shared_ptr camera); void enable_debug(); + virtual void sort_jobs(); + virtual void queue_job(RenderingType rendering_type, gfx::Mesh& mesh, glm::mat4 position); virtual void queue_job(RenderingJob job); virtual void create_job_buffers(RenderingJob& job); @@ -40,5 +49,16 @@ namespace simpleengine::gfx { virtual void update(const float& delta_time) override; virtual void render() override; + + /** + * @brief Renders a single job. + * + * @param job The job that will be rendered. + * @return true if the job was rendered successfully. + * @return false if there was an error when trying to render the job. + */ + virtual bool render_job(const RenderingJob& job); + virtual void render_job_queue(std::queue& queue); + virtual void render_job_queue(std::map>& queue); }; } \ No newline at end of file diff --git a/include/simpleengine/gfx/rendering_type.h b/include/simpleengine/gfx/rendering_type.h new file mode 100644 index 0000000..d6c66dc --- /dev/null +++ b/include/simpleengine/gfx/rendering_type.h @@ -0,0 +1,8 @@ +#pragma once + +namespace simpleengine::gfx { + enum RenderingType { + RendType_OPAQUE = 0, + RendType_TRANSPARENT = 1, + }; +} \ No newline at end of file diff --git a/include/simpleengine/gfx/texture.h b/include/simpleengine/gfx/texture.h index 302f14b..69c492e 100644 --- a/include/simpleengine/gfx/texture.h +++ b/include/simpleengine/gfx/texture.h @@ -23,17 +23,30 @@ namespace simpleengine::gfx { TexFlags_FLIP_VERTICALLY = 0b00000010, TexFlags_FLIP_HORIZONTALLY = 0b00000100, TexFlags_MIPMAP = 0b00001000, + /* TexFlags_NO_COLOR = 0b00010000, + TexFlags_RGB = 0b00100000, + TexFlags_RGBA = 0b01000000, */ }; class Texture { private: - unsigned char* img_data; // TODO Free this if its not used anymore + //unsigned char* img_data; // TODO Free this if its not used anymore unsigned int texture_id; unsigned int image_type_gl; Texture() = default; public: + /** + * @brief The default Texture flags including the color. + * + * The default flags are `TexFlags_IMG_2D | TexFlags_MIPMAP` + * + * @see simpleengine::gfx::Texture::default_flags_no_color + * + */ + static constexpr int default_flags = TexFlags_IMG_2D | TexFlags_MIPMAP; + int height; int width; int channels; @@ -47,8 +60,7 @@ namespace simpleengine::gfx { * @param img_2d Whether or not the texture is 2D. * @param mipmap Whether or not to generate mipmaps for this texture. */ - Texture(const char* path, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = TextureFlags::TexFlags_IMG_2D | - TextureFlags::TexFlags_MIPMAP); + Texture(const char* path, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = Texture::default_flags); /** * @brief Construct a new Texture object from the loaded file buffer. @@ -58,8 +70,7 @@ namespace simpleengine::gfx { * @param img_2d Whether or not the texture is 2D. * @param mipmap Whether or not to generate mipmaps for this texture. */ - Texture(const unsigned char *const buffer, int buffer_length, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = TextureFlags::TexFlags_IMG_2D | - TextureFlags::TexFlags_MIPMAP); + Texture(const unsigned char *const buffer, int buffer_length, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = Texture::default_flags); /** * @brief Construct a new Texture object from the loaded file buffer. @@ -68,8 +79,7 @@ namespace simpleengine::gfx { * @param img_2d Whether or not the texture is 2D. * @param mipmap Whether or not to generate mipmaps for this texture. */ - Texture(std::vector buffer, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = TextureFlags::TexFlags_IMG_2D | - TextureFlags::TexFlags_MIPMAP); + Texture(std::vector buffer, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = Texture::default_flags); static Texture white_texture(); diff --git a/include/simpleengine/gfx/vao.h b/include/simpleengine/gfx/vao.h index f9867e0..ab7ddad 100644 --- a/include/simpleengine/gfx/vao.h +++ b/include/simpleengine/gfx/vao.h @@ -51,6 +51,10 @@ namespace simpleengine::gfx { glBindVertexArray(handle); } + void unbind() const { + glBindVertexArray(0); + } + // TODO: Fix this. void enable_attrib(const VBO& vbo, GLuint index, GLint size, GLenum type, GLsizei stride, size_t offset, bool should_bind = true) const { if (should_bind) { diff --git a/include/simpleengine/gfx/vbo.h b/include/simpleengine/gfx/vbo.h index 0148b94..a24932a 100644 --- a/include/simpleengine/gfx/vbo.h +++ b/include/simpleengine/gfx/vbo.h @@ -40,6 +40,10 @@ namespace simpleengine::gfx { glBindBuffer(type, handle); } + void unbind() const { + glBindBuffer(type, 0); + } + void buffer(void *data, size_t offset, size_t size) { bind(); glBufferData(type, size - offset, data, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); diff --git a/include/simpleengine/vertex.h b/include/simpleengine/vertex.h index ccbccd7..e43b2ac 100644 --- a/include/simpleengine/vertex.h +++ b/include/simpleengine/vertex.h @@ -26,22 +26,21 @@ namespace simpleengine { glm::vec3 color; glm::vec2 tex_coord; glm::vec3 normal; - float texture_id = -1.f; LitVertex() = default; LitVertex(simpleengine::Vectorf position, glm::vec3 color, glm::vec2 tex_coord, int texture_id = -1.f) : - position(position), color(color), tex_coord(tex_coord), normal(glm::vec3(0.f)), texture_id((float) texture_id) { + position(position), color(color), tex_coord(tex_coord), normal(glm::vec3(0.f)) { } LitVertex(simpleengine::Vectorf position, glm::vec3 color, glm::vec2 tex_coord, glm::vec3 normal, int texture_id = -1.f) : - position(position), color(color), tex_coord(tex_coord), normal(normal), texture_id((float) texture_id) { + position(position), color(color), tex_coord(tex_coord), normal(normal) { } LitVertex(simpleengine::Vectorf position, glm::vec2 tex_coord, glm::vec3 normal, int texture_id = -1.f) : - position(position), color(glm::vec3(1.f)), tex_coord(tex_coord), normal(normal), texture_id((float) texture_id) { + position(position), color(glm::vec3(1.f)), tex_coord(tex_coord), normal(normal) { } }; diff --git a/resources/shaders/core/3d/fragment_core.glsl b/resources/shaders/core/3d/fragment_core.glsl index 962e57b..0751d1c 100644 --- a/resources/shaders/core/3d/fragment_core.glsl +++ b/resources/shaders/core/3d/fragment_core.glsl @@ -3,56 +3,94 @@ in vec3 vs_position; in vec3 vs_color; in vec3 vs_normal; +in vec3 vs_world_normal; // Normal in world space in vec2 vs_texcoord; -flat in float vs_tex_id; // < 0 is reserved for solid colored objects. in mat4 vs_transform; -in vec3 vs_to_light; -in vec3 vs_to_camera; -uniform sampler2D u_textures[16]; -uniform float u_texture_shine[16]; -uniform float u_texture_reflectivity[16]; +in vec3 vs_view_pos; +in vec3 vs_light_pos; +in vec3 vs_frag_pos; -uniform vec3 light_color; +//in mat3 vs_tbn; + +in vec3 vs_tangent_light_pos; +in vec3 vs_tangent_view_pos; +in vec3 vs_tangent_frag_pos; + +const int SAMP_DIFFUSE = 0; +const int SAMP_SPECULAR = 1; + +struct Material { + sampler2D diffuse; + + bool has_specular_map; + sampler2D specular_map; + + // TODO: Make Optional + bool has_normal_map; + sampler2D normal_map; + + float ambient_strength; + float diffuse_strength; + float specular_strength; + + float shine_factor; +}; + +uniform Material u_material; + +uniform vec3 u_light_color; out vec4 fs_color; -vec3 calculate_specular(vec3 unit_normal, float shine_damper, float reflectivity); +vec3 calculate_lighting(); void main() { - // Lighting - vec3 unit_normal = normalize(vs_normal); - vec3 unit_light_vector = normalize(vs_to_light); + // Skip completely transparent fragments. + /* vec4 diffuse = texture(u_material.diffuse, vs_texcoord); + if (diffuse.a < 0.1) { + discard; + } */ - float dot_prod = dot(unit_normal, unit_light_vector); - float brightness = max(dot_prod, 0.f); - vec3 diffuse = brightness * light_color; + vec3 lighting = calculate_lighting(); - if (vs_tex_id > -1) { - int id = int(vs_tex_id); - - float shine_damper = u_texture_shine[id]; - float reflectivity = u_texture_reflectivity[id]; - vec3 final_specular = calculate_specular(unit_normal, shine_damper, reflectivity); - - fs_color = vec4(diffuse, 1.f) * texture(u_textures[id], vs_texcoord) + vec4(final_specular, 1.f); - } else { - fs_color = vec4(vs_color, 1.f); // We don't add any reflectivity to solid colored vectors. - } + fs_color = vec4(lighting, 1.f) * texture(u_material.diffuse, vs_texcoord); } -vec3 calculate_specular(vec3 unit_normal, float shine_damper, float reflectivity) { - vec3 final_specular = vec3(0.f); - if (reflectivity > 0) { - vec3 unit_vector_to_camera = normalize(vs_to_camera); - vec3 light_direction = -unit_vector_to_camera; - vec3 reflected_light_dir = reflect(light_direction, unit_normal); - float specular_factor = dot(reflected_light_dir, unit_vector_to_camera); - specular_factor = max(specular_factor, 0.f); - float damped_specular = pow(specular_factor, shine_damper); - final_specular = damped_specular * reflectivity * light_color; +vec3 calculate_lighting() { + vec3 normal = vec3(0.f, 0.f, 1.f); // default tangent-space normal + + // Check if the normal map is set before trying to apply it. + if (u_material.has_normal_map) { + normal = texture(u_material.normal_map, vs_texcoord).rgb; + + // transform normal vector to range [-1,1] + normal = normalize(normal * 2.f - 1.f); // this normal is in tangent space + } + + // Get diffuse color + vec3 diffuse_map = texture(u_material.diffuse, vs_texcoord).rgb; + + // Ambient lighting + vec3 ambient = 0.1f * u_material.ambient_strength * diffuse_map; + + // Diffuse lighting + vec3 light_dir = normalize(vs_tangent_light_pos - vs_tangent_frag_pos); + float diff = max(dot(light_dir, normal), 0.f); + vec3 diffuse = diff * u_material.diffuse_strength * diffuse_map; + + // Specular lighting + vec3 view_dir = normalize(vs_tangent_view_pos - vs_tangent_frag_pos); + vec3 reflect_dir = reflect(-light_dir, normal); + vec3 halfway_dir = normalize(light_dir + view_dir); + float spec = pow(max(dot(normal, halfway_dir), 0.f), 32.f * u_material.shine_factor); + vec3 specular = vec3(0.2f * u_material.specular_strength) * spec; + + // Check if the specular map is set before trying to apply it. + if (u_material.has_specular_map) { + specular = specular * texture(u_material.specular_map, vs_texcoord).r; } - return final_specular; + return ambient + diffuse + specular; } \ No newline at end of file diff --git a/resources/shaders/core/3d/vertex_core.glsl b/resources/shaders/core/3d/vertex_core.glsl index cc5d36f..c64ca61 100644 --- a/resources/shaders/core/3d/vertex_core.glsl +++ b/resources/shaders/core/3d/vertex_core.glsl @@ -4,38 +4,62 @@ layout (location = 0) in vec3 vertex_position; layout (location = 1) in vec3 vertex_color; layout (location = 2) in vec3 vertex_normal; layout (location = 3) in vec2 vertex_texcoord; -layout (location = 4) in float vertex_tex_id; +// TODO: Make these optional +layout (location = 4) in vec3 vertex_tangent; out vec3 vs_position; out vec3 vs_color; out vec3 vs_normal; +out vec3 vs_world_normal; // normal in world space out vec2 vs_texcoord; -flat out float vs_tex_id; out mat4 vs_transform; -out vec3 vs_to_light; -out vec3 vs_to_camera; -uniform mat4 transform_matrix; -uniform mat4 view_matrix; -uniform mat4 projection_matrix; -uniform vec3 light_position; +out vec3 vs_view_pos; +out vec3 vs_light_pos; +out vec3 vs_frag_pos; + +//out mat3 vs_tbn; + +out vec3 vs_tangent_light_pos; +out vec3 vs_tangent_view_pos; +out vec3 vs_tangent_frag_pos; + +uniform mat4 u_transform_matrix; +uniform mat4 u_view_matrix; +uniform mat4 u_projection_matrix; + +uniform vec3 u_view_pos; +uniform vec3 u_light_position; void main() { - vec4 world_pos = (transform_matrix * vec4(vertex_position, 1.f)); + vec4 world_pos = (u_transform_matrix * vec4(vertex_position, 1.f)); // Directly pass things to the fragment shader. vs_position = world_pos.xyz; - vs_transform = transform_matrix; + vs_transform = u_transform_matrix; vs_texcoord = vertex_texcoord; vs_color = vertex_color; - vs_tex_id = vertex_tex_id; - - - gl_Position = projection_matrix * view_matrix * world_pos; + gl_Position = u_projection_matrix * u_view_matrix * world_pos; - vs_normal = (transform_matrix * vec4(vertex_normal, 0.f)).xyz; - vs_to_light = light_position - world_pos.xyz; - vs_to_camera = (inverse(view_matrix) * vec4(0.f, 0.f, 0.f, 1.f)).xyz - world_pos.xyz; + // Apply transform matrix to normal. + vs_normal = (u_transform_matrix * vec4(vertex_normal, 0.f)).xyz; + + vs_view_pos = u_view_pos; + vs_light_pos = u_light_position; + vs_frag_pos = vec3(u_transform_matrix * vec4(vertex_position, 1.f)); + vs_world_normal = mat3(transpose(inverse(u_transform_matrix))) * vertex_normal; // TODO: Do this calculation on the CPU then send to GPU via a uniform + + // Calculate tangent space stuff. + mat3 normal_matrix = transpose(inverse(mat3(u_transform_matrix))); + vec3 T = normalize(normal_matrix * vertex_tangent); + vec3 N = normalize(normal_matrix * vertex_normal); + T = normalize(T - dot(T, N) * N); + vec3 B = cross(N, T); + + mat3 TBN = transpose(mat3(T, B, N)); + vs_tangent_light_pos = TBN * u_light_position; + vs_tangent_view_pos = TBN * u_view_pos; + vs_tangent_frag_pos = TBN * vs_frag_pos; } \ No newline at end of file diff --git a/src/camera.cpp b/src/camera.cpp index 5400a5c..64236f0 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -63,19 +63,19 @@ namespace simpleengine { } if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) { - rotation.z += camera_speed * .3; + rotation.z += camera_speed * .4; } if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) { - rotation.z -= camera_speed * .3; + rotation.z -= camera_speed * .4; } if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) { - rotation.y -= camera_speed * .3; + rotation.y -= camera_speed * .4; } if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) { - rotation.y += camera_speed * .3; + rotation.y += camera_speed * .4; } // Limit the pitch of the camera. @@ -96,8 +96,9 @@ namespace simpleengine { view_matrix = glm::lookAt(position, position + camera_front, camera_up); shader.use(); - shader.set_uniform_matrix_4f("view_matrix", view_matrix, false); - shader.set_uniform_matrix_4f("projection_matrix", projection_matrix, false); + shader.set_uniform_float_vec3("u_view_pos", position, false); + shader.set_uniform_matrix_4f("u_view_matrix", view_matrix, false); + shader.set_uniform_matrix_4f("u_projection_matrix", projection_matrix, false); shader.unuse(); } } \ No newline at end of file diff --git a/src/game.cpp b/src/game.cpp index 9d603e9..dc7d5ac 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -48,9 +48,7 @@ simpleengine::Game::Game(int w, int h, const std::string& window_name, const int } void simpleengine::Game::enable_default_gl_options() const { - glEnable(GL_DEPTH_TEST); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); + //glFrontFace(GL_CW); update_enabled_vsync(); diff --git a/src/gfx/mesh.cpp b/src/gfx/mesh.cpp index ba48518..4c47090 100644 --- a/src/gfx/mesh.cpp +++ b/src/gfx/mesh.cpp @@ -1,23 +1,26 @@ #include "gfx/mesh.h" +#include "gfx/vbo.h" +#include "vector.h" #include namespace simpleengine::gfx { Mesh::Mesh(std::vector vertices, std::vector indicies, Material material) : material(std::make_optional(material)), vertices(vertices), indicies(indicies), vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)), ebo(gfx::VBO::init(GL_ELEMENT_ARRAY_BUFFER, false)), - vao(gfx::VAO::init()) { + vao(gfx::VAO::init()), tangent_vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)) { } Mesh::Mesh(std::vector vertices, std::vector indicies, std::optional material) : material(material), vertices(vertices), indicies(indicies), vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)), ebo(gfx::VBO::init(GL_ELEMENT_ARRAY_BUFFER, false)), - vao(gfx::VAO::init()) { + vao(gfx::VAO::init()), tangent_vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)) { } - Mesh::Mesh(std::vector vertices, std::vector indicies, Material material, gfx::VBO ebo, gfx::VBO vbo, gfx::VAO vao) : - vertices(vertices), indicies(indicies), material(material), ebo(ebo), vbo(vbo), vao(vao) { + Mesh::Mesh(std::vector vertices, std::vector indicies, Material material, gfx::VBO ebo, gfx::VBO vbo, gfx::VAO vao, + gfx::VBO tangent_vbo) : + vertices(vertices), indicies(indicies), material(material), ebo(ebo), vbo(vbo), vao(vao), tangent_vbo(tangent_vbo) { } @@ -76,4 +79,40 @@ namespace simpleengine::gfx { vertices[i].normal = normals[i]; } } + + void Mesh::calculate_tangents() { + // Resize the tangents vector, they will overwritten, + // so no need to clear the vector. + tangents.resize(vertices.size()); + + for (int i = 0; i < indicies.size(); i += 3) { + int index0 = indicies[i]; + int index1 = indicies[i+1]; + int index2 = indicies[i+2]; + + LitVertex& lit_vertex0 = vertices[index0]; + LitVertex& lit_vertex1 = vertices[index1]; + LitVertex& lit_vertex2 = vertices[index2]; + + glm::vec3 pos0 = lit_vertex0.position; + glm::vec3 pos1 = lit_vertex1.position; + glm::vec3 pos2 = lit_vertex2.position; + + // Edges of the triangle : postion delta + glm::vec3 delta_pos1 = pos1 - pos0; + glm::vec3 delta_pos2 = pos2 - pos0; + + // UV delta + glm::vec2 delta_uv1 = lit_vertex1.tex_coord - lit_vertex0.tex_coord; + glm::vec2 delta_uv2 = lit_vertex2.tex_coord - lit_vertex0.tex_coord; + + float r = 1.0f / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x); + glm::vec3 tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y)*r; + //glm::vec3 bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x)*r; + + tangents[index0] = tangent; + tangents[index1] = tangent; + tangents[index2] = tangent; + } + } } \ No newline at end of file diff --git a/src/gfx/model.cpp b/src/gfx/model.cpp index 98d8f7a..956ea1d 100644 --- a/src/gfx/model.cpp +++ b/src/gfx/model.cpp @@ -1,5 +1,6 @@ #include "gfx/model.h" #include "gfx/material.h" +#include "gfx/rendering_type.h" #include "gfx/texture.h" #include "vector.h" @@ -10,6 +11,7 @@ #include #include +#include #include #include @@ -32,6 +34,10 @@ namespace simpleengine::gfx { void Model::load_model(std::string path) { Assimp::Importer importer; + if (model_processing_flags & ModelProcessingFlags::MdlProcFlag_CALCULATE_TANGENT_SPACE) { + additional_assimp_flags |= aiProcess_CalcTangentSpace; + } + // assimp post processing options: http://assimp.sourceforge.net/lib_html/postprocess_8h.html const aiScene *scene = importer.ReadFile(path, additional_assimp_flags | aiProcess_Triangulate | aiProcess_FlipUVs); @@ -63,27 +69,33 @@ namespace simpleengine::gfx { gfx::Mesh Model::process_mesh(std::unordered_map>>& processed_textures, aiMesh* mesh, const aiScene* scene) { std::vector vertices; - std::vector indices; + std::vector indicies; + std::vector tangents; for (unsigned int i = 0; i < mesh->mNumVertices; i++) { LitVertex vertex; vertex.color = glm::vec3(1.f); - vertex.texture_id = 0; simpleengine::Vectorf position(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z); vertex.position = position; + // Only process normals if they exist. if (mesh->HasNormals()) { glm::vec3 normal(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z); vertex.normal = normal; } - if (mesh->mTextureCoords[0]) { + // Only process texture coords if they exist. + if (mesh->HasTextureCoords(0)) { glm::vec2 tex_coord(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y); vertex.tex_coord = tex_coord; } + if (mesh->HasTangentsAndBitangents()) { + tangents.emplace_back(mesh->mTangents[i].x, mesh->mTangents[i].y, mesh->mTangents[i].z); + } + vertices.push_back(vertex); } @@ -92,15 +104,21 @@ namespace simpleengine::gfx { aiFace face = mesh->mFaces[i]; for (int j = 0; j < face.mNumIndices; j++) { - indices.push_back(face.mIndices[j]); + indicies.push_back(face.mIndices[j]); } } + // Get the rendering type + RenderingType rendering_type = RenderingType::RendType_OPAQUE; + if (model_processing_flags & ModelProcessingFlags::MdlProcFlag_TRANSPARENT) { + rendering_type = RenderingType::RendType_TRANSPARENT; + } + // Create a default material and white texture. auto white_texture = gfx::Texture::white_texture(); std::unordered_map>> default_textures; default_textures.emplace(white_texture.type, std::vector>{ std::make_shared(white_texture) }); - gfx::Material mat(default_textures, 1.f, 0.f, 0.f, 0.f, 0.f); + gfx::Material mat(default_textures, rendering_type); if (mesh->mMaterialIndex >= 0) { std::cout << "TODO: Process model materials!" << std::endl; @@ -109,14 +127,30 @@ namespace simpleengine::gfx { aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex]; // Load Diffuse texture maps - std::vector> diffuse_maps = load_material_texture(processed_textures, material, aiTextureType_DIFFUSE); - if (!diffuse_maps.empty()) textures.emplace(aiTextureType_DIFFUSE, diffuse_maps); + aiTextureType loading_type = aiTextureType_DIFFUSE; + std::vector> diffuse_maps = load_material_texture(processed_textures, material, loading_type); + if (!diffuse_maps.empty()) textures.emplace(loading_type, diffuse_maps); + + // Load specular texture maps + loading_type = aiTextureType_SPECULAR; + std::vector> spec_maps = load_material_texture(processed_textures, material, loading_type); + if (!spec_maps.empty()) textures.emplace(loading_type, spec_maps); + + // Load normals texture maps + loading_type = aiTextureType_NORMALS; + std::vector> normal_maps = load_material_texture(processed_textures, material, loading_type); + if (!normal_maps.empty()) { + textures.emplace(loading_type, normal_maps); + + // Force calculation of tangent space. + model_processing_flags |= MdlProcFlag_CALCULATE_TANGENT_SPACE; + } // TODO Handle other types of texture maps if (!textures.empty()) { // TODO: Find a way to let the user set the scalars. - mat = Material(textures, 1.f, 0.f, 0.f, 0.f, 0.f); + mat = Material(textures, rendering_type); // Add `textures` into the `processed_textures` list. for (const auto& pair : textures) { @@ -144,12 +178,16 @@ namespace simpleengine::gfx { } } - Mesh se_mesh(vertices, indices, mat); + Mesh se_mesh(vertices, indicies, mat); if (!mesh->HasNormals()) { se_mesh.calculate_normals(); } + if (mesh->HasTangentsAndBitangents()) { + se_mesh.tangents = tangents; + } + return se_mesh; } @@ -185,7 +223,7 @@ namespace simpleengine::gfx { ss << model_directory << "/" << texture_path; std::string full_path = ss.str(); - Texture texture(full_path.c_str(), type, /* TextureFlags::TexFlags_FLIP_VERTICALLY | */ TextureFlags::TexFlags_IMG_2D | TextureFlags::TexFlags_MIPMAP); + Texture texture(full_path.c_str(), type); texture.path = texture_path; textures.emplace_back(std::make_shared(texture)); @@ -210,4 +248,50 @@ namespace simpleengine::gfx { } } } + + void Model::calculate_tangents() { + for (auto& mesh : meshes) { + // TODO: Find a way to check if normals and UVs are set before calculating tangents + mesh.calculate_tangents(); + } + } + + /* std::vector> calculate_tangent_space( + std::vector vertices, std::vector indices) { + + std::vector> tangents; + tangents.resize(vertices.size()); + + for (int i = 0; i < indices.size(); i += 3) { + int index0 = indices[i]; + int index1 = indices[i+1]; + int index2 = indices[i+2]; + + LitVertex& lit_vertex0 = vertices[index0]; + LitVertex& lit_vertex1 = vertices[index1]; + LitVertex& lit_vertex2 = vertices[index2]; + + glm::vec3 pos0 = lit_vertex0.position; + glm::vec3 pos1 = lit_vertex1.position; + glm::vec3 pos2 = lit_vertex2.position; + + // Edges of the triangle : postion delta + glm::vec3 delta_pos1 = pos1 - pos0; + glm::vec3 delta_pos2 = pos2 - pos0; + + // UV delta + glm::vec2 delta_uv1 = lit_vertex1.tex_coord - lit_vertex0.tex_coord; + glm::vec2 delta_uv2 = lit_vertex2.tex_coord - lit_vertex0.tex_coord; + + float r = 1.0f / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x); + glm::vec3 tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y)*r; + glm::vec3 bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x)*r; + + tangents[0] = {tangent, bitangent}; + tangents[1] = {tangent, bitangent}; + tangents[2] = {tangent, bitangent}; + } + + return tangents; + } */ } // namespace simpleengine::gfx \ No newline at end of file diff --git a/src/gfx/renderer.cpp b/src/gfx/renderer.cpp index 11f18a5..19879e9 100644 --- a/src/gfx/renderer.cpp +++ b/src/gfx/renderer.cpp @@ -5,28 +5,31 @@ #include "ecs/component/mesh_component.h" #include "ecs/component/model_component.h" +#include "vector.h" #include #include +#include +#include +// TODO: Check if initialized before trying to do stuff namespace simpleengine::gfx { - void create_mesh_buffers(simpleengine::gfx::Mesh& mesh); + void create_mesh_buffers(simpleengine::gfx::Mesh &mesh); - Renderer::Renderer(GLFWwindow* window, gfx::Shader shader): window(window), shader(shader) { - - } + RenderingJob::RenderingJob(RenderingType rendering_type, gfx::Mesh &mesh, glm::mat4 position) + : rendering_type(rendering_type), rendering_mesh(&mesh), transform_mat(position) {} - Renderer::Renderer(GLFWwindow* window, GLuint shader_program): Renderer(window, - gfx::Shader(shader_program)) { + Renderer::Renderer(GLFWwindow *window, gfx::Shader shader, std::shared_ptr camera) + : window(window), shader(shader), camera(camera)/* , transparent_render_queue(CameraDistanceComparator(camera)) */ {} - } + Renderer::Renderer(GLFWwindow *window, GLuint shader_program, std::shared_ptr camera) + : Renderer(window, gfx::Shader(shader_program), camera) {} + + void debug_message_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, + const GLchar *message, const void *userParam) { - void debug_message_callback(GLenum source, GLenum type, GLuint id, GLenum severity, - GLsizei length, const GLchar* message, const void* userParam) { - fprintf(stderr, "%s type = 0x%x, severity = 0x%x, message = %s\n", - (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), - type, severity, message); + (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), type, severity, message); } void Renderer::enable_debug() { @@ -34,24 +37,49 @@ namespace simpleengine::gfx { glDebugMessageCallback(debug_message_callback, 0); } - void Renderer::queue_job(RenderingJob job) { - RenderingJob& emplace = rendering_queue.emplace(job); - create_job_buffers(emplace); + void Renderer::sort_jobs() { + // Sort transparents + + // std::sort() } - void Renderer::create_job_buffers(RenderingJob& job) { - Mesh& rendering_mesh = job.rendering_mesh; + void Renderer::queue_job(RenderingType rendering_type, gfx::Mesh &mesh, glm::mat4 position) { + RenderingJob job(rendering_type, mesh, position); - if (!rendering_mesh.are_buffers_created) { - gfx::VBO& vbo = rendering_mesh.vbo; - gfx::VBO& ebo = rendering_mesh.ebo; - gfx::VAO& vao = rendering_mesh.vao; + this->queue_job(job); + } + + void Renderer::queue_job(RenderingJob job) { + create_job_buffers(job); + + switch (job.rendering_type) { + case RenderingType::RendType_TRANSPARENT: { + /* glm::vec3 pos = job.transform_mat[3]; + float distance = glm::distance(pos, camera->position); */ + this->transparent_render_queue.emplace(job); + break; + } + default: + this->other_render_queue.emplace(job); + break; + } + + sort_jobs(); + } + + void Renderer::create_job_buffers(RenderingJob &job) { + Mesh *rendering_mesh = job.rendering_mesh; + + if (!rendering_mesh->are_buffers_created) { + gfx::VBO &vbo = rendering_mesh->vbo; + gfx::VBO &ebo = rendering_mesh->ebo; + gfx::VAO &vao = rendering_mesh->vao; vao.bind(); - vbo.buffer(rendering_mesh.vertices.data(), 0, sizeof(LitVertex) * rendering_mesh.vertices.size()); + vbo.buffer(rendering_mesh->vertices.data(), 0, sizeof(LitVertex) * rendering_mesh->vertices.size()); - if (!rendering_mesh.indicies.empty()) { - ebo.buffer(rendering_mesh.indicies.data(), 0, rendering_mesh.indicies.size() * sizeof(GLuint)); + if (!rendering_mesh->indicies.empty()) { + ebo.buffer(rendering_mesh->indicies.data(), 0, rendering_mesh->indicies.size() * sizeof(GLuint)); } // Enable VAO attributes @@ -59,22 +87,30 @@ namespace simpleengine::gfx { vao.enable_attrib(vbo, 1, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, color), false); vao.enable_attrib(vbo, 2, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, normal), false); vao.enable_attrib(vbo, 3, 2, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, tex_coord), false); - vao.enable_attrib(vbo, 4, 1, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, texture_id), false); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); + rendering_mesh->tangent_vbo.buffer(rendering_mesh->tangents.data(), 0, + rendering_mesh->tangents.size() * sizeof(Vectorf)); + vao.enable_attrib(rendering_mesh->tangent_vbo, 4, 3, GL_FLOAT, sizeof(Vectorf), 0, false); - rendering_mesh.are_buffers_created = true; + vbo.unbind(); + vao.unbind(); + + rendering_mesh->are_buffers_created = true; std::cout << "Created render job buffers" << std::endl; } } - void Renderer::update(const float& delta_time) { - - } + void Renderer::update(const float &delta_time) {} void Renderer::initialize() { + glEnable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + std::cout << "Base Renderer initialized" << std::endl; } @@ -86,48 +122,112 @@ namespace simpleengine::gfx { } */ } - void Renderer::render() { + bool Renderer::render_job(const RenderingJob &job) { + Mesh *mesh = job.rendering_mesh; + + shader.set_uniform_matrix_4f("u_transform_matrix", job.transform_mat, false); + + std::optional &material = mesh->material; + + if (material.has_value()) { + shader.set_uniform_float("u_material.ambient_strength", material->ambient_strength, false); + shader.set_uniform_float("u_material.diffuse_strength", material->diffuse_strength, false); + shader.set_uniform_float("u_material.specular_strength", material->specular_strength, false); + shader.set_uniform_float("u_material.shine_factor", material->shine_factor, false); + // shader.set_uniform_float("u_material.reflect_factor", .1f, false); + + auto diffuse_maps = material->textures.find(aiTextureType_DIFFUSE); + auto diffuse_map = diffuse_maps->second.front(); + + shader.set_uniform_int("u_material.diffuse", 0, false); + + glActiveTexture(GL_TEXTURE0); + diffuse_map->bind(); + + // Apply the specular map if it exists + auto specular_maps = material->textures.find(aiTextureType_SPECULAR); + if (specular_maps != material->textures.end()) { + auto spec = specular_maps->second.front(); + + shader.set_uniform_int("u_material.has_specular_map", 1, false); + shader.set_uniform_int("u_material.specular_map", 1, false); + + glActiveTexture(GL_TEXTURE1); + spec->bind(); + } else { + shader.set_uniform_int("u_material.has_specular_map", 0, false); + } + + // Apply the normal map if it exists + auto normal_maps = material->textures.find(aiTextureType_NORMALS); + if (normal_maps != material->textures.end()) { + auto normal = normal_maps->second.front(); + + shader.set_uniform_int("u_material.has_normal_map", 1, false); + shader.set_uniform_int("u_material.normal_map", 2, false); + + glActiveTexture(GL_TEXTURE2); + normal->bind(); + } else { + shader.set_uniform_int("u_material.has_normal_map", 0, false); + } + } + + mesh->vao.bind(); + if (mesh->indicies.empty()) { + glDrawArrays(GL_TRIANGLES, 0, mesh->vertices.size()); + } else { + glDrawElements(GL_TRIANGLES, mesh->indicies.size(), GL_UNSIGNED_INT, 0); + } + + return true; + } + + void Renderer::render_job_queue(std::queue &rendering_queue) { shader.use(); - + while (!rendering_queue.empty()) { // Get the job from the queue, we'll remove it after we render. - RenderingJob& job = rendering_queue.front(); - Mesh& mesh = job.rendering_mesh; + RenderingJob &job = rendering_queue.front(); - shader.set_uniform_matrix_4f("transform_matrix", job.transform_mat, false); + bool res = this->render_job(job); - std::optional& material = mesh.material; - - shader.set_uniform_int("u_textures", 0, false); - - if (material.has_value()) { - shader.set_uniform_float("u_texture_shine", material->shine, false); - shader.set_uniform_float("u_texture_reflectivity", material->reflectivity, false); - - int texture_count = 0; - auto diffuse_maps = material->textures.find(aiTextureType_DIFFUSE); - for (const auto& texture : diffuse_maps->second) { - // We can only bind to 16 textures at a time (indexes are 0-15) - if (texture_count >= 16) break; - - glActiveTexture(GL_TEXTURE0 + texture_count); - glBindTextureUnit(texture_count, texture->get_texture_id()); - - texture_count++; - } + if (res) { + // Now we'll remove the job from the queue. + rendering_queue.pop(); } - - mesh.vao.bind(); - if (mesh.indicies.empty()) { - glDrawArrays(GL_TRIANGLES, 0, mesh.vertices.size()); - } else { - glDrawElements(GL_TRIANGLES, mesh.indicies.size(), GL_UNSIGNED_INT, 0); - } - - // Now we'll remove the job from the queue. - rendering_queue.pop(); } shader.unuse(); } -} \ No newline at end of file + + void Renderer::render_job_queue(std::map>& rendering_queue) { + shader.use(); + + // Render each job then clear the queue + for (const auto& it : rendering_queue) { + this->render_job(it.second); + } + rendering_queue.clear(); + + shader.unuse(); + } + + void Renderer::render() { + // Render other (opaque) objects first + this->render_job_queue(other_render_queue); + + // Render transparent objects + std::map> transparent_jobs; + while (!transparent_render_queue.empty()) { + RenderingJob& job = transparent_render_queue.front(); + + glm::vec3 pos = job.transform_mat[3]; + float distance = glm::distance(pos, camera->position); + transparent_jobs.emplace(distance, job); + + transparent_render_queue.pop(); + } + this->render_job_queue(transparent_jobs); + } +} // namespace simpleengine::gfx \ No newline at end of file diff --git a/src/gfx/texture.cpp b/src/gfx/texture.cpp index ad0bede..257c5d0 100644 --- a/src/gfx/texture.cpp +++ b/src/gfx/texture.cpp @@ -1,4 +1,5 @@ #include "gfx/texture.h" +#include #define STB_IMAGE_IMPLEMENTATION #include @@ -23,8 +24,7 @@ namespace simpleengine::gfx { stbi_set_flip_vertically_on_load(flip_vertically); - // Read 4 channels (RGBA) - img_data = stbi_load(path, &width, &height, &channels, 4); + unsigned char*img_data = stbi_load(path, &width, &height, &channels, 0); if(!img_data) { const char* failure = stbi_failure_reason(); std::cerr << "Failed to load texture! (" << failure << ")" << std::endl; @@ -32,13 +32,27 @@ namespace simpleengine::gfx { } std::cout << "Loaded image with a width of " << width << "px, a height of " << height << "px and " << channels << " channels" << std::endl; - glTexImage2D(image_type_gl, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_data); + // Get the color type + int color_format = 0; + if (channels == 1) { + color_format = GL_RED; + } else if (channels == 3) { + color_format = GL_RGB; + } else if (channels == 4) { + color_format = GL_RGBA; + } else { + std::cerr << "Unknown texture color format with " << channels << " channels!" << std::endl; + throw std::runtime_error("Unknown texture color format!"); + } + + glTexImage2D(image_type_gl, 0, color_format, width, height, 0, color_format, GL_UNSIGNED_BYTE, img_data); if (mipmap) { glGenerateMipmap(image_type_gl); } stbi_set_flip_vertically_on_load(false); + stbi_image_free(img_data); unbind(); } @@ -62,8 +76,7 @@ namespace simpleengine::gfx { stbi_set_flip_vertically_on_load(flip_vertically); - // Read 4 channels (RGBA) - img_data = stbi_load_from_memory(buffer, buffer_length, &width, &height, &channels, 4); + unsigned char* img_data = stbi_load_from_memory(buffer, buffer_length, &width, &height, &channels, 0); if(!img_data) { const char* failure = stbi_failure_reason(); std::cerr << "Failed to load texture! (" << failure << ")" << std::endl; @@ -71,13 +84,27 @@ namespace simpleengine::gfx { } std::cout << "Loaded image with a width of " << width << "px, a height of " << height << "px and " << channels << " channels" << std::endl; - glTexImage2D(image_type_gl, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_data); + // Get the color type + int color_format = 0; + if (channels == 1) { + color_format = GL_RED; + } else if (channels == 3) { + color_format = GL_RGB; + } else if (channels == 4) { + color_format = GL_RGBA; + } else { + std::cerr << "Unknown texture color format with " << channels << " channels!" << std::endl; + throw std::runtime_error("Unknown texture color format!"); + } + + glTexImage2D(image_type_gl, 0, color_format, width, height, 0, color_format, GL_UNSIGNED_BYTE, img_data); if (mipmap) { glGenerateMipmap(image_type_gl); } stbi_set_flip_vertically_on_load(false); + stbi_image_free(img_data); unbind(); } @@ -103,7 +130,6 @@ namespace simpleengine::gfx { texture.height = height; texture.channels = 4; texture.type = aiTextureType::aiTextureType_DIFFUSE; - texture.img_data = data; glGenTextures(1, &texture.texture_id); texture.bind(); @@ -113,11 +139,11 @@ namespace simpleengine::gfx { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.img_data); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); texture.unbind(); - + free(data); return texture; } diff --git a/src/scene.cpp b/src/scene.cpp index c2caeff..8a5e735 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -19,12 +19,22 @@ namespace simpleengine { // Is there a way these can be grouped? registry.view().each([this](const TransformComponent& transform, ModelComponent& model_component) { for (auto& mesh : model_component.model.meshes) { - renderer->queue_job(gfx::RenderingJob(mesh, transform.transform_matrix)); + auto rendering_type = gfx::RenderingType::RendType_OPAQUE; + if (mesh.material) { + rendering_type = mesh.material->rendering_type; + } + + renderer->queue_job(gfx::RenderingJob(rendering_type, mesh, transform.transform_matrix)); } }); registry.view().each([this](const TransformComponent& transform, MeshComponent& mesh_component) { - renderer->queue_job(gfx::RenderingJob(mesh_component.mesh, transform.transform_matrix)); + auto rendering_type = gfx::RenderingType::RendType_OPAQUE; + if (mesh_component.mesh.material) { + rendering_type = mesh_component.mesh.material->rendering_type; + } + + renderer->queue_job(gfx::RenderingJob(rendering_type, mesh_component.mesh, transform.transform_matrix)); }); registry.view().each([this, &delta_time](TransformComponent& transform, RotatingComponent& rotating) {