diff --git a/examples/dev_testing/src/main.cpp b/examples/dev_testing/src/main.cpp index 15aaf60..ed4d271 100644 --- a/examples/dev_testing/src/main.cpp +++ b/examples/dev_testing/src/main.cpp @@ -172,13 +172,14 @@ int main(int argc, char *argv[]) { // entity.add_component("examples/dev_testing/resources/viper/viper.obj"); // entity.add_component("examples/dev_testing/resources/halot/chief.fbx"); - entity.add_component("examples/dev_testing/resources/planks/planks.fbx"); + //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/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(7.f, -4.f, 0.f); + transform_comp.translate(7.f, 0.f, 0.f); // transform_comp.scale(0.05f); // transform_comp.rotate_z(-90.f); // transform_comp.rotate_y(-90.f); diff --git a/include/simpleengine/gfx/mesh.h b/include/simpleengine/gfx/mesh.h index e696053..716e326 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,14 +24,23 @@ namespace simpleengine::gfx { std::vector vertices; std::vector indicies; + std::vector tangents; + std::vector bitangents; + bool are_buffers_created = false; gfx::VBO ebo; gfx::VBO vbo; gfx::VAO vao; + gfx::VBO tangent_vbo; + //gfx::VAO tangent_vao; + + gfx::VBO bitangent_vbo; + //gfx::VAO bitangent_vao; + 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, gfx::VBO bitangent_vbo); virtual void destroy() override; @@ -45,5 +55,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 db36ef4..4aa8a31 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,7 @@ namespace simpleengine::gfx { MdlProcFlag_NONE = 0b00000000, MdlProcFlag_FLIP_TEX_COORDS_VERTICALLY = 0b00000001, MdlProcFlag_FLIP_TEX_COORDS_HORIZONTALLY = 0b00000010, + MdlProcFlag_CALCULATE_TANGENT_SPACE = 0b00000100, }; /** @@ -39,12 +41,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, TextureFlags texture_color); - 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/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/resources/shaders/core/3d/fragment_core.glsl b/resources/shaders/core/3d/fragment_core.glsl index d93f7df..c025137 100644 --- a/resources/shaders/core/3d/fragment_core.glsl +++ b/resources/shaders/core/3d/fragment_core.glsl @@ -12,6 +12,11 @@ in vec3 vs_view_pos; in vec3 vs_light_pos; in vec3 vs_frag_pos; +in mat3 vs_tbn; + +in vec3 vs_tangent; +in vec3 vs_bitangent; + const int SAMP_DIFFUSE = 0; const int SAMP_SPECULAR = 1; @@ -21,6 +26,9 @@ struct Material { bool has_specular_map; sampler2D specular_map; + // TODO: Make Optional + sampler2D normal_map; + float ambient_strength; float diffuse_strength; float specular_strength; @@ -37,14 +45,12 @@ out vec4 fs_color; vec3 calculate_lighting(); void main() { - - - // Combine diffuse lighting, specular, and the texture into one color. - //fs_color = vec4(diffuse, 1.f) * texture(u_material.diffuse, vs_texcoord) + vec4(final_specular, 1.f); - vec3 lighting = calculate_lighting(); + fs_color = vec4(lighting, 1.f) * texture(u_material.diffuse, vs_texcoord); + //fs_color = vec4(vs_tangent, 1.f); + //fs_color = vec4(vs_bitangent, 1.f); } vec3 calculate_lighting() { @@ -52,8 +58,14 @@ vec3 calculate_lighting() { //float ambient_strength = 0.1; vec3 ambient = u_material.ambient_strength * u_light_color; + // Apply the normal map to the lighting. + vec3 normal = vs_normal; + normal = texture(u_material.normal_map, vs_texcoord).rgb; + normal = normal * 2.0 - 1.0; + normal = normalize(vs_tbn * normal); + // Diffuse - vec3 norm = normalize(vs_world_normal); + vec3 norm = normalize(normal); vec3 light_dir = normalize(vs_light_pos - vs_frag_pos); float diff = max(dot(norm, light_dir), 0.f); vec3 diffuse = (diff * u_material.diffuse_strength) * u_light_color; diff --git a/resources/shaders/core/3d/vertex_core.glsl b/resources/shaders/core/3d/vertex_core.glsl index 6efa103..5dbace3 100644 --- a/resources/shaders/core/3d/vertex_core.glsl +++ b/resources/shaders/core/3d/vertex_core.glsl @@ -4,6 +4,9 @@ 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; +// TODO: Make these optional +layout (location = 4) in vec3 vertex_tangent; +layout (location = 5) in vec3 vertex_bitangent; out vec3 vs_position; out vec3 vs_color; @@ -17,6 +20,11 @@ out vec3 vs_view_pos; out vec3 vs_light_pos; out vec3 vs_frag_pos; +out mat3 vs_tbn; + +out vec3 vs_tangent; +out vec3 vs_bitangent; + uniform mat4 u_transform_matrix; uniform mat4 u_view_matrix; uniform mat4 u_projection_matrix; @@ -41,4 +49,13 @@ void main() { 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 the TBN (Tangent, Bi-tangent, and normal matrix) + vec3 T = normalize(vec3(u_transform_matrix * vec4(vertex_tangent, 1.f))); + vec3 B = normalize(vec3(u_transform_matrix * vec4(vertex_bitangent, 1.f))); + vec3 N = normalize(vec3(u_transform_matrix * vec4(vertex_normal, 1.f))); + vs_tbn = mat3(T, B, N); + + vs_tangent = vertex_tangent; + vs_bitangent = vertex_bitangent; } \ No newline at end of file diff --git a/src/gfx/mesh.cpp b/src/gfx/mesh.cpp index ba48518..06440bf 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)), bitangent_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)), bitangent_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, gfx::VBO bitangent_vbo) : + vertices(vertices), indicies(indicies), material(material), ebo(ebo), vbo(vbo), vao(vao), tangent_vbo(tangent_vbo), bitangent_vbo(bitangent_vbo) { } @@ -76,4 +79,52 @@ namespace simpleengine::gfx { vertices[i].normal = normals[i]; } } + + void Mesh::calculate_tangents() { + // Resize the tangent and bitangents + //tangents.clear(); + tangents.resize(vertices.size()); + //bitangents.clear(); + bitangents.resize(vertices.size()); + /* std::vector tangents; + std::vector bitangents; */ + + 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; + + bitangents[index0] = bitangent; + bitangents[index1] = bitangent; + bitangents[index2] = bitangent; + } + + /* this->tangents = std::optional, std::vector>>( + {tangents, bitangents} + ); */ + } } \ No newline at end of file diff --git a/src/gfx/model.cpp b/src/gfx/model.cpp index 718e23a..89de207 100644 --- a/src/gfx/model.cpp +++ b/src/gfx/model.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -27,13 +28,18 @@ namespace simpleengine::gfx { if (model_processing_flags & ModelProcessingFlags::MdlProcFlag_FLIP_TEX_COORDS_HORIZONTALLY) { horizontally_flip_tex_coords(); } + + if (model_processing_flags & ModelProcessingFlags::MdlProcFlag_CALCULATE_TANGENT_SPACE) { + calculate_tangents(); + } } void Model::load_model(std::string path) { Assimp::Importer importer; // 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); + const aiScene *scene = importer.ReadFile(path, additional_assimp_flags | aiProcess_Triangulate | aiProcess_FlipUVs + | aiProcess_CalcTangentSpace); if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { std::cout << "ERROR::ASSIMP::" << importer.GetErrorString() << std::endl; @@ -63,7 +69,7 @@ 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; for (unsigned int i = 0; i < mesh->mNumVertices; i++) { LitVertex vertex; @@ -93,7 +99,7 @@ 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]); } } @@ -121,8 +127,13 @@ namespace simpleengine::gfx { // Load normals texture maps loading_type = aiTextureType_NORMALS; - std::vector> normal_maps = load_material_texture(processed_textures, material, loading_type, TextureFlags::TexFlags_NO_COLOR); - if (!normal_maps.empty()) textures.emplace(loading_type, normal_maps); + std::vector> normal_maps = load_material_texture(processed_textures, material, loading_type, TextureFlags::TexFlags_RGB); + 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 @@ -156,7 +167,7 @@ namespace simpleengine::gfx { } } - Mesh se_mesh(vertices, indices, mat); + Mesh se_mesh(vertices, indicies, mat); if (!mesh->HasNormals()) { se_mesh.calculate_normals(); @@ -222,4 +233,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 6377cfa..9a770b6 100644 --- a/src/gfx/renderer.cpp +++ b/src/gfx/renderer.cpp @@ -5,6 +5,7 @@ #include "ecs/component/mesh_component.h" #include "ecs/component/model_component.h" +#include "vector.h" #include #include @@ -60,8 +61,14 @@ namespace simpleengine::gfx { 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); - 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.bitangent_vbo.buffer(rendering_mesh.bitangents.data(), 0, rendering_mesh.bitangents.size() * sizeof(Vectorf)); + vao.enable_attrib(rendering_mesh.bitangent_vbo, 5, 3, GL_FLOAT, sizeof(Vectorf), 0, false); + + vbo.unbind(); + vao.unbind(); rendering_mesh.are_buffers_created = true; @@ -125,6 +132,21 @@ namespace simpleengine::gfx { } 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); + std::cout << "No normal map for model!" << std::endl; + } } mesh.vao.bind();