General
- added Conjugate() and Rotate() to aiQuaternion - SkeletonMeshBuilder can now start hierarchy construction at a given node MD5 - MD5MESH and MD5 anim now working. - MD5ANIM files can be loaded without corresponding MD5MESHES - Config option to prevent automatic loading of MD5ANIMs - WIP version of MD5CAMERA support. - added test files. No anim test file yet. BHV - fixing formatting LimitBoneWeights - prints now statistics to the logging system Viewer - does now specify the LBW post-processing step. git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@359 67173fc5-114c-0410-ac8e-9d2fd5bffc1fpull/1/head
parent
04d0a859a5
commit
7080ba231c
|
@ -431,16 +431,16 @@ void BVHLoader::CreateAnimation( aiScene* pScene)
|
||||||
poskey->mTime = double( fr);
|
poskey->mTime = double( fr);
|
||||||
|
|
||||||
// Now compute all translations in the right order
|
// Now compute all translations in the right order
|
||||||
for( unsigned int channel = 0; channel < 3; ++channel)
|
for( unsigned int channel = 0; channel < 3; ++channel)
|
||||||
{
|
{
|
||||||
switch( node.mChannels[channel])
|
switch( node.mChannels[channel])
|
||||||
{
|
{
|
||||||
case Channel_PositionX: poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
|
case Channel_PositionX: poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
|
||||||
case Channel_PositionY: poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
|
case Channel_PositionY: poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
|
||||||
case Channel_PositionZ: poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
|
case Channel_PositionZ: poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
|
||||||
default: throw new ImportErrorException( "Unexpected animation channel setup at node " + nodeName );
|
default: throw new ImportErrorException( "Unexpected animation channel setup at node " + nodeName );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++poskey;
|
++poskey;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
|
@ -468,23 +468,23 @@ void BVHLoader::CreateAnimation( aiScene* pScene)
|
||||||
aiQuatKey* rotkey = nodeAnim->mRotationKeys;
|
aiQuatKey* rotkey = nodeAnim->mRotationKeys;
|
||||||
for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr)
|
for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr)
|
||||||
{
|
{
|
||||||
aiMatrix4x4 temp;
|
aiMatrix4x4 temp;
|
||||||
aiMatrix3x3 rotMatrix;
|
aiMatrix3x3 rotMatrix;
|
||||||
|
|
||||||
for( unsigned int channel = 0; channel < 3; ++channel)
|
for( unsigned int channel = 0; channel < 3; ++channel)
|
||||||
{
|
{
|
||||||
// translate ZXY euler angels into a quaternion
|
// translate ZXY euler angels into a quaternion
|
||||||
const float angle = node.mChannelValues[fr * node.mChannels.size() + rotOffset + channel] * float( AI_MATH_PI) / 180.0f;
|
const float angle = node.mChannelValues[fr * node.mChannels.size() + rotOffset + channel] * float( AI_MATH_PI) / 180.0f;
|
||||||
|
|
||||||
// Compute rotation transformations in the right order
|
// Compute rotation transformations in the right order
|
||||||
switch (node.mChannels[rotOffset+channel])
|
switch (node.mChannels[rotOffset+channel])
|
||||||
{
|
{
|
||||||
case Channel_RotationX: aiMatrix4x4::RotationX( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
|
case Channel_RotationX: aiMatrix4x4::RotationX( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
|
||||||
case Channel_RotationY: aiMatrix4x4::RotationY( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
|
case Channel_RotationY: aiMatrix4x4::RotationY( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
|
||||||
case Channel_RotationZ: aiMatrix4x4::RotationZ( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
|
case Channel_RotationZ: aiMatrix4x4::RotationZ( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
|
||||||
default: throw new ImportErrorException( "Unexpected animation channel setup at node " + nodeName );
|
default: throw new ImportErrorException( "Unexpected animation channel setup at node " + nodeName );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rotkey->mTime = double( fr);
|
rotkey->mTime = double( fr);
|
||||||
rotkey->mValue = aiQuaternion( rotMatrix);
|
rotkey->mValue = aiQuaternion( rotMatrix);
|
||||||
|
|
|
@ -72,8 +72,11 @@ bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const
|
||||||
// Executes the post processing step on the given imported data.
|
// Executes the post processing step on the given imported data.
|
||||||
void LimitBoneWeightsProcess::Execute( aiScene* pScene)
|
void LimitBoneWeightsProcess::Execute( aiScene* pScene)
|
||||||
{
|
{
|
||||||
|
DefaultLogger::get()->debug("LimitBoneWeightsProcess begin");
|
||||||
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
|
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
|
||||||
ProcessMesh( pScene->mMeshes[a]);
|
ProcessMesh( pScene->mMeshes[a]);
|
||||||
|
|
||||||
|
DefaultLogger::get()->debug("LimitBoneWeightsProcess end");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -106,6 +109,8 @@ void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int removed = 0, old_bones = pMesh->mNumBones;
|
||||||
|
|
||||||
// now cut the weight count if it exceeds the maximum
|
// now cut the weight count if it exceeds the maximum
|
||||||
bool bChanged = false;
|
bool bChanged = false;
|
||||||
for( WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit)
|
for( WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit)
|
||||||
|
@ -120,7 +125,9 @@ void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh)
|
||||||
std::sort( vit->begin(), vit->end());
|
std::sort( vit->begin(), vit->end());
|
||||||
|
|
||||||
// now kill everything beyond the maximum count
|
// now kill everything beyond the maximum count
|
||||||
|
unsigned int m = vit->size();
|
||||||
vit->erase( vit->begin() + mMaxWeights, vit->end());
|
vit->erase( vit->begin() + mMaxWeights, vit->end());
|
||||||
|
removed += m-vit->size();
|
||||||
|
|
||||||
// and renormalize the weights
|
// and renormalize the weights
|
||||||
float sum = 0.0f;
|
float sum = 0.0f;
|
||||||
|
@ -130,9 +137,7 @@ void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh)
|
||||||
it->mWeight /= sum;
|
it->mWeight /= sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bChanged)
|
if (bChanged) {
|
||||||
{
|
|
||||||
|
|
||||||
// rebuild the vertex weight array for all bones
|
// rebuild the vertex weight array for all bones
|
||||||
typedef std::vector< std::vector< aiVertexWeight > > WeightsPerBone;
|
typedef std::vector< std::vector< aiVertexWeight > > WeightsPerBone;
|
||||||
WeightsPerBone boneWeights( pMesh->mNumBones);
|
WeightsPerBone boneWeights( pMesh->mNumBones);
|
||||||
|
@ -183,12 +188,8 @@ void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh)
|
||||||
aiBone** ppcCur = pMesh->mBones;
|
aiBone** ppcCur = pMesh->mBones;
|
||||||
aiBone** ppcSrc = ppcCur;
|
aiBone** ppcSrc = ppcCur;
|
||||||
|
|
||||||
for (std::vector<bool>::const_iterator
|
for (std::vector<bool>::const_iterator iter = abNoNeed.begin();iter != abNoNeed.end() ;++iter) {
|
||||||
iter = abNoNeed.begin();
|
if (*iter) {
|
||||||
iter != abNoNeed.end() ;++iter)
|
|
||||||
{
|
|
||||||
if (*iter)
|
|
||||||
{
|
|
||||||
delete *ppcSrc;
|
delete *ppcSrc;
|
||||||
--pMesh->mNumBones;
|
--pMesh->mNumBones;
|
||||||
}
|
}
|
||||||
|
@ -196,5 +197,11 @@ void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh)
|
||||||
++ppcSrc;
|
++ppcSrc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!DefaultLogger::isNullLogger()) {
|
||||||
|
char buffer[1024];
|
||||||
|
::sprintf(buffer,"Removed %i weights. Input bones: %i. Output bones: %i",removed,old_bones,pMesh->mNumBones);
|
||||||
|
DefaultLogger::get()->info(buffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,12 +52,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "MD5Loader.h"
|
#include "MD5Loader.h"
|
||||||
#include "StringComparison.h"
|
#include "StringComparison.h"
|
||||||
#include "fast_atof.h"
|
#include "fast_atof.h"
|
||||||
|
#include "SkeletonMeshBuilder.h"
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
|
|
||||||
|
// Minimum weight value. Weights inside [-n ... n] are ignored
|
||||||
|
#define AI_MD5_WEIGHT_EPSILON 1e-5f
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Constructor to be privately used by Importer
|
// Constructor to be privately used by Importer
|
||||||
MD5Importer::MD5Importer()
|
MD5Importer::MD5Importer()
|
||||||
|
: configNoAutoLoad (false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -71,7 +76,7 @@ bool MD5Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool
|
||||||
{
|
{
|
||||||
const std::string extension = GetExtension(pFile);
|
const std::string extension = GetExtension(pFile);
|
||||||
|
|
||||||
if (extension == "md5anim" || extension == "md5mesh")
|
if (extension == "md5anim" || extension == "md5mesh" || extension == "md5camera")
|
||||||
return true;
|
return true;
|
||||||
else if (!extension.length() || checkSig) {
|
else if (!extension.length() || checkSig) {
|
||||||
if (!pIOHandler)
|
if (!pIOHandler)
|
||||||
|
@ -86,72 +91,106 @@ bool MD5Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool
|
||||||
// Get list of all supported extensions
|
// Get list of all supported extensions
|
||||||
void MD5Importer::GetExtensionList(std::string& append)
|
void MD5Importer::GetExtensionList(std::string& append)
|
||||||
{
|
{
|
||||||
append.append("*.md5mesh;*.md5anim");
|
append.append("*.md5mesh;*.md5anim;*.md5camera");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Setup import properties
|
||||||
|
void MD5Importer::SetupProperties(const Importer* pImp)
|
||||||
|
{
|
||||||
|
// AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD
|
||||||
|
configNoAutoLoad = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD,0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// Imports the given file into the given scene structure.
|
// Imports the given file into the given scene structure.
|
||||||
void MD5Importer::InternReadFile(
|
void MD5Importer::InternReadFile( const std::string& pFile,
|
||||||
const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
|
aiScene* _pScene, IOSystem* _pIOHandler)
|
||||||
{
|
{
|
||||||
|
pIOHandler = _pIOHandler;
|
||||||
|
pScene = _pScene;
|
||||||
|
bHadMD5Mesh = bHadMD5Anim = bHadMD5Camera = false;
|
||||||
|
|
||||||
// remove the file extension
|
// remove the file extension
|
||||||
std::string::size_type pos = pFile.find_last_of('.');
|
std::string::size_type pos = pFile.find_last_of('.');
|
||||||
mFile = pFile.substr(0,pos+1);
|
mFile = (std::string::npos == pos ? pFile : pFile.substr(0,pos+1));
|
||||||
this->pIOHandler = pIOHandler;
|
|
||||||
this->pScene = pScene;
|
|
||||||
|
|
||||||
bHadMD5Mesh = bHadMD5Anim = false;
|
const std::string extension = GetExtension(pFile);
|
||||||
|
try {
|
||||||
|
if (extension == "md5camera") {
|
||||||
|
LoadMD5CameraFile();
|
||||||
|
}
|
||||||
|
else if (configNoAutoLoad || extension == "md5anim") {
|
||||||
|
// determine file extension and process just *one* file
|
||||||
|
if (extension.length() == 0) {
|
||||||
|
/* fixme */
|
||||||
|
}
|
||||||
|
if (extension == "md5anim") {
|
||||||
|
LoadMD5AnimFile();
|
||||||
|
}
|
||||||
|
else if (extension == "md5mesh") {
|
||||||
|
LoadMD5MeshFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LoadMD5MeshFile();
|
||||||
|
LoadMD5AnimFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch ( ImportErrorException* ex) {
|
||||||
|
UnloadFileFromMemory();
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
// load the animation keyframes
|
// make sure we have at least one file
|
||||||
LoadMD5AnimFile();
|
|
||||||
|
|
||||||
// load the mesh vertices and bones
|
|
||||||
LoadMD5MeshFile();
|
|
||||||
|
|
||||||
// make sure we return no incomplete data
|
|
||||||
if (!bHadMD5Mesh && !bHadMD5Anim)
|
if (!bHadMD5Mesh && !bHadMD5Anim)
|
||||||
throw new ImportErrorException("Failed to read valid data from this MD5");
|
throw new ImportErrorException("Failed to read valid contents from this MD5 data set");
|
||||||
|
|
||||||
if (!bHadMD5Mesh)pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
|
// the output scene wouldn't pass the validation without this flag
|
||||||
|
if (!bHadMD5Mesh)
|
||||||
|
pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Load a file into a memory buffer
|
||||||
void MD5Importer::LoadFileIntoMemory (IOStream* file)
|
void MD5Importer::LoadFileIntoMemory (IOStream* file)
|
||||||
{
|
{
|
||||||
ai_assert(NULL != file);
|
ai_assert(NULL != file);
|
||||||
|
|
||||||
this->fileSize = (unsigned int)file->FileSize();
|
fileSize = (unsigned int)file->FileSize();
|
||||||
|
|
||||||
// allocate storage and copy the contents of the file to a memory buffer
|
// allocate storage and copy the contents of the file to a memory buffer
|
||||||
this->pScene = pScene;
|
pScene = pScene;
|
||||||
this->mBuffer = new char[this->fileSize+1];
|
mBuffer = new char[fileSize+1];
|
||||||
file->Read( (void*)mBuffer, 1, this->fileSize);
|
file->Read( (void*)mBuffer, 1, fileSize);
|
||||||
this->iLineNumber = 1;
|
iLineNumber = 1;
|
||||||
|
|
||||||
// append a terminal 0
|
// append a terminal 0
|
||||||
this->mBuffer[this->fileSize] = '\0';
|
mBuffer[fileSize] = '\0';
|
||||||
|
|
||||||
// now remove all line comments from the file
|
// now remove all line comments from the file
|
||||||
CommentRemover::RemoveLineComments("//",this->mBuffer,' ');
|
CommentRemover::RemoveLineComments("//",mBuffer,' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Unload the current memory buffer
|
||||||
void MD5Importer::UnloadFileFromMemory ()
|
void MD5Importer::UnloadFileFromMemory ()
|
||||||
{
|
{
|
||||||
// delete the file buffer
|
// delete the file buffer
|
||||||
delete[] this->mBuffer;
|
delete[] mBuffer;
|
||||||
this->mBuffer = NULL;
|
mBuffer = NULL;
|
||||||
this->fileSize = 0;
|
fileSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void MakeDataUnique (MD5::MeshDesc& meshSrc)
|
// Build unique vertices
|
||||||
|
void MD5Importer::MakeDataUnique (MD5::MeshDesc& meshSrc)
|
||||||
{
|
{
|
||||||
std::vector<bool> abHad(meshSrc.mVertices.size(),false);
|
std::vector<bool> abHad(meshSrc.mVertices.size(),false);
|
||||||
|
|
||||||
// allocate enough storage to keep the output structures
|
// allocate enough storage to keep the output structures
|
||||||
const unsigned int iNewNum = (unsigned int)meshSrc.mFaces.size()*3;
|
const unsigned int iNewNum = meshSrc.mFaces.size()*3;
|
||||||
unsigned int iNewIndex = (unsigned int)meshSrc.mVertices.size();
|
unsigned int iNewIndex = meshSrc.mVertices.size();
|
||||||
meshSrc.mVertices.resize(iNewNum);
|
meshSrc.mVertices.resize(iNewNum);
|
||||||
|
|
||||||
// try to guess how much storage we'll need for new weights
|
// try to guess how much storage we'll need for new weights
|
||||||
|
@ -165,26 +204,10 @@ void MakeDataUnique (MD5::MeshDesc& meshSrc)
|
||||||
const aiFace& face = *iter;
|
const aiFace& face = *iter;
|
||||||
for (unsigned int i = 0; i < 3;++i)
|
for (unsigned int i = 0; i < 3;++i)
|
||||||
{
|
{
|
||||||
if (abHad[face.mIndices[i]])
|
if (abHad[face.mIndices[i]]) {
|
||||||
{
|
|
||||||
// generate a new vertex
|
// generate a new vertex
|
||||||
meshSrc.mVertices[iNewIndex] = meshSrc.mVertices[face.mIndices[i]];
|
meshSrc.mVertices[iNewIndex] = meshSrc.mVertices[face.mIndices[i]];
|
||||||
face.mIndices[i] = iNewIndex++;
|
face.mIndices[i] = iNewIndex++;
|
||||||
|
|
||||||
// FIX: removed this ...
|
|
||||||
#if 0
|
|
||||||
// the algorithm in MD5Importer::LoadMD5MeshFile() doesn't work if
|
|
||||||
// a weight is referenced by more than one vertex. This shouldn't
|
|
||||||
// occur in MD5 files, but we must take care that we generate new
|
|
||||||
// weights now, too.
|
|
||||||
|
|
||||||
vertNew.mFirstWeight = (unsigned int)meshSrc.mWeights.size();
|
|
||||||
meshSrc.mWeights.resize(vertNew.mFirstWeight+vertNew.mNumWeights);
|
|
||||||
for (unsigned int q = 0; q < vertNew.mNumWeights;++q)
|
|
||||||
{
|
|
||||||
meshSrc.mWeights[vertNew.mFirstWeight+q] = meshSrc.mWeights[vertOld.mFirstWeight+q];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
else abHad[face.mIndices[i]] = true;
|
else abHad[face.mIndices[i]] = true;
|
||||||
}
|
}
|
||||||
|
@ -192,77 +215,109 @@ void MakeDataUnique (MD5::MeshDesc& meshSrc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
void AttachChilds(int iParentID,aiNode* piParent,BoneList& bones)
|
// Recursive node graph construction from a MD5MESH
|
||||||
|
void MD5Importer::AttachChilds_Mesh(int iParentID,aiNode* piParent, BoneList& bones)
|
||||||
{
|
{
|
||||||
ai_assert(NULL != piParent && !piParent->mNumChildren);
|
ai_assert(NULL != piParent && !piParent->mNumChildren);
|
||||||
for (int i = 0; i < (int)bones.size();++i)
|
|
||||||
{
|
// First find out how many children we'll have
|
||||||
// (avoid infinite recursion)
|
for (int i = 0; i < (int)bones.size();++i) {
|
||||||
if (iParentID != i && bones[i].mParentIndex == iParentID)
|
if (iParentID != i && bones[i].mParentIndex == iParentID) {
|
||||||
{
|
|
||||||
// have it ...
|
|
||||||
++piParent->mNumChildren;
|
++piParent->mNumChildren;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (piParent->mNumChildren)
|
if (piParent->mNumChildren) {
|
||||||
{
|
|
||||||
piParent->mChildren = new aiNode*[piParent->mNumChildren];
|
piParent->mChildren = new aiNode*[piParent->mNumChildren];
|
||||||
for (int i = 0; i < (int)bones.size();++i)
|
for (int i = 0; i < (int)bones.size();++i) {
|
||||||
{
|
|
||||||
// (avoid infinite recursion)
|
// (avoid infinite recursion)
|
||||||
if (iParentID != i && bones[i].mParentIndex == iParentID)
|
if (iParentID != i && bones[i].mParentIndex == iParentID) {
|
||||||
{
|
|
||||||
aiNode* pc;
|
aiNode* pc;
|
||||||
|
// setup a new node
|
||||||
*piParent->mChildren++ = pc = new aiNode();
|
*piParent->mChildren++ = pc = new aiNode();
|
||||||
pc->mName = aiString(bones[i].mName);
|
pc->mName = aiString(bones[i].mName);
|
||||||
pc->mParent = piParent;
|
pc->mParent = piParent;
|
||||||
|
|
||||||
// get the transformation matrix from rotation and translational components
|
// get the transformation matrix from rotation and translational components
|
||||||
aiQuaternion quat = aiQuaternion ( bones[i].mRotationQuat );
|
aiQuaternion quat;
|
||||||
//quat.w *= -1.0f; // DX to OGL
|
MD5::ConvertQuaternion ( bones[i].mRotationQuat, quat );
|
||||||
pc->mTransformation = aiMatrix4x4 ( quat.GetMatrix());
|
|
||||||
aiMatrix4x4 mTranslate;
|
bones[i].mTransform = aiMatrix4x4 ( quat.GetMatrix());
|
||||||
mTranslate.a4 = bones[i].mPositionXYZ.x;
|
bones[i].mTransform.a4 = bones[i].mPositionXYZ.x;
|
||||||
mTranslate.b4 = bones[i].mPositionXYZ.y;
|
bones[i].mTransform.b4 = bones[i].mPositionXYZ.y;
|
||||||
mTranslate.c4 = bones[i].mPositionXYZ.z;
|
bones[i].mTransform.c4 = bones[i].mPositionXYZ.z;
|
||||||
pc->mTransformation = pc->mTransformation*mTranslate;
|
|
||||||
|
|
||||||
// store it for later use
|
// store it for later use
|
||||||
bones[i].mTransform = bones[i].mInvTransform = pc->mTransformation;
|
pc->mTransformation = bones[i].mInvTransform = bones[i].mTransform;
|
||||||
bones[i].mInvTransform.Inverse();
|
bones[i].mInvTransform.Inverse();
|
||||||
|
|
||||||
// the transformations for each bone are absolute,
|
// the transformations for each bone are absolute, so we need to multiply them
|
||||||
// so we need to multiply them with the inverse
|
// with the inverse of the absolute matrix of the parent joint
|
||||||
// of the absolut matrix of the parent
|
if (-1 != iParentID) {
|
||||||
if (-1 != iParentID)
|
pc->mTransformation = bones[iParentID].mInvTransform * pc->mTransformation;
|
||||||
{
|
|
||||||
pc->mTransformation = bones[iParentID].mInvTransform*pc->mTransformation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add children to this node, too
|
// add children to this node, too
|
||||||
AttachChilds( i, pc, bones);
|
AttachChilds_Mesh( i, pc, bones);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// undo our nice shift
|
// undo offset computations
|
||||||
piParent->mChildren -= piParent->mNumChildren;
|
piParent->mChildren -= piParent->mNumChildren;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Recursive node graph construction from a MD5ANIM
|
||||||
|
void MD5Importer::AttachChilds_Anim(int iParentID,aiNode* piParent, AnimBoneList& bones,const aiNodeAnim** node_anims)
|
||||||
|
{
|
||||||
|
ai_assert(NULL != piParent && !piParent->mNumChildren);
|
||||||
|
|
||||||
|
// First find out how many children we'll have
|
||||||
|
for (int i = 0; i < (int)bones.size();++i) {
|
||||||
|
if (iParentID != i && bones[i].mParentIndex == iParentID) {
|
||||||
|
++piParent->mNumChildren;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (piParent->mNumChildren) {
|
||||||
|
piParent->mChildren = new aiNode*[piParent->mNumChildren];
|
||||||
|
for (int i = 0; i < (int)bones.size();++i) {
|
||||||
|
// (avoid infinite recursion)
|
||||||
|
if (iParentID != i && bones[i].mParentIndex == iParentID)
|
||||||
|
{
|
||||||
|
aiNode* pc;
|
||||||
|
// setup a new node
|
||||||
|
*piParent->mChildren++ = pc = new aiNode();
|
||||||
|
pc->mName = aiString(bones[i].mName);
|
||||||
|
pc->mParent = piParent;
|
||||||
|
|
||||||
|
// get the corresponding animation channel and its first frame
|
||||||
|
const aiNodeAnim** cur = node_anims;
|
||||||
|
while ((**cur).mNodeName != pc->mName)++cur;
|
||||||
|
|
||||||
|
aiMatrix4x4::Translation((**cur).mPositionKeys[0].mValue,pc->mTransformation);
|
||||||
|
pc->mTransformation = pc->mTransformation * aiMatrix4x4((**cur).mRotationKeys[0].mValue.GetMatrix()) ;
|
||||||
|
|
||||||
|
// add children to this node, too
|
||||||
|
AttachChilds_Anim( i, pc, bones,node_anims);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// undo offset computations
|
||||||
|
piParent->mChildren -= piParent->mNumChildren;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Load a MD5MESH file
|
||||||
void MD5Importer::LoadMD5MeshFile ()
|
void MD5Importer::LoadMD5MeshFile ()
|
||||||
{
|
{
|
||||||
std::string pFile = this->mFile + "MD5MESH";
|
std::string pFile = mFile + "md5mesh";
|
||||||
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
|
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
|
||||||
|
|
||||||
// Check whether we can read from the file
|
// Check whether we can read from the file
|
||||||
if( file.get() == NULL)
|
if( file.get() == NULL) {
|
||||||
{
|
DefaultLogger::get()->warn("Failed to read MD5MESH file: " + pFile);
|
||||||
DefaultLogger::get()->warn("Failed to read MD5 mesh file: " + pFile);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bHadMD5Mesh = true;
|
bHadMD5Mesh = true;
|
||||||
|
|
||||||
// now load the file into memory
|
|
||||||
LoadFileIntoMemory(file.get());
|
LoadFileIntoMemory(file.get());
|
||||||
|
|
||||||
// now construct a parser and parse the file
|
// now construct a parser and parse the file
|
||||||
|
@ -271,29 +326,29 @@ void MD5Importer::LoadMD5MeshFile ()
|
||||||
// load the mesh information from it
|
// load the mesh information from it
|
||||||
MD5::MD5MeshParser meshParser(parser.mSections);
|
MD5::MD5MeshParser meshParser(parser.mSections);
|
||||||
|
|
||||||
// create the bone hierarchy - first the root node
|
// create the bone hierarchy - first the root node and dummy nodes for all meshes
|
||||||
// and dummy nodes for all meshes
|
pScene->mRootNode = new aiNode("<MD5_Root>");
|
||||||
pScene->mRootNode = new aiNode();
|
|
||||||
pScene->mRootNode->mNumChildren = 2;
|
pScene->mRootNode->mNumChildren = 2;
|
||||||
pScene->mRootNode->mChildren = new aiNode*[2];
|
pScene->mRootNode->mChildren = new aiNode*[2];
|
||||||
|
|
||||||
// now create the hierarchy of animated bones
|
// build the hierarchy from the MD5MESH file
|
||||||
aiNode* pcNode = pScene->mRootNode->mChildren[1] = new aiNode();
|
aiNode* pcNode = pScene->mRootNode->mChildren[1] = new aiNode();
|
||||||
pcNode->mName.Set("MD5Anim");
|
pcNode->mName.Set("<MD5_Hierarchy>");
|
||||||
pcNode->mParent = pScene->mRootNode;
|
pcNode->mParent = pScene->mRootNode;
|
||||||
AttachChilds(-1,pcNode,meshParser.mJoints);
|
AttachChilds_Mesh(-1,pcNode,meshParser.mJoints);
|
||||||
|
|
||||||
pcNode = pScene->mRootNode->mChildren[0] = new aiNode();
|
pcNode = pScene->mRootNode->mChildren[0] = new aiNode();
|
||||||
pcNode->mName.Set("MD5Mesh");
|
pcNode->mName.Set("<MD5_Mesh>");
|
||||||
pcNode->mParent = pScene->mRootNode;
|
pcNode->mParent = pScene->mRootNode;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (pScene->mRootNode->mChildren[1]->mNumChildren) /* start at the right hierarchy level */
|
||||||
|
SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[1]->mChildren[0]);
|
||||||
|
#else
|
||||||
std::vector<MD5::MeshDesc>::const_iterator end = meshParser.mMeshes.end();
|
std::vector<MD5::MeshDesc>::const_iterator end = meshParser.mMeshes.end();
|
||||||
|
|
||||||
// FIX: MD5 files exported from Blender can have empty meshes
|
// FIX: MD5 files exported from Blender can have empty meshes
|
||||||
for (std::vector<MD5::MeshDesc>::const_iterator
|
for (std::vector<MD5::MeshDesc>::const_iterator it = meshParser.mMeshes.begin(),end = meshParser.mMeshes.end(); it != end;++it) {
|
||||||
it = meshParser.mMeshes.begin(),
|
|
||||||
end = meshParser.mMeshes.end(); it != end;++it)
|
|
||||||
{
|
|
||||||
if (!(*it).mFaces.empty() && !(*it).mVertices.empty())
|
if (!(*it).mFaces.empty() && !(*it).mVertices.empty())
|
||||||
++pScene->mNumMaterials;
|
++pScene->mNumMaterials;
|
||||||
}
|
}
|
||||||
|
@ -310,10 +365,7 @@ void MD5Importer::LoadMD5MeshFile ()
|
||||||
pcNode->mMeshes[m] = m;
|
pcNode->mMeshes[m] = m;
|
||||||
|
|
||||||
unsigned int n = 0;
|
unsigned int n = 0;
|
||||||
for (std::vector<MD5::MeshDesc>::iterator
|
for (std::vector<MD5::MeshDesc>::iterator it = meshParser.mMeshes.begin(),end = meshParser.mMeshes.end(); it != end;++it) {
|
||||||
it = meshParser.mMeshes.begin(),
|
|
||||||
end = meshParser.mMeshes.end(); it != end;++it)
|
|
||||||
{
|
|
||||||
MD5::MeshDesc& meshSrc = *it;
|
MD5::MeshDesc& meshSrc = *it;
|
||||||
if (meshSrc.mFaces.empty() || meshSrc.mVertices.empty())
|
if (meshSrc.mFaces.empty() || meshSrc.mVertices.empty())
|
||||||
continue;
|
continue;
|
||||||
|
@ -331,10 +383,7 @@ void MD5Importer::LoadMD5MeshFile ()
|
||||||
|
|
||||||
// copy texture coordinates
|
// copy texture coordinates
|
||||||
aiVector3D* pv = mesh->mTextureCoords[0];
|
aiVector3D* pv = mesh->mTextureCoords[0];
|
||||||
for (MD5::VertexList::const_iterator
|
for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
|
||||||
iter = meshSrc.mVertices.begin();
|
|
||||||
iter != meshSrc.mVertices.end();++iter,++pv)
|
|
||||||
{
|
|
||||||
pv->x = (*iter).mUV.x;
|
pv->x = (*iter).mUV.x;
|
||||||
pv->y = 1.0f-(*iter).mUV.y; // D3D to OpenGL
|
pv->y = 1.0f-(*iter).mUV.y; // D3D to OpenGL
|
||||||
pv->z = 0.0f;
|
pv->z = 0.0f;
|
||||||
|
@ -348,7 +397,9 @@ void MD5Importer::LoadMD5MeshFile ()
|
||||||
for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
|
for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
|
||||||
{
|
{
|
||||||
MD5::WeightDesc& desc = meshSrc.mWeights[w];
|
MD5::WeightDesc& desc = meshSrc.mWeights[w];
|
||||||
++piCount[desc.mBone];
|
/* FIX for some invalid exporters */
|
||||||
|
if (!(desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON ))
|
||||||
|
++piCount[desc.mBone];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,50 +417,55 @@ void MD5Importer::LoadMD5MeshFile ()
|
||||||
p->mNumWeights = piCount[q];
|
p->mNumWeights = piCount[q];
|
||||||
p->mWeights = new aiVertexWeight[p->mNumWeights];
|
p->mWeights = new aiVertexWeight[p->mNumWeights];
|
||||||
p->mName = aiString(meshParser.mJoints[q].mName);
|
p->mName = aiString(meshParser.mJoints[q].mName);
|
||||||
|
p->mOffsetMatrix = meshParser.mJoints[q].mInvTransform;
|
||||||
|
|
||||||
// store the index for later use
|
// store the index for later use
|
||||||
meshParser.mJoints[q].mMap = h++;
|
MD5::BoneDesc& boneSrc = meshParser.mJoints[q];
|
||||||
|
boneSrc.mMap = h++;
|
||||||
|
|
||||||
|
// compute w-component of quaternion
|
||||||
|
MD5::ConvertQuaternion( boneSrc.mRotationQuat, boneSrc.mRotationQuatConverted );
|
||||||
|
|
||||||
|
//boneSrc.mPositionXYZ.z *= -1.f;
|
||||||
|
//boneSrc.mRotationQuatConverted = boneSrc.mRotationQuatConverted * aiQuaternion(-0.5f, -0.5f, -0.5f, 0.5f) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
//unsigned int g = 0;
|
//unsigned int g = 0;
|
||||||
pv = mesh->mVertices;
|
pv = mesh->mVertices;
|
||||||
for (MD5::VertexList::const_iterator
|
for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
|
||||||
iter = meshSrc.mVertices.begin();
|
|
||||||
iter != meshSrc.mVertices.end();++iter,++pv)
|
|
||||||
{
|
|
||||||
// compute the final vertex position from all single weights
|
// compute the final vertex position from all single weights
|
||||||
*pv = aiVector3D();
|
*pv = aiVector3D();
|
||||||
|
|
||||||
// there are models which have weights which don't sum to 1 ...
|
// there are models which have weights which don't sum to 1 ...
|
||||||
// granite.md5mesh for example
|
|
||||||
float fSum = 0.0f;
|
float fSum = 0.0f;
|
||||||
for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
|
for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
|
||||||
fSum += meshSrc.mWeights[w].mWeight;
|
fSum += meshSrc.mWeights[w].mWeight;
|
||||||
if (!fSum)throw new ImportErrorException("The sum of all vertex bone weights is 0");
|
if (!fSum) {
|
||||||
|
DefaultLogger::get()->error("MD5MESH: The sum of all vertex bone weights is 0");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// process bone weights
|
// process bone weights
|
||||||
for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
|
for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
|
||||||
{
|
{
|
||||||
MD5::WeightDesc& desc = meshSrc.mWeights[w];
|
MD5::WeightDesc& desc = meshSrc.mWeights[w];
|
||||||
float fNewWeight = desc.mWeight / fSum;
|
if ( desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const float fNewWeight = desc.mWeight / fSum;
|
||||||
|
|
||||||
// transform the local position into worldspace
|
// transform the local position into worldspace
|
||||||
MD5::BoneDesc& boneSrc = meshParser.mJoints[desc.mBone];
|
MD5::BoneDesc& boneSrc = meshParser.mJoints[desc.mBone];
|
||||||
aiVector3D v = desc.vOffsetPosition;
|
const aiVector3D v = boneSrc.mRotationQuatConverted.Rotate (desc.vOffsetPosition);
|
||||||
aiQuaternion quat = aiQuaternion( boneSrc.mRotationQuat );
|
|
||||||
//quat.w *= -1.0f;
|
|
||||||
v = quat.GetMatrix() * v;
|
|
||||||
v += boneSrc.mPositionXYZ;
|
|
||||||
|
|
||||||
// use the original weight to compute the vertex position
|
// use the original weight to compute the vertex position
|
||||||
// (some MD5s seem to depend on the invalid weight values ...)
|
// (some MD5s seem to depend on the invalid weight values ...)
|
||||||
pv->operator +=(v * desc.mWeight);
|
*pv += ((boneSrc.mPositionXYZ+v)* desc.mWeight);
|
||||||
|
|
||||||
aiBone* bone = mesh->mBones[boneSrc.mMap];
|
aiBone* bone = mesh->mBones[boneSrc.mMap];
|
||||||
*bone->mWeights++ = aiVertexWeight((unsigned int)(pv-mesh->mVertices),fNewWeight);
|
*bone->mWeights++ = aiVertexWeight((unsigned int)(pv-mesh->mVertices),fNewWeight);
|
||||||
}
|
}
|
||||||
// convert from DOOM coordinate system to OGL
|
//pv->z *= -1.f;
|
||||||
std::swap((float&)pv->z,(float&)pv->y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// undo our nice offset tricks ...
|
// undo our nice offset tricks ...
|
||||||
|
@ -433,47 +489,69 @@ void MD5Importer::LoadMD5MeshFile ()
|
||||||
// generate a material for the mesh
|
// generate a material for the mesh
|
||||||
MaterialHelper* mat = new MaterialHelper();
|
MaterialHelper* mat = new MaterialHelper();
|
||||||
pScene->mMaterials[n] = mat;
|
pScene->mMaterials[n] = mat;
|
||||||
mat->AddProperty(&meshSrc.mShader,AI_MATKEY_TEXTURE_DIFFUSE(0));
|
|
||||||
|
// insert the typical doom3 textures:
|
||||||
|
// nnn_local.tga - normal map
|
||||||
|
// nnn_h.tga - height map
|
||||||
|
// nnn_s.tga - specular map
|
||||||
|
// nnn_d.tga - diffuse map
|
||||||
|
if (meshSrc.mShader.length && !strchr(meshSrc.mShader.data,'.')) {
|
||||||
|
|
||||||
|
aiString temp(meshSrc.mShader);
|
||||||
|
temp.Append("_local.tga");
|
||||||
|
mat->AddProperty(&temp,AI_MATKEY_TEXTURE_NORMALS(0));
|
||||||
|
|
||||||
|
temp = aiString(meshSrc.mShader);
|
||||||
|
temp.Append("_s.tga");
|
||||||
|
mat->AddProperty(&temp,AI_MATKEY_TEXTURE_SPECULAR(0));
|
||||||
|
|
||||||
|
temp = aiString(meshSrc.mShader);
|
||||||
|
temp.Append("_d.tga");
|
||||||
|
mat->AddProperty(&temp,AI_MATKEY_TEXTURE_DIFFUSE(0));
|
||||||
|
|
||||||
|
temp = aiString(meshSrc.mShader);
|
||||||
|
temp.Append("_h.tga");
|
||||||
|
mat->AddProperty(&temp,AI_MATKEY_TEXTURE_HEIGHT(0));
|
||||||
|
|
||||||
|
// set this also as material name
|
||||||
|
mat->AddProperty(&meshSrc.mShader,AI_MATKEY_NAME);
|
||||||
|
}
|
||||||
|
else mat->AddProperty(&meshSrc.mShader,AI_MATKEY_TEXTURE_DIFFUSE(0));
|
||||||
mesh->mMaterialIndex = n++;
|
mesh->mMaterialIndex = n++;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
// delete the file again
|
// delete the file again
|
||||||
UnloadFileFromMemory();
|
UnloadFileFromMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Load an MD5ANIM file
|
||||||
void MD5Importer::LoadMD5AnimFile ()
|
void MD5Importer::LoadMD5AnimFile ()
|
||||||
{
|
{
|
||||||
std::string pFile = mFile + "MD5ANIM";
|
std::string pFile = mFile + "md5anim";
|
||||||
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
|
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
|
||||||
|
|
||||||
// Check whether we can read from the file
|
// Check whether we can read from the file
|
||||||
if( file.get() == NULL)
|
if( file.get() == NULL) {
|
||||||
{
|
DefaultLogger::get()->warn("Failed to read MD5ANIM file: " + pFile);
|
||||||
DefaultLogger::get()->warn("Failed to read MD5 anim file: " + pFile);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bHadMD5Anim = true;
|
bHadMD5Anim = true;
|
||||||
|
|
||||||
// now load the file into memory
|
|
||||||
LoadFileIntoMemory(file.get());
|
LoadFileIntoMemory(file.get());
|
||||||
|
|
||||||
// now construct a parser and parse the file
|
// parse the basic file structure
|
||||||
MD5::MD5Parser parser(mBuffer,fileSize);
|
MD5::MD5Parser parser(mBuffer,fileSize);
|
||||||
|
|
||||||
// load the animation information from it
|
// load the animation information from the parse tree
|
||||||
MD5::MD5AnimParser animParser(parser.mSections);
|
MD5::MD5AnimParser animParser(parser.mSections);
|
||||||
|
|
||||||
// generate and fill the output animation
|
// generate and fill the output animation
|
||||||
if (!animParser.mAnimatedBones.empty())
|
if (!animParser.mAnimatedBones.empty()) {
|
||||||
{
|
pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations = 1];
|
||||||
pScene->mNumAnimations = 1;
|
|
||||||
pScene->mAnimations = new aiAnimation*[1];
|
|
||||||
aiAnimation* anim = pScene->mAnimations[0] = new aiAnimation();
|
aiAnimation* anim = pScene->mAnimations[0] = new aiAnimation();
|
||||||
anim->mNumChannels = (unsigned int)animParser.mAnimatedBones.size();
|
anim->mNumChannels = (unsigned int)animParser.mAnimatedBones.size();
|
||||||
anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
|
anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
|
||||||
for (unsigned int i = 0; i < anim->mNumChannels;++i)
|
for (unsigned int i = 0; i < anim->mNumChannels;++i) {
|
||||||
{
|
|
||||||
aiNodeAnim* node = anim->mChannels[i] = new aiNodeAnim();
|
aiNodeAnim* node = anim->mChannels[i] = new aiNodeAnim();
|
||||||
node->mNodeName = aiString( animParser.mAnimatedBones[i].mName );
|
node->mNodeName = aiString( animParser.mAnimatedBones[i].mName );
|
||||||
|
|
||||||
|
@ -486,70 +564,49 @@ void MD5Importer::LoadMD5AnimFile ()
|
||||||
// 1 tick == 1 frame
|
// 1 tick == 1 frame
|
||||||
anim->mTicksPerSecond = animParser.fFrameRate;
|
anim->mTicksPerSecond = animParser.fFrameRate;
|
||||||
|
|
||||||
for (FrameList::const_iterator iter = animParser.mFrames.begin(),
|
for (FrameList::const_iterator iter = animParser.mFrames.begin(), iterEnd = animParser.mFrames.end();iter != iterEnd;++iter){
|
||||||
iterEnd = animParser.mFrames.end();iter != iterEnd;++iter)
|
|
||||||
{
|
|
||||||
double dTime = (double)(*iter).iIndex;
|
double dTime = (double)(*iter).iIndex;
|
||||||
if (!(*iter).mValues.empty())
|
if (!(*iter).mValues.empty())
|
||||||
{
|
{
|
||||||
// now process all values in there ... read all joints
|
// now process all values in there ... read all joints
|
||||||
aiNodeAnim** pcAnimNode = anim->mChannels;
|
aiNodeAnim** pcAnimNode = anim->mChannels;
|
||||||
MD5::BaseFrameDesc* pcBaseFrame = &animParser.mBaseFrames[0];
|
MD5::BaseFrameDesc* pcBaseFrame = &animParser.mBaseFrames[0];
|
||||||
for (AnimBoneList::const_iterator
|
for (AnimBoneList::const_iterator iter2 = animParser.mAnimatedBones.begin(); iter2 != animParser.mAnimatedBones.end();++iter2,
|
||||||
iter2 = animParser.mAnimatedBones.begin(),
|
++pcAnimNode,++pcBaseFrame)
|
||||||
iterEnd2 = animParser.mAnimatedBones.end();
|
|
||||||
iter2 != iterEnd2;++iter2,++pcAnimNode,++pcBaseFrame)
|
|
||||||
{
|
{
|
||||||
if((*iter2).iFirstKeyIndex >= (*iter).mValues.size())
|
if((*iter2).iFirstKeyIndex >= (*iter).mValues.size()) {
|
||||||
{
|
DefaultLogger::get()->error("MD5: Keyframe index is out of range");
|
||||||
// TODO: add proper array checks for all cases here ...
|
|
||||||
DefaultLogger::get()->error("Keyframe index is out of range. ");
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const float* fpCur = &(*iter).mValues[(*iter2).iFirstKeyIndex];
|
const float* fpCur = &(*iter).mValues[(*iter2).iFirstKeyIndex];
|
||||||
|
|
||||||
aiNodeAnim* pcCurAnimBone = *pcAnimNode;
|
aiNodeAnim* pcCurAnimBone = *pcAnimNode;
|
||||||
aiVectorKey* vKey = pcCurAnimBone->mPositionKeys++;
|
aiVectorKey* vKey = pcCurAnimBone->mPositionKeys++;
|
||||||
|
|
||||||
// translation on the x axis
|
|
||||||
if ((*iter2).iFlags & AI_MD5_ANIMATION_FLAG_TRANSLATE_X)
|
|
||||||
vKey->mValue.x = *fpCur++;
|
|
||||||
else vKey->mValue.x = pcBaseFrame->vPositionXYZ.x;
|
|
||||||
|
|
||||||
// translation on the y axis
|
|
||||||
if ((*iter2).iFlags & AI_MD5_ANIMATION_FLAG_TRANSLATE_Y)
|
|
||||||
vKey->mValue.y = *fpCur++;
|
|
||||||
else vKey->mValue.y = pcBaseFrame->vPositionXYZ.y;
|
|
||||||
|
|
||||||
// translation on the z axis
|
|
||||||
if ((*iter2).iFlags & AI_MD5_ANIMATION_FLAG_TRANSLATE_Z)
|
|
||||||
vKey->mValue.z = *fpCur++;
|
|
||||||
else vKey->mValue.z = pcBaseFrame->vPositionXYZ.z;
|
|
||||||
|
|
||||||
|
|
||||||
// rotation quaternion, x component
|
|
||||||
aiQuatKey* qKey = pcCurAnimBone->mRotationKeys++;
|
aiQuatKey* qKey = pcCurAnimBone->mRotationKeys++;
|
||||||
aiVector3D vTemp;
|
aiVector3D vTemp;
|
||||||
if ((*iter2).iFlags & AI_MD5_ANIMATION_FLAG_ROTQUAT_X)
|
|
||||||
vTemp.x = *fpCur++;
|
|
||||||
else vTemp.x = pcBaseFrame->vRotationQuat.x;
|
|
||||||
|
|
||||||
// rotation quaternion, y component
|
// translational component
|
||||||
if ((*iter2).iFlags & AI_MD5_ANIMATION_FLAG_ROTQUAT_Y)
|
for (unsigned int i = 0; i < 3; ++i) {
|
||||||
vTemp.y = *fpCur++;
|
if ((*iter2).iFlags & (1u << i))
|
||||||
else vTemp.y = pcBaseFrame->vRotationQuat.y;
|
vKey->mValue[i] = *fpCur++;
|
||||||
|
else vKey->mValue[i] = pcBaseFrame->vPositionXYZ[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// orientation component
|
||||||
|
for (unsigned int i = 0; i < 3; ++i) {
|
||||||
|
if ((*iter2).iFlags & (8u << i))
|
||||||
|
vTemp[i] = *fpCur++;
|
||||||
|
else vTemp[i] = pcBaseFrame->vRotationQuat[i];
|
||||||
|
}
|
||||||
|
|
||||||
// rotation quaternion, z component
|
MD5::ConvertQuaternion(vTemp, qKey->mValue);
|
||||||
if ((*iter2).iFlags & AI_MD5_ANIMATION_FLAG_ROTQUAT_Z)
|
|
||||||
vTemp.z = *fpCur++;
|
|
||||||
else vTemp.z = pcBaseFrame->vRotationQuat.z;
|
|
||||||
|
|
||||||
// compute the w component of the quaternion - invert it (DX to OGL)
|
aiMatrix4x4 m;
|
||||||
qKey->mValue = aiQuaternion(vTemp);
|
aiMatrix4x4::Translation(vKey->mValue,m);
|
||||||
//qKey->mValue.w *= -1.0f;
|
m = m*aiMatrix4x4( qKey->mValue.GetMatrix() );
|
||||||
|
m.DecomposeNoScaling(qKey->mValue,vKey->mValue);
|
||||||
|
|
||||||
qKey->mTime = dTime;
|
qKey->mTime = vKey->mTime = dTime;
|
||||||
vKey->mTime = dTime;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// compute the duration of the animation
|
// compute the duration of the animation
|
||||||
|
@ -557,16 +614,53 @@ void MD5Importer::LoadMD5AnimFile ()
|
||||||
}
|
}
|
||||||
|
|
||||||
// undo our offset computations
|
// undo our offset computations
|
||||||
for (unsigned int i = 0; i < anim->mNumChannels;++i)
|
for (unsigned int i = 0; i < anim->mNumChannels;++i) {
|
||||||
{
|
|
||||||
aiNodeAnim* node = anim->mChannels[i];
|
aiNodeAnim* node = anim->mChannels[i];
|
||||||
node->mPositionKeys -= node->mNumPositionKeys;
|
node->mPositionKeys -= node->mNumPositionKeys;
|
||||||
node->mRotationKeys -= node->mNumPositionKeys;
|
node->mRotationKeys -= node->mNumPositionKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we didn't build the hierarchy yet (== we didn't load a MD5MESH),
|
||||||
|
// construct it now from the data given in the MD5ANIM.
|
||||||
|
if (!pScene->mRootNode) {
|
||||||
|
pScene->mRootNode = new aiNode();
|
||||||
|
pScene->mRootNode->mName.Set("<MD5_Hierarchy>");
|
||||||
|
AttachChilds_Anim(-1,pScene->mRootNode,animParser.mAnimatedBones,(const aiNodeAnim**)anim->mChannels);
|
||||||
|
|
||||||
|
// Call SkeletonMeshBuilder to construct a mesh to represent the shape
|
||||||
|
if (pScene->mRootNode->mNumChildren) {
|
||||||
|
SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete the file again
|
// delete the file again
|
||||||
UnloadFileFromMemory();
|
UnloadFileFromMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Load an MD5CAMERA file
|
||||||
|
void MD5Importer::LoadMD5CameraFile ()
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
std::string pFile = mFile + "md5camera";
|
||||||
|
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
|
||||||
|
|
||||||
|
// Check whether we can read from the file
|
||||||
|
if( file.get() == NULL) {
|
||||||
|
DefaultLogger::get()->warn("Failed to read MD5CAMERA file: " + pFile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bHadMD5Camera = true;
|
||||||
|
LoadFileIntoMemory(file.get());
|
||||||
|
|
||||||
|
// parse the basic file structure
|
||||||
|
MD5::MD5Parser parser(mBuffer,fileSize);
|
||||||
|
|
||||||
|
// load the camera animation data from the parse tree
|
||||||
|
MD5::MD5CameraParser cameraParser(parser.mSections);
|
||||||
|
#endif
|
||||||
|
throw new ImportErrorException("MD5Camera is not yet supported");
|
||||||
|
}
|
||||||
|
|
||||||
#endif // !! ASSIMP_BUILD_NO_MD5_IMPORTER
|
#endif // !! ASSIMP_BUILD_NO_MD5_IMPORTER
|
||||||
|
|
|
@ -57,7 +57,7 @@ class IOStream;
|
||||||
using namespace Assimp::MD5;
|
using namespace Assimp::MD5;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** Used to load MD5 files
|
/** Importer class for the MD5 file format
|
||||||
*/
|
*/
|
||||||
class MD5Importer : public BaseImporter
|
class MD5Importer : public BaseImporter
|
||||||
{
|
{
|
||||||
|
@ -74,7 +74,8 @@ public:
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Returns whether the class can handle the format of the given file.
|
/** Returns whether the class can handle the format of the given file.
|
||||||
* See BaseImporter::CanRead() for details. */
|
* See BaseImporter::CanRead() for details.
|
||||||
|
*/
|
||||||
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
|
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
|
||||||
bool checkSig) const;
|
bool checkSig) const;
|
||||||
|
|
||||||
|
@ -85,11 +86,18 @@ protected:
|
||||||
* See BaseImporter::GetExtensionList() for details
|
* See BaseImporter::GetExtensionList() for details
|
||||||
*/
|
*/
|
||||||
void GetExtensionList(std::string& append);
|
void GetExtensionList(std::string& append);
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
/** Called prior to ReadFile().
|
||||||
|
* The function is a request to the importer to update its configuration
|
||||||
|
* basing on the Importer's configuration property list.
|
||||||
|
*/
|
||||||
|
void SetupProperties(const Importer* pImp);
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Imports the given file into the given scene structure.
|
/** Imports the given file into the given scene structure.
|
||||||
* See BaseImporter::InternReadFile() for details
|
* See BaseImporter::InternReadFile() for details
|
||||||
*/
|
*/
|
||||||
void InternReadFile( const std::string& pFile, aiScene* pScene,
|
void InternReadFile( const std::string& pFile, aiScene* pScene,
|
||||||
IOSystem* pIOHandler);
|
IOSystem* pIOHandler);
|
||||||
|
|
||||||
|
@ -97,22 +105,49 @@ protected:
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Load the *.MD5MESH file.
|
/** Load a *.MD5MESH file.
|
||||||
* Must be called at first.
|
*/
|
||||||
*/
|
|
||||||
void LoadMD5MeshFile ();
|
void LoadMD5MeshFile ();
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Load the *.MD5ANIM file.
|
/** Load a *.MD5ANIM file.
|
||||||
*/
|
*/
|
||||||
void LoadMD5AnimFile ();
|
void LoadMD5AnimFile ();
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
/** Load a *.MD5CAMERA file.
|
||||||
|
*/
|
||||||
|
void LoadMD5CameraFile ();
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
/** Construct node hierarchy from a given MD5ANIM
|
||||||
|
* @param iParentID Current parent ID
|
||||||
|
* @param piParent Parent node to attach to
|
||||||
|
* @param bones Input bones
|
||||||
|
* @param node_anims Generated node animations
|
||||||
|
*/
|
||||||
|
void AttachChilds_Anim(int iParentID,aiNode* piParent,
|
||||||
|
AnimBoneList& bones,const aiNodeAnim** node_anims);
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
/** Construct node hierarchy from a given MD5MESH
|
||||||
|
* @param iParentID Current parent ID
|
||||||
|
* @param piParent Parent node to attach to
|
||||||
|
* @param bones Input bones
|
||||||
|
*/
|
||||||
|
void AttachChilds_Mesh(int iParentID,aiNode* piParent,BoneList& bones);
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
/** Build unique vertex buffers from a given MD5ANIM
|
||||||
|
* @param meshSrc Input data
|
||||||
|
*/
|
||||||
|
void MakeDataUnique (MD5::MeshDesc& meshSrc);
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
/** Load the contents of a specific file into memory and
|
/** Load the contents of a specific file into memory and
|
||||||
* alocates a buffer to keep it.
|
* alocates a buffer to keep it.
|
||||||
*
|
*
|
||||||
* mBuffer is changed to point to this buffer.
|
* mBuffer is modified to point to this buffer.
|
||||||
* Don't forget to delete it later ...
|
|
||||||
* @param pFile File stream to be read
|
* @param pFile File stream to be read
|
||||||
*/
|
*/
|
||||||
void LoadFileIntoMemory (IOStream* pFile);
|
void LoadFileIntoMemory (IOStream* pFile);
|
||||||
|
@ -141,11 +176,17 @@ protected:
|
||||||
/** (Custom) I/O handler implementation */
|
/** (Custom) I/O handler implementation */
|
||||||
IOSystem* pIOHandler;
|
IOSystem* pIOHandler;
|
||||||
|
|
||||||
/** true if the MD5MESH file has already been parsed */
|
/** true if a MD5MESH file has already been parsed */
|
||||||
bool bHadMD5Mesh;
|
bool bHadMD5Mesh;
|
||||||
|
|
||||||
/** true if the MD5ANIM file has already been parsed */
|
/** true if a MD5ANIM file has already been parsed */
|
||||||
bool bHadMD5Anim;
|
bool bHadMD5Anim;
|
||||||
|
|
||||||
|
/** true if a MD5CAMERA file has already been parsed */
|
||||||
|
bool bHadMD5Camera;
|
||||||
|
|
||||||
|
/** configuration option: prevent anim autoload */
|
||||||
|
bool configNoAutoLoad;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end of namespace Assimp
|
} // end of namespace Assimp
|
||||||
|
|
|
@ -39,8 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file Implementation of the MD5 parser class */
|
/** @file MD5Parser.cpp
|
||||||
|
* @brief Implementation of the MD5 parser class
|
||||||
|
*/
|
||||||
#include "AssimpPCH.h"
|
#include "AssimpPCH.h"
|
||||||
|
|
||||||
// internal headers
|
// internal headers
|
||||||
|
@ -50,90 +51,88 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "ParsingUtils.h"
|
#include "ParsingUtils.h"
|
||||||
#include "StringComparison.h"
|
#include "StringComparison.h"
|
||||||
|
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
using namespace Assimp::MD5;
|
using namespace Assimp::MD5;
|
||||||
|
|
||||||
#if _MSC_VER >= 1400
|
|
||||||
# define sprintf sprintf_s
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
MD5Parser::MD5Parser(char* buffer, unsigned int fileSize)
|
// Parse the segment structure fo a MD5 file
|
||||||
|
MD5Parser::MD5Parser(char* _buffer, unsigned int _fileSize)
|
||||||
{
|
{
|
||||||
ai_assert(NULL != buffer && 0 != fileSize);
|
ai_assert(NULL != _buffer && 0 != _fileSize);
|
||||||
|
|
||||||
this->buffer = buffer;
|
buffer = _buffer;
|
||||||
this->fileSize = fileSize;
|
fileSize = fileSize;
|
||||||
this->lineNumber = 0;
|
lineNumber = 0;
|
||||||
|
|
||||||
DefaultLogger::get()->debug("MD5Parser begin");
|
DefaultLogger::get()->debug("MD5Parser begin");
|
||||||
|
|
||||||
// parse the file header
|
// parse the file header
|
||||||
this->ParseHeader();
|
ParseHeader();
|
||||||
|
|
||||||
// and read all sections until we're finished
|
// and read all sections until we're finished
|
||||||
while (true)
|
while (1) {
|
||||||
{
|
mSections.push_back(Section());
|
||||||
this->mSections.push_back(Section());
|
Section& sec = mSections.back();
|
||||||
Section& sec = this->mSections.back();
|
if(!ParseSection(sec)) {
|
||||||
if(!this->ParseSection(sec))
|
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !DefaultLogger::isNullLogger())
|
if ( !DefaultLogger::isNullLogger()) {
|
||||||
{
|
|
||||||
char szBuffer[128]; // should be sufficiently large
|
char szBuffer[128]; // should be sufficiently large
|
||||||
::sprintf(szBuffer,"MD5Parser end. Parsed %i sections",(int)this->mSections.size());
|
::sprintf(szBuffer,"MD5Parser end. Parsed %i sections",(int)mSections.size());
|
||||||
DefaultLogger::get()->debug(szBuffer);
|
DefaultLogger::get()->debug(szBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Report error to the log stream
|
||||||
/*static*/ void MD5Parser::ReportError (const char* error, unsigned int line)
|
/*static*/ void MD5Parser::ReportError (const char* error, unsigned int line)
|
||||||
{
|
{
|
||||||
char szBuffer[1024]; // you, listen to me, you HAVE TO BE sufficiently large
|
char szBuffer[1024];
|
||||||
::sprintf(szBuffer,"Line %i: %s",line,error);
|
::sprintf(szBuffer,"[MD5] Line %i: %s",line,error);
|
||||||
throw new ImportErrorException(szBuffer);
|
throw new ImportErrorException(szBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Report warning to the log stream
|
||||||
/*static*/ void MD5Parser::ReportWarning (const char* warn, unsigned int line)
|
/*static*/ void MD5Parser::ReportWarning (const char* warn, unsigned int line)
|
||||||
{
|
{
|
||||||
char szBuffer[1024]; // you, listen to me, you HAVE TO BE sufficiently large
|
char szBuffer[1024];
|
||||||
::sprintf(szBuffer,"Line %i: %s",line,warn);
|
::sprintf(szBuffer,"[MD5] Line %i: %s",line,warn);
|
||||||
DefaultLogger::get()->warn(szBuffer);
|
DefaultLogger::get()->warn(szBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Parse and validate the MD5 header
|
||||||
void MD5Parser::ParseHeader()
|
void MD5Parser::ParseHeader()
|
||||||
{
|
{
|
||||||
// parse and validate the file version
|
// parse and validate the file version
|
||||||
SkipSpaces();
|
SkipSpaces();
|
||||||
if (0 != ASSIMP_strincmp(buffer,"MD5Version",10) ||
|
if (!TokenMatch(buffer,"MD5Version",10)) {
|
||||||
!IsSpace(*(buffer+=10)))
|
|
||||||
{
|
|
||||||
ReportError("Invalid MD5 file: MD5Version tag has not been found");
|
ReportError("Invalid MD5 file: MD5Version tag has not been found");
|
||||||
}
|
}
|
||||||
SkipSpaces();
|
SkipSpaces();
|
||||||
unsigned int iVer = ::strtol10(buffer,(const char**)&buffer);
|
unsigned int iVer = ::strtol10(buffer,(const char**)&buffer);
|
||||||
if (10 != iVer)
|
if (10 != iVer) {
|
||||||
{
|
|
||||||
ReportWarning("MD5 version tag is unknown (10 is expected)");
|
ReportWarning("MD5 version tag is unknown (10 is expected)");
|
||||||
}
|
}
|
||||||
SkipLine();
|
SkipLine();
|
||||||
|
|
||||||
// print the command line options to the console
|
// print the command line options to the console
|
||||||
// fix: can break the log length limit ...
|
// FIX: can break the log length limit, so we need to be careful
|
||||||
char* sz = buffer;
|
char* sz = buffer;
|
||||||
while (!IsLineEnd( *buffer++));
|
while (!IsLineEnd( *buffer++));
|
||||||
DefaultLogger::get()->info(std::string(sz,std::min((uintptr_t)MAX_LOG_MESSAGE_LENGTH, (uintptr_t)(buffer-sz))));
|
DefaultLogger::get()->info(std::string(sz,std::min((uintptr_t)MAX_LOG_MESSAGE_LENGTH, (uintptr_t)(buffer-sz))));
|
||||||
SkipSpacesAndLineEnd();
|
SkipSpacesAndLineEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Recursive MD5 parsing function
|
||||||
bool MD5Parser::ParseSection(Section& out)
|
bool MD5Parser::ParseSection(Section& out)
|
||||||
{
|
{
|
||||||
// store the current line number for use in error messages
|
// store the current line number for use in error messages
|
||||||
out.iLineNumber = this->lineNumber;
|
out.iLineNumber = lineNumber;
|
||||||
|
|
||||||
// first parse the name of the section
|
// first parse the name of the section
|
||||||
char* sz = buffer;
|
char* sz = buffer;
|
||||||
|
@ -141,20 +140,16 @@ bool MD5Parser::ParseSection(Section& out)
|
||||||
out.mName = std::string(sz,(uintptr_t)(buffer-sz));
|
out.mName = std::string(sz,(uintptr_t)(buffer-sz));
|
||||||
SkipSpaces();
|
SkipSpaces();
|
||||||
|
|
||||||
while (true)
|
while (1) {
|
||||||
{
|
if ('{' == *buffer) {
|
||||||
if ('{' == *buffer)
|
|
||||||
{
|
|
||||||
// it is a normal section so read all lines
|
// it is a normal section so read all lines
|
||||||
buffer++;
|
buffer++;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (!SkipSpacesAndLineEnd())
|
if (!SkipSpacesAndLineEnd()) {
|
||||||
{
|
|
||||||
return false; // seems this was the last section
|
return false; // seems this was the last section
|
||||||
}
|
}
|
||||||
if ('}' == *buffer)
|
if ('}' == *buffer) {
|
||||||
{
|
|
||||||
buffer++;
|
buffer++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -167,19 +162,13 @@ bool MD5Parser::ParseSection(Section& out)
|
||||||
|
|
||||||
// terminate the line with zero - remove all spaces at the end
|
// terminate the line with zero - remove all spaces at the end
|
||||||
while (!IsLineEnd( *buffer))buffer++;
|
while (!IsLineEnd( *buffer))buffer++;
|
||||||
//const char* end = buffer;
|
do {buffer--;}while (IsSpace(*buffer));
|
||||||
do {buffer--;}
|
buffer++;*buffer++ = '\0';
|
||||||
while (IsSpace(*buffer));
|
|
||||||
buffer++;
|
|
||||||
*buffer++ = '\0';
|
|
||||||
//if (*end) ++lineNumber;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (!IsSpaceOrNewLine(*buffer))
|
else if (!IsSpaceOrNewLine(*buffer)) {
|
||||||
{
|
|
||||||
// it is an element at global scope. Parse its value and go on
|
// it is an element at global scope. Parse its value and go on
|
||||||
// FIX: for MD5ANIm files - frame 0 {...} is allowed
|
|
||||||
sz = buffer;
|
sz = buffer;
|
||||||
while (!IsSpaceOrNewLine( *buffer++));
|
while (!IsSpaceOrNewLine( *buffer++));
|
||||||
out.mGlobalValue = std::string(sz,(uintptr_t)(buffer-sz));
|
out.mGlobalValue = std::string(sz,(uintptr_t)(buffer-sz));
|
||||||
|
@ -189,7 +178,9 @@ bool MD5Parser::ParseSection(Section& out)
|
||||||
}
|
}
|
||||||
return SkipSpacesAndLineEnd();
|
return SkipSpacesAndLineEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// Some dirty macros just because they're so funny and easy to debug
|
||||||
|
|
||||||
// skip all spaces ... handle EOL correctly
|
// skip all spaces ... handle EOL correctly
|
||||||
#define AI_MD5_SKIP_SPACES() if(!SkipSpaces(&sz)) \
|
#define AI_MD5_SKIP_SPACES() if(!SkipSpaces(&sz)) \
|
||||||
|
@ -230,47 +221,30 @@ bool MD5Parser::ParseSection(Section& out)
|
||||||
out.data[out.length] = '\0';
|
out.data[out.length] = '\0';
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// .MD5MESH parsing function
|
||||||
MD5MeshParser::MD5MeshParser(SectionList& mSections)
|
MD5MeshParser::MD5MeshParser(SectionList& mSections)
|
||||||
{
|
{
|
||||||
DefaultLogger::get()->debug("MD5MeshParser begin");
|
DefaultLogger::get()->debug("MD5MeshParser begin");
|
||||||
|
|
||||||
// now parse all sections
|
// now parse all sections
|
||||||
for (SectionList::const_iterator
|
for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end();iter != iterEnd;++iter){
|
||||||
iter = mSections.begin(), iterEnd = mSections.end();
|
|
||||||
iter != iterEnd;++iter)
|
|
||||||
{
|
|
||||||
if ((*iter).mGlobalValue.length())
|
if ((*iter).mGlobalValue.length())
|
||||||
{
|
{
|
||||||
if ( !::strcmp("numMeshes",(*iter).mName.c_str()))
|
if ( (*iter).mName == "numMeshes") {
|
||||||
{
|
mMeshes.reserve(::strtol10((*iter).mGlobalValue.c_str()));
|
||||||
unsigned int iNumMeshes;
|
|
||||||
if((iNumMeshes = ::strtol10((*iter).mGlobalValue.c_str())))
|
|
||||||
{
|
|
||||||
mMeshes.reserve(iNumMeshes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if ( !::strcmp("numJoints",(*iter).mName.c_str()))
|
else if ( (*iter).mName == "numJoints") {
|
||||||
{
|
mJoints.reserve(::strtol10((*iter).mGlobalValue.c_str()));
|
||||||
unsigned int iNumJoints;
|
|
||||||
if((iNumJoints = ::strtol10((*iter).mGlobalValue.c_str())))
|
|
||||||
{
|
|
||||||
mJoints.reserve(iNumJoints);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!::strcmp("joints",(*iter).mName.c_str()))
|
else if ((*iter).mName == "joints")
|
||||||
{
|
{
|
||||||
// now read all elements
|
|
||||||
// "origin" -1 ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000000 0.707107 )
|
// "origin" -1 ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000000 0.707107 )
|
||||||
for (ElementList::const_iterator
|
for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();eit != eitEnd; ++eit){
|
||||||
eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();
|
|
||||||
eit != eitEnd; ++eit)
|
|
||||||
{
|
|
||||||
mJoints.push_back(BoneDesc());
|
mJoints.push_back(BoneDesc());
|
||||||
BoneDesc& desc = mJoints.back();
|
BoneDesc& desc = mJoints.back();
|
||||||
|
|
||||||
const char* sz = (*eit).szStart;
|
const char* sz = (*eit).szStart;
|
||||||
|
|
||||||
AI_MD5_PARSE_STRING(desc.mName);
|
AI_MD5_PARSE_STRING(desc.mName);
|
||||||
AI_MD5_SKIP_SPACES();
|
AI_MD5_SKIP_SPACES();
|
||||||
|
|
||||||
|
@ -285,63 +259,48 @@ MD5MeshParser::MD5MeshParser(SectionList& mSections)
|
||||||
AI_MD5_READ_TRIPLE(desc.mRotationQuat); // normalized quaternion, so w is not there
|
AI_MD5_READ_TRIPLE(desc.mRotationQuat); // normalized quaternion, so w is not there
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!::strcmp("mesh",(*iter).mName.c_str()))
|
else if ((*iter).mName == "mesh")
|
||||||
{
|
{
|
||||||
mMeshes.push_back(MeshDesc());
|
mMeshes.push_back(MeshDesc());
|
||||||
MeshDesc& desc = mMeshes.back();
|
MeshDesc& desc = mMeshes.back();
|
||||||
|
|
||||||
// now read all elements
|
for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();eit != eitEnd; ++eit){
|
||||||
for (ElementList::const_iterator
|
|
||||||
eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();
|
|
||||||
eit != eitEnd; ++eit)
|
|
||||||
{
|
|
||||||
const char* sz = (*eit).szStart;
|
const char* sz = (*eit).szStart;
|
||||||
|
|
||||||
// shader attribute
|
// shader attribute
|
||||||
if (!ASSIMP_strincmp(sz,"shader",6) &&
|
if (TokenMatch(sz,"shader",6))
|
||||||
IsSpaceOrNewLine(*(sz+=6)++))
|
|
||||||
{
|
{
|
||||||
// don't expect quotation marks
|
// don't expect quotation marks
|
||||||
AI_MD5_SKIP_SPACES();
|
AI_MD5_SKIP_SPACES();
|
||||||
AI_MD5_PARSE_STRING(desc.mShader);
|
AI_MD5_PARSE_STRING(desc.mShader);
|
||||||
}
|
}
|
||||||
// numverts attribute
|
// numverts attribute
|
||||||
else if (!ASSIMP_strincmp(sz,"numverts",8) &&
|
else if (TokenMatch(sz,"numverts",8))
|
||||||
IsSpaceOrNewLine(*(sz+=8)++))
|
|
||||||
{
|
{
|
||||||
// reserve enough storage
|
// reserve enough storage
|
||||||
AI_MD5_SKIP_SPACES();
|
AI_MD5_SKIP_SPACES();
|
||||||
unsigned int iNumVertices;
|
desc.mVertices.resize(::strtol10(sz));
|
||||||
if((iNumVertices = ::strtol10(sz)))
|
|
||||||
desc.mVertices.resize(iNumVertices);
|
|
||||||
}
|
}
|
||||||
// numtris attribute
|
// numtris attribute
|
||||||
else if (!ASSIMP_strincmp(sz,"numtris",7) &&
|
else if (TokenMatch(sz,"numtris",7))
|
||||||
IsSpaceOrNewLine(*(sz+=7)++))
|
|
||||||
{
|
{
|
||||||
// reserve enough storage
|
// reserve enough storage
|
||||||
AI_MD5_SKIP_SPACES();
|
AI_MD5_SKIP_SPACES();
|
||||||
unsigned int iNumTris;
|
desc.mFaces.resize(::strtol10(sz));
|
||||||
if((iNumTris = ::strtol10(sz)))
|
|
||||||
desc.mFaces.resize(iNumTris);
|
|
||||||
}
|
}
|
||||||
// numweights attribute
|
// numweights attribute
|
||||||
else if (!ASSIMP_strincmp(sz,"numweights",10) &&
|
else if (TokenMatch(sz,"numweights",10))
|
||||||
IsSpaceOrNewLine(*(sz+=10)++))
|
|
||||||
{
|
{
|
||||||
// reserve enough storage
|
// reserve enough storage
|
||||||
AI_MD5_SKIP_SPACES();
|
AI_MD5_SKIP_SPACES();
|
||||||
unsigned int iNumWeights;
|
desc.mWeights.resize(::strtol10(sz));
|
||||||
if((iNumWeights = ::strtol10(sz)))
|
|
||||||
desc.mWeights.resize(iNumWeights);
|
|
||||||
}
|
}
|
||||||
// vert attribute
|
// vert attribute
|
||||||
// "vert 0 ( 0.394531 0.513672 ) 0 1"
|
// "vert 0 ( 0.394531 0.513672 ) 0 1"
|
||||||
else if (!ASSIMP_strincmp(sz,"vert",4) &&
|
else if (TokenMatch(sz,"vert",4))
|
||||||
IsSpaceOrNewLine(*(sz+=4)++))
|
|
||||||
{
|
{
|
||||||
AI_MD5_SKIP_SPACES();
|
AI_MD5_SKIP_SPACES();
|
||||||
unsigned int idx = ::strtol10(sz,&sz);
|
const unsigned int idx = ::strtol10(sz,&sz);
|
||||||
AI_MD5_SKIP_SPACES();
|
AI_MD5_SKIP_SPACES();
|
||||||
if (idx >= desc.mVertices.size())
|
if (idx >= desc.mVertices.size())
|
||||||
desc.mVertices.resize(idx+1);
|
desc.mVertices.resize(idx+1);
|
||||||
|
@ -363,11 +322,9 @@ MD5MeshParser::MD5MeshParser(SectionList& mSections)
|
||||||
}
|
}
|
||||||
// tri attribute
|
// tri attribute
|
||||||
// "tri 0 15 13 12"
|
// "tri 0 15 13 12"
|
||||||
else if (!ASSIMP_strincmp(sz,"tri",3) &&
|
else if (TokenMatch(sz,"tri",3)) {
|
||||||
IsSpaceOrNewLine(*(sz+=3)++))
|
|
||||||
{
|
|
||||||
AI_MD5_SKIP_SPACES();
|
AI_MD5_SKIP_SPACES();
|
||||||
unsigned int idx = ::strtol10(sz,&sz);
|
const unsigned int idx = ::strtol10(sz,&sz);
|
||||||
if (idx >= desc.mFaces.size())
|
if (idx >= desc.mFaces.size())
|
||||||
desc.mFaces.resize(idx+1);
|
desc.mFaces.resize(idx+1);
|
||||||
|
|
||||||
|
@ -381,11 +338,10 @@ MD5MeshParser::MD5MeshParser(SectionList& mSections)
|
||||||
}
|
}
|
||||||
// weight attribute
|
// weight attribute
|
||||||
// "weight 362 5 0.500000 ( -3.553583 11.893474 9.719339 )"
|
// "weight 362 5 0.500000 ( -3.553583 11.893474 9.719339 )"
|
||||||
else if (!ASSIMP_strincmp(sz,"weight",6) &&
|
else if (TokenMatch(sz,"weight",6))
|
||||||
IsSpaceOrNewLine(*(sz+=6)++))
|
|
||||||
{
|
{
|
||||||
AI_MD5_SKIP_SPACES();
|
AI_MD5_SKIP_SPACES();
|
||||||
unsigned int idx = ::strtol10(sz,&sz);
|
const unsigned int idx = ::strtol10(sz,&sz);
|
||||||
AI_MD5_SKIP_SPACES();
|
AI_MD5_SKIP_SPACES();
|
||||||
if (idx >= desc.mWeights.size())
|
if (idx >= desc.mWeights.size())
|
||||||
desc.mWeights.resize(idx+1);
|
desc.mWeights.resize(idx+1);
|
||||||
|
@ -403,6 +359,7 @@ MD5MeshParser::MD5MeshParser(SectionList& mSections)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// .MD5ANIM parsing function
|
||||||
MD5AnimParser::MD5AnimParser(SectionList& mSections)
|
MD5AnimParser::MD5AnimParser(SectionList& mSections)
|
||||||
{
|
{
|
||||||
DefaultLogger::get()->debug("MD5AnimParser begin");
|
DefaultLogger::get()->debug("MD5AnimParser begin");
|
||||||
|
@ -411,18 +368,10 @@ MD5AnimParser::MD5AnimParser(SectionList& mSections)
|
||||||
mNumAnimatedComponents = 0xffffffff;
|
mNumAnimatedComponents = 0xffffffff;
|
||||||
|
|
||||||
// now parse all sections
|
// now parse all sections
|
||||||
for (SectionList::const_iterator
|
for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end();iter != iterEnd;++iter) {
|
||||||
iter = mSections.begin(), iterEnd = mSections.end();
|
if ((*iter).mName == "hierarchy") {
|
||||||
iter != iterEnd;++iter)
|
|
||||||
{
|
|
||||||
if (!::strcmp("hierarchy",(*iter).mName.c_str()))
|
|
||||||
{
|
|
||||||
// now read all elements
|
|
||||||
// "sheath" 0 63 6
|
// "sheath" 0 63 6
|
||||||
for (ElementList::const_iterator
|
for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();eit != eitEnd; ++eit) {
|
||||||
eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();
|
|
||||||
eit != eitEnd; ++eit)
|
|
||||||
{
|
|
||||||
mAnimatedBones.push_back ( AnimBoneDesc () );
|
mAnimatedBones.push_back ( AnimBoneDesc () );
|
||||||
AnimBoneDesc& desc = mAnimatedBones.back();
|
AnimBoneDesc& desc = mAnimatedBones.back();
|
||||||
|
|
||||||
|
@ -430,20 +379,13 @@ MD5AnimParser::MD5AnimParser(SectionList& mSections)
|
||||||
AI_MD5_PARSE_STRING(desc.mName);
|
AI_MD5_PARSE_STRING(desc.mName);
|
||||||
AI_MD5_SKIP_SPACES();
|
AI_MD5_SKIP_SPACES();
|
||||||
|
|
||||||
// parent index
|
// parent index - negative values are allowed (at least -1)
|
||||||
// negative values can occur here ...
|
desc.mParentIndex = ::strtol10s(sz,&sz);
|
||||||
bool bNeg = false;
|
|
||||||
if ('-' == *sz){sz++;bNeg = true;}
|
|
||||||
else if ('+' == *sz){sz++;}
|
|
||||||
desc.mParentIndex = (int)::strtol10(sz,&sz);
|
|
||||||
if (bNeg)desc.mParentIndex *= -1;
|
|
||||||
|
|
||||||
// flags (highest is 2^6-1)
|
// flags (highest is 2^6-1)
|
||||||
AI_MD5_SKIP_SPACES();
|
AI_MD5_SKIP_SPACES();
|
||||||
if(63 < (desc.iFlags = ::strtol10(sz,&sz)))
|
if(63 < (desc.iFlags = ::strtol10(sz,&sz))){
|
||||||
{
|
MD5Parser::ReportWarning("Invalid flag combination in hierarchy section",(*eit).iLineNumber);
|
||||||
MD5Parser::ReportWarning("Invalid flag combination in hierarchy section",
|
|
||||||
(*eit).iLineNumber);
|
|
||||||
}
|
}
|
||||||
AI_MD5_SKIP_SPACES();
|
AI_MD5_SKIP_SPACES();
|
||||||
|
|
||||||
|
@ -451,14 +393,9 @@ MD5AnimParser::MD5AnimParser(SectionList& mSections)
|
||||||
desc.iFirstKeyIndex = ::strtol10(sz,&sz);
|
desc.iFirstKeyIndex = ::strtol10(sz,&sz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(!::strcmp("baseframe",(*iter).mName.c_str()))
|
else if((*iter).mName == "baseframe") {
|
||||||
{
|
|
||||||
// now read all elements
|
|
||||||
// ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000242 0.707107 )
|
// ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000242 0.707107 )
|
||||||
for (ElementList::const_iterator
|
for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end(); eit != eitEnd; ++eit) {
|
||||||
eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();
|
|
||||||
eit != eitEnd; ++eit)
|
|
||||||
{
|
|
||||||
const char* sz = (*eit).szStart;
|
const char* sz = (*eit).szStart;
|
||||||
|
|
||||||
mBaseFrames.push_back ( BaseFrameDesc () );
|
mBaseFrames.push_back ( BaseFrameDesc () );
|
||||||
|
@ -468,12 +405,10 @@ MD5AnimParser::MD5AnimParser(SectionList& mSections)
|
||||||
AI_MD5_READ_TRIPLE(desc.vRotationQuat);
|
AI_MD5_READ_TRIPLE(desc.vRotationQuat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(!::strcmp("frame",(*iter).mName.c_str()))
|
else if((*iter).mName == "frame") {
|
||||||
{
|
|
||||||
if (!(*iter).mGlobalValue.length())
|
if (!(*iter).mGlobalValue.length())
|
||||||
{
|
{
|
||||||
MD5Parser::ReportWarning("A frame section must have a frame index",
|
MD5Parser::ReportWarning("A frame section must have a frame index",(*iter).iLineNumber);
|
||||||
(*iter).iLineNumber);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,57 +420,43 @@ MD5AnimParser::MD5AnimParser(SectionList& mSections)
|
||||||
if (0xffffffff != mNumAnimatedComponents)
|
if (0xffffffff != mNumAnimatedComponents)
|
||||||
desc.mValues.reserve(mNumAnimatedComponents);
|
desc.mValues.reserve(mNumAnimatedComponents);
|
||||||
|
|
||||||
// now read all elements
|
// now read all elements (continous list of floats)
|
||||||
// (continous list of float values)
|
for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end(); eit != eitEnd; ++eit){
|
||||||
for (ElementList::const_iterator
|
|
||||||
eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();
|
|
||||||
eit != eitEnd; ++eit)
|
|
||||||
{
|
|
||||||
const char* sz = (*eit).szStart;
|
const char* sz = (*eit).szStart;
|
||||||
while (SkipSpaces(sz,&sz))
|
while (SkipSpacesAndLineEnd(&sz)) {
|
||||||
{
|
float f;sz = fast_atof_move(sz,f);
|
||||||
float f;
|
|
||||||
sz = fast_atof_move(sz,f);
|
|
||||||
desc.mValues.push_back(f);
|
desc.mValues.push_back(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(!::strcmp("numFrames",(*iter).mName.c_str()))
|
else if((*iter).mName == "numFrames") {
|
||||||
{
|
mFrames.reserve(::strtol10((*iter).mGlobalValue.c_str()));
|
||||||
unsigned int iNum;
|
|
||||||
if((iNum = ::strtol10((*iter).mGlobalValue.c_str())))
|
|
||||||
{
|
|
||||||
mFrames.reserve(iNum);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if(!::strcmp("numJoints",(*iter).mName.c_str()))
|
else if((*iter).mName == "numJoints") {
|
||||||
{
|
const unsigned int num = ::strtol10((*iter).mGlobalValue.c_str());
|
||||||
unsigned int iNum;
|
mAnimatedBones.reserve(num);
|
||||||
if((iNum = ::strtol10((*iter).mGlobalValue.c_str())))
|
|
||||||
{
|
|
||||||
mAnimatedBones.reserve(iNum);
|
|
||||||
|
|
||||||
// try to guess the number of animated components if that element is not given
|
// try to guess the number of animated components if that element is not given
|
||||||
if (0xffffffff == mNumAnimatedComponents)
|
if (0xffffffff == mNumAnimatedComponents)
|
||||||
mNumAnimatedComponents = iNum * 6;
|
mNumAnimatedComponents = num * 6;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if(!::strcmp("numAnimatedComponents",(*iter).mName.c_str()))
|
else if((*iter).mName == "numAnimatedComponents") {
|
||||||
{
|
mAnimatedBones.reserve( ::strtol10((*iter).mGlobalValue.c_str()));
|
||||||
unsigned int iNum;
|
|
||||||
if((iNum = ::strtol10((*iter).mGlobalValue.c_str())))
|
|
||||||
{
|
|
||||||
mAnimatedBones.reserve(iNum);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if(!::strcmp("frameRate",(*iter).mName.c_str()))
|
else if((*iter).mName == "frameRate") {
|
||||||
{
|
fast_atof_move((*iter).mGlobalValue.c_str(),fFrameRate);
|
||||||
fast_atof_move((*iter).mGlobalValue.c_str(),this->fFrameRate);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DefaultLogger::get()->debug("MD5AnimParser end");
|
DefaultLogger::get()->debug("MD5AnimParser end");
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef AI_MD5_SKIP_SPACES
|
// ------------------------------------------------------------------------------------------------
|
||||||
#undef AI_MD5_READ_TRIPLE
|
// .MD5CAMERA parsing function
|
||||||
#undef AI_MD5_PARSE_STRING
|
MD5CameraParser::MD5CameraParser(SectionList& mSections)
|
||||||
|
{
|
||||||
|
DefaultLogger::get()->debug("MD5CameraParser begin");
|
||||||
|
fFrameRate = 24.0f;
|
||||||
|
|
||||||
|
DefaultLogger::get()->debug("MD5CameraParser end");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,21 +98,28 @@ struct Section
|
||||||
typedef std::vector< Section> SectionList;
|
typedef std::vector< Section> SectionList;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** Represents a bone (joint) descriptor in a MD5Mesh file
|
/** Basic information about a joint
|
||||||
*/
|
*/
|
||||||
struct BoneDesc
|
struct BaseJointDescription
|
||||||
{
|
{
|
||||||
//! Name of the bone
|
//! Name of the bone
|
||||||
aiString mName;
|
aiString mName;
|
||||||
|
|
||||||
//! Parent index of the bone
|
//! Parent index of the bone
|
||||||
int mParentIndex;
|
int mParentIndex;
|
||||||
|
};
|
||||||
|
|
||||||
//! Relative position of the bone
|
// ---------------------------------------------------------------------------
|
||||||
|
/** Represents a bone (joint) descriptor in a MD5Mesh file
|
||||||
|
*/
|
||||||
|
struct BoneDesc : BaseJointDescription
|
||||||
|
{
|
||||||
|
//! Absolute position of the bone
|
||||||
aiVector3D mPositionXYZ;
|
aiVector3D mPositionXYZ;
|
||||||
|
|
||||||
//! Relative rotation of the bone
|
//! Absolute rotation of the bone
|
||||||
aiVector3D mRotationQuat;
|
aiVector3D mRotationQuat;
|
||||||
|
aiQuaternion mRotationQuatConverted;
|
||||||
|
|
||||||
//! Absolute transformation of the bone
|
//! Absolute transformation of the bone
|
||||||
//! (temporary)
|
//! (temporary)
|
||||||
|
@ -131,14 +138,8 @@ typedef std::vector< BoneDesc > BoneList;
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** Represents a bone (joint) descriptor in a MD5Anim file
|
/** Represents a bone (joint) descriptor in a MD5Anim file
|
||||||
*/
|
*/
|
||||||
struct AnimBoneDesc
|
struct AnimBoneDesc : BaseJointDescription
|
||||||
{
|
{
|
||||||
//! Name of the bone
|
|
||||||
aiString mName;
|
|
||||||
|
|
||||||
//! Parent index of the bone
|
|
||||||
int mParentIndex;
|
|
||||||
|
|
||||||
//! Flags (AI_MD5_ANIMATION_FLAG_xxx)
|
//! Flags (AI_MD5_ANIMATION_FLAG_xxx)
|
||||||
unsigned int iFlags;
|
unsigned int iFlags;
|
||||||
|
|
||||||
|
@ -160,6 +161,15 @@ struct BaseFrameDesc
|
||||||
|
|
||||||
typedef std::vector< BaseFrameDesc > BaseFrameList;
|
typedef std::vector< BaseFrameDesc > BaseFrameList;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
/** Represents a camera animation frame in a MDCamera file
|
||||||
|
*/
|
||||||
|
struct CameraAnimFrameDesc : BaseFrameDesc
|
||||||
|
{
|
||||||
|
float fFOV;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::vector< CameraAnimFrameDesc > CameraFrameList;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** Represents a frame descriptor in a MD5Anim file
|
/** Represents a frame descriptor in a MD5Anim file
|
||||||
|
@ -237,6 +247,21 @@ struct MeshDesc
|
||||||
|
|
||||||
typedef std::vector< MeshDesc > MeshList;
|
typedef std::vector< MeshDesc > MeshList;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Convert a quaternion to its usual representation
|
||||||
|
inline void ConvertQuaternion (const aiVector3D& in, aiQuaternion& out) {
|
||||||
|
|
||||||
|
out.x = in.x;
|
||||||
|
out.y = in.y;
|
||||||
|
out.z = in.z;
|
||||||
|
|
||||||
|
const float t = 1.0f - (in.x*in.x) - (in.y*in.y) - (in.z*in.z);
|
||||||
|
|
||||||
|
if (t < 0.0f)
|
||||||
|
out.w = 0.0f;
|
||||||
|
else out.w = sqrt (t);
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** Parses the data sections of a MD5 mesh file
|
/** Parses the data sections of a MD5 mesh file
|
||||||
*/
|
*/
|
||||||
|
@ -259,14 +284,6 @@ public:
|
||||||
BoneList mJoints;
|
BoneList mJoints;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define AI_MD5_ANIMATION_FLAG_TRANSLATE_X 0x1
|
|
||||||
#define AI_MD5_ANIMATION_FLAG_TRANSLATE_Y 0x2
|
|
||||||
#define AI_MD5_ANIMATION_FLAG_TRANSLATE_Z 0x4
|
|
||||||
|
|
||||||
#define AI_MD5_ANIMATION_FLAG_ROTQUAT_X 0x8
|
|
||||||
#define AI_MD5_ANIMATION_FLAG_ROTQUAT_Y 0x10
|
|
||||||
#define AI_MD5_ANIMATION_FLAG_ROTQUAT_Z 0x12
|
|
||||||
|
|
||||||
// remove this flag if you need to the bounding box data
|
// remove this flag if you need to the bounding box data
|
||||||
#define AI_MD5_PARSE_NO_BOUNDS
|
#define AI_MD5_PARSE_NO_BOUNDS
|
||||||
|
|
||||||
|
@ -302,6 +319,32 @@ public:
|
||||||
unsigned int mNumAnimatedComponents;
|
unsigned int mNumAnimatedComponents;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
/** Parses the data sections of a MD5 camera animation file
|
||||||
|
*/
|
||||||
|
class MD5CameraParser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
/** Constructs a new MD5CameraParser instance from an existing
|
||||||
|
* preparsed list of file sections.
|
||||||
|
*
|
||||||
|
* @param mSections List of file sections (output of MD5Parser)
|
||||||
|
*/
|
||||||
|
MD5CameraParser(SectionList& mSections);
|
||||||
|
|
||||||
|
|
||||||
|
//! Output frame rate
|
||||||
|
float fFrameRate;
|
||||||
|
|
||||||
|
//! List of cuts
|
||||||
|
std::vector<unsigned int> cuts;
|
||||||
|
|
||||||
|
//! Frames
|
||||||
|
CameraFrameList frames;
|
||||||
|
};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** Parses the block structure of MD5MESH and MD5ANIM files (but does no
|
/** Parses the block structure of MD5MESH and MD5ANIM files (but does no
|
||||||
* further processing)
|
* further processing)
|
||||||
|
|
|
@ -48,14 +48,17 @@ using namespace Assimp;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// The constructor processes the given scene and adds a mesh there.
|
// The constructor processes the given scene and adds a mesh there.
|
||||||
SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene)
|
SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root)
|
||||||
{
|
{
|
||||||
// nothing to do if there's mesh data already present at the scene
|
// nothing to do if there's mesh data already present at the scene
|
||||||
if( pScene->mNumMeshes > 0 || pScene->mRootNode == NULL)
|
if( pScene->mNumMeshes > 0 || pScene->mRootNode == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!root)
|
||||||
|
root = pScene->mRootNode;
|
||||||
|
|
||||||
// build some faces around each node
|
// build some faces around each node
|
||||||
CreateGeometry( pScene->mRootNode);
|
CreateGeometry( root );
|
||||||
|
|
||||||
// create a mesh to hold all the generated faces
|
// create a mesh to hold all the generated faces
|
||||||
pScene->mNumMeshes = 1;
|
pScene->mNumMeshes = 1;
|
||||||
|
|
|
@ -63,8 +63,9 @@ public:
|
||||||
/** The constructor processes the given scene and adds a mesh there. Does nothing
|
/** The constructor processes the given scene and adds a mesh there. Does nothing
|
||||||
* if the scene already has mesh data.
|
* if the scene already has mesh data.
|
||||||
* @param pScene The scene for which a skeleton mesh should be constructed.
|
* @param pScene The scene for which a skeleton mesh should be constructed.
|
||||||
|
* @param root The node to start with. NULL is the scene root
|
||||||
*/
|
*/
|
||||||
SkeletonMeshBuilder( aiScene* pScene);
|
SkeletonMeshBuilder( aiScene* pScene, aiNode* root = NULL);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/** Recursively builds a simple mesh representation for the given node and also creates
|
/** Recursively builds a simple mesh representation for the given node and also creates
|
||||||
|
|
|
@ -367,6 +367,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#define AI_CONFIG_PP_FD_REMOVE \
|
#define AI_CONFIG_PP_FD_REMOVE \
|
||||||
"PP_FD_REMOVE"
|
"PP_FD_REMOVE"
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
/** @brief Configures the MD5 loader to not load the MD5ANIM file for
|
||||||
|
* a MD5MESH file automatically.
|
||||||
|
*
|
||||||
|
* The default strategy is to look for a file with the same name but the
|
||||||
|
* MD5ANIM extension in the same directory. If it is found, it is loaded
|
||||||
|
* and combined with the MD5MESH file. This configuration option can be
|
||||||
|
* used to disable this behaviour.
|
||||||
|
*
|
||||||
|
* Property type: integer (0: false; !0: true). Default value: false.
|
||||||
|
*/
|
||||||
|
#define AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD \
|
||||||
|
"IMPORT_MD5_NO_ANIM_AUTOLOAD"
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
/** @brief Specifies the shape of the scene returned by the CSM format loader.
|
/** @brief Specifies the shape of the scene returned by the CSM format loader.
|
||||||
|
|
|
@ -84,6 +84,12 @@ struct aiQuaternion
|
||||||
/** Normalize the quaternion */
|
/** Normalize the quaternion */
|
||||||
aiQuaternion& Normalize();
|
aiQuaternion& Normalize();
|
||||||
|
|
||||||
|
/** Compute quaternion conjugate */
|
||||||
|
aiQuaternion& Conjugate ();
|
||||||
|
|
||||||
|
/** Rotate a point by this quaternion */
|
||||||
|
aiVector3D Rotate (const aiVector3D& in);
|
||||||
|
|
||||||
/** Multiply two quaternions */
|
/** Multiply two quaternions */
|
||||||
aiQuaternion operator* (const aiQuaternion& two) const;
|
aiQuaternion operator* (const aiQuaternion& two) const;
|
||||||
|
|
||||||
|
@ -204,13 +210,11 @@ inline aiQuaternion::aiQuaternion( aiVector3D normalized)
|
||||||
y = normalized.y;
|
y = normalized.y;
|
||||||
z = normalized.z;
|
z = normalized.z;
|
||||||
|
|
||||||
float t = 1.0f - (normalized.x * normalized.x) -
|
const float t = 1.0f - (x*x) - (y*y) - (z*z);
|
||||||
(normalized.y * normalized.y) - (normalized.z * normalized.z);
|
|
||||||
|
|
||||||
if (t < 0.0f)
|
if (t < 0.0f)
|
||||||
w = 0.0f;
|
w = 0.0f;
|
||||||
else w = sqrt (t);
|
else w = sqrt (t);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -280,6 +284,26 @@ inline aiQuaternion aiQuaternion::operator* (const aiQuaternion& t) const
|
||||||
w*t.z + z*t.w + x*t.y - y*t.x);
|
w*t.z + z*t.w + x*t.y - y*t.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
inline aiQuaternion& aiQuaternion::Conjugate ()
|
||||||
|
{
|
||||||
|
x = -x;
|
||||||
|
y = -y;
|
||||||
|
z = -z;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
inline aiVector3D aiQuaternion::Rotate (const aiVector3D& v)
|
||||||
|
{
|
||||||
|
aiQuaternion q2(0.f,v.x,v.y,v.z), q = *this, qinv = q;
|
||||||
|
q.Conjugate();
|
||||||
|
|
||||||
|
q = q*q2*qinv;
|
||||||
|
return aiVector3D(q.x,q.y,q.z);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
} // end extern "C"
|
} // end extern "C"
|
||||||
|
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
MD5Version 10
|
||||||
|
commandline "mesh models/code/code.ma -dest models/code/code.md5mesh -game blackrose -game sw"
|
||||||
|
|
||||||
|
numJoints 3
|
||||||
|
numMeshes 1
|
||||||
|
|
||||||
|
joints {
|
||||||
|
"origin" -1 ( 0 0 0 ) ( 0 0 0 ) //
|
||||||
|
"root" 0 ( 0 0 0 ) ( -0.5001951456 0.4998047054 -0.4998047054 ) // origin
|
||||||
|
"joint1" 1 ( 0.0249728262 0.0000019062 31.9803333282 ) ( -0.7071067691 0.0000000527 0.0000000105 ) // root
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh {
|
||||||
|
// meshes: Mesh
|
||||||
|
shader ""
|
||||||
|
|
||||||
|
numverts 24
|
||||||
|
vert 0 ( 0.1875 -0.25 ) 0 2
|
||||||
|
vert 1 ( -0.3125 -0.25 ) 2 2
|
||||||
|
vert 2 ( -0.3125 0.25 ) 4 2
|
||||||
|
vert 3 ( 0.1875 0.25 ) 6 2
|
||||||
|
vert 4 ( 0.1875 -0.25 ) 8 2
|
||||||
|
vert 5 ( -0.3125 -0.25 ) 10 2
|
||||||
|
vert 6 ( -0.3125 0.25 ) 12 2
|
||||||
|
vert 7 ( 0.1875 0.25 ) 14 2
|
||||||
|
vert 8 ( -0.25 -0.5 ) 10 2
|
||||||
|
vert 9 ( -0.25 0 ) 4 2
|
||||||
|
vert 10 ( 0.25 0 ) 2 2
|
||||||
|
vert 11 ( 0.25 -0.5 ) 12 2
|
||||||
|
vert 12 ( -0.3125 -0.5 ) 12 2
|
||||||
|
vert 13 ( -0.3125 0 ) 2 2
|
||||||
|
vert 14 ( 0.1875 0 ) 0 2
|
||||||
|
vert 15 ( 0.1875 -0.5 ) 14 2
|
||||||
|
vert 16 ( -0.25 -0.5 ) 14 2
|
||||||
|
vert 17 ( -0.25 0 ) 0 2
|
||||||
|
vert 18 ( 0.25 0 ) 6 2
|
||||||
|
vert 19 ( 0.25 -0.5 ) 8 2
|
||||||
|
vert 20 ( -0.1875 -0.5 ) 8 2
|
||||||
|
vert 21 ( -0.1875 0 ) 6 2
|
||||||
|
vert 22 ( 0.3125 0 ) 4 2
|
||||||
|
vert 23 ( 0.3125 -0.5 ) 10 2
|
||||||
|
|
||||||
|
numtris 12
|
||||||
|
tri 0 2 1 0
|
||||||
|
tri 1 2 0 3
|
||||||
|
tri 2 6 5 4
|
||||||
|
tri 3 6 4 7
|
||||||
|
tri 4 10 9 8
|
||||||
|
tri 5 10 8 11
|
||||||
|
tri 6 14 13 12
|
||||||
|
tri 7 14 12 15
|
||||||
|
tri 8 18 17 16
|
||||||
|
tri 9 18 16 19
|
||||||
|
tri 10 22 21 20
|
||||||
|
tri 11 22 20 23
|
||||||
|
|
||||||
|
numweights 16
|
||||||
|
weight 0 1 0.7386021614 ( -31.9750022888 -32.0249786377 -32 )
|
||||||
|
weight 1 2 0.2613978386 ( 31.9750270844 -63.9803352356 -32 )
|
||||||
|
weight 2 1 0.7386021614 ( -31.9750022888 -32.0249786377 32 )
|
||||||
|
weight 3 2 0.2613978386 ( 31.9750270844 -63.9803352356 32 )
|
||||||
|
weight 4 1 0.7387529016 ( -32.0249786377 31.9750022888 32 )
|
||||||
|
weight 5 2 0.2612471282 ( -32.0249710083 -63.9803352356 32 )
|
||||||
|
weight 6 1 0.7387529016 ( -32.0249786377 31.9750022888 -32 )
|
||||||
|
weight 7 2 0.2612471282 ( -32.0249710083 -63.9803352356 -32 )
|
||||||
|
weight 8 1 0.5 ( 31.9750022888 32.0249786377 -32 )
|
||||||
|
weight 9 2 0.5 ( -32.0249710083 0.0196647644 -32 )
|
||||||
|
weight 10 1 0.5 ( 31.9750022888 32.0249786377 32 )
|
||||||
|
weight 11 2 0.5 ( -32.0249710083 0.0196647644 32 )
|
||||||
|
weight 12 1 0.5 ( 32.0249786377 -31.9750022888 32 )
|
||||||
|
weight 13 2 0.5 ( 31.9750270844 0.0196647644 32 )
|
||||||
|
weight 14 1 0.5 ( 32.0249786377 -31.9750022888 -32 )
|
||||||
|
weight 15 2 0.5 ( 31.9750270844 0.0196647644 -32 )
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
From http://www.doom3world.org/phpbb2/viewtopic.php?f=3&t=16842 (thanks, Rayne, whoever you are)
|
|
@ -148,6 +148,7 @@ DWORD WINAPI LoadThreadProc(LPVOID lpParameter)
|
||||||
aiProcess_GenUVCoords | // convert spherical, cylindrical, box and planar mapping to proper UVs
|
aiProcess_GenUVCoords | // convert spherical, cylindrical, box and planar mapping to proper UVs
|
||||||
aiProcess_TransformUVCoords | // preprocess UV transformations (scaling, translation ...)
|
aiProcess_TransformUVCoords | // preprocess UV transformations (scaling, translation ...)
|
||||||
aiProcess_FindInstances | // search for instanced meshes and remove them by references to one master
|
aiProcess_FindInstances | // search for instanced meshes and remove them by references to one master
|
||||||
|
aiProcess_LimitBoneWeights | // limit bone weights to 4 per vertex
|
||||||
// aiProcess_PreTransformVertices |
|
// aiProcess_PreTransformVertices |
|
||||||
0);
|
0);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue