Moved MD5[Anim|Mesh]Parser loading code to Loader
Similary to OBJParser Former-commit-id: 243b05f2fbc3899089ef05a29672979d3bbca695
This commit is contained in:
parent
4ccf021376
commit
fba2e5ae01
|
|
@ -27,7 +27,7 @@ NzOBJParser::~NzOBJParser()
|
||||||
|
|
||||||
const NzString* NzOBJParser::GetMaterials() const
|
const NzString* NzOBJParser::GetMaterials() const
|
||||||
{
|
{
|
||||||
return &m_materials[0];
|
return m_materials.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int NzOBJParser::GetMaterialCount() const
|
unsigned int NzOBJParser::GetMaterialCount() const
|
||||||
|
|
@ -37,7 +37,7 @@ unsigned int NzOBJParser::GetMaterialCount() const
|
||||||
|
|
||||||
const NzOBJParser::Mesh* NzOBJParser::GetMeshes() const
|
const NzOBJParser::Mesh* NzOBJParser::GetMeshes() const
|
||||||
{
|
{
|
||||||
return &m_meshes[0];
|
return m_meshes.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int NzOBJParser::GetMeshCount() const
|
unsigned int NzOBJParser::GetMeshCount() const
|
||||||
|
|
@ -52,7 +52,7 @@ const NzString& NzOBJParser::GetMtlLib() const
|
||||||
|
|
||||||
const NzVector3f* NzOBJParser::GetNormals() const
|
const NzVector3f* NzOBJParser::GetNormals() const
|
||||||
{
|
{
|
||||||
return &m_normals[0];
|
return m_normals.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int NzOBJParser::GetNormalCount() const
|
unsigned int NzOBJParser::GetNormalCount() const
|
||||||
|
|
@ -62,7 +62,7 @@ unsigned int NzOBJParser::GetNormalCount() const
|
||||||
|
|
||||||
const NzVector4f* NzOBJParser::GetPositions() const
|
const NzVector4f* NzOBJParser::GetPositions() const
|
||||||
{
|
{
|
||||||
return &m_positions[0];
|
return m_positions.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int NzOBJParser::GetPositionCount() const
|
unsigned int NzOBJParser::GetPositionCount() const
|
||||||
|
|
@ -72,7 +72,7 @@ unsigned int NzOBJParser::GetPositionCount() const
|
||||||
|
|
||||||
const NzVector3f* NzOBJParser::GetTexCoords() const
|
const NzVector3f* NzOBJParser::GetTexCoords() const
|
||||||
{
|
{
|
||||||
return &m_texCoords[0];
|
return m_texCoords.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int NzOBJParser::GetTexCoordCount() const
|
unsigned int NzOBJParser::GetTexCoordCount() const
|
||||||
|
|
@ -265,8 +265,8 @@ bool NzOBJParser::Parse()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||||
case 's':
|
case 's':
|
||||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
|
||||||
if (m_currentLine.GetSize() <= 2 || m_currentLine[1] == ' ')
|
if (m_currentLine.GetSize() <= 2 || m_currentLine[1] == ' ')
|
||||||
{
|
{
|
||||||
NzString param = m_currentLine.SubString(2);
|
NzString param = m_currentLine.SubString(2);
|
||||||
|
|
@ -275,8 +275,8 @@ bool NzOBJParser::Parse()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
UnrecognizedLine();
|
UnrecognizedLine();
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
case 'u':
|
case 'u':
|
||||||
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
#if NAZARA_UTILITY_STRICT_RESOURCE_PARSING
|
||||||
|
|
|
||||||
|
|
@ -15,14 +15,62 @@ namespace
|
||||||
|
|
||||||
nzTernary Check(NzInputStream& stream, const NzAnimationParams& parameters)
|
nzTernary Check(NzInputStream& stream, const NzAnimationParams& parameters)
|
||||||
{
|
{
|
||||||
NzMD5AnimParser parser(stream, parameters);
|
NzMD5AnimParser parser(stream);
|
||||||
return parser.Check();
|
return parser.Check();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Load(NzAnimation* animation, NzInputStream& stream, const NzAnimationParams& parameters)
|
bool Load(NzAnimation* animation, NzInputStream& stream, const NzAnimationParams& parameters)
|
||||||
{
|
{
|
||||||
NzMD5AnimParser parser(stream, parameters);
|
NzMD5AnimParser parser(stream);
|
||||||
return parser.Parse(animation);
|
|
||||||
|
if (!parser.Parse())
|
||||||
|
{
|
||||||
|
NazaraError("MD5Anim parser failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NzMD5AnimParser::Frame* frames = parser.GetFrames();
|
||||||
|
unsigned int frameCount = parser.GetFrameCount();
|
||||||
|
unsigned int frameRate = parser.GetFrameRate();
|
||||||
|
const NzMD5AnimParser::Joint* joints = parser.GetJoints();
|
||||||
|
unsigned int jointCount = parser.GetJointCount();
|
||||||
|
|
||||||
|
// À ce stade, nous sommes censés avoir assez d'informations pour créer l'animation
|
||||||
|
animation->CreateSkeletal(frameCount, jointCount);
|
||||||
|
|
||||||
|
NzSequence sequence;
|
||||||
|
sequence.firstFrame = 0;
|
||||||
|
sequence.frameCount = frameCount;
|
||||||
|
sequence.frameRate = frameRate;
|
||||||
|
sequence.name = stream.GetPath().SubStringFrom(NAZARA_DIRECTORY_SEPARATOR, -1, true);
|
||||||
|
|
||||||
|
animation->AddSequence(sequence);
|
||||||
|
|
||||||
|
NzSequenceJoint* sequenceJoints = animation->GetSequenceJoints();
|
||||||
|
|
||||||
|
// Pour que le squelette soit correctement aligné, il faut appliquer un quaternion "de correction" aux joints à la base du squelette
|
||||||
|
NzQuaternionf rotationQuat = NzEulerAnglesf(-90.f, 90.f, 0.f);
|
||||||
|
for (unsigned int i = 0; i < jointCount; ++i)
|
||||||
|
{
|
||||||
|
int parent = joints[i].parent;
|
||||||
|
for (unsigned int j = 0; j < frameCount; ++j)
|
||||||
|
{
|
||||||
|
NzSequenceJoint& sequenceJoint = sequenceJoints[j*jointCount + i];
|
||||||
|
|
||||||
|
if (parent >= 0)
|
||||||
|
{
|
||||||
|
sequenceJoint.position = frames[j].joints[i].pos;
|
||||||
|
sequenceJoint.rotation = frames[j].joints[i].orient;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sequenceJoint.position = rotationQuat * frames[j].joints[i].pos;
|
||||||
|
sequenceJoint.rotation = rotationQuat * frames[j].joints[i].orient;
|
||||||
|
}
|
||||||
|
|
||||||
|
sequenceJoint.scale.Set(1.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,8 @@
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <Nazara/Utility/Debug.hpp>
|
#include <Nazara/Utility/Debug.hpp>
|
||||||
|
|
||||||
NzMD5AnimParser::NzMD5AnimParser(NzInputStream& stream, const NzAnimationParams& parameters) :
|
NzMD5AnimParser::NzMD5AnimParser(NzInputStream& stream) :
|
||||||
m_stream(stream),
|
m_stream(stream),
|
||||||
m_parameters(parameters),
|
|
||||||
m_keepLastLine(false),
|
m_keepLastLine(false),
|
||||||
m_frameIndex(0),
|
m_frameIndex(0),
|
||||||
m_frameRate(0),
|
m_frameRate(0),
|
||||||
|
|
@ -47,7 +46,37 @@ nzTernary NzMD5AnimParser::Check()
|
||||||
return nzTernary_False;
|
return nzTernary_False;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NzMD5AnimParser::Parse(NzAnimation* animation)
|
unsigned int NzMD5AnimParser::GetAnimatedComponentCount() const
|
||||||
|
{
|
||||||
|
return m_animatedComponents.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
const NzMD5AnimParser::Frame* NzMD5AnimParser::GetFrames() const
|
||||||
|
{
|
||||||
|
return m_frames.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int NzMD5AnimParser::GetFrameCount() const
|
||||||
|
{
|
||||||
|
return m_frames.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int NzMD5AnimParser::GetFrameRate() const
|
||||||
|
{
|
||||||
|
return m_frameRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NzMD5AnimParser::Joint* NzMD5AnimParser::GetJoints() const
|
||||||
|
{
|
||||||
|
return m_joints.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int NzMD5AnimParser::GetJointCount() const
|
||||||
|
{
|
||||||
|
return m_joints.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NzMD5AnimParser::Parse()
|
||||||
{
|
{
|
||||||
while (Advance(false))
|
while (Advance(false))
|
||||||
{
|
{
|
||||||
|
|
@ -204,47 +233,6 @@ bool NzMD5AnimParser::Parse(NzAnimation* animation)
|
||||||
m_frameRate = 24;
|
m_frameRate = 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
// À ce stade, nous sommes censés avoir assez d'informations pour créer l'animation
|
|
||||||
if (!animation->CreateSkeletal(frameCount, jointCount))
|
|
||||||
{
|
|
||||||
NazaraError("Failed to create animation");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
NzSequence sequence;
|
|
||||||
sequence.firstFrame = 0;
|
|
||||||
sequence.frameCount = m_frames.size();
|
|
||||||
sequence.frameRate = m_frameRate;
|
|
||||||
sequence.name = m_stream.GetPath().SubStringFrom(NAZARA_DIRECTORY_SEPARATOR, -1, true);
|
|
||||||
if (!animation->AddSequence(sequence))
|
|
||||||
NazaraWarning("Failed to add sequence");
|
|
||||||
|
|
||||||
NzSequenceJoint* sequenceJoints = animation->GetSequenceJoints();
|
|
||||||
|
|
||||||
// Pour que le squelette soit correctement aligné, il faut appliquer un quaternion "de correction" aux joints à la base du squelette
|
|
||||||
NzQuaternionf rotationQuat = NzEulerAnglesf(-90.f, 90.f, 0.f);
|
|
||||||
for (unsigned int i = 0; i < jointCount; ++i)
|
|
||||||
{
|
|
||||||
int parent = m_joints[i].parent;
|
|
||||||
for (unsigned int j = 0; j < frameCount; ++j)
|
|
||||||
{
|
|
||||||
NzSequenceJoint& sequenceJoint = sequenceJoints[j*jointCount + i];
|
|
||||||
|
|
||||||
if (parent >= 0)
|
|
||||||
{
|
|
||||||
sequenceJoint.position = m_frames[j].joints[i].pos;
|
|
||||||
sequenceJoint.rotation = m_frames[j].joints[i].orient;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sequenceJoint.position = rotationQuat * m_frames[j].joints[i].pos;
|
|
||||||
sequenceJoint.rotation = rotationQuat * m_frames[j].joints[i].orient;
|
|
||||||
}
|
|
||||||
|
|
||||||
sequenceJoint.scale.Set(1.f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -340,7 +328,7 @@ bool NzMD5AnimParser::ParseBounds()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_frames[i].aabb.Set(min, max);
|
m_frames[i].bounds.Set(min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Advance())
|
if (!Advance())
|
||||||
|
|
|
||||||
|
|
@ -18,23 +18,16 @@
|
||||||
class NzMD5AnimParser
|
class NzMD5AnimParser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NzMD5AnimParser(NzInputStream& stream, const NzAnimationParams& parameters);
|
struct FrameJoint
|
||||||
~NzMD5AnimParser();
|
{
|
||||||
|
NzQuaternionf orient;
|
||||||
|
NzVector3f pos;
|
||||||
|
};
|
||||||
|
|
||||||
nzTernary Check();
|
|
||||||
bool Parse(NzAnimation* animation);
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct Frame
|
struct Frame
|
||||||
{
|
{
|
||||||
struct Joint
|
std::vector<FrameJoint> joints;
|
||||||
{
|
NzBoxf bounds;
|
||||||
NzQuaternionf orient;
|
|
||||||
NzVector3f pos;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Joint> joints;
|
|
||||||
NzBoxf aabb;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Joint
|
struct Joint
|
||||||
|
|
@ -47,6 +40,21 @@ class NzMD5AnimParser
|
||||||
unsigned int index;
|
unsigned int index;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NzMD5AnimParser(NzInputStream& stream);
|
||||||
|
~NzMD5AnimParser();
|
||||||
|
|
||||||
|
nzTernary Check();
|
||||||
|
|
||||||
|
unsigned int GetAnimatedComponentCount() const;
|
||||||
|
const Frame* GetFrames() const;
|
||||||
|
unsigned int GetFrameCount() const;
|
||||||
|
unsigned int GetFrameRate() const;
|
||||||
|
const Joint* GetJoints() const;
|
||||||
|
unsigned int GetJointCount() const;
|
||||||
|
|
||||||
|
bool Parse();
|
||||||
|
|
||||||
|
private:
|
||||||
bool Advance(bool required = true);
|
bool Advance(bool required = true);
|
||||||
void Error(const NzString& message);
|
void Error(const NzString& message);
|
||||||
bool ParseBaseframe();
|
bool ParseBaseframe();
|
||||||
|
|
@ -61,7 +69,6 @@ class NzMD5AnimParser
|
||||||
std::vector<Joint> m_joints;
|
std::vector<Joint> m_joints;
|
||||||
NzInputStream& m_stream;
|
NzInputStream& m_stream;
|
||||||
NzString m_currentLine;
|
NzString m_currentLine;
|
||||||
const NzAnimationParams& m_parameters;
|
|
||||||
bool m_keepLastLine;
|
bool m_keepLastLine;
|
||||||
unsigned int m_frameIndex;
|
unsigned int m_frameIndex;
|
||||||
unsigned int m_frameRate;
|
unsigned int m_frameRate;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,11 @@
|
||||||
|
|
||||||
#include <Nazara/Utility/Loaders/MD5Mesh.hpp>
|
#include <Nazara/Utility/Loaders/MD5Mesh.hpp>
|
||||||
#include <Nazara/Utility/Loaders/MD5Mesh/Parser.hpp>
|
#include <Nazara/Utility/Loaders/MD5Mesh/Parser.hpp>
|
||||||
|
#include <Nazara/Utility/IndexIterator.hpp>
|
||||||
|
#include <Nazara/Utility/IndexMapper.hpp>
|
||||||
|
#include <Nazara/Utility/SkeletalMesh.hpp>
|
||||||
|
#include <Nazara/Utility/StaticMesh.hpp>
|
||||||
|
#include <memory>
|
||||||
#include <Nazara/Utility/Debug.hpp>
|
#include <Nazara/Utility/Debug.hpp>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
|
@ -15,14 +20,290 @@ namespace
|
||||||
|
|
||||||
nzTernary Check(NzInputStream& stream, const NzMeshParams& parameters)
|
nzTernary Check(NzInputStream& stream, const NzMeshParams& parameters)
|
||||||
{
|
{
|
||||||
NzMD5MeshParser parser(stream, parameters);
|
NazaraUnused(parameters);
|
||||||
|
|
||||||
|
NzMD5MeshParser parser(stream);
|
||||||
return parser.Check();
|
return parser.Check();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Load(NzMesh* mesh, NzInputStream& stream, const NzMeshParams& parameters)
|
bool Load(NzMesh* mesh, NzInputStream& stream, const NzMeshParams& parameters)
|
||||||
{
|
{
|
||||||
NzMD5MeshParser parser(stream, parameters);
|
NzMD5MeshParser parser(stream);
|
||||||
return parser.Parse(mesh);
|
if (!parser.Parse())
|
||||||
|
{
|
||||||
|
NazaraError("MD5Mesh parser failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pour que le squelette soit correctement aligné, il faut appliquer un quaternion "de correction" aux joints à la base du squelette
|
||||||
|
NzQuaternionf rotationQuat = NzEulerAnglesf(-90.f, 180.f, 0.f);
|
||||||
|
NzString baseDir = stream.GetDirectory();
|
||||||
|
|
||||||
|
// Le hellknight de Doom 3 fait ~120 unités, et il est dit qu'il fait trois mètres
|
||||||
|
// Nous réduisons donc la taille générale des fichiers MD5 de 1/40
|
||||||
|
NzVector3f scale(parameters.scale/40.f);
|
||||||
|
|
||||||
|
const NzMD5MeshParser::Joint* joints = parser.GetJoints();
|
||||||
|
const NzMD5MeshParser::Mesh* meshes = parser.GetMeshes();
|
||||||
|
unsigned int jointCount = parser.GetJointCount();
|
||||||
|
unsigned int meshCount = parser.GetMeshCount();
|
||||||
|
|
||||||
|
if (parameters.animated)
|
||||||
|
{
|
||||||
|
mesh->CreateSkeletal(jointCount);
|
||||||
|
|
||||||
|
NzSkeleton* skeleton = mesh->GetSkeleton();
|
||||||
|
for (unsigned int i = 0; i < jointCount; ++i)
|
||||||
|
{
|
||||||
|
NzJoint* joint = skeleton->GetJoint(i);
|
||||||
|
|
||||||
|
int parent = joints[i].parent;
|
||||||
|
if (parent >= 0)
|
||||||
|
joint->SetParent(skeleton->GetJoint(parent));
|
||||||
|
|
||||||
|
joint->SetName(joints[i].name);
|
||||||
|
|
||||||
|
NzMatrix4f bindMatrix;
|
||||||
|
|
||||||
|
if (parent >= 0)
|
||||||
|
bindMatrix.MakeTransform(joints[i].bindPos, joints[i].bindOrient);
|
||||||
|
else
|
||||||
|
bindMatrix.MakeTransform(rotationQuat * joints[i].bindPos, rotationQuat * joints[i].bindOrient);
|
||||||
|
|
||||||
|
joint->SetInverseBindMatrix(bindMatrix.InverseAffine());
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh->SetMaterialCount(meshCount);
|
||||||
|
for (unsigned int i = 0; i < meshCount; ++i)
|
||||||
|
{
|
||||||
|
const NzMD5MeshParser::Mesh& md5Mesh = meshes[i];
|
||||||
|
|
||||||
|
unsigned int indexCount = md5Mesh.triangles.size()*3;
|
||||||
|
unsigned int vertexCount = md5Mesh.vertices.size();
|
||||||
|
|
||||||
|
bool largeIndices = (vertexCount > std::numeric_limits<nzUInt16>::max());
|
||||||
|
|
||||||
|
std::unique_ptr<NzIndexBuffer> indexBuffer(new NzIndexBuffer(largeIndices, indexCount, parameters.storage));
|
||||||
|
indexBuffer->SetPersistent(false);
|
||||||
|
|
||||||
|
std::unique_ptr<NzVertexBuffer> vertexBuffer(new NzVertexBuffer(NzVertexDeclaration::Get(nzVertexLayout_XYZ_Normal_UV_Tangent_Skinning), vertexCount, parameters.storage, nzBufferUsage_Static));
|
||||||
|
vertexBuffer->SetPersistent(false);
|
||||||
|
|
||||||
|
// Index buffer
|
||||||
|
NzIndexMapper indexMapper(indexBuffer.get(), nzBufferAccess_DiscardAndWrite);
|
||||||
|
|
||||||
|
// Le format définit un set de triangles nous permettant de retrouver facilement les indices
|
||||||
|
// Cependant les sommets des triangles ne sont pas spécifiés dans le même ordre que ceux du moteur
|
||||||
|
// (On parle ici de winding)
|
||||||
|
unsigned int index = 0;
|
||||||
|
for (const NzMD5MeshParser::Triangle& triangle : md5Mesh.triangles)
|
||||||
|
{
|
||||||
|
// On les respécifie dans le bon ordre (inversion du winding)
|
||||||
|
indexMapper.Set(index++, triangle.x);
|
||||||
|
indexMapper.Set(index++, triangle.z);
|
||||||
|
indexMapper.Set(index++, triangle.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
indexMapper.Unmap();
|
||||||
|
|
||||||
|
// Vertex buffer
|
||||||
|
struct Weight
|
||||||
|
{
|
||||||
|
float bias;
|
||||||
|
unsigned int jointIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Weight> tempWeights;
|
||||||
|
|
||||||
|
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer.get(), nzBufferAccess_WriteOnly);
|
||||||
|
NzSkeletalMeshVertex* vertices = static_cast<NzSkeletalMeshVertex*>(vertexMapper.GetPointer());
|
||||||
|
for (const NzMD5MeshParser::Vertex& vertex : md5Mesh.vertices)
|
||||||
|
{
|
||||||
|
// Skinning MD5 (Formule d'Id Tech)
|
||||||
|
NzVector3f finalPos(NzVector3f::Zero());
|
||||||
|
|
||||||
|
// On stocke tous les poids dans le tableau temporaire en même temps qu'on calcule la position finale du sommet.
|
||||||
|
tempWeights.resize(vertex.weightCount);
|
||||||
|
for (unsigned int j = 0; j < vertex.weightCount; ++j)
|
||||||
|
{
|
||||||
|
const NzMD5MeshParser::Weight& weight = md5Mesh.weights[vertex.startWeight + j];
|
||||||
|
const NzMD5MeshParser::Joint& joint = joints[weight.joint];
|
||||||
|
|
||||||
|
finalPos += (joint.bindPos + joint.bindOrient*weight.pos) * weight.bias;
|
||||||
|
|
||||||
|
// 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};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avons nous plus de poids que le moteur ne peut en supporter ?
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (j < weightCount)
|
||||||
|
{
|
||||||
|
// On donne une valeur aux poids présents
|
||||||
|
vertices->weights[j] = tempWeights[j].bias;
|
||||||
|
vertices->jointIndexes[j] = tempWeights[j].jointIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Et un poids de 0 sur le joint 0 pour les autres (nécessaire pour le GPU Skinning)
|
||||||
|
// La raison est que le GPU ne tiendra pas compte du nombre de poids pour des raisons de performances.
|
||||||
|
vertices->weights[j] = 0.f;
|
||||||
|
vertices->jointIndexes[j] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vertices->position = finalPos;
|
||||||
|
|
||||||
|
// Le format MD5 spécifie ses UV avec l'origine en bas à gauche, contrairement au moteur
|
||||||
|
// dont l'origine est en haut à gauche, nous inversons donc la valeur en Y.
|
||||||
|
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));
|
||||||
|
subMesh->Create(vertexBuffer.get());
|
||||||
|
vertexBuffer.release();
|
||||||
|
|
||||||
|
if (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();
|
||||||
|
|
||||||
|
// Animation
|
||||||
|
// Il est peut-être éventuellement possible que la probabilité que l'animation ait le même nom soit non-nulle.
|
||||||
|
NzString path = stream.GetPath();
|
||||||
|
if (!path.IsEmpty())
|
||||||
|
{
|
||||||
|
path.Replace(".md5mesh", ".md5anim", -8, NzString::CaseInsensitive);
|
||||||
|
if (NzFile::Exists(path))
|
||||||
|
mesh->SetAnimation(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!mesh->CreateStatic()) // Ne devrait jamais échouer
|
||||||
|
{
|
||||||
|
NazaraInternalError("Failed to create mesh");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh->SetMaterialCount(meshCount);
|
||||||
|
for (unsigned int i = 0; i < meshCount; ++i)
|
||||||
|
{
|
||||||
|
const NzMD5MeshParser::Mesh& md5Mesh = meshes[i];
|
||||||
|
unsigned int indexCount = md5Mesh.triangles.size()*3;
|
||||||
|
unsigned int vertexCount = md5Mesh.vertices.size();
|
||||||
|
|
||||||
|
// Index buffer
|
||||||
|
bool largeIndices = (vertexCount > std::numeric_limits<nzUInt16>::max());
|
||||||
|
|
||||||
|
std::unique_ptr<NzIndexBuffer> indexBuffer(new NzIndexBuffer(largeIndices, indexCount, parameters.storage));
|
||||||
|
indexBuffer->SetPersistent(false);
|
||||||
|
|
||||||
|
NzIndexMapper indexMapper(indexBuffer.get(), nzBufferAccess_DiscardAndWrite);
|
||||||
|
NzIndexIterator index = indexMapper.begin();
|
||||||
|
|
||||||
|
for (const NzMD5MeshParser::Triangle& triangle : md5Mesh.triangles)
|
||||||
|
{
|
||||||
|
// On les respécifie dans le bon ordre
|
||||||
|
*index++ = triangle.x;
|
||||||
|
*index++ = triangle.z;
|
||||||
|
*index++ = triangle.y;
|
||||||
|
}
|
||||||
|
indexMapper.Unmap();
|
||||||
|
|
||||||
|
// Vertex buffer
|
||||||
|
std::unique_ptr<NzVertexBuffer> vertexBuffer(new NzVertexBuffer(NzVertexDeclaration::Get(nzVertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.storage));
|
||||||
|
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer.get(), nzBufferAccess_WriteOnly);
|
||||||
|
|
||||||
|
NzMeshVertex* vertex = reinterpret_cast<NzMeshVertex*>(vertexMapper.GetPointer());
|
||||||
|
for (const NzMD5MeshParser::Vertex& md5Vertex : md5Mesh.vertices)
|
||||||
|
{
|
||||||
|
// Skinning MD5 (Formule d'Id Tech)
|
||||||
|
NzVector3f finalPos(NzVector3f::Zero());
|
||||||
|
for (unsigned int j = 0; j < md5Vertex.weightCount; ++j)
|
||||||
|
{
|
||||||
|
const NzMD5MeshParser::Weight& weight = md5Mesh.weights[md5Vertex.startWeight + j];
|
||||||
|
const NzMD5MeshParser::Joint& joint = joints[weight.joint];
|
||||||
|
|
||||||
|
finalPos += (joint.bindPos + joint.bindOrient*weight.pos) * weight.bias;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On retourne le modèle dans le bon sens
|
||||||
|
vertex->position = scale * (rotationQuat * finalPos);
|
||||||
|
vertex->uv.Set(md5Vertex.uv.x, 1.f - md5Vertex.uv.y);
|
||||||
|
vertex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertexMapper.Unmap();
|
||||||
|
|
||||||
|
// Submesh
|
||||||
|
std::unique_ptr<NzStaticMesh> subMesh(new NzStaticMesh(mesh));
|
||||||
|
subMesh->Create(vertexBuffer.get());
|
||||||
|
|
||||||
|
vertexBuffer->SetPersistent(false);
|
||||||
|
vertexBuffer.release();
|
||||||
|
|
||||||
|
if (parameters.optimizeIndexBuffers)
|
||||||
|
indexBuffer->Optimize();
|
||||||
|
|
||||||
|
subMesh->SetIndexBuffer(indexBuffer.get());
|
||||||
|
indexBuffer.release();
|
||||||
|
|
||||||
|
// Material
|
||||||
|
mesh->SetMaterial(i, baseDir + md5Mesh.shader);
|
||||||
|
|
||||||
|
subMesh->GenerateAABB();
|
||||||
|
subMesh->GenerateNormalsAndTangents();
|
||||||
|
subMesh->SetMaterialIndex(i);
|
||||||
|
|
||||||
|
if (parameters.center)
|
||||||
|
subMesh->Center();
|
||||||
|
|
||||||
|
mesh->AddSubMesh(subMesh.get());
|
||||||
|
subMesh.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,8 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <Nazara/Utility/Debug.hpp>
|
#include <Nazara/Utility/Debug.hpp>
|
||||||
|
|
||||||
NzMD5MeshParser::NzMD5MeshParser(NzInputStream& stream, const NzMeshParams& parameters) :
|
NzMD5MeshParser::NzMD5MeshParser(NzInputStream& stream) :
|
||||||
m_stream(stream),
|
m_stream(stream),
|
||||||
m_parameters(parameters),
|
|
||||||
m_keepLastLine(false),
|
m_keepLastLine(false),
|
||||||
m_lineCount(0),
|
m_lineCount(0),
|
||||||
m_meshIndex(0),
|
m_meshIndex(0),
|
||||||
|
|
@ -52,7 +51,27 @@ nzTernary NzMD5MeshParser::Check()
|
||||||
return nzTernary_False;
|
return nzTernary_False;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NzMD5MeshParser::Parse(NzMesh* mesh)
|
const NzMD5MeshParser::Joint* NzMD5MeshParser::GetJoints() const
|
||||||
|
{
|
||||||
|
return m_joints.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int NzMD5MeshParser::GetJointCount() const
|
||||||
|
{
|
||||||
|
return m_joints.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
const NzMD5MeshParser::Mesh* NzMD5MeshParser::GetMeshes() const
|
||||||
|
{
|
||||||
|
return m_meshes.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int NzMD5MeshParser::GetMeshCount() const
|
||||||
|
{
|
||||||
|
return m_meshes.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NzMD5MeshParser::Parse()
|
||||||
{
|
{
|
||||||
while (Advance(false))
|
while (Advance(false))
|
||||||
{
|
{
|
||||||
|
|
@ -151,271 +170,6 @@ bool NzMD5MeshParser::Parse(NzMesh* mesh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pour que le squelette soit correctement aligné, il faut appliquer un quaternion "de correction" aux joints à la base du squelette
|
|
||||||
NzQuaternionf rotationQuat = NzEulerAnglesf(-90.f, 180.f, 0.f);
|
|
||||||
NzString baseDir = m_stream.GetDirectory();
|
|
||||||
|
|
||||||
// Le hellknight de Doom 3 fait ~120 unités, et il est dit qu'il fait trois mètres
|
|
||||||
// Nous réduisons donc la taille générale des fichiers MD5 de 1/40
|
|
||||||
NzVector3f scale(m_parameters.scale/40.f);
|
|
||||||
|
|
||||||
if (m_parameters.animated)
|
|
||||||
{
|
|
||||||
if (!mesh->CreateSkeletal(m_joints.size())) // Ne devrait jamais échouer
|
|
||||||
{
|
|
||||||
NazaraInternalError("Failed to create mesh");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
NzSkeleton* skeleton = mesh->GetSkeleton();
|
|
||||||
for (unsigned int i = 0; i < m_joints.size(); ++i)
|
|
||||||
{
|
|
||||||
NzJoint* joint = skeleton->GetJoint(i);
|
|
||||||
|
|
||||||
int parent = m_joints[i].parent;
|
|
||||||
if (parent >= 0)
|
|
||||||
joint->SetParent(skeleton->GetJoint(parent));
|
|
||||||
|
|
||||||
joint->SetName(m_joints[i].name);
|
|
||||||
|
|
||||||
NzMatrix4f bindMatrix;
|
|
||||||
|
|
||||||
if (parent >= 0)
|
|
||||||
bindMatrix.MakeTransform(m_joints[i].bindPos, m_joints[i].bindOrient);
|
|
||||||
else
|
|
||||||
bindMatrix.MakeTransform(rotationQuat * m_joints[i].bindPos, rotationQuat * m_joints[i].bindOrient);
|
|
||||||
|
|
||||||
joint->SetInverseBindMatrix(bindMatrix.InverseAffine());
|
|
||||||
}
|
|
||||||
|
|
||||||
mesh->SetMaterialCount(m_meshes.size());
|
|
||||||
for (unsigned int i = 0; i < m_meshes.size(); ++i)
|
|
||||||
{
|
|
||||||
const Mesh& md5Mesh = m_meshes[i];
|
|
||||||
|
|
||||||
unsigned int indexCount = md5Mesh.triangles.size()*3;
|
|
||||||
unsigned int vertexCount = md5Mesh.vertices.size();
|
|
||||||
|
|
||||||
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;
|
|
||||||
for (const Mesh::Triangle& triangle : md5Mesh.triangles)
|
|
||||||
{
|
|
||||||
// On les respécifie dans le bon ordre
|
|
||||||
indexMapper.Set(index++, triangle.x);
|
|
||||||
indexMapper.Set(index++, triangle.z);
|
|
||||||
indexMapper.Set(index++, triangle.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
indexMapper.Unmap();
|
|
||||||
|
|
||||||
// Vertex buffer
|
|
||||||
struct Weight
|
|
||||||
{
|
|
||||||
float bias;
|
|
||||||
unsigned int jointIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Weight> tempWeights(NAZARA_UTILITY_SKINNING_MAX_WEIGHTS);
|
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
// 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};
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
// Animation
|
|
||||||
// Il est peut-être éventuellement possible que la probabilité que l'animation ait le même nom soit non-nulle.
|
|
||||||
NzString path = m_stream.GetPath();
|
|
||||||
if (!path.IsEmpty())
|
|
||||||
{
|
|
||||||
path.Replace(".md5mesh", ".md5anim", -8, NzString::CaseInsensitive);
|
|
||||||
if (NzFile::Exists(path))
|
|
||||||
mesh->SetAnimation(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!mesh->CreateStatic()) // Ne devrait jamais échouer
|
|
||||||
{
|
|
||||||
NazaraInternalError("Failed to create mesh");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mesh->SetMaterialCount(m_meshes.size());
|
|
||||||
for (unsigned int i = 0; i < m_meshes.size(); ++i)
|
|
||||||
{
|
|
||||||
const Mesh& md5Mesh = m_meshes[i];
|
|
||||||
unsigned int indexCount = md5Mesh.triangles.size()*3;
|
|
||||||
unsigned int vertexCount = md5Mesh.vertices.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);
|
|
||||||
|
|
||||||
NzIndexMapper indexMapper(indexBuffer.get(), nzBufferAccess_DiscardAndWrite);
|
|
||||||
NzIndexIterator index = indexMapper.begin();
|
|
||||||
|
|
||||||
for (const Mesh::Triangle& triangle : md5Mesh.triangles)
|
|
||||||
{
|
|
||||||
// On les respécifie dans le bon ordre
|
|
||||||
*index++ = triangle.x;
|
|
||||||
*index++ = triangle.z;
|
|
||||||
*index++ = triangle.y;
|
|
||||||
}
|
|
||||||
indexMapper.Unmap();
|
|
||||||
|
|
||||||
// Vertex buffer
|
|
||||||
std::unique_ptr<NzVertexBuffer> vertexBuffer(new NzVertexBuffer(NzVertexDeclaration::Get(nzVertexLayout_XYZ_Normal_UV_Tangent), vertexCount, m_parameters.storage));
|
|
||||||
NzBufferMapper<NzVertexBuffer> vertexMapper(vertexBuffer.get(), nzBufferAccess_WriteOnly);
|
|
||||||
|
|
||||||
NzMeshVertex* vertex = reinterpret_cast<NzMeshVertex*>(vertexMapper.GetPointer());
|
|
||||||
for (const Mesh::Vertex& md5Vertex : md5Mesh.vertices)
|
|
||||||
{
|
|
||||||
// Skinning MD5 (Formule d'Id Tech)
|
|
||||||
NzVector3f finalPos(NzVector3f::Zero());
|
|
||||||
for (unsigned int j = 0; j < md5Vertex.weightCount; ++j)
|
|
||||||
{
|
|
||||||
const Mesh::Weight& weight = md5Mesh.weights[md5Vertex.startWeight + j];
|
|
||||||
const Joint& joint = m_joints[weight.joint];
|
|
||||||
|
|
||||||
finalPos += (joint.bindPos + joint.bindOrient*weight.pos) * weight.bias;
|
|
||||||
}
|
|
||||||
|
|
||||||
// On retourne le modèle dans le bon sens
|
|
||||||
vertex->position = scale * (rotationQuat * finalPos);
|
|
||||||
vertex->uv.Set(md5Vertex.uv.x, 1.f - md5Vertex.uv.y);
|
|
||||||
vertex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
vertexMapper.Unmap();
|
|
||||||
|
|
||||||
// Submesh
|
|
||||||
std::unique_ptr<NzStaticMesh> subMesh(new NzStaticMesh(mesh));
|
|
||||||
if (!subMesh->Create(vertexBuffer.get()))
|
|
||||||
{
|
|
||||||
NazaraError("Failed to create static submesh");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
vertexBuffer->SetPersistent(false);
|
|
||||||
vertexBuffer.release();
|
|
||||||
|
|
||||||
if (m_parameters.optimizeIndexBuffers)
|
|
||||||
indexBuffer->Optimize();
|
|
||||||
|
|
||||||
subMesh->SetIndexBuffer(indexBuffer.get());
|
|
||||||
indexBuffer.release();
|
|
||||||
|
|
||||||
// Material
|
|
||||||
mesh->SetMaterial(i, baseDir + md5Mesh.shader);
|
|
||||||
|
|
||||||
subMesh->GenerateAABB();
|
|
||||||
subMesh->GenerateNormalsAndTangents();
|
|
||||||
subMesh->SetMaterialIndex(i);
|
|
||||||
|
|
||||||
if (m_parameters.center)
|
|
||||||
subMesh->Center();
|
|
||||||
|
|
||||||
mesh->AddSubMesh(subMesh.get());
|
|
||||||
subMesh.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -555,7 +309,7 @@ bool NzMD5MeshParser::ParseMesh()
|
||||||
if (!Advance())
|
if (!Advance())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Mesh::Triangle& triangle = m_meshes[m_meshIndex].triangles[i];
|
Triangle& triangle = m_meshes[m_meshIndex].triangles[i];
|
||||||
unsigned int index;
|
unsigned int index;
|
||||||
if (std::sscanf(&m_currentLine[0], "tri %u %u %u %u", &index, &triangle.x, &triangle.y, &triangle.z) != 4)
|
if (std::sscanf(&m_currentLine[0], "tri %u %u %u %u", &index, &triangle.x, &triangle.y, &triangle.z) != 4)
|
||||||
{
|
{
|
||||||
|
|
@ -578,7 +332,7 @@ bool NzMD5MeshParser::ParseMesh()
|
||||||
if (!Advance())
|
if (!Advance())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Mesh::Vertex& vertex = m_meshes[m_meshIndex].vertices[i];
|
Vertex& vertex = m_meshes[m_meshIndex].vertices[i];
|
||||||
unsigned int index;
|
unsigned int index;
|
||||||
if (std::sscanf(&m_currentLine[0], "vert %u ( %f %f ) %u %u", &index, &vertex.uv.x, &vertex.uv.y, &vertex.startWeight, &vertex.weightCount) != 5)
|
if (std::sscanf(&m_currentLine[0], "vert %u ( %f %f ) %u %u", &index, &vertex.uv.x, &vertex.uv.y, &vertex.startWeight, &vertex.weightCount) != 5)
|
||||||
{
|
{
|
||||||
|
|
@ -601,7 +355,7 @@ bool NzMD5MeshParser::ParseMesh()
|
||||||
if (!Advance())
|
if (!Advance())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Mesh::Weight& weight = m_meshes[m_meshIndex].weights[i];
|
Weight& weight = m_meshes[m_meshIndex].weights[i];
|
||||||
unsigned int index;
|
unsigned int index;
|
||||||
if (std::sscanf(&m_currentLine[0], "weight %u %u %f ( %f %f %f )", &index, &weight.joint, &weight.bias,
|
if (std::sscanf(&m_currentLine[0], "weight %u %u %f ( %f %f %f )", &index, &weight.joint, &weight.bias,
|
||||||
&weight.pos.x, &weight.pos.y, &weight.pos.z) != 6)
|
&weight.pos.x, &weight.pos.y, &weight.pos.z) != 6)
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,6 @@
|
||||||
class NzMD5MeshParser
|
class NzMD5MeshParser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NzMD5MeshParser(NzInputStream& stream, const NzMeshParams& parameters);
|
|
||||||
~NzMD5MeshParser();
|
|
||||||
|
|
||||||
nzTernary Check();
|
|
||||||
bool Parse(NzMesh* mesh);
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct Joint
|
struct Joint
|
||||||
{
|
{
|
||||||
NzQuaternionf bindOrient;
|
NzQuaternionf bindOrient;
|
||||||
|
|
@ -33,30 +26,43 @@ class NzMD5MeshParser
|
||||||
int parent;
|
int parent;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef NzVector3ui Triangle;
|
||||||
|
|
||||||
|
struct Vertex
|
||||||
|
{
|
||||||
|
NzVector2f uv;
|
||||||
|
unsigned int startWeight;
|
||||||
|
unsigned int weightCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Weight
|
||||||
|
{
|
||||||
|
NzVector3f pos;
|
||||||
|
float bias;
|
||||||
|
unsigned int joint;
|
||||||
|
};
|
||||||
|
|
||||||
struct Mesh
|
struct Mesh
|
||||||
{
|
{
|
||||||
typedef NzVector3ui Triangle;
|
|
||||||
|
|
||||||
struct Vertex
|
|
||||||
{
|
|
||||||
NzVector2f uv;
|
|
||||||
unsigned int startWeight;
|
|
||||||
unsigned int weightCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Weight
|
|
||||||
{
|
|
||||||
NzVector3f pos;
|
|
||||||
float bias;
|
|
||||||
unsigned int joint;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Triangle> triangles;
|
std::vector<Triangle> triangles;
|
||||||
std::vector<Vertex> vertices;
|
std::vector<Vertex> vertices;
|
||||||
std::vector<Weight> weights;
|
std::vector<Weight> weights;
|
||||||
NzString shader;
|
NzString shader;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NzMD5MeshParser(NzInputStream& stream);
|
||||||
|
~NzMD5MeshParser();
|
||||||
|
|
||||||
|
nzTernary Check();
|
||||||
|
|
||||||
|
const Joint* GetJoints() const;
|
||||||
|
unsigned int GetJointCount() const;
|
||||||
|
const Mesh* GetMeshes() const;
|
||||||
|
unsigned int GetMeshCount() const;
|
||||||
|
|
||||||
|
bool Parse();
|
||||||
|
|
||||||
|
private:
|
||||||
bool Advance(bool required = true);
|
bool Advance(bool required = true);
|
||||||
void Error(const NzString& message);
|
void Error(const NzString& message);
|
||||||
bool ParseJoints();
|
bool ParseJoints();
|
||||||
|
|
@ -68,7 +74,6 @@ class NzMD5MeshParser
|
||||||
std::vector<Mesh> m_meshes;
|
std::vector<Mesh> m_meshes;
|
||||||
NzInputStream& m_stream;
|
NzInputStream& m_stream;
|
||||||
NzString m_currentLine;
|
NzString m_currentLine;
|
||||||
const NzMeshParams& m_parameters;
|
|
||||||
bool m_keepLastLine;
|
bool m_keepLastLine;
|
||||||
unsigned int m_lineCount;
|
unsigned int m_lineCount;
|
||||||
unsigned int m_meshIndex;
|
unsigned int m_meshIndex;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue