diff --git a/include/Nazara/Utility/SkeletalMesh.hpp b/include/Nazara/Utility/SkeletalMesh.hpp index c8c60a19c..2ac55cd82 100644 --- a/include/Nazara/Utility/SkeletalMesh.hpp +++ b/include/Nazara/Utility/SkeletalMesh.hpp @@ -11,54 +11,37 @@ #include #include -class NzSkeleton; - -struct NzVertexWeight -{ - std::vector weights; ///FIXME: Niveau fragmentation mémoire ça doit pas être génial -}; - -struct NzWeight -{ - float weight; - unsigned int jointIndex; -}; - class NzSkeletalMesh; using NzSkeletalMeshConstRef = NzResourceRef; using NzSkeletalMeshRef = NzResourceRef; -struct NzSkeletalMeshImpl; - class NAZARA_API NzSkeletalMesh final : public NzSubMesh { public: NzSkeletalMesh(const NzMesh* parent); - virtual ~NzSkeletalMesh(); + ~NzSkeletalMesh(); - bool Create(unsigned int vertexCount, unsigned int weightCount); + bool Create(NzVertexBuffer* vertexBuffer); void Destroy(); - const NzBoxf& GetAABB() const; + const NzBoxf& GetAABB() const override; nzAnimationType GetAnimationType() const final; - NzMeshVertex* GetBindPoseBuffer(); - const NzMeshVertex* GetBindPoseBuffer() const; const NzIndexBuffer* GetIndexBuffer() const override; + NzVertexBuffer* GetVertexBuffer(); + const NzVertexBuffer* GetVertexBuffer() const; unsigned int GetVertexCount() const override; - NzVertexWeight* GetVertexWeight(unsigned int vertexIndex = 0); - const NzVertexWeight* GetVertexWeight(unsigned int vertexIndex = 0) const; - NzWeight* GetWeight(unsigned int weightIndex = 0); - const NzWeight* GetWeight(unsigned int weightIndex = 0) const; - unsigned int GetWeightCount() const; bool IsAnimated() const final; bool IsValid() const; + void SetAABB(const NzBoxf& aabb); void SetIndexBuffer(const NzIndexBuffer* indexBuffer); private: - NzSkeletalMeshImpl* m_impl = nullptr; + NzBoxf m_aabb; + NzIndexBufferConstRef m_indexBuffer = nullptr; + NzVertexBufferRef m_vertexBuffer = nullptr; }; #endif // NAZARA_SKELETALMESH_HPP diff --git a/src/Nazara/Utility/Loaders/MD5Mesh/Parser.cpp b/src/Nazara/Utility/Loaders/MD5Mesh/Parser.cpp index 2fb891269..4415fad1b 100644 --- a/src/Nazara/Utility/Loaders/MD5Mesh/Parser.cpp +++ b/src/Nazara/Utility/Loaders/MD5Mesh/Parser.cpp @@ -195,12 +195,16 @@ bool NzMD5MeshParser::Parse(NzMesh* mesh) unsigned int indexCount = md5Mesh.triangles.size()*3; unsigned int vertexCount = md5Mesh.vertices.size(); - unsigned int weightCount = md5Mesh.weights.size(); - // Index buffer bool largeIndices = (vertexCount > std::numeric_limits::max()); std::unique_ptr indexBuffer(new NzIndexBuffer(largeIndices, indexCount, m_parameters.storage)); + indexBuffer->SetPersistent(false); + + std::unique_ptr vertexBuffer(new NzVertexBuffer(NzVertexDeclaration::Get(nzVertexLayout_XYZ_Normal_UV_Tangent_Skinning), vertexCount, m_parameters.storage, nzBufferUsage_Static)); + vertexBuffer->SetPersistent(false); + + // Index buffer NzIndexMapper indexMapper(indexBuffer.get(), nzBufferAccess_DiscardAndWrite); unsigned int index = 0; @@ -214,53 +218,99 @@ bool NzMD5MeshParser::Parse(NzMesh* mesh) indexMapper.Unmap(); - std::unique_ptr subMesh(new NzSkeletalMesh(mesh)); - if (!subMesh->Create(vertexCount, weightCount)) + // Vertex buffer + struct Weight { - NazaraError("Failed to create skeletal mesh"); - continue; - } + float bias; + unsigned int jointIndex; + }; - subMesh->SetIndexBuffer(indexBuffer.get()); - indexBuffer->SetPersistent(false); - indexBuffer.release(); + std::vector tempWeights(NAZARA_UTILITY_SKINNING_MAX_WEIGHTS); - NzWeight* weights = subMesh->GetWeight(); - for (unsigned int j = 0; j < weightCount; ++j) - { - weights->jointIndex = md5Mesh.weights[j].joint; - weights->weight = md5Mesh.weights[j].bias; - weights++; - } - - NzMeshVertex* bindPosVertex = reinterpret_cast(subMesh->GetBindPoseBuffer()); - NzVertexWeight* vertexWeight = subMesh->GetVertexWeight(); + NzBufferMapper vertexMapper(vertexBuffer.get(), nzBufferAccess_WriteOnly); + NzSkeletalMeshVertex* vertices = static_cast(vertexMapper.GetPointer()); for (const Mesh::Vertex& vertex : md5Mesh.vertices) { // Skinning MD5 (Formule d'Id Tech) NzVector3f finalPos(NzVector3f::Zero()); - vertexWeight->weights.resize(vertex.weightCount); + tempWeights.resize(vertex.weightCount); for (unsigned int j = 0; j < vertex.weightCount; ++j) { const Mesh::Weight& weight = md5Mesh.weights[vertex.startWeight + j]; const Joint& joint = m_joints[weight.joint]; finalPos += (joint.bindPos + joint.bindOrient*weight.pos) * weight.bias; - vertexWeight->weights[j] = vertex.startWeight + j; + + // Avant d'ajouter les poids, il faut s'assurer qu'il n'y en ait pas plus que le maximum supporté + // et dans le cas contraire, garder les poids les plus importants et les renormaliser + tempWeights[j] = {weight.bias, weight.joint}; } - bindPosVertex->position = finalPos; - bindPosVertex->uv.Set(vertex.uv.x, 1.f-vertex.uv.y); - bindPosVertex++; - vertexWeight++; + unsigned int weightCount = vertex.weightCount; + if (weightCount > NAZARA_UTILITY_SKINNING_MAX_WEIGHTS) + { + // Pour augmenter la qualité du skinning tout en ne gardant que X poids, on ne garde que les poids + // les plus importants, ayant le plus d'impact sur le sommet final + std::sort(tempWeights.begin(), tempWeights.end(), [] (const Weight& a, const Weight& b) -> bool { + return a.bias > b.bias; + }); + + // Sans oublier bien sûr de renormaliser les poids (que leur somme soit 1) + float weightSum = 0.f; + for (unsigned int j = 0; j < NAZARA_UTILITY_SKINNING_MAX_WEIGHTS; ++j) + weightSum += tempWeights[j].bias; + + for (unsigned int j = 0; j < NAZARA_UTILITY_SKINNING_MAX_WEIGHTS; ++j) + tempWeights[j].bias /= weightSum; + + weightCount = NAZARA_UTILITY_SKINNING_MAX_WEIGHTS; + } + + vertices->weightCount = weightCount; + for (unsigned int j = 0; j < NAZARA_UTILITY_SKINNING_MAX_WEIGHTS; ++j) + { + // On donne une valeur aux poids présents, et 0 pour les autres (nécessaire pour le GPU Skinning) + if (j < weightCount) + { + vertices->weights[j] = tempWeights[j].bias; + vertices->jointIndexes[j] = tempWeights[j].jointIndex; + } + else + { + vertices->weights[j] = 0.f; + vertices->jointIndexes[j] = 0; + } + } + + vertices->position = finalPos; + vertices->uv.Set(vertex.uv.x, 1.f-vertex.uv.y); + vertices++; } + vertexMapper.Unmap(); + // Material mesh->SetMaterial(i, baseDir + md5Mesh.shader); + // Submesh + std::unique_ptr subMesh(new NzSkeletalMesh(mesh)); + if (!subMesh->Create(vertexBuffer.get())) + { + NazaraError("Failed to create skeletal mesh"); + continue; + } + vertexBuffer.release(); + + if (m_parameters.optimizeIndexBuffers) + indexBuffer->Optimize(); + + subMesh->SetIndexBuffer(indexBuffer.get()); + indexBuffer.release(); + subMesh->GenerateNormalsAndTangents(); subMesh->SetMaterialIndex(i); + subMesh->SetPrimitiveMode(nzPrimitiveMode_TriangleList); mesh->AddSubMesh(subMesh.get()); subMesh.release(); diff --git a/src/Nazara/Utility/SkeletalMesh.cpp b/src/Nazara/Utility/SkeletalMesh.cpp index 314d0a46a..5926e7880 100644 --- a/src/Nazara/Utility/SkeletalMesh.cpp +++ b/src/Nazara/Utility/SkeletalMesh.cpp @@ -9,16 +9,6 @@ #include #include -struct NzSkeletalMeshImpl -{ - std::unique_ptr bindPoseBuffer; - std::vector vertexWeights; - std::vector weights; - NzBoxf aabb; - NzIndexBufferConstRef indexBuffer; - unsigned int vertexCount; -}; - NzSkeletalMesh::NzSkeletalMesh(const NzMesh* parent) : NzSubMesh(parent) { @@ -29,57 +19,36 @@ NzSkeletalMesh::~NzSkeletalMesh() Destroy(); } -bool NzSkeletalMesh::Create(unsigned int vertexCount, unsigned int weightCount) +bool NzSkeletalMesh::Create(NzVertexBuffer* vertexBuffer) { Destroy(); #if NAZARA_UTILITY_SAFE - if (vertexCount == 0) + if (!vertexBuffer) { - NazaraError("Vertex count must be over 0"); - return false; - } - - if (weightCount == 0) - { - NazaraError("Weight count must be over 0"); + NazaraError("Invalid vertex buffer"); return false; } #endif - m_impl = new NzSkeletalMeshImpl; - m_impl->bindPoseBuffer.reset(new NzMeshVertex[vertexCount]); - m_impl->vertexCount = vertexCount; - m_impl->vertexWeights.resize(vertexCount); - m_impl->weights.resize(weightCount); - + m_vertexBuffer = vertexBuffer; return true; } void NzSkeletalMesh::Destroy() { - if (m_impl) + if (m_vertexBuffer) { NotifyDestroy(); - delete m_impl; - m_impl = nullptr; + m_indexBuffer.Reset(); + m_vertexBuffer.Reset(); } } const NzBoxf& NzSkeletalMesh::GetAABB() const { - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Skeletal mesh not created"); - - static NzBoxf dummy; - return dummy; - } - #endif - - return m_impl->aabb; + return m_aabb; } nzAnimationType NzSkeletalMesh::GetAnimationType() const @@ -87,121 +56,24 @@ nzAnimationType NzSkeletalMesh::GetAnimationType() const return nzAnimationType_Skeletal; } -NzMeshVertex* NzSkeletalMesh::GetBindPoseBuffer() -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Skeletal mesh not created"); - return nullptr; - } - #endif - - return m_impl->bindPoseBuffer.get(); -} - -const NzMeshVertex* NzSkeletalMesh::GetBindPoseBuffer() const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Skeletal mesh not created"); - return nullptr; - } - #endif - - return m_impl->bindPoseBuffer.get(); -} - const NzIndexBuffer* NzSkeletalMesh::GetIndexBuffer() const { - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Skeletal mesh not created"); - return nullptr; - } - #endif + return m_indexBuffer; +} - return m_impl->indexBuffer; +NzVertexBuffer* NzSkeletalMesh::GetVertexBuffer() +{ + return m_vertexBuffer; +} + +const NzVertexBuffer* NzSkeletalMesh::GetVertexBuffer() const +{ + return m_vertexBuffer; } unsigned int NzSkeletalMesh::GetVertexCount() const { - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Skeletal mesh not created"); - return 0; - } - #endif - - return m_impl->vertexCount; -} - -NzVertexWeight* NzSkeletalMesh::GetVertexWeight(unsigned int vertexIndex) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Skeletal mesh not created"); - return nullptr; - } - #endif - - return &m_impl->vertexWeights[vertexIndex]; -} - -const NzVertexWeight* NzSkeletalMesh::GetVertexWeight(unsigned int vertexIndex) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Skeletal mesh not created"); - return nullptr; - } - #endif - - return &m_impl->vertexWeights[vertexIndex]; -} - -NzWeight* NzSkeletalMesh::GetWeight(unsigned int weightIndex) -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Skeletal mesh not created"); - return nullptr; - } - #endif - - return &m_impl->weights[weightIndex]; -} - -const NzWeight* NzSkeletalMesh::GetWeight(unsigned int weightIndex) const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Skeletal mesh not created"); - return nullptr; - } - #endif - - return &m_impl->weights[weightIndex]; -} - -unsigned int NzSkeletalMesh::GetWeightCount() const -{ - #if NAZARA_UTILITY_SAFE - if (!m_impl) - { - NazaraError("Skeletal mesh not created"); - return 0; - } - #endif - - return m_impl->weights.size(); + return m_vertexBuffer->GetVertexCount(); } bool NzSkeletalMesh::IsAnimated() const @@ -211,10 +83,15 @@ bool NzSkeletalMesh::IsAnimated() const bool NzSkeletalMesh::IsValid() const { - return m_impl != nullptr; + return m_vertexBuffer != nullptr; +} + +void NzSkeletalMesh::SetAABB(const NzBoxf& aabb) +{ + m_aabb = aabb; } void NzSkeletalMesh::SetIndexBuffer(const NzIndexBuffer* indexBuffer) { - m_impl->indexBuffer = indexBuffer; + m_indexBuffer = indexBuffer; }