Reworked SkeletalMesh class
It now uses a VertexBuffer Former-commit-id: 81d5db90d9eb8a097342a1ae613468edcae730a6
This commit is contained in:
parent
d636174c04
commit
af1d2d9146
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue