diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp index aa32bd59..5f4ff6f5 100644 --- a/src/graphics/engine/engine.cpp +++ b/src/graphics/engine/engine.cpp @@ -4883,7 +4883,10 @@ int CEngine::AddStaticMesh(const std::string& key, const CModelMesh* mesh, const { const auto& part = mesh->GetPart(i); - AddBaseObjTriangles(baseObjRank, part.GetVertices(), part.GetMaterial(), EngineTriangleType::TRIANGLES); + std::vector triangles; + part->GetTriangles(triangles); + + AddBaseObjTriangles(baseObjRank, triangles); } m_staticMeshBaseObjects[key] = baseObjRank; } diff --git a/src/graphics/engine/oldmodelmanager.cpp b/src/graphics/engine/oldmodelmanager.cpp index d293c10c..80295be1 100644 --- a/src/graphics/engine/oldmodelmanager.cpp +++ b/src/graphics/engine/oldmodelmanager.cpp @@ -48,7 +48,7 @@ COldModelManager::~COldModelManager() bool COldModelManager::LoadModel(const std::string& name, bool mirrored, int team) { - CModel model; + std::unique_ptr model; try { auto extension = std::filesystem::path(name).extension().string(); @@ -57,9 +57,9 @@ bool COldModelManager::LoadModel(const std::string& name, bool mirrored, int tea { GetLogger()->Debug("Loading model '%s'\n", name.c_str()); - ModelInput::Read(model, "models/" + name); + model = ModelInput::Read("models/" + name); - if (model.GetMeshCount() == 0) + if (model->GetMeshCount() == 0) return false; goto skip; @@ -71,9 +71,9 @@ bool COldModelManager::LoadModel(const std::string& name, bool mirrored, int tea { GetLogger()->Debug("Loading model '%s'\n", (name + ".gltf").c_str()); - ModelInput::Read(model, gltf_path); + model = ModelInput::Read(gltf_path); - if (model.GetMeshCount() > 0) + if (model->GetMeshCount() > 0) goto skip; } @@ -83,9 +83,9 @@ bool COldModelManager::LoadModel(const std::string& name, bool mirrored, int tea { GetLogger()->Debug("Loading model '%s'\n", (name + ".mod").c_str()); - ModelInput::Read(model, mod_path); + model = ModelInput::Read(mod_path); - if (model.GetMeshCount() > 0) + if (model->GetMeshCount() > 0) goto skip; } @@ -98,7 +98,7 @@ bool COldModelManager::LoadModel(const std::string& name, bool mirrored, int tea } skip: - CModelMesh* mesh = model.GetMesh(); + CModelMesh* mesh = model->GetMesh(); assert(mesh != nullptr); ModelInfo modelInfo; diff --git a/src/graphics/model/model.cpp b/src/graphics/model/model.cpp index 8c205471..733cc132 100644 --- a/src/graphics/model/model.cpp +++ b/src/graphics/model/model.cpp @@ -34,7 +34,7 @@ CModelMesh* CModel::GetMesh() { if (m_meshes.size() == 1) { - return &m_meshes.begin()->second; + return m_meshes.begin()->second.get(); } else { @@ -50,7 +50,7 @@ CModelMesh* CModel::GetMesh(const std::string& name) GetLogger()->Error("Mesh named '%s' not found in model!\n", name.c_str()); return nullptr; } - return &(it->second); + return it->second.get(); } const CModelMesh* CModel::GetMesh(const std::string& name) const @@ -61,12 +61,12 @@ const CModelMesh* CModel::GetMesh(const std::string& name) const GetLogger()->Error("Mesh named '%s' not found in model!\n", name.c_str()); return nullptr; } - return &(it->second); + return it->second.get(); } -void CModel::AddMesh(const std::string& name, CModelMesh&& mesh) +void CModel::AddMesh(const std::string& name, std::unique_ptr mesh) { - m_meshes[name] = mesh; + m_meshes[name] = std::move(mesh); } std::vector CModel::GetMeshNames() const diff --git a/src/graphics/model/model.h b/src/graphics/model/model.h index e2d5d15f..4a778e7a 100644 --- a/src/graphics/model/model.h +++ b/src/graphics/model/model.h @@ -26,9 +26,10 @@ #include "math/sphere.h" #include +#include +#include #include #include -#include namespace Gfx { @@ -49,7 +50,7 @@ public: //! Return a mesh with given \a name const CModelMesh* GetMesh(const std::string& name) const; //! Add new \a mesh with given \a name - void AddMesh(const std::string& name, CModelMesh&& mesh); + void AddMesh(const std::string& name, std::unique_ptr mesh); //! Returns list of mesh names std::vector GetMeshNames() const; @@ -75,7 +76,7 @@ public: bool HasCameraCollisionSphere() const; private: - std::map m_meshes; + std::map> m_meshes; std::vector m_crashSpheres; std::optional m_shadowSpot; std::optional m_cameraCollisionSphere; diff --git a/src/graphics/model/model_gltf.cpp b/src/graphics/model/model_gltf.cpp index b201b1f3..452b2302 100644 --- a/src/graphics/model/model_gltf.cpp +++ b/src/graphics/model/model_gltf.cpp @@ -71,12 +71,7 @@ struct Sampler class GLTFLoader { public: - void Load(const std::filesystem::path& path); - - CModel GetModel() - { - return m_model; - } + std::unique_ptr Load(const std::filesystem::path& path); private: void ReadBuffers(); @@ -94,7 +89,7 @@ private: std::vector ReadColors(int index); std::vector ReadIndices(int index); - CModel m_model; + std::unique_ptr m_model; std::filesystem::path m_directory; json m_root; @@ -108,16 +103,14 @@ private: std::vector m_samplers; }; -void ReadGLTFModel(CModel& model, const std::filesystem::path& path) +std::unique_ptr ReadGLTFModel(const std::filesystem::path& path) { GLTFLoader loader; - loader.Load(path); - - model = loader.GetModel(); + return loader.Load(path); } -void GLTFLoader::Load(const std::filesystem::path& path) +std::unique_ptr GLTFLoader::Load(const std::filesystem::path& path) { m_directory = path.parent_path(); @@ -125,6 +118,8 @@ void GLTFLoader::Load(const std::filesystem::path& path) stream >> m_root; + m_model = std::make_unique(); + ReadBuffers(); ReadBufferViews(); ReadAccessors(); @@ -133,6 +128,8 @@ void GLTFLoader::Load(const std::filesystem::path& path) ReadTextures(); ReadMaterials(); ReadMeshes(); + + return std::move(m_model); } void GLTFLoader::ReadBuffers() @@ -470,13 +467,13 @@ void GLTFLoader::ReadMeshes() { auto name = node["name"].get(); - CModelMesh mesh; + auto mesh = std::make_unique(); for (const auto& primitive : node["primitives"]) { const auto& material = m_materials[primitive["material"].get()]; - auto& part = mesh.AddPart(material); + auto part = mesh->AddPart(material); std::vector positions; std::vector normals; @@ -565,15 +562,15 @@ void GLTFLoader::ReadMeshes() } for (const auto& vertex : vertices) - part.AddVertex(vertex); + part->AddVertex(vertex); for (const auto& index : indices) - part.AddIndex(index); + part->AddIndex(index); } } - if (mesh.GetPartCount() > 0) - m_model.AddMesh(name, std::move(mesh)); + if (mesh->GetPartCount() > 0) + m_model->AddMesh(name, std::move(mesh)); } } diff --git a/src/graphics/model/model_gltf.h b/src/graphics/model/model_gltf.h index db130fa7..4d810ae8 100644 --- a/src/graphics/model/model_gltf.h +++ b/src/graphics/model/model_gltf.h @@ -31,6 +31,6 @@ namespace Gfx::ModelIO { -void ReadGLTFModel(CModel& model, const std::filesystem::path& path); +std::unique_ptr ReadGLTFModel(const std::filesystem::path& path); } diff --git a/src/graphics/model/model_input.cpp b/src/graphics/model/model_input.cpp index 872e56db..a6336ea3 100644 --- a/src/graphics/model/model_input.cpp +++ b/src/graphics/model/model_input.cpp @@ -28,21 +28,21 @@ namespace Gfx { -void ModelInput::Read(CModel& model, const std::filesystem::path& path) +std::unique_ptr ModelInput::Read(const std::filesystem::path& path) { auto extension = path.extension(); if (extension == ".mod") { - ModelIO::ReadOldModel(model, path); + return ModelIO::ReadOldModel(path); } else if (extension == ".txt") { - ModelIO::ReadTextModel(model, path); + return ModelIO::ReadTextModel(path); } else if (extension == ".gltf") { - ModelIO::ReadGLTFModel(model, path); + return ModelIO::ReadGLTFModel(path); } else { diff --git a/src/graphics/model/model_input.h b/src/graphics/model/model_input.h index 195fb647..1e1b7f71 100644 --- a/src/graphics/model/model_input.h +++ b/src/graphics/model/model_input.h @@ -33,7 +33,7 @@ namespace Gfx */ namespace ModelInput { - void Read(CModel& model, const std::filesystem::path& path); + std::unique_ptr Read(const std::filesystem::path& path); } } // namespace Gfx diff --git a/src/graphics/model/model_manager.cpp b/src/graphics/model/model_manager.cpp index d51e586e..fffff708 100644 --- a/src/graphics/model/model_manager.cpp +++ b/src/graphics/model/model_manager.cpp @@ -29,21 +29,19 @@ namespace Gfx { -CModel& CModelManager::GetModel(const std::string& modelName) +CModel* CModelManager::GetModel(const std::string& modelName) { auto it = m_models.find(modelName); if (it != m_models.end()) - return it->second; + return it->second.get(); std::filesystem::path modelFile = "models-new/" + modelName + ".txt"; GetLogger()->Debug("Loading new model: %s\n", modelFile.c_str()); - CModel model; - ModelInput::Read(model, modelFile); - m_models[modelName] = model; + m_models[modelName] = ModelInput::Read(modelFile); - return m_models[modelName]; + return m_models[modelName].get(); } void CModelManager::ClearCache() diff --git a/src/graphics/model/model_manager.h b/src/graphics/model/model_manager.h index 1ff04a9a..6bb192de 100644 --- a/src/graphics/model/model_manager.h +++ b/src/graphics/model/model_manager.h @@ -21,6 +21,7 @@ #include "graphics/model/model.h" +#include #include #include @@ -36,13 +37,13 @@ class CModelManager public: //! Returns a model named \a modelName /** @throws CModelIOException on read error */ - CModel& GetModel(const std::string& modelName); + CModel* GetModel(const std::string& modelName); //! Clears cached models void ClearCache(); private: - std::unordered_map m_models; + std::unordered_map> m_models; }; } // namespace Gfx diff --git a/src/graphics/model/model_mesh.cpp b/src/graphics/model/model_mesh.cpp index 33c84686..34662fa4 100644 --- a/src/graphics/model/model_mesh.cpp +++ b/src/graphics/model/model_mesh.cpp @@ -19,94 +19,350 @@ #include "graphics/model/model_mesh.h" +#include + namespace Gfx { -CModelPart::CModelPart(const Material& material) - : m_material(material) {} +CVertexProxy::CVertexProxy(CModelPart* part, size_t index) + : m_part(part), m_index(index) +{ + +} + +const glm::vec3& CVertexProxy::GetPosition() const +{ + return m_part->m_positions.array[m_index]; +} + +void CVertexProxy::SetPosition(const glm::vec3& position) const +{ + m_part->m_positions.array[m_index] = position; +} + +const glm::u8vec4& CVertexProxy::GetColor() const +{ + return m_part->m_colors.array[m_index]; +} + +void CVertexProxy::SetColor(const glm::u8vec4& color) const +{ + m_part->m_colors.array[m_index] = color; +} + +const glm::vec2& CVertexProxy::GetUV1() const +{ + return m_part->m_uvs1.array[m_index]; +} + +void CVertexProxy::SetUV1(const glm::vec2& uv) const +{ + m_part->m_uvs1.array[m_index] = uv; +} + +const glm::vec2& CVertexProxy::GetUV2() const +{ + return m_part->m_uvs2.array[m_index]; +} + +void CVertexProxy::SetUV2(const glm::vec2& uv) const +{ + m_part->m_uvs2.array[m_index] = uv; +} + +const glm::vec3& CVertexProxy::GetNormal() const +{ + return m_part->m_normals.array[m_index]; +} + +void CVertexProxy::SetNormal(const glm::vec3& normal) const +{ + m_part->m_normals.array[m_index] = normal; +} + +const glm::vec4& CVertexProxy::GetTangent() const +{ + return m_part->m_tangents.array[m_index]; +} + +void CVertexProxy::SetTangent(const glm::vec4& tangent) const +{ + m_part->m_tangents.array[m_index] = tangent; +} + +const glm::u8vec4& CVertexProxy::GetBoneIndices() const +{ + return m_part->m_boneIndices.array[m_index]; +} + +void CVertexProxy::SetBoneIndices(const glm::u8vec4& indices) const +{ + m_part->m_boneIndices.array[m_index] = indices; +} + +const glm::vec4& CVertexProxy::GetBoneWeights() const +{ + return m_part->m_boneWeights.array[m_index]; +} + +void CVertexProxy::SetBoneWeights(const glm::vec4& weights) const +{ + m_part->m_boneWeights.array[m_index] = weights; +} + + +CModelPart::CModelPart(const Material& material, size_t vertices, size_t indices) + : m_material(material) { + m_positions.array.resize(vertices); + m_indices.array.resize(indices); +} const Material& CModelPart::GetMaterial() const { return m_material; } +void CModelPart::SetVertices(size_t count) +{ + m_positions.array.resize(count); + + if (m_colors.enabled) m_colors.array.resize(count); + if (m_uvs1.enabled) m_uvs1.array.resize(count); + if (m_uvs2.enabled) m_uvs2.array.resize(count); + if (m_normals.enabled) m_normals.array.resize(count); + if (m_tangents.enabled) m_tangents.array.resize(count); + if (m_boneIndices.enabled) m_boneIndices.array.resize(count); + if (m_boneWeights.enabled) m_boneWeights.array.resize(count); +} + +void CModelPart::SetIndices(size_t count) +{ + m_indices.array.resize(count); +} + +bool CModelPart::Has(VertexAttribute array) const +{ + switch (array) + { + case VertexAttribute::POSITION: + return true; + case VertexAttribute::COLOR: + return m_colors.enabled; + case VertexAttribute::UV1: + return m_uvs1.enabled; + case VertexAttribute::UV2: + return m_uvs2.enabled; + case VertexAttribute::NORMAL: + return m_normals.enabled; + case VertexAttribute::TANGENT: + return m_tangents.enabled; + case VertexAttribute::BONE_INDICES: + return m_boneIndices.enabled; + case VertexAttribute::BONE_WEIGHTS: + return m_boneWeights.enabled; + default: + return false; + } +} + +void CModelPart::Add(VertexAttribute attribute) +{ + switch (attribute) + { + case VertexAttribute::COLOR: + m_colors.enabled = true; + m_colors.array.resize(m_positions.array.size()); + break; + case VertexAttribute::UV1: + m_uvs1.enabled = true; + m_uvs1.array.resize(m_positions.array.size()); + break; + case VertexAttribute::UV2: + m_uvs2.enabled = true; + m_uvs2.array.resize(m_positions.array.size()); + break; + case VertexAttribute::NORMAL: + m_normals.enabled = true; + m_normals.array.resize(m_positions.array.size()); + break; + case VertexAttribute::TANGENT: + m_tangents.enabled = true; + m_tangents.array.resize(m_positions.array.size()); + break; + case VertexAttribute::BONE_INDICES: + m_boneIndices.enabled = true; + m_boneIndices.array.resize(m_positions.array.size()); + break; + case VertexAttribute::BONE_WEIGHTS: + m_boneWeights.enabled = true; + m_boneWeights.array.resize(m_positions.array.size()); + break; + } +} + +void CModelPart::Remove(VertexAttribute attribute) +{ + switch (attribute) + { + case VertexAttribute::COLOR: + m_colors.enabled = false; + m_colors.array.resize(0); + return; + case VertexAttribute::UV1: + m_uvs1.enabled = false; + m_uvs1.array.resize(0); + return; + case VertexAttribute::UV2: + m_uvs2.enabled = false; + m_uvs2.array.resize(0); + return; + case VertexAttribute::NORMAL: + m_normals.enabled = false; + m_normals.array.resize(0); + return; + case VertexAttribute::TANGENT: + m_tangents.enabled = false; + m_tangents.array.resize(0); + return; + case VertexAttribute::BONE_INDICES: + m_boneIndices.enabled = false; + m_boneIndices.array.resize(0); + return; + case VertexAttribute::BONE_WEIGHTS: + m_boneWeights.enabled = false; + m_boneWeights.array.resize(0); + return; + } +} + bool CModelPart::IsIndexed() const { - return !m_indices.empty(); + return m_indices.enabled; } size_t CModelPart::GetVertexCount() const { - return m_vertices.size(); -} - -const std::vector& CModelPart::GetVertices() const -{ - return m_vertices; + return m_positions.array.size(); } size_t CModelPart::GetIndexCount() const { - return m_indices.size(); + return m_indices.array.size(); } const std::vector& CModelPart::GetIndices() const { - return m_indices; + return m_indices.array; +} + +CVertexProxy CModelPart::GetVertex(size_t index) +{ + return CVertexProxy(this, index); +} + +std::uint32_t CModelPart::GetIndex(size_t index) +{ + return m_indices.array[index]; } void CModelPart::AddVertex(const Vertex3D& vertex) { - m_vertices.push_back(vertex); + m_positions.array.push_back(vertex.position); + + if (m_colors.enabled) m_colors.array.push_back(vertex.color); + if (m_uvs1.enabled) m_uvs1.array.push_back(vertex.uv); + if (m_uvs2.enabled) m_uvs2.array.push_back(vertex.uv2); + if (m_normals.enabled) m_normals.array.push_back(vertex.normal); + if (m_tangents.enabled) m_tangents.array.push_back({ 1.0f, 0.0f, 0.0f, 1.0f }); + if (m_boneIndices.enabled) m_boneIndices.array.push_back({ 0, 0, 0, 0 }); + if (m_boneWeights.enabled) m_boneWeights.array.push_back({ 1.0f, 0.0f, 0.0f, 0.0f }); } void CModelPart::AddIndex(unsigned int index) { - m_indices.push_back(index); + if (m_indices.enabled) m_indices.array.push_back(index); +} + +void CModelPart::GetTriangles(std::vector& triangles) +{ + size_t n = IsIndexed() + ? GetIndexCount() + : GetVertexCount(); + + for (size_t i = 0; i < n - 2; i += 3) + { + std::array verts; + + for (size_t j = 0; j < 3; j++) + { + size_t index = IsIndexed() + ? GetIndex(i + j) + : i + j; + auto vertex = GetVertex(index); + + verts[j].position = vertex.GetPosition(); + verts[j].color = vertex.GetColor(); + verts[j].uv = vertex.GetUV1(); + verts[j].uv2 = vertex.GetUV2(); + verts[j].normal = vertex.GetNormal(); + } + + triangles.push_back({ verts[0], verts[1], verts[2], GetMaterial() }); + } } void CModelMesh::AddTriangle(const ModelTriangle& triangle) { for (auto& part : m_parts) { - if (part.GetMaterial() == triangle.material) + if (part->GetMaterial() == triangle.material) { - part.AddVertex(triangle.p1); - part.AddVertex(triangle.p2); - part.AddVertex(triangle.p3); + part->AddVertex(triangle.p1); + part->AddVertex(triangle.p2); + part->AddVertex(triangle.p3); return; } } - CModelPart part(triangle.material); + auto part = std::make_unique(triangle.material, 0, 0); - part.AddVertex(triangle.p1); - part.AddVertex(triangle.p2); - part.AddVertex(triangle.p3); + part->Add(VertexAttribute::COLOR); + part->Add(VertexAttribute::UV1); + part->Add(VertexAttribute::UV2); + part->Add(VertexAttribute::NORMAL); - m_parts.emplace_back(part); + part->AddVertex(triangle.p1); + part->AddVertex(triangle.p2); + part->AddVertex(triangle.p3); + + m_parts.push_back(std::move(part)); } void CModelMesh::AddTriangle(const Triangle& triangle, const Material& material) { for (auto& part : m_parts) { - if (part.GetMaterial() == material) + if (part->GetMaterial() == material) { - part.AddVertex(triangle.p1); - part.AddVertex(triangle.p2); - part.AddVertex(triangle.p3); + part->AddVertex(triangle.p1); + part->AddVertex(triangle.p2); + part->AddVertex(triangle.p3); return; } } - CModelPart part(material); + auto part = std::make_unique(material, 0, 0); - part.AddVertex(triangle.p1); - part.AddVertex(triangle.p2); - part.AddVertex(triangle.p3); + part->Add(VertexAttribute::COLOR); + part->Add(VertexAttribute::UV1); + part->Add(VertexAttribute::UV2); + part->Add(VertexAttribute::NORMAL); - m_parts.emplace_back(part); + part->AddVertex(triangle.p1); + part->AddVertex(triangle.p2); + part->AddVertex(triangle.p3); + + m_parts.push_back(std::move(part)); } size_t CModelMesh::GetPartCount() const @@ -114,24 +370,26 @@ size_t CModelMesh::GetPartCount() const return m_parts.size(); } -const CModelPart& CModelMesh::GetPart(size_t index) const +CModelPart* CModelMesh::GetPart(size_t index) const { - return m_parts[index]; + return m_parts[index].get(); } -CModelPart& CModelMesh::AddPart(const Material& material) +CModelPart* CModelMesh::AddPart(const Material& material) { for (auto& part : m_parts) { - if (part.GetMaterial() == material) + if (part->GetMaterial() == material) { - return part; + return part.get(); } } - m_parts.push_back(CModelPart(material)); + auto part = std::make_unique(material, 0, 0); - return m_parts.back(); + m_parts.push_back(std::move(part)); + + return m_parts.back().get(); } const glm::vec3& CModelMesh::GetPosition() const @@ -180,35 +438,7 @@ std::vector CModelMesh::GetTriangles() const for (const auto& part : m_parts) { - if (part.IsIndexed()) - { - const auto& vertices = part.GetVertices(); - const auto& indices = part.GetIndices(); - - for (size_t i = 0; i < indices.size() - 2; i += 3) - { - triangles.push_back({ - vertices[indices[i]], - vertices[indices[i + 1]], - vertices[indices[i + 2]], - part.GetMaterial() - }); - } - } - else - { - const auto& vertices = part.GetVertices(); - - for (size_t i = 0; i < vertices.size() - 2; i += 3) - { - triangles.push_back({ - vertices[i], - vertices[i + 1], - vertices[i + 2], - part.GetMaterial() - }); - } - } + part->GetTriangles(triangles); } return triangles; diff --git a/src/graphics/model/model_mesh.h b/src/graphics/model/model_mesh.h index ab4b4200..5b865206 100644 --- a/src/graphics/model/model_mesh.h +++ b/src/graphics/model/model_mesh.h @@ -21,11 +21,91 @@ #include "graphics/model/model_triangle.h" +#include #include namespace Gfx { +/** + * \enum VertexAttribute + * \brief An enum for vertex attributes + */ +enum class VertexAttribute +{ + POSITION, + COLOR, + UV1, + UV2, + NORMAL, + TANGENT, + BONE_INDICES, + BONE_WEIGHTS, +}; + +class CModelPart; + + +/** + * \class CVertexProxy + * \brief Proxy object for manipulating vertex data + */ +class CVertexProxy +{ + CModelPart* m_part; + size_t m_index; + +public: + CVertexProxy(CModelPart* part, size_t index); + + //! Returns vertex position + const glm::vec3& GetPosition() const; + //! Sets vertex position + void SetPosition(const glm::vec3& position) const; + + //! Returns vertex color + const glm::u8vec4& GetColor() const; + //! Sets vertex color + void SetColor(const glm::u8vec4& color) const; + + //! Returns 1st UV + const glm::vec2& GetUV1() const; + //! Sets 1st UV + void SetUV1(const glm::vec2& uv) const; + + //! Returns 2nd UV + const glm::vec2& GetUV2() const; + //! Sets 2nd UV + void SetUV2(const glm::vec2& uv) const; + + //! Returns normal + const glm::vec3& GetNormal() const; + //! Sets normal + void SetNormal(const glm::vec3& normal) const; + + //! Returns tangent + const glm::vec4& GetTangent() const; + //! Sets tangent + void SetTangent(const glm::vec4& tangent) const; + + //! Returns bone indices + const glm::u8vec4& GetBoneIndices() const; + //! Sets bone indices + void SetBoneIndices(const glm::u8vec4& indices) const; + + //! Returns bone weights + const glm::vec4& GetBoneWeights() const; + //! Sets bone weights + void SetBoneWeights(const glm::vec4& weights) const; +}; + +template +struct VertexAttributeArray +{ + bool enabled = false; + std::vector array = {}; +}; + /** * \class CModelPart * \brief Part of mesh with a common material @@ -34,34 +114,70 @@ class CModelPart { public: //! Creates new part for given material - CModelPart(const Material& material); + CModelPart(const Material& material, size_t vertices = 0, size_t indices = 0); //! Returns this part's material const Material& GetMaterial() const; + //! Sets the number of vertices + void SetVertices(size_t count); + //! Sets the number of indices + void SetIndices(size_t count); + + //! Returns whether this mesh part contains specific vertex attribute + bool Has(VertexAttribute attribute) const; + //! Adds vertex attribute to this mesh part + void Add(VertexAttribute attribute); + //! Removes vertex attribute from this mesh part + void Remove(VertexAttribute attribute); + //! Returns true if this part is indexed bool IsIndexed() const; //! Returns the number of vertices in this part size_t GetVertexCount() const; - //! Returns the vertices in this part - const std::vector& GetVertices() const; //! Returns the number of indices in this part size_t GetIndexCount() const; //! Returns the indices in this part const std::vector& GetIndices() const; + //! Returns vertex proxy object for manipulating vertex data + CVertexProxy GetVertex(size_t index); + //! Returns vertex index + std::uint32_t GetIndex(size_t index); + //! Adds a vertex void AddVertex(const Vertex3D& vertex); //! Adds an index void AddIndex(unsigned int index); + //! Fills the array with converted model triangles + void GetTriangles(std::vector& triangles); + + friend class CVertexProxy; + private: //! Material Material m_material; - //! Vertices - std::vector m_vertices; + + //! Positions + VertexAttributeArray m_positions; + //! Colors + VertexAttributeArray m_colors; + //! UVs 1 + VertexAttributeArray m_uvs1; + //! UVs 2 + VertexAttributeArray m_uvs2; + //! Normals + VertexAttributeArray m_normals; + //! Tangents + VertexAttributeArray m_tangents; + //! Bone indices + VertexAttributeArray m_boneIndices; + //! Bone weights + VertexAttributeArray m_boneWeights; + //! Indices - std::vector m_indices; + VertexAttributeArray m_indices; }; /** @@ -79,9 +195,9 @@ public: //! Returns the number of parts size_t GetPartCount() const; //! Returns a part with given index - const CModelPart& GetPart(size_t index) const; + CModelPart* GetPart(size_t index) const; //! Adds a new part with given material or returns an existing one - CModelPart& AddPart(const Material& material); + CModelPart* AddPart(const Material& material); //! Returns the mesh position const glm::vec3& GetPosition() const; @@ -107,10 +223,10 @@ public: std::vector GetTriangles() const; private: - std::vector m_parts; - glm::vec3 m_position; - glm::vec3 m_rotation; - glm::vec3 m_scale; + std::vector> m_parts; + glm::vec3 m_position = { 0, 0, 0 }; + glm::vec3 m_rotation = { 0, 0, 0 }; + glm::vec3 m_scale = { 1, 1, 1 }; std::string m_parent; }; diff --git a/src/graphics/model/model_mod.cpp b/src/graphics/model/model_mod.cpp index 7aacbbad..839c90af 100644 --- a/src/graphics/model/model_mod.cpp +++ b/src/graphics/model/model_mod.cpp @@ -47,7 +47,7 @@ void ConvertOldTex1Name(ModelTriangle& triangle, const char* tex1Name); void ConvertFromOldRenderState(ModelTriangle& triangle, int state); ModelLODLevel MinMaxToLodLevel(float min, float max); -void ReadOldModel(CModel& model, const std::filesystem::path& path) +std::unique_ptr ReadOldModel(const std::filesystem::path& path) { CInputStream stream(path); @@ -88,12 +88,16 @@ void ReadOldModel(CModel& model, const std::filesystem::path& path) throw CModelIOException(std::string("Error reading model triangles: ") + e.what()); } - CModelMesh mesh; + auto mesh = std::make_unique(); for (const auto& triangle : triangles) - mesh.AddTriangle(triangle); + mesh->AddTriangle(triangle); - model.AddMesh("main", std::move(mesh)); + auto model = std::make_unique(); + + model->AddMesh("main", std::move(mesh)); + + return model; } std::vector ReadOldModelV1(std::istream& stream, int totalTriangles) diff --git a/src/graphics/model/model_mod.h b/src/graphics/model/model_mod.h index 8c4056ba..33f5cab8 100644 --- a/src/graphics/model/model_mod.h +++ b/src/graphics/model/model_mod.h @@ -31,6 +31,6 @@ namespace Gfx::ModelIO { -void ReadOldModel(CModel& model, const std::filesystem::path& path); +std::unique_ptr ReadOldModel(const std::filesystem::path& path); } diff --git a/src/graphics/model/model_txt.cpp b/src/graphics/model/model_txt.cpp index 9543d936..7fdac273 100644 --- a/src/graphics/model/model_txt.cpp +++ b/src/graphics/model/model_txt.cpp @@ -36,7 +36,7 @@ namespace Gfx::ModelIO void ReadTextModelV1AndV2(CModel& model, std::istream& stream); void ReadTextModelV3(CModel& model, std::istream& stream); ModelHeaderV3 ReadTextHeader(std::istream& stream); -CModelMesh ReadTextMesh(std::istream& stream); +std::unique_ptr ReadTextMesh(std::istream& stream); std::string ReadLineString(std::istream& stream, const std::string& expectedPrefix); void ReadValuePrefix(std::istream& stream, const std::string& expectedPrefix); @@ -51,7 +51,7 @@ std::string ParseSpecialMark(const std::string& text); void ConvertFromOldRenderState(ModelTriangle& triangle, int state); -void ReadTextModel(CModel& model, const std::filesystem::path& path) +std::unique_ptr ReadTextModel(const std::filesystem::path& path) { CInputStream stream(path); @@ -66,12 +66,16 @@ void ReadTextModel(CModel& model, const std::filesystem::path& path) throw CModelIOException(std::string("Error reading version number: ") + e.what()); } + auto model = std::make_unique(); + if (version == 1 || version == 2) - ReadTextModelV1AndV2(model, stream); + ReadTextModelV1AndV2(*model, stream); else if (version == 3) - ReadTextModelV3(model, stream); + ReadTextModelV3(*model, stream); else throw CModelIOException(std::string("Unexpected version number: ") + boost::lexical_cast(version)); + + return model; } void ReadTextModelV1AndV2(CModel& model, std::istream& stream) @@ -88,7 +92,7 @@ void ReadTextModelV1AndV2(CModel& model, std::istream& stream) throw CModelIOException(std::string("Error reading model header: ") + e.what()); } - CModelMesh mesh; + auto mesh = std::make_unique(); for (int i = 0; i < header.totalTriangles; ++i) { @@ -134,7 +138,7 @@ void ReadTextModelV1AndV2(CModel& model, std::istream& stream) triangle.material.variableDetail = t.variableTex2; ConvertFromOldRenderState(triangle, t.state); - mesh.AddTriangle(triangle); + mesh->AddTriangle(triangle); } model.AddMesh("main", std::move(mesh)); @@ -174,7 +178,7 @@ void ReadTextModelV3(CModel& model, std::istream& stream) for (int i = 0; i < header.totalMeshes; ++i) { std::string meshName = ReadLineString(stream, "mesh"); - CModelMesh mesh = ReadTextMesh(stream); + auto mesh = ReadTextMesh(stream); model.AddMesh(meshName, std::move(mesh)); } } @@ -190,14 +194,14 @@ ModelHeaderV3 ReadTextHeader(std::istream& stream) return header; } -CModelMesh ReadTextMesh(std::istream& stream) +std::unique_ptr ReadTextMesh(std::istream& stream) { - CModelMesh mesh; + auto mesh = std::make_unique(); - mesh.SetPosition(ParseVector(ReadLineString(stream, "position"))); - mesh.SetRotation(ParseVector(ReadLineString(stream, "rotation"))); - mesh.SetScale(ParseVector(ReadLineString(stream, "scale"))); - mesh.SetParent(ReadLineString(stream, "parent")); + mesh->SetPosition(ParseVector(ReadLineString(stream, "position"))); + mesh->SetRotation(ParseVector(ReadLineString(stream, "rotation"))); + mesh->SetScale(ParseVector(ReadLineString(stream, "scale"))); + mesh->SetParent(ReadLineString(stream, "parent")); int totalTriangles = boost::lexical_cast(ReadLineString(stream, "total_triangles")); @@ -234,7 +238,7 @@ CModelMesh ReadTextMesh(std::istream& stream) if (t.material.alphaMode != AlphaMode::NONE) t.material.alphaThreshold = 0.5f; - mesh.AddTriangle(t); + mesh->AddTriangle(t); } return mesh; diff --git a/src/graphics/model/model_txt.h b/src/graphics/model/model_txt.h index 46a9dc16..f003b100 100644 --- a/src/graphics/model/model_txt.h +++ b/src/graphics/model/model_txt.h @@ -31,6 +31,6 @@ namespace Gfx::ModelIO { -void ReadTextModel(CModel& model, const std::filesystem::path& path); +std::unique_ptr ReadTextModel(const std::filesystem::path& path); } diff --git a/src/object/subclass/static_object.cpp b/src/object/subclass/static_object.cpp index d886dd4f..1633613b 100644 --- a/src/object/subclass/static_object.cpp +++ b/src/object/subclass/static_object.cpp @@ -44,12 +44,12 @@ CStaticObject::CStaticObject(int id, const std::string& key, const glm::vec3& position, float angleY, - const Gfx::CModel& model, + const Gfx::CModel* model, Gfx::CEngine* engine) : CObject(id, type) , m_engine(engine) { - const Gfx::CModelMesh* mesh = model.GetMesh("main"); + const Gfx::CModelMesh* mesh = model->GetMesh("main"); assert(mesh != nullptr); m_position = position; @@ -58,13 +58,13 @@ CStaticObject::CStaticObject(int id, glm::mat4 worldMatrix = ComputeWorldMatrix(position, angleY); m_meshHandle = m_engine->AddStaticMesh(key, mesh, worldMatrix); - if (model.HasShadowSpot()) - m_engine->AddStaticMeshShadowSpot(m_meshHandle, model.GetShadowSpot()); + if (model->HasShadowSpot()) + m_engine->AddStaticMeshShadowSpot(m_meshHandle, model->GetShadowSpot()); - SetCrashSpheres(model.GetCrashSpheres()); + SetCrashSpheres(model->GetCrashSpheres()); - if (model.HasCameraCollisionSphere()) - SetCameraCollisionSphere(model.GetCameraCollisionSphere()); + if (model->HasCameraCollisionSphere()) + SetCameraCollisionSphere(model->GetCameraCollisionSphere()); } CStaticObject::~CStaticObject() @@ -134,9 +134,9 @@ CStaticObjectUPtr CStaticObject::Create(int id, try { - Gfx::CModel& model = modelManager->GetModel(modelFile); + auto model = modelManager->GetModel(modelFile); - if (model.GetMeshCount() != 1 || model.GetMesh("main") == nullptr) + if (model->GetMeshCount() != 1 || model->GetMesh("main") == nullptr) throw CObjectCreateException("Unexpected mesh configuration", type, modelFile); return std::make_unique(id, type, modelFile, adjustedPosition, angleY, model, engine); diff --git a/src/object/subclass/static_object.h b/src/object/subclass/static_object.h index 78181b2a..afbfac11 100644 --- a/src/object/subclass/static_object.h +++ b/src/object/subclass/static_object.h @@ -45,7 +45,7 @@ public: const std::string& key, const glm::vec3& position, float angleY, - const Gfx::CModel& model, + const Gfx::CModel* model, Gfx::CEngine* engine); virtual ~CStaticObject();