Reworked SkeletalMesh class

It now uses a VertexBuffer


Former-commit-id: 81d5db90d9eb8a097342a1ae613468edcae730a6
This commit is contained in:
Lynix 2014-07-08 11:18:09 +02:00
parent d636174c04
commit af1d2d9146
3 changed files with 111 additions and 201 deletions

View File

@ -11,54 +11,37 @@
#include <Nazara/Utility/Mesh.hpp>
#include <Nazara/Utility/SubMesh.hpp>
class NzSkeleton;
struct NzVertexWeight
{
std::vector<unsigned int> weights; ///FIXME: Niveau fragmentation mémoire ça doit pas être génial
};
struct NzWeight
{
float weight;
unsigned int jointIndex;
};
class NzSkeletalMesh;
using NzSkeletalMeshConstRef = NzResourceRef<const NzSkeletalMesh>;
using NzSkeletalMeshRef = NzResourceRef<NzSkeletalMesh>;
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

View File

@ -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<nzUInt16>::max());
std::unique_ptr<NzIndexBuffer> indexBuffer(new NzIndexBuffer(largeIndices, indexCount, m_parameters.storage));
indexBuffer->SetPersistent(false);
std::unique_ptr<NzVertexBuffer> 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<NzSkeletalMesh> 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<Weight> 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<NzMeshVertex*>(subMesh->GetBindPoseBuffer());
NzVertexWeight* vertexWeight = subMesh->GetVertexWeight();
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer.get(), nzBufferAccess_WriteOnly);
NzSkeletalMeshVertex* vertices = static_cast<NzSkeletalMeshVertex*>(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<NzSkeletalMesh> 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();

View File

@ -9,16 +9,6 @@
#include <vector>
#include <Nazara/Utility/Debug.hpp>
struct NzSkeletalMeshImpl
{
std::unique_ptr<NzMeshVertex[]> bindPoseBuffer;
std::vector<NzVertexWeight> vertexWeights;
std::vector<NzWeight> 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;
}