diff --git a/include/renderer/Mesh.hpp b/include/renderer/Mesh.hpp index ac74eb5..481101e 100644 --- a/include/renderer/Mesh.hpp +++ b/include/renderer/Mesh.hpp @@ -11,6 +11,10 @@ struct Vertex { glm::vec3 m_position; glm::vec3 m_normal; glm::vec2 m_tex_coords; + + // first byte diffuse + // second byte opacity + uint32_t texture_indices; }; struct AABB { @@ -20,25 +24,28 @@ struct AABB { AABB merge(AABB const&); }; +struct IntermediateMesh { + std::vector vertices; + std::vector indices; + std::vector textures; + AABB aabb; + + bool merge(IntermediateMesh const&); +}; + class Mesh { public: - std::vector m_vertices; - std::vector m_indices; - Texture const* m_texture_diffuse; - Texture const* m_texture_opacity; + std::vector textures; AABB aabb; - Mesh(std::vector vertices, - std::vector indices, - Texture const* texture_diffuse, - Texture const* texture_opacity, - AABB); + Mesh(IntermediateMesh); void draw() const; void draw(ViewingMode) const; [[nodiscard]] bool is_fully_loaded() const; - void setup_mesh(); + void setup_mesh(std::vector const& vertices, std::vector const& indices); private: unsigned int m_vao{0}, m_vbo{0}, m_ebo{0}; + unsigned int m_indices_size; }; diff --git a/include/renderer/Shader.hpp b/include/renderer/Shader.hpp index 6854a64..e2a1414 100644 --- a/include/renderer/Shader.hpp +++ b/include/renderer/Shader.hpp @@ -54,7 +54,7 @@ struct UniformLocations { int projection{-1}; // Fragment - int texture_diffuse{-1}; + int textures[16]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; int texture_opacity{-1}; int light_direction{-1}; int light_color{-1}; diff --git a/src/core/ModelLoader.cpp b/src/core/ModelLoader.cpp index 480572d..ead37b0 100644 --- a/src/core/ModelLoader.cpp +++ b/src/core/ModelLoader.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include glm::vec3 ai_to_glm_vec(aiVector3D vector) @@ -145,7 +144,7 @@ Texture const* load_mask_texture(aiMaterial* mat, std::filesystem::path director } } -Mesh process_mesh(aiMesh* mesh, aiScene const* scene, std::filesystem::path directory) +IntermediateMesh process_mesh(aiMesh* mesh, aiScene const* scene, std::filesystem::path directory) { std::vector vertices; std::vector indices; @@ -176,6 +175,9 @@ Mesh process_mesh(aiMesh* mesh, aiScene const* scene, std::filesystem::path dire vertex.m_tex_coords = {0.0f, 0.0f}; } + // diffuse: 0; opacity: 1 + vertex.texture_indices = 0x00010000; + vertices.push_back(vertex); // if you want to add more attributes do it here @@ -191,25 +193,26 @@ Mesh process_mesh(aiMesh* mesh, aiScene const* scene, std::filesystem::path dire // assign materials if any auto material = scene->mMaterials[mesh->mMaterialIndex]; + std::vector textures; + auto texture_diffuse = load_material_texture(material, aiTextureType_DIFFUSE, directory); if (!texture_diffuse) { texture_diffuse = Project::get_current()->fallback_texture(); } + textures.push_back(texture_diffuse); - // TODO: Find mask texture - // Each texture has a .meta file that includes a guid - // And each material has a list of linked textures that include a base and mask texture guid auto texture_opacity = load_mask_texture(material, directory); if (!texture_opacity) { texture_opacity = Project::get_current()->white_texture(); } + textures.push_back(texture_opacity); auto aabb = AABB{ .min = ai_to_glm_vec(mesh->mAABB.mMin), .max = ai_to_glm_vec(mesh->mAABB.mMax), }; - return Mesh{vertices, indices, texture_diffuse, texture_opacity, aabb}; + return IntermediateMesh{vertices, indices, textures, aabb}; } Node process_node(aiNode* node, aiScene const* scene, std::filesystem::path directory, NodeLocation parent_location) @@ -230,13 +233,13 @@ Node process_node(aiNode* node, aiScene const* scene, std::filesystem::path dire auto location = NodeLocation::file(parent_location.file_path, parent_location.node_path / name); auto new_node = Node::create(name, new_transform, location); - std::map, Mesh> merged_meshes; + std::vector merged_meshes; // This messy code transforms the vertices and the positions in such a way that the mesh vertices are built around the object center. // This ensures that the gizmos aren't diplayed somewhere far away. - // It also merges meshes with identical textures to improve performance. + // It also merges meshes to improve performance. - // 1. Load all meshes and compute the node's AABB + // 1. Load and merge all meshes and compute the node's AABB std::optional aabb; for (unsigned int i = 0; i < node->mNumMeshes; ++i) { aiMesh* mesh = scene->mMeshes[node->mMeshes[i]]; @@ -247,16 +250,8 @@ Node process_node(aiNode* node, aiScene const* scene, std::filesystem::path dire aabb = aabb->merge(new_mesh.aabb); } - auto key = std::make_pair(new_mesh.m_texture_diffuse, new_mesh.m_texture_opacity); - if (merged_meshes.contains(key)) { - auto& merged_mesh = merged_meshes.at(key); - auto start_index = merged_mesh.m_vertices.size(); - for (auto index : new_mesh.m_indices) { - merged_mesh.m_indices.push_back(start_index + index); - } - merged_mesh.m_vertices.insert(merged_mesh.m_vertices.end(), new_mesh.m_vertices.begin(), new_mesh.m_vertices.end()); - } else { - merged_meshes.emplace(key, std::move(new_mesh)); + if (merged_meshes.size() == 0 || !merged_meshes.back().merge(new_mesh)) { + merged_meshes.push_back(new_mesh); } } @@ -271,14 +266,13 @@ Node process_node(aiNode* node, aiScene const* scene, std::filesystem::path dire } // 3. Move vertices, setup mesh buffers and add the meshes to the node - for (auto& [_, mesh] : merged_meshes) { - for (auto& vertex : mesh.m_vertices) { + for (auto& mesh : merged_meshes) { + for (auto& vertex : mesh.vertices) { vertex.m_position -= center; } mesh.aabb.min -= center; mesh.aabb.max -= center; - mesh.setup_mesh(); - new_node.meshes.push_back(std::move(mesh)); + new_node.meshes.push_back(mesh); } // 4. Load and move the child nodes diff --git a/src/renderer/Camera.cpp b/src/renderer/Camera.cpp index cec9e63..8cd1e8a 100644 --- a/src/renderer/Camera.cpp +++ b/src/renderer/Camera.cpp @@ -202,8 +202,9 @@ void Camera::draw_outline(Framebuffer const& framebuffer, InstancedNode const& n Shader::albedo.set_uniform(Shader::albedo.uniform_locations.gamma, 1.0f); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, project->white_texture()->id); - Shader::albedo.set_uniform(Shader::albedo.uniform_locations.texture_diffuse, 0); - Shader::albedo.set_uniform(Shader::albedo.uniform_locations.texture_opacity, 0); + for (std::size_t i = 0; i < sizeof(Shader::albedo.uniform_locations.textures) / sizeof(Shader::albedo.uniform_locations.textures[0]); ++i) { + Shader::albedo.set_uniform(Shader::albedo.uniform_locations.textures[i], 0); + } node.traverse([&](auto transform_matrix, auto const& node_data) { Shader::albedo.set_uniform(Shader::albedo.uniform_locations.model, transform_matrix); diff --git a/src/renderer/Mesh.cpp b/src/renderer/Mesh.cpp index fe01684..ae62e5f 100644 --- a/src/renderer/Mesh.cpp +++ b/src/renderer/Mesh.cpp @@ -1,42 +1,85 @@ #include "renderer/Mesh.hpp" #include "core/Project.hpp" +#include +#include -Mesh::Mesh(std::vector vertices, std::vector indices, Texture const* texture_diffuse, Texture const* texture_opacity, AABB aabb) - : m_vertices(vertices) - , m_indices(indices) - , m_texture_diffuse(texture_diffuse) - , m_texture_opacity(texture_opacity) - , aabb(aabb) -{ } +bool IntermediateMesh::merge(IntermediateMesh const& other) +{ + std::size_t new_textures_count = 0; + for (auto other_texture : other.textures) { + auto found_it = std::find(textures.begin(), textures.end(), other_texture); + if (found_it == textures.end()) { + ++new_textures_count; + } + } + + if (textures.size() + new_textures_count > 16) { + return false; + } + + std::map texture_mapping; + for (std::size_t i = 0; auto other_texture : other.textures) { + auto found_it = std::find(textures.begin(), textures.end(), other_texture); + if (found_it == textures.end()) { + texture_mapping[i] = textures.size(); + textures.push_back(other_texture); + } else { + texture_mapping[i] = found_it - textures.begin(); + } + ++i; + } + + for (auto vertex : other.vertices) { + auto diffuse_index = texture_mapping[vertex.texture_indices >> 24]; + auto opacity_index = texture_mapping[(vertex.texture_indices >> 16) & 0xff]; + vertex.texture_indices = (diffuse_index << 24) | (opacity_index << 16); + vertices.push_back(vertex); + } + + auto index_offset = indices.size(); + for (auto index : other.indices) { + indices.push_back(index + index_offset); + } + + aabb = aabb.merge(other.aabb); + return true; +} + +Mesh::Mesh(IntermediateMesh mesh) +{ + m_indices_size = mesh.indices.size(); + textures = mesh.textures; + + setup_mesh(mesh.vertices, mesh.indices); +} void Mesh::draw() const { glBindVertexArray(m_vao); - glDrawElements(GL_TRIANGLES, m_indices.size(), GL_UNSIGNED_INT, nullptr); + glDrawElements(GL_TRIANGLES, m_indices_size, GL_UNSIGNED_INT, nullptr); } void Mesh::draw(ViewingMode mode) const { auto const& shader = Shader::get_shader_for_mode(mode); - auto diffuse_texture_id = mode == ViewingMode::SOLID ? Project::get_current()->fallback_texture()->id : m_texture_diffuse->id; - // set diffuse texture - glActiveTexture(GL_TEXTURE0); - shader.set_uniform(shader.uniform_locations.texture_diffuse, 0); - glBindTexture(GL_TEXTURE_2D, diffuse_texture_id); + for (std::size_t i = 0; auto const& texture : textures) { + auto id = mode == ViewingMode::SOLID ? Project::get_current()->fallback_texture()->id : texture->id; + + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(GL_TEXTURE_2D, id); + shader.set_uniform(shader.uniform_locations.textures[i], static_cast(i)); - // set opacity texture - glActiveTexture(GL_TEXTURE1); - shader.set_uniform(shader.uniform_locations.texture_opacity, 1); - glBindTexture(GL_TEXTURE_2D, m_texture_opacity->id); + ++i; + } // set active glBindVertexArray(m_vao); - glDrawElements(GL_TRIANGLES, static_cast(m_indices.size()), GL_UNSIGNED_INT, nullptr); + glDrawElements(GL_TRIANGLES, static_cast(m_indices_size), GL_UNSIGNED_INT, nullptr); } -void Mesh::setup_mesh() +void Mesh::setup_mesh(std::vector const& vertices, std::vector const& indices) { if (m_vao != 0) { return; @@ -48,10 +91,10 @@ void Mesh::setup_mesh() glBindVertexArray(m_vao); glBindBuffer(GL_ARRAY_BUFFER, m_vbo); - glBufferData(GL_ARRAY_BUFFER, m_vertices.size() * sizeof(Vertex), &m_vertices[0], GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indices.size() * sizeof(unsigned int), &m_indices[0], GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0); glEnableVertexAttribArray(0); @@ -61,11 +104,19 @@ void Mesh::setup_mesh() // vertex texture coords glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, m_tex_coords)); glEnableVertexAttribArray(2); + // texture index + glVertexAttribIPointer(3, 1, GL_UNSIGNED_INT, sizeof(Vertex), (void*)offsetof(Vertex, texture_indices)); + glEnableVertexAttribArray(3); } bool Mesh::is_fully_loaded() const { - return m_texture_diffuse->is_loaded; + for (auto const& texture : textures) { + if (!texture->is_loaded) { + return false; + } + } + return true; } AABB AABB::merge(AABB const& other) diff --git a/src/renderer/Shader.cpp b/src/renderer/Shader.cpp index 5b996c0..fdbbf88 100644 --- a/src/renderer/Shader.cpp +++ b/src/renderer/Shader.cpp @@ -1,6 +1,7 @@ #include "renderer/Shader.hpp" #include +#include /** * Implements basic texture mapping, computing @@ -13,8 +14,11 @@ auto const albedo_source = ShaderSource{ layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; layout (location = 2) in vec2 aTexCoords; + layout (location = 3) in uint aTexIndex; out vec2 TexCoords; + flat out uint diffuse_index; + flat out uint opacity_index; uniform mat4 model; uniform mat4 view; @@ -22,6 +26,8 @@ auto const albedo_source = ShaderSource{ void main() { TexCoords = aTexCoords; + diffuse_index = aTexIndex >> 24; + opacity_index = (aTexIndex >> 16) & 0xff; gl_Position = projection * view * model * vec4(aPos, 1.0); })", @@ -29,18 +35,19 @@ auto const albedo_source = ShaderSource{ #version 410 core out vec4 FragColor; in vec2 TexCoords; + flat in uint diffuse_index; + flat in uint opacity_index; in vec4 gl_FragCoord; - uniform sampler2D texture_diffuse; - uniform sampler2D texture_opacity; + uniform sampler2D textures[16]; uniform float gamma; void main() { - vec3 color = texture(texture_diffuse, TexCoords).rgb; + vec3 color = texture(textures[diffuse_index], TexCoords).rgb; vec3 gammaCorrection = pow(color, vec3(1. / gamma)); FragColor = vec4(gammaCorrection, 1.0f); - float alpha = texture(texture_opacity, TexCoords).r; + float alpha = texture(textures[opacity_index], TexCoords).r; if (alpha <= 0.01f) { discard; }; @@ -51,8 +58,9 @@ auto const albedo_source = ShaderSource{ cache(locations.view, "view"); cache(locations.projection, "projection"); - cache(locations.texture_diffuse, "texture_diffuse"); - cache(locations.texture_opacity, "texture_opacity"); + for (std::size_t i = 0; i < 16; ++i) { + cache(locations.textures[i], std::string{"textures[" + std::to_string(i) + "]"}.c_str()); + } cache(locations.gamma, "gamma"); }, }; @@ -68,6 +76,7 @@ auto const lighting_source = ShaderSource{ layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; layout (location = 2) in vec2 aTexCoords; + layout (location = 3) in uint aTexIndex; uniform mat4 model; uniform mat4 view; @@ -76,11 +85,15 @@ auto const lighting_source = ShaderSource{ out vec3 FragPos; out vec3 Normal; out vec2 TexCoords; + flat out uint diffuse_index; + flat out uint opacity_index; void main() { FragPos = vec3(model * vec4(aPos, 1.0)); Normal = normalize(mat3(transpose(inverse(model))) * aNormal); TexCoords = aTexCoords; + diffuse_index = aTexIndex >> 24; + opacity_index = (aTexIndex >> 16) & 0xff; gl_Position = projection * view * vec4(FragPos, 1.0); })", @@ -95,10 +108,11 @@ auto const lighting_source = ShaderSource{ in vec3 FragPos; in vec3 Normal; in vec2 TexCoords; + flat in uint diffuse_index; + flat in uint opacity_index; in vec4 gl_FragCoord; - uniform sampler2D texture_diffuse; - uniform sampler2D texture_opacity; + uniform sampler2D textures[16]; uniform Light light; uniform vec3 cameraPos; uniform float ambientStrength; @@ -109,7 +123,7 @@ auto const lighting_source = ShaderSource{ out vec4 FragColor; void main() { - vec3 tex = texture(texture_diffuse, TexCoords).rgb; + vec3 tex = texture(textures[diffuse_index], TexCoords).rgb; vec3 normal = normalize(Normal); vec3 lightDir = normalize(-light.direction); @@ -131,7 +145,7 @@ auto const lighting_source = ShaderSource{ vec3 gammaCorrection = pow(color, vec3(1. / gamma)); FragColor = vec4(gammaCorrection, 1.0f); - float alpha = texture(texture_opacity, TexCoords).r; + float alpha = texture(textures[opacity_index], TexCoords).r; if (alpha <= 0.01f) { discard; }; @@ -142,7 +156,9 @@ auto const lighting_source = ShaderSource{ cache(locations.view, "view"); cache(locations.projection, "projection"); - cache(locations.texture_diffuse, "texture_diffuse"); + for (std::size_t i = 0; i < 16; ++i) { + cache(locations.textures[i], std::string{"textures[" + std::to_string(i) + "]"}.c_str()); + } cache(locations.texture_opacity, "texture_opacity"); cache(locations.light_direction, "light.direction"); cache(locations.light_color, "light.color");