Rewritten CModelMesh to use separate vertex attributes

dev
Tomasz Kapuściński 2022-11-12 18:52:21 +01:00
parent 3805851255
commit e3ba33a508
18 changed files with 512 additions and 158 deletions

View File

@ -4883,7 +4883,10 @@ int CEngine::AddStaticMesh(const std::string& key, const CModelMesh* mesh, const
{ {
const auto& part = mesh->GetPart(i); const auto& part = mesh->GetPart(i);
AddBaseObjTriangles(baseObjRank, part.GetVertices(), part.GetMaterial(), EngineTriangleType::TRIANGLES); std::vector<Gfx::ModelTriangle> triangles;
part->GetTriangles(triangles);
AddBaseObjTriangles(baseObjRank, triangles);
} }
m_staticMeshBaseObjects[key] = baseObjRank; m_staticMeshBaseObjects[key] = baseObjRank;
} }

View File

@ -48,7 +48,7 @@ COldModelManager::~COldModelManager()
bool COldModelManager::LoadModel(const std::string& name, bool mirrored, int team) bool COldModelManager::LoadModel(const std::string& name, bool mirrored, int team)
{ {
CModel model; std::unique_ptr<CModel> model;
try try
{ {
auto extension = std::filesystem::path(name).extension().string(); 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()); 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; return false;
goto skip; 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()); 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; 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()); 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; goto skip;
} }
@ -98,7 +98,7 @@ bool COldModelManager::LoadModel(const std::string& name, bool mirrored, int tea
} }
skip: skip:
CModelMesh* mesh = model.GetMesh(); CModelMesh* mesh = model->GetMesh();
assert(mesh != nullptr); assert(mesh != nullptr);
ModelInfo modelInfo; ModelInfo modelInfo;

View File

@ -34,7 +34,7 @@ CModelMesh* CModel::GetMesh()
{ {
if (m_meshes.size() == 1) if (m_meshes.size() == 1)
{ {
return &m_meshes.begin()->second; return m_meshes.begin()->second.get();
} }
else 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()); GetLogger()->Error("Mesh named '%s' not found in model!\n", name.c_str());
return nullptr; return nullptr;
} }
return &(it->second); return it->second.get();
} }
const CModelMesh* CModel::GetMesh(const std::string& name) const 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()); GetLogger()->Error("Mesh named '%s' not found in model!\n", name.c_str());
return nullptr; 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<CModelMesh> mesh)
{ {
m_meshes[name] = mesh; m_meshes[name] = std::move(mesh);
} }
std::vector<std::string> CModel::GetMeshNames() const std::vector<std::string> CModel::GetMeshNames() const

View File

@ -26,9 +26,10 @@
#include "math/sphere.h" #include "math/sphere.h"
#include <map> #include <map>
#include <memory>
#include <optional>
#include <string> #include <string>
#include <vector> #include <vector>
#include <optional>
namespace Gfx namespace Gfx
{ {
@ -49,7 +50,7 @@ public:
//! Return a mesh with given \a name //! Return a mesh with given \a name
const CModelMesh* GetMesh(const std::string& name) const; const CModelMesh* GetMesh(const std::string& name) const;
//! Add new \a mesh with given \a name //! 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<CModelMesh> mesh);
//! Returns list of mesh names //! Returns list of mesh names
std::vector<std::string> GetMeshNames() const; std::vector<std::string> GetMeshNames() const;
@ -75,7 +76,7 @@ public:
bool HasCameraCollisionSphere() const; bool HasCameraCollisionSphere() const;
private: private:
std::map<std::string, CModelMesh> m_meshes; std::map<std::string, std::unique_ptr<CModelMesh>> m_meshes;
std::vector<ModelCrashSphere> m_crashSpheres; std::vector<ModelCrashSphere> m_crashSpheres;
std::optional<ModelShadowSpot> m_shadowSpot; std::optional<ModelShadowSpot> m_shadowSpot;
std::optional<Math::Sphere> m_cameraCollisionSphere; std::optional<Math::Sphere> m_cameraCollisionSphere;

View File

@ -71,12 +71,7 @@ struct Sampler
class GLTFLoader class GLTFLoader
{ {
public: public:
void Load(const std::filesystem::path& path); std::unique_ptr<CModel> Load(const std::filesystem::path& path);
CModel GetModel()
{
return m_model;
}
private: private:
void ReadBuffers(); void ReadBuffers();
@ -94,7 +89,7 @@ private:
std::vector<glm::u8vec4> ReadColors(int index); std::vector<glm::u8vec4> ReadColors(int index);
std::vector<unsigned> ReadIndices(int index); std::vector<unsigned> ReadIndices(int index);
CModel m_model; std::unique_ptr<CModel> m_model;
std::filesystem::path m_directory; std::filesystem::path m_directory;
json m_root; json m_root;
@ -108,16 +103,14 @@ private:
std::vector<Sampler> m_samplers; std::vector<Sampler> m_samplers;
}; };
void ReadGLTFModel(CModel& model, const std::filesystem::path& path) std::unique_ptr<CModel> ReadGLTFModel(const std::filesystem::path& path)
{ {
GLTFLoader loader; GLTFLoader loader;
loader.Load(path); return loader.Load(path);
model = loader.GetModel();
} }
void GLTFLoader::Load(const std::filesystem::path& path) std::unique_ptr<CModel> GLTFLoader::Load(const std::filesystem::path& path)
{ {
m_directory = path.parent_path(); m_directory = path.parent_path();
@ -125,6 +118,8 @@ void GLTFLoader::Load(const std::filesystem::path& path)
stream >> m_root; stream >> m_root;
m_model = std::make_unique<CModel>();
ReadBuffers(); ReadBuffers();
ReadBufferViews(); ReadBufferViews();
ReadAccessors(); ReadAccessors();
@ -133,6 +128,8 @@ void GLTFLoader::Load(const std::filesystem::path& path)
ReadTextures(); ReadTextures();
ReadMaterials(); ReadMaterials();
ReadMeshes(); ReadMeshes();
return std::move(m_model);
} }
void GLTFLoader::ReadBuffers() void GLTFLoader::ReadBuffers()
@ -470,13 +467,13 @@ void GLTFLoader::ReadMeshes()
{ {
auto name = node["name"].get<std::string>(); auto name = node["name"].get<std::string>();
CModelMesh mesh; auto mesh = std::make_unique<CModelMesh>();
for (const auto& primitive : node["primitives"]) for (const auto& primitive : node["primitives"])
{ {
const auto& material = m_materials[primitive["material"].get<int>()]; const auto& material = m_materials[primitive["material"].get<int>()];
auto& part = mesh.AddPart(material); auto part = mesh->AddPart(material);
std::vector<glm::vec3> positions; std::vector<glm::vec3> positions;
std::vector<glm::vec3> normals; std::vector<glm::vec3> normals;
@ -565,15 +562,15 @@ void GLTFLoader::ReadMeshes()
} }
for (const auto& vertex : vertices) for (const auto& vertex : vertices)
part.AddVertex(vertex); part->AddVertex(vertex);
for (const auto& index : indices) for (const auto& index : indices)
part.AddIndex(index); part->AddIndex(index);
} }
} }
if (mesh.GetPartCount() > 0) if (mesh->GetPartCount() > 0)
m_model.AddMesh(name, std::move(mesh)); m_model->AddMesh(name, std::move(mesh));
} }
} }

View File

@ -31,6 +31,6 @@
namespace Gfx::ModelIO namespace Gfx::ModelIO
{ {
void ReadGLTFModel(CModel& model, const std::filesystem::path& path); std::unique_ptr<CModel> ReadGLTFModel(const std::filesystem::path& path);
} }

View File

@ -28,21 +28,21 @@
namespace Gfx namespace Gfx
{ {
void ModelInput::Read(CModel& model, const std::filesystem::path& path) std::unique_ptr<CModel> ModelInput::Read(const std::filesystem::path& path)
{ {
auto extension = path.extension(); auto extension = path.extension();
if (extension == ".mod") if (extension == ".mod")
{ {
ModelIO::ReadOldModel(model, path); return ModelIO::ReadOldModel(path);
} }
else if (extension == ".txt") else if (extension == ".txt")
{ {
ModelIO::ReadTextModel(model, path); return ModelIO::ReadTextModel(path);
} }
else if (extension == ".gltf") else if (extension == ".gltf")
{ {
ModelIO::ReadGLTFModel(model, path); return ModelIO::ReadGLTFModel(path);
} }
else else
{ {

View File

@ -33,7 +33,7 @@ namespace Gfx
*/ */
namespace ModelInput namespace ModelInput
{ {
void Read(CModel& model, const std::filesystem::path& path); std::unique_ptr<CModel> Read(const std::filesystem::path& path);
} }
} // namespace Gfx } // namespace Gfx

View File

@ -29,21 +29,19 @@
namespace Gfx namespace Gfx
{ {
CModel& CModelManager::GetModel(const std::string& modelName) CModel* CModelManager::GetModel(const std::string& modelName)
{ {
auto it = m_models.find(modelName); auto it = m_models.find(modelName);
if (it != m_models.end()) if (it != m_models.end())
return it->second; return it->second.get();
std::filesystem::path modelFile = "models-new/" + modelName + ".txt"; std::filesystem::path modelFile = "models-new/" + modelName + ".txt";
GetLogger()->Debug("Loading new model: %s\n", modelFile.c_str()); GetLogger()->Debug("Loading new model: %s\n", modelFile.c_str());
CModel model; m_models[modelName] = ModelInput::Read(modelFile);
ModelInput::Read(model, modelFile);
m_models[modelName] = model;
return m_models[modelName]; return m_models[modelName].get();
} }
void CModelManager::ClearCache() void CModelManager::ClearCache()

View File

@ -21,6 +21,7 @@
#include "graphics/model/model.h" #include "graphics/model/model.h"
#include <memory>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@ -36,13 +37,13 @@ class CModelManager
public: public:
//! Returns a model named \a modelName //! Returns a model named \a modelName
/** @throws CModelIOException on read error */ /** @throws CModelIOException on read error */
CModel& GetModel(const std::string& modelName); CModel* GetModel(const std::string& modelName);
//! Clears cached models //! Clears cached models
void ClearCache(); void ClearCache();
private: private:
std::unordered_map<std::string, CModel> m_models; std::unordered_map<std::string, std::unique_ptr<CModel>> m_models;
}; };
} // namespace Gfx } // namespace Gfx

View File

@ -19,94 +19,350 @@
#include "graphics/model/model_mesh.h" #include "graphics/model/model_mesh.h"
#include <array>
namespace Gfx namespace Gfx
{ {
CModelPart::CModelPart(const Material& material) CVertexProxy::CVertexProxy(CModelPart* part, size_t index)
: m_material(material) {} : 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 const Material& CModelPart::GetMaterial() const
{ {
return m_material; 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 bool CModelPart::IsIndexed() const
{ {
return !m_indices.empty(); return m_indices.enabled;
} }
size_t CModelPart::GetVertexCount() const size_t CModelPart::GetVertexCount() const
{ {
return m_vertices.size(); return m_positions.array.size();
}
const std::vector<Vertex3D>& CModelPart::GetVertices() const
{
return m_vertices;
} }
size_t CModelPart::GetIndexCount() const size_t CModelPart::GetIndexCount() const
{ {
return m_indices.size(); return m_indices.array.size();
} }
const std::vector<unsigned int>& CModelPart::GetIndices() const const std::vector<unsigned int>& 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) 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) 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<Gfx::ModelTriangle>& triangles)
{
size_t n = IsIndexed()
? GetIndexCount()
: GetVertexCount();
for (size_t i = 0; i < n - 2; i += 3)
{
std::array<Gfx::Vertex3D, 3> 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) void CModelMesh::AddTriangle(const ModelTriangle& triangle)
{ {
for (auto& part : m_parts) for (auto& part : m_parts)
{ {
if (part.GetMaterial() == triangle.material) if (part->GetMaterial() == triangle.material)
{ {
part.AddVertex(triangle.p1); part->AddVertex(triangle.p1);
part.AddVertex(triangle.p2); part->AddVertex(triangle.p2);
part.AddVertex(triangle.p3); part->AddVertex(triangle.p3);
return; return;
} }
} }
CModelPart part(triangle.material); auto part = std::make_unique<CModelPart>(triangle.material, 0, 0);
part.AddVertex(triangle.p1); part->Add(VertexAttribute::COLOR);
part.AddVertex(triangle.p2); part->Add(VertexAttribute::UV1);
part.AddVertex(triangle.p3); 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) void CModelMesh::AddTriangle(const Triangle& triangle, const Material& material)
{ {
for (auto& part : m_parts) for (auto& part : m_parts)
{ {
if (part.GetMaterial() == material) if (part->GetMaterial() == material)
{ {
part.AddVertex(triangle.p1); part->AddVertex(triangle.p1);
part.AddVertex(triangle.p2); part->AddVertex(triangle.p2);
part.AddVertex(triangle.p3); part->AddVertex(triangle.p3);
return; return;
} }
} }
CModelPart part(material); auto part = std::make_unique<CModelPart>(material, 0, 0);
part.AddVertex(triangle.p1); part->Add(VertexAttribute::COLOR);
part.AddVertex(triangle.p2); part->Add(VertexAttribute::UV1);
part.AddVertex(triangle.p3); 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 size_t CModelMesh::GetPartCount() const
@ -114,24 +370,26 @@ size_t CModelMesh::GetPartCount() const
return m_parts.size(); 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) 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<CModelPart>(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 const glm::vec3& CModelMesh::GetPosition() const
@ -180,35 +438,7 @@ std::vector<ModelTriangle> CModelMesh::GetTriangles() const
for (const auto& part : m_parts) for (const auto& part : m_parts)
{ {
if (part.IsIndexed()) part->GetTriangles(triangles);
{
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()
});
}
}
} }
return triangles; return triangles;

View File

@ -21,11 +21,91 @@
#include "graphics/model/model_triangle.h" #include "graphics/model/model_triangle.h"
#include <memory>
#include <vector> #include <vector>
namespace Gfx 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<typename T>
struct VertexAttributeArray
{
bool enabled = false;
std::vector<T> array = {};
};
/** /**
* \class CModelPart * \class CModelPart
* \brief Part of mesh with a common material * \brief Part of mesh with a common material
@ -34,34 +114,70 @@ class CModelPart
{ {
public: public:
//! Creates new part for given material //! 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 //! Returns this part's material
const Material& GetMaterial() const; 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 //! Returns true if this part is indexed
bool IsIndexed() const; bool IsIndexed() const;
//! Returns the number of vertices in this part //! Returns the number of vertices in this part
size_t GetVertexCount() const; size_t GetVertexCount() const;
//! Returns the vertices in this part
const std::vector<Vertex3D>& GetVertices() const;
//! Returns the number of indices in this part //! Returns the number of indices in this part
size_t GetIndexCount() const; size_t GetIndexCount() const;
//! Returns the indices in this part //! Returns the indices in this part
const std::vector<unsigned int>& GetIndices() const; const std::vector<unsigned int>& 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 //! Adds a vertex
void AddVertex(const Vertex3D& vertex); void AddVertex(const Vertex3D& vertex);
//! Adds an index //! Adds an index
void AddIndex(unsigned int index); void AddIndex(unsigned int index);
//! Fills the array with converted model triangles
void GetTriangles(std::vector<Gfx::ModelTriangle>& triangles);
friend class CVertexProxy;
private: private:
//! Material //! Material
Material m_material; Material m_material;
//! Vertices
std::vector<Vertex3D> m_vertices; //! Positions
VertexAttributeArray<glm::vec3> m_positions;
//! Colors
VertexAttributeArray<glm::u8vec4> m_colors;
//! UVs 1
VertexAttributeArray<glm::vec2> m_uvs1;
//! UVs 2
VertexAttributeArray<glm::vec2> m_uvs2;
//! Normals
VertexAttributeArray<glm::vec3> m_normals;
//! Tangents
VertexAttributeArray<glm::vec4> m_tangents;
//! Bone indices
VertexAttributeArray<glm::u8vec4> m_boneIndices;
//! Bone weights
VertexAttributeArray<glm::vec4> m_boneWeights;
//! Indices //! Indices
std::vector<unsigned int> m_indices; VertexAttributeArray<std::uint32_t> m_indices;
}; };
/** /**
@ -79,9 +195,9 @@ public:
//! Returns the number of parts //! Returns the number of parts
size_t GetPartCount() const; size_t GetPartCount() const;
//! Returns a part with given index //! 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 //! 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 //! Returns the mesh position
const glm::vec3& GetPosition() const; const glm::vec3& GetPosition() const;
@ -107,10 +223,10 @@ public:
std::vector<ModelTriangle> GetTriangles() const; std::vector<ModelTriangle> GetTriangles() const;
private: private:
std::vector<CModelPart> m_parts; std::vector<std::unique_ptr<CModelPart>> m_parts;
glm::vec3 m_position; glm::vec3 m_position = { 0, 0, 0 };
glm::vec3 m_rotation; glm::vec3 m_rotation = { 0, 0, 0 };
glm::vec3 m_scale; glm::vec3 m_scale = { 1, 1, 1 };
std::string m_parent; std::string m_parent;
}; };

View File

@ -47,7 +47,7 @@ void ConvertOldTex1Name(ModelTriangle& triangle, const char* tex1Name);
void ConvertFromOldRenderState(ModelTriangle& triangle, int state); void ConvertFromOldRenderState(ModelTriangle& triangle, int state);
ModelLODLevel MinMaxToLodLevel(float min, float max); ModelLODLevel MinMaxToLodLevel(float min, float max);
void ReadOldModel(CModel& model, const std::filesystem::path& path) std::unique_ptr<CModel> ReadOldModel(const std::filesystem::path& path)
{ {
CInputStream stream(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()); throw CModelIOException(std::string("Error reading model triangles: ") + e.what());
} }
CModelMesh mesh; auto mesh = std::make_unique<CModelMesh>();
for (const auto& triangle : triangles) for (const auto& triangle : triangles)
mesh.AddTriangle(triangle); mesh->AddTriangle(triangle);
model.AddMesh("main", std::move(mesh)); auto model = std::make_unique<CModel>();
model->AddMesh("main", std::move(mesh));
return model;
} }
std::vector<ModelTriangle> ReadOldModelV1(std::istream& stream, int totalTriangles) std::vector<ModelTriangle> ReadOldModelV1(std::istream& stream, int totalTriangles)

View File

@ -31,6 +31,6 @@
namespace Gfx::ModelIO namespace Gfx::ModelIO
{ {
void ReadOldModel(CModel& model, const std::filesystem::path& path); std::unique_ptr<CModel> ReadOldModel(const std::filesystem::path& path);
} }

View File

@ -36,7 +36,7 @@ namespace Gfx::ModelIO
void ReadTextModelV1AndV2(CModel& model, std::istream& stream); void ReadTextModelV1AndV2(CModel& model, std::istream& stream);
void ReadTextModelV3(CModel& model, std::istream& stream); void ReadTextModelV3(CModel& model, std::istream& stream);
ModelHeaderV3 ReadTextHeader(std::istream& stream); ModelHeaderV3 ReadTextHeader(std::istream& stream);
CModelMesh ReadTextMesh(std::istream& stream); std::unique_ptr<CModelMesh> ReadTextMesh(std::istream& stream);
std::string ReadLineString(std::istream& stream, const std::string& expectedPrefix); std::string ReadLineString(std::istream& stream, const std::string& expectedPrefix);
void ReadValuePrefix(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 ConvertFromOldRenderState(ModelTriangle& triangle, int state);
void ReadTextModel(CModel& model, const std::filesystem::path& path) std::unique_ptr<CModel> ReadTextModel(const std::filesystem::path& path)
{ {
CInputStream stream(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()); throw CModelIOException(std::string("Error reading version number: ") + e.what());
} }
auto model = std::make_unique<CModel>();
if (version == 1 || version == 2) if (version == 1 || version == 2)
ReadTextModelV1AndV2(model, stream); ReadTextModelV1AndV2(*model, stream);
else if (version == 3) else if (version == 3)
ReadTextModelV3(model, stream); ReadTextModelV3(*model, stream);
else else
throw CModelIOException(std::string("Unexpected version number: ") + boost::lexical_cast<std::string>(version)); throw CModelIOException(std::string("Unexpected version number: ") + boost::lexical_cast<std::string>(version));
return model;
} }
void ReadTextModelV1AndV2(CModel& model, std::istream& stream) 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()); throw CModelIOException(std::string("Error reading model header: ") + e.what());
} }
CModelMesh mesh; auto mesh = std::make_unique<CModelMesh>();
for (int i = 0; i < header.totalTriangles; ++i) for (int i = 0; i < header.totalTriangles; ++i)
{ {
@ -134,7 +138,7 @@ void ReadTextModelV1AndV2(CModel& model, std::istream& stream)
triangle.material.variableDetail = t.variableTex2; triangle.material.variableDetail = t.variableTex2;
ConvertFromOldRenderState(triangle, t.state); ConvertFromOldRenderState(triangle, t.state);
mesh.AddTriangle(triangle); mesh->AddTriangle(triangle);
} }
model.AddMesh("main", std::move(mesh)); 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) for (int i = 0; i < header.totalMeshes; ++i)
{ {
std::string meshName = ReadLineString(stream, "mesh"); std::string meshName = ReadLineString(stream, "mesh");
CModelMesh mesh = ReadTextMesh(stream); auto mesh = ReadTextMesh(stream);
model.AddMesh(meshName, std::move(mesh)); model.AddMesh(meshName, std::move(mesh));
} }
} }
@ -190,14 +194,14 @@ ModelHeaderV3 ReadTextHeader(std::istream& stream)
return header; return header;
} }
CModelMesh ReadTextMesh(std::istream& stream) std::unique_ptr<CModelMesh> ReadTextMesh(std::istream& stream)
{ {
CModelMesh mesh; auto mesh = std::make_unique<CModelMesh>();
mesh.SetPosition(ParseVector(ReadLineString(stream, "position"))); mesh->SetPosition(ParseVector(ReadLineString(stream, "position")));
mesh.SetRotation(ParseVector(ReadLineString(stream, "rotation"))); mesh->SetRotation(ParseVector(ReadLineString(stream, "rotation")));
mesh.SetScale(ParseVector(ReadLineString(stream, "scale"))); mesh->SetScale(ParseVector(ReadLineString(stream, "scale")));
mesh.SetParent(ReadLineString(stream, "parent")); mesh->SetParent(ReadLineString(stream, "parent"));
int totalTriangles = boost::lexical_cast<int>(ReadLineString(stream, "total_triangles")); int totalTriangles = boost::lexical_cast<int>(ReadLineString(stream, "total_triangles"));
@ -234,7 +238,7 @@ CModelMesh ReadTextMesh(std::istream& stream)
if (t.material.alphaMode != AlphaMode::NONE) if (t.material.alphaMode != AlphaMode::NONE)
t.material.alphaThreshold = 0.5f; t.material.alphaThreshold = 0.5f;
mesh.AddTriangle(t); mesh->AddTriangle(t);
} }
return mesh; return mesh;

View File

@ -31,6 +31,6 @@
namespace Gfx::ModelIO namespace Gfx::ModelIO
{ {
void ReadTextModel(CModel& model, const std::filesystem::path& path); std::unique_ptr<CModel> ReadTextModel(const std::filesystem::path& path);
} }

View File

@ -44,12 +44,12 @@ CStaticObject::CStaticObject(int id,
const std::string& key, const std::string& key,
const glm::vec3& position, const glm::vec3& position,
float angleY, float angleY,
const Gfx::CModel& model, const Gfx::CModel* model,
Gfx::CEngine* engine) Gfx::CEngine* engine)
: CObject(id, type) : CObject(id, type)
, m_engine(engine) , m_engine(engine)
{ {
const Gfx::CModelMesh* mesh = model.GetMesh("main"); const Gfx::CModelMesh* mesh = model->GetMesh("main");
assert(mesh != nullptr); assert(mesh != nullptr);
m_position = position; m_position = position;
@ -58,13 +58,13 @@ CStaticObject::CStaticObject(int id,
glm::mat4 worldMatrix = ComputeWorldMatrix(position, angleY); glm::mat4 worldMatrix = ComputeWorldMatrix(position, angleY);
m_meshHandle = m_engine->AddStaticMesh(key, mesh, worldMatrix); m_meshHandle = m_engine->AddStaticMesh(key, mesh, worldMatrix);
if (model.HasShadowSpot()) if (model->HasShadowSpot())
m_engine->AddStaticMeshShadowSpot(m_meshHandle, model.GetShadowSpot()); m_engine->AddStaticMeshShadowSpot(m_meshHandle, model->GetShadowSpot());
SetCrashSpheres(model.GetCrashSpheres()); SetCrashSpheres(model->GetCrashSpheres());
if (model.HasCameraCollisionSphere()) if (model->HasCameraCollisionSphere())
SetCameraCollisionSphere(model.GetCameraCollisionSphere()); SetCameraCollisionSphere(model->GetCameraCollisionSphere());
} }
CStaticObject::~CStaticObject() CStaticObject::~CStaticObject()
@ -134,9 +134,9 @@ CStaticObjectUPtr CStaticObject::Create(int id,
try 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); throw CObjectCreateException("Unexpected mesh configuration", type, modelFile);
return std::make_unique<CStaticObject>(id, type, modelFile, adjustedPosition, angleY, model, engine); return std::make_unique<CStaticObject>(id, type, modelFile, adjustedPosition, angleY, model, engine);

View File

@ -45,7 +45,7 @@ public:
const std::string& key, const std::string& key,
const glm::vec3& position, const glm::vec3& position,
float angleY, float angleY,
const Gfx::CModel& model, const Gfx::CModel* model,
Gfx::CEngine* engine); Gfx::CEngine* engine);
virtual ~CStaticObject(); virtual ~CStaticObject();