vrshoot

annotate libs/assimp/MD5Loader.cpp @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
rev   line source
nuclear@0 1 /*
nuclear@0 2 ---------------------------------------------------------------------------
nuclear@0 3 Open Asset Import Library (assimp)
nuclear@0 4 ---------------------------------------------------------------------------
nuclear@0 5
nuclear@0 6 Copyright (c) 2006-2012, assimp team
nuclear@0 7
nuclear@0 8 All rights reserved.
nuclear@0 9
nuclear@0 10 Redistribution and use of this software in source and binary forms,
nuclear@0 11 with or without modification, are permitted provided that the following
nuclear@0 12 conditions are met:
nuclear@0 13
nuclear@0 14 * Redistributions of source code must retain the above
nuclear@0 15 copyright notice, this list of conditions and the
nuclear@0 16 following disclaimer.
nuclear@0 17
nuclear@0 18 * Redistributions in binary form must reproduce the above
nuclear@0 19 copyright notice, this list of conditions and the
nuclear@0 20 following disclaimer in the documentation and/or other
nuclear@0 21 materials provided with the distribution.
nuclear@0 22
nuclear@0 23 * Neither the name of the assimp team, nor the names of its
nuclear@0 24 contributors may be used to endorse or promote products
nuclear@0 25 derived from this software without specific prior
nuclear@0 26 written permission of the assimp team.
nuclear@0 27
nuclear@0 28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
nuclear@0 29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
nuclear@0 30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
nuclear@0 31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
nuclear@0 32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
nuclear@0 33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
nuclear@0 34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
nuclear@0 35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
nuclear@0 36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
nuclear@0 37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
nuclear@0 38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
nuclear@0 39 ---------------------------------------------------------------------------
nuclear@0 40 */
nuclear@0 41
nuclear@0 42 /** @file MD5Loader.cpp
nuclear@0 43 * @brief Implementation of the MD5 importer class
nuclear@0 44 */
nuclear@0 45
nuclear@0 46 #include "AssimpPCH.h"
nuclear@0 47 #ifndef ASSIMP_BUILD_NO_MD5_IMPORTER
nuclear@0 48
nuclear@0 49 // internal headers
nuclear@0 50 #include "RemoveComments.h"
nuclear@0 51 #include "MD5Loader.h"
nuclear@0 52 #include "StringComparison.h"
nuclear@0 53 #include "fast_atof.h"
nuclear@0 54 #include "SkeletonMeshBuilder.h"
nuclear@0 55
nuclear@0 56 using namespace Assimp;
nuclear@0 57
nuclear@0 58 // Minimum weight value. Weights inside [-n ... n] are ignored
nuclear@0 59 #define AI_MD5_WEIGHT_EPSILON 1e-5f
nuclear@0 60
nuclear@0 61
nuclear@0 62 static const aiImporterDesc desc = {
nuclear@0 63 "Doom 3 / MD5 Mesh Importer",
nuclear@0 64 "",
nuclear@0 65 "",
nuclear@0 66 "",
nuclear@0 67 aiImporterFlags_SupportBinaryFlavour,
nuclear@0 68 0,
nuclear@0 69 0,
nuclear@0 70 0,
nuclear@0 71 0,
nuclear@0 72 "md5mesh md5camera md5anim"
nuclear@0 73 };
nuclear@0 74
nuclear@0 75 // ------------------------------------------------------------------------------------------------
nuclear@0 76 // Constructor to be privately used by Importer
nuclear@0 77 MD5Importer::MD5Importer()
nuclear@0 78 : mBuffer()
nuclear@0 79 , configNoAutoLoad (false)
nuclear@0 80 {}
nuclear@0 81
nuclear@0 82 // ------------------------------------------------------------------------------------------------
nuclear@0 83 // Destructor, private as well
nuclear@0 84 MD5Importer::~MD5Importer()
nuclear@0 85 {}
nuclear@0 86
nuclear@0 87 // ------------------------------------------------------------------------------------------------
nuclear@0 88 // Returns whether the class can handle the format of the given file.
nuclear@0 89 bool MD5Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
nuclear@0 90 {
nuclear@0 91 const std::string extension = GetExtension(pFile);
nuclear@0 92
nuclear@0 93 if (extension == "md5anim" || extension == "md5mesh" || extension == "md5camera")
nuclear@0 94 return true;
nuclear@0 95 else if (!extension.length() || checkSig) {
nuclear@0 96 if (!pIOHandler) {
nuclear@0 97 return true;
nuclear@0 98 }
nuclear@0 99 const char* tokens[] = {"MD5Version"};
nuclear@0 100 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
nuclear@0 101 }
nuclear@0 102 return false;
nuclear@0 103 }
nuclear@0 104
nuclear@0 105 // ------------------------------------------------------------------------------------------------
nuclear@0 106 // Get list of all supported extensions
nuclear@0 107 const aiImporterDesc* MD5Importer::GetInfo () const
nuclear@0 108 {
nuclear@0 109 return &desc;
nuclear@0 110 }
nuclear@0 111
nuclear@0 112 // ------------------------------------------------------------------------------------------------
nuclear@0 113 // Setup import properties
nuclear@0 114 void MD5Importer::SetupProperties(const Importer* pImp)
nuclear@0 115 {
nuclear@0 116 // AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD
nuclear@0 117 configNoAutoLoad = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD,0));
nuclear@0 118 }
nuclear@0 119
nuclear@0 120 // ------------------------------------------------------------------------------------------------
nuclear@0 121 // Imports the given file into the given scene structure.
nuclear@0 122 void MD5Importer::InternReadFile( const std::string& pFile,
nuclear@0 123 aiScene* _pScene, IOSystem* _pIOHandler)
nuclear@0 124 {
nuclear@0 125 pIOHandler = _pIOHandler;
nuclear@0 126 pScene = _pScene;
nuclear@0 127 bHadMD5Mesh = bHadMD5Anim = bHadMD5Camera = false;
nuclear@0 128
nuclear@0 129 // remove the file extension
nuclear@0 130 const std::string::size_type pos = pFile.find_last_of('.');
nuclear@0 131 mFile = (std::string::npos == pos ? pFile : pFile.substr(0,pos+1));
nuclear@0 132
nuclear@0 133 const std::string extension = GetExtension(pFile);
nuclear@0 134 try {
nuclear@0 135 if (extension == "md5camera") {
nuclear@0 136 LoadMD5CameraFile();
nuclear@0 137 }
nuclear@0 138 else if (configNoAutoLoad || extension == "md5anim") {
nuclear@0 139 // determine file extension and process just *one* file
nuclear@0 140 if (extension.length() == 0) {
nuclear@0 141 throw DeadlyImportError("Failure, need file extension to determine MD5 part type");
nuclear@0 142 }
nuclear@0 143 if (extension == "md5anim") {
nuclear@0 144 LoadMD5AnimFile();
nuclear@0 145 }
nuclear@0 146 else if (extension == "md5mesh") {
nuclear@0 147 LoadMD5MeshFile();
nuclear@0 148 }
nuclear@0 149 }
nuclear@0 150 else {
nuclear@0 151 LoadMD5MeshFile();
nuclear@0 152 LoadMD5AnimFile();
nuclear@0 153 }
nuclear@0 154 }
nuclear@0 155 catch ( ... ) { // std::exception, Assimp::DeadlyImportError
nuclear@0 156 UnloadFileFromMemory();
nuclear@0 157 throw;
nuclear@0 158 }
nuclear@0 159
nuclear@0 160 // make sure we have at least one file
nuclear@0 161 if (!bHadMD5Mesh && !bHadMD5Anim && !bHadMD5Camera) {
nuclear@0 162 throw DeadlyImportError("Failed to read valid contents out of this MD5* file");
nuclear@0 163 }
nuclear@0 164
nuclear@0 165 // Now rotate the whole scene 90 degrees around the x axis to match our internal coordinate system
nuclear@0 166 pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
nuclear@0 167 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
nuclear@0 168
nuclear@0 169 // the output scene wouldn't pass the validation without this flag
nuclear@0 170 if (!bHadMD5Mesh) {
nuclear@0 171 pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
nuclear@0 172 }
nuclear@0 173
nuclear@0 174 // clean the instance -- the BaseImporter instance may be reused later.
nuclear@0 175 UnloadFileFromMemory();
nuclear@0 176 }
nuclear@0 177
nuclear@0 178 // ------------------------------------------------------------------------------------------------
nuclear@0 179 // Load a file into a memory buffer
nuclear@0 180 void MD5Importer::LoadFileIntoMemory (IOStream* file)
nuclear@0 181 {
nuclear@0 182 // unload the previous buffer, if any
nuclear@0 183 UnloadFileFromMemory();
nuclear@0 184
nuclear@0 185 ai_assert(NULL != file);
nuclear@0 186 fileSize = (unsigned int)file->FileSize();
nuclear@0 187 ai_assert(fileSize);
nuclear@0 188
nuclear@0 189 // allocate storage and copy the contents of the file to a memory buffer
nuclear@0 190 mBuffer = new char[fileSize+1];
nuclear@0 191 file->Read( (void*)mBuffer, 1, fileSize);
nuclear@0 192 iLineNumber = 1;
nuclear@0 193
nuclear@0 194 // append a terminal 0
nuclear@0 195 mBuffer[fileSize] = '\0';
nuclear@0 196
nuclear@0 197 // now remove all line comments from the file
nuclear@0 198 CommentRemover::RemoveLineComments("//",mBuffer,' ');
nuclear@0 199 }
nuclear@0 200
nuclear@0 201 // ------------------------------------------------------------------------------------------------
nuclear@0 202 // Unload the current memory buffer
nuclear@0 203 void MD5Importer::UnloadFileFromMemory ()
nuclear@0 204 {
nuclear@0 205 // delete the file buffer
nuclear@0 206 delete[] mBuffer;
nuclear@0 207 mBuffer = NULL;
nuclear@0 208 fileSize = 0;
nuclear@0 209 }
nuclear@0 210
nuclear@0 211 // ------------------------------------------------------------------------------------------------
nuclear@0 212 // Build unique vertices
nuclear@0 213 void MD5Importer::MakeDataUnique (MD5::MeshDesc& meshSrc)
nuclear@0 214 {
nuclear@0 215 std::vector<bool> abHad(meshSrc.mVertices.size(),false);
nuclear@0 216
nuclear@0 217 // allocate enough storage to keep the output structures
nuclear@0 218 const unsigned int iNewNum = meshSrc.mFaces.size()*3;
nuclear@0 219 unsigned int iNewIndex = meshSrc.mVertices.size();
nuclear@0 220 meshSrc.mVertices.resize(iNewNum);
nuclear@0 221
nuclear@0 222 // try to guess how much storage we'll need for new weights
nuclear@0 223 const float fWeightsPerVert = meshSrc.mWeights.size() / (float)iNewIndex;
nuclear@0 224 const unsigned int guess = (unsigned int)(fWeightsPerVert*iNewNum);
nuclear@0 225 meshSrc.mWeights.reserve(guess + (guess >> 3)); // + 12.5% as buffer
nuclear@0 226
nuclear@0 227 for (FaceList::const_iterator iter = meshSrc.mFaces.begin(),iterEnd = meshSrc.mFaces.end();iter != iterEnd;++iter){
nuclear@0 228 const aiFace& face = *iter;
nuclear@0 229 for (unsigned int i = 0; i < 3;++i) {
nuclear@0 230 if (face.mIndices[0] >= meshSrc.mVertices.size()) {
nuclear@0 231 throw DeadlyImportError("MD5MESH: Invalid vertex index");
nuclear@0 232 }
nuclear@0 233
nuclear@0 234 if (abHad[face.mIndices[i]]) {
nuclear@0 235 // generate a new vertex
nuclear@0 236 meshSrc.mVertices[iNewIndex] = meshSrc.mVertices[face.mIndices[i]];
nuclear@0 237 face.mIndices[i] = iNewIndex++;
nuclear@0 238 }
nuclear@0 239 else abHad[face.mIndices[i]] = true;
nuclear@0 240 }
nuclear@0 241 // swap face order
nuclear@0 242 std::swap(face.mIndices[0],face.mIndices[2]);
nuclear@0 243 }
nuclear@0 244 }
nuclear@0 245
nuclear@0 246 // ------------------------------------------------------------------------------------------------
nuclear@0 247 // Recursive node graph construction from a MD5MESH
nuclear@0 248 void MD5Importer::AttachChilds_Mesh(int iParentID,aiNode* piParent, BoneList& bones)
nuclear@0 249 {
nuclear@0 250 ai_assert(NULL != piParent && !piParent->mNumChildren);
nuclear@0 251
nuclear@0 252 // First find out how many children we'll have
nuclear@0 253 for (int i = 0; i < (int)bones.size();++i) {
nuclear@0 254 if (iParentID != i && bones[i].mParentIndex == iParentID) {
nuclear@0 255 ++piParent->mNumChildren;
nuclear@0 256 }
nuclear@0 257 }
nuclear@0 258 if (piParent->mNumChildren) {
nuclear@0 259 piParent->mChildren = new aiNode*[piParent->mNumChildren];
nuclear@0 260 for (int i = 0; i < (int)bones.size();++i) {
nuclear@0 261 // (avoid infinite recursion)
nuclear@0 262 if (iParentID != i && bones[i].mParentIndex == iParentID) {
nuclear@0 263 aiNode* pc;
nuclear@0 264 // setup a new node
nuclear@0 265 *piParent->mChildren++ = pc = new aiNode();
nuclear@0 266 pc->mName = aiString(bones[i].mName);
nuclear@0 267 pc->mParent = piParent;
nuclear@0 268
nuclear@0 269 // get the transformation matrix from rotation and translational components
nuclear@0 270 aiQuaternion quat;
nuclear@0 271 MD5::ConvertQuaternion ( bones[i].mRotationQuat, quat );
nuclear@0 272
nuclear@0 273 // FIX to get to Assimp's quaternion conventions
nuclear@0 274 quat.w *= -1.f;
nuclear@0 275
nuclear@0 276 bones[i].mTransform = aiMatrix4x4 ( quat.GetMatrix());
nuclear@0 277 bones[i].mTransform.a4 = bones[i].mPositionXYZ.x;
nuclear@0 278 bones[i].mTransform.b4 = bones[i].mPositionXYZ.y;
nuclear@0 279 bones[i].mTransform.c4 = bones[i].mPositionXYZ.z;
nuclear@0 280
nuclear@0 281 // store it for later use
nuclear@0 282 pc->mTransformation = bones[i].mInvTransform = bones[i].mTransform;
nuclear@0 283 bones[i].mInvTransform.Inverse();
nuclear@0 284
nuclear@0 285 // the transformations for each bone are absolute, so we need to multiply them
nuclear@0 286 // with the inverse of the absolute matrix of the parent joint
nuclear@0 287 if (-1 != iParentID) {
nuclear@0 288 pc->mTransformation = bones[iParentID].mInvTransform * pc->mTransformation;
nuclear@0 289 }
nuclear@0 290
nuclear@0 291 // add children to this node, too
nuclear@0 292 AttachChilds_Mesh( i, pc, bones);
nuclear@0 293 }
nuclear@0 294 }
nuclear@0 295 // undo offset computations
nuclear@0 296 piParent->mChildren -= piParent->mNumChildren;
nuclear@0 297 }
nuclear@0 298 }
nuclear@0 299
nuclear@0 300 // ------------------------------------------------------------------------------------------------
nuclear@0 301 // Recursive node graph construction from a MD5ANIM
nuclear@0 302 void MD5Importer::AttachChilds_Anim(int iParentID,aiNode* piParent, AnimBoneList& bones,const aiNodeAnim** node_anims)
nuclear@0 303 {
nuclear@0 304 ai_assert(NULL != piParent && !piParent->mNumChildren);
nuclear@0 305
nuclear@0 306 // First find out how many children we'll have
nuclear@0 307 for (int i = 0; i < (int)bones.size();++i) {
nuclear@0 308 if (iParentID != i && bones[i].mParentIndex == iParentID) {
nuclear@0 309 ++piParent->mNumChildren;
nuclear@0 310 }
nuclear@0 311 }
nuclear@0 312 if (piParent->mNumChildren) {
nuclear@0 313 piParent->mChildren = new aiNode*[piParent->mNumChildren];
nuclear@0 314 for (int i = 0; i < (int)bones.size();++i) {
nuclear@0 315 // (avoid infinite recursion)
nuclear@0 316 if (iParentID != i && bones[i].mParentIndex == iParentID)
nuclear@0 317 {
nuclear@0 318 aiNode* pc;
nuclear@0 319 // setup a new node
nuclear@0 320 *piParent->mChildren++ = pc = new aiNode();
nuclear@0 321 pc->mName = aiString(bones[i].mName);
nuclear@0 322 pc->mParent = piParent;
nuclear@0 323
nuclear@0 324 // get the corresponding animation channel and its first frame
nuclear@0 325 const aiNodeAnim** cur = node_anims;
nuclear@0 326 while ((**cur).mNodeName != pc->mName)++cur;
nuclear@0 327
nuclear@0 328 aiMatrix4x4::Translation((**cur).mPositionKeys[0].mValue,pc->mTransformation);
nuclear@0 329 pc->mTransformation = pc->mTransformation * aiMatrix4x4((**cur).mRotationKeys[0].mValue.GetMatrix()) ;
nuclear@0 330
nuclear@0 331 // add children to this node, too
nuclear@0 332 AttachChilds_Anim( i, pc, bones,node_anims);
nuclear@0 333 }
nuclear@0 334 }
nuclear@0 335 // undo offset computations
nuclear@0 336 piParent->mChildren -= piParent->mNumChildren;
nuclear@0 337 }
nuclear@0 338 }
nuclear@0 339
nuclear@0 340 // ------------------------------------------------------------------------------------------------
nuclear@0 341 // Load a MD5MESH file
nuclear@0 342 void MD5Importer::LoadMD5MeshFile ()
nuclear@0 343 {
nuclear@0 344 std::string pFile = mFile + "md5mesh";
nuclear@0 345 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
nuclear@0 346
nuclear@0 347 // Check whether we can read from the file
nuclear@0 348 if( file.get() == NULL || !file->FileSize()) {
nuclear@0 349 DefaultLogger::get()->warn("Failed to access MD5MESH file: " + pFile);
nuclear@0 350 return;
nuclear@0 351 }
nuclear@0 352 bHadMD5Mesh = true;
nuclear@0 353 LoadFileIntoMemory(file.get());
nuclear@0 354
nuclear@0 355 // now construct a parser and parse the file
nuclear@0 356 MD5::MD5Parser parser(mBuffer,fileSize);
nuclear@0 357
nuclear@0 358 // load the mesh information from it
nuclear@0 359 MD5::MD5MeshParser meshParser(parser.mSections);
nuclear@0 360
nuclear@0 361 // create the bone hierarchy - first the root node and dummy nodes for all meshes
nuclear@0 362 pScene->mRootNode = new aiNode("<MD5_Root>");
nuclear@0 363 pScene->mRootNode->mNumChildren = 2;
nuclear@0 364 pScene->mRootNode->mChildren = new aiNode*[2];
nuclear@0 365
nuclear@0 366 // build the hierarchy from the MD5MESH file
nuclear@0 367 aiNode* pcNode = pScene->mRootNode->mChildren[1] = new aiNode();
nuclear@0 368 pcNode->mName.Set("<MD5_Hierarchy>");
nuclear@0 369 pcNode->mParent = pScene->mRootNode;
nuclear@0 370 AttachChilds_Mesh(-1,pcNode,meshParser.mJoints);
nuclear@0 371
nuclear@0 372 pcNode = pScene->mRootNode->mChildren[0] = new aiNode();
nuclear@0 373 pcNode->mName.Set("<MD5_Mesh>");
nuclear@0 374 pcNode->mParent = pScene->mRootNode;
nuclear@0 375
nuclear@0 376 #if 0
nuclear@0 377 if (pScene->mRootNode->mChildren[1]->mNumChildren) /* start at the right hierarchy level */
nuclear@0 378 SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[1]->mChildren[0]);
nuclear@0 379 #else
nuclear@0 380
nuclear@0 381 // FIX: MD5 files exported from Blender can have empty meshes
nuclear@0 382 for (std::vector<MD5::MeshDesc>::const_iterator it = meshParser.mMeshes.begin(),end = meshParser.mMeshes.end(); it != end;++it) {
nuclear@0 383 if (!(*it).mFaces.empty() && !(*it).mVertices.empty())
nuclear@0 384 ++pScene->mNumMaterials;
nuclear@0 385 }
nuclear@0 386
nuclear@0 387 // generate all meshes
nuclear@0 388 pScene->mNumMeshes = pScene->mNumMaterials;
nuclear@0 389 pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
nuclear@0 390 pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
nuclear@0 391
nuclear@0 392 // storage for node mesh indices
nuclear@0 393 pcNode->mNumMeshes = pScene->mNumMeshes;
nuclear@0 394 pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
nuclear@0 395 for (unsigned int m = 0; m < pcNode->mNumMeshes;++m)
nuclear@0 396 pcNode->mMeshes[m] = m;
nuclear@0 397
nuclear@0 398 unsigned int n = 0;
nuclear@0 399 for (std::vector<MD5::MeshDesc>::iterator it = meshParser.mMeshes.begin(),end = meshParser.mMeshes.end(); it != end;++it) {
nuclear@0 400 MD5::MeshDesc& meshSrc = *it;
nuclear@0 401 if (meshSrc.mFaces.empty() || meshSrc.mVertices.empty())
nuclear@0 402 continue;
nuclear@0 403
nuclear@0 404 aiMesh* mesh = pScene->mMeshes[n] = new aiMesh();
nuclear@0 405 mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
nuclear@0 406
nuclear@0 407 // generate unique vertices in our internal verbose format
nuclear@0 408 MakeDataUnique(meshSrc);
nuclear@0 409
nuclear@0 410 mesh->mNumVertices = (unsigned int) meshSrc.mVertices.size();
nuclear@0 411 mesh->mVertices = new aiVector3D[mesh->mNumVertices];
nuclear@0 412 mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
nuclear@0 413 mesh->mNumUVComponents[0] = 2;
nuclear@0 414
nuclear@0 415 // copy texture coordinates
nuclear@0 416 aiVector3D* pv = mesh->mTextureCoords[0];
nuclear@0 417 for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
nuclear@0 418 pv->x = (*iter).mUV.x;
nuclear@0 419 pv->y = 1.0f-(*iter).mUV.y; // D3D to OpenGL
nuclear@0 420 pv->z = 0.0f;
nuclear@0 421 }
nuclear@0 422
nuclear@0 423 // sort all bone weights - per bone
nuclear@0 424 unsigned int* piCount = new unsigned int[meshParser.mJoints.size()];
nuclear@0 425 ::memset(piCount,0,sizeof(unsigned int)*meshParser.mJoints.size());
nuclear@0 426
nuclear@0 427 for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
nuclear@0 428 for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
nuclear@0 429 {
nuclear@0 430 MD5::WeightDesc& desc = meshSrc.mWeights[w];
nuclear@0 431 /* FIX for some invalid exporters */
nuclear@0 432 if (!(desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON ))
nuclear@0 433 ++piCount[desc.mBone];
nuclear@0 434 }
nuclear@0 435 }
nuclear@0 436
nuclear@0 437 // check how many we will need
nuclear@0 438 for (unsigned int p = 0; p < meshParser.mJoints.size();++p)
nuclear@0 439 if (piCount[p])mesh->mNumBones++;
nuclear@0 440
nuclear@0 441 if (mesh->mNumBones) // just for safety
nuclear@0 442 {
nuclear@0 443 mesh->mBones = new aiBone*[mesh->mNumBones];
nuclear@0 444 for (unsigned int q = 0,h = 0; q < meshParser.mJoints.size();++q)
nuclear@0 445 {
nuclear@0 446 if (!piCount[q])continue;
nuclear@0 447 aiBone* p = mesh->mBones[h] = new aiBone();
nuclear@0 448 p->mNumWeights = piCount[q];
nuclear@0 449 p->mWeights = new aiVertexWeight[p->mNumWeights];
nuclear@0 450 p->mName = aiString(meshParser.mJoints[q].mName);
nuclear@0 451 p->mOffsetMatrix = meshParser.mJoints[q].mInvTransform;
nuclear@0 452
nuclear@0 453 // store the index for later use
nuclear@0 454 MD5::BoneDesc& boneSrc = meshParser.mJoints[q];
nuclear@0 455 boneSrc.mMap = h++;
nuclear@0 456
nuclear@0 457 // compute w-component of quaternion
nuclear@0 458 MD5::ConvertQuaternion( boneSrc.mRotationQuat, boneSrc.mRotationQuatConverted );
nuclear@0 459 }
nuclear@0 460
nuclear@0 461 //unsigned int g = 0;
nuclear@0 462 pv = mesh->mVertices;
nuclear@0 463 for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
nuclear@0 464 // compute the final vertex position from all single weights
nuclear@0 465 *pv = aiVector3D();
nuclear@0 466
nuclear@0 467 // there are models which have weights which don't sum to 1 ...
nuclear@0 468 float fSum = 0.0f;
nuclear@0 469 for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
nuclear@0 470 fSum += meshSrc.mWeights[w].mWeight;
nuclear@0 471 if (!fSum) {
nuclear@0 472 DefaultLogger::get()->error("MD5MESH: The sum of all vertex bone weights is 0");
nuclear@0 473 continue;
nuclear@0 474 }
nuclear@0 475
nuclear@0 476 // process bone weights
nuclear@0 477 for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w) {
nuclear@0 478 if (w >= meshSrc.mWeights.size())
nuclear@0 479 throw DeadlyImportError("MD5MESH: Invalid weight index");
nuclear@0 480
nuclear@0 481 MD5::WeightDesc& desc = meshSrc.mWeights[w];
nuclear@0 482 if ( desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON) {
nuclear@0 483 continue;
nuclear@0 484 }
nuclear@0 485
nuclear@0 486 const float fNewWeight = desc.mWeight / fSum;
nuclear@0 487
nuclear@0 488 // transform the local position into worldspace
nuclear@0 489 MD5::BoneDesc& boneSrc = meshParser.mJoints[desc.mBone];
nuclear@0 490 const aiVector3D v = boneSrc.mRotationQuatConverted.Rotate (desc.vOffsetPosition);
nuclear@0 491
nuclear@0 492 // use the original weight to compute the vertex position
nuclear@0 493 // (some MD5s seem to depend on the invalid weight values ...)
nuclear@0 494 *pv += ((boneSrc.mPositionXYZ+v)* desc.mWeight);
nuclear@0 495
nuclear@0 496 aiBone* bone = mesh->mBones[boneSrc.mMap];
nuclear@0 497 *bone->mWeights++ = aiVertexWeight((unsigned int)(pv-mesh->mVertices),fNewWeight);
nuclear@0 498 }
nuclear@0 499 }
nuclear@0 500
nuclear@0 501 // undo our nice offset tricks ...
nuclear@0 502 for (unsigned int p = 0; p < mesh->mNumBones;++p) {
nuclear@0 503 mesh->mBones[p]->mWeights -= mesh->mBones[p]->mNumWeights;
nuclear@0 504 }
nuclear@0 505 }
nuclear@0 506
nuclear@0 507 delete[] piCount;
nuclear@0 508
nuclear@0 509 // now setup all faces - we can directly copy the list
nuclear@0 510 // (however, take care that the aiFace destructor doesn't delete the mIndices array)
nuclear@0 511 mesh->mNumFaces = (unsigned int)meshSrc.mFaces.size();
nuclear@0 512 mesh->mFaces = new aiFace[mesh->mNumFaces];
nuclear@0 513 for (unsigned int c = 0; c < mesh->mNumFaces;++c) {
nuclear@0 514 mesh->mFaces[c].mNumIndices = 3;
nuclear@0 515 mesh->mFaces[c].mIndices = meshSrc.mFaces[c].mIndices;
nuclear@0 516 meshSrc.mFaces[c].mIndices = NULL;
nuclear@0 517 }
nuclear@0 518
nuclear@0 519 // generate a material for the mesh
nuclear@0 520 aiMaterial* mat = new aiMaterial();
nuclear@0 521 pScene->mMaterials[n] = mat;
nuclear@0 522
nuclear@0 523 // insert the typical doom3 textures:
nuclear@0 524 // nnn_local.tga - normal map
nuclear@0 525 // nnn_h.tga - height map
nuclear@0 526 // nnn_s.tga - specular map
nuclear@0 527 // nnn_d.tga - diffuse map
nuclear@0 528 if (meshSrc.mShader.length && !strchr(meshSrc.mShader.data,'.')) {
nuclear@0 529
nuclear@0 530 aiString temp(meshSrc.mShader);
nuclear@0 531 temp.Append("_local.tga");
nuclear@0 532 mat->AddProperty(&temp,AI_MATKEY_TEXTURE_NORMALS(0));
nuclear@0 533
nuclear@0 534 temp = aiString(meshSrc.mShader);
nuclear@0 535 temp.Append("_s.tga");
nuclear@0 536 mat->AddProperty(&temp,AI_MATKEY_TEXTURE_SPECULAR(0));
nuclear@0 537
nuclear@0 538 temp = aiString(meshSrc.mShader);
nuclear@0 539 temp.Append("_d.tga");
nuclear@0 540 mat->AddProperty(&temp,AI_MATKEY_TEXTURE_DIFFUSE(0));
nuclear@0 541
nuclear@0 542 temp = aiString(meshSrc.mShader);
nuclear@0 543 temp.Append("_h.tga");
nuclear@0 544 mat->AddProperty(&temp,AI_MATKEY_TEXTURE_HEIGHT(0));
nuclear@0 545
nuclear@0 546 // set this also as material name
nuclear@0 547 mat->AddProperty(&meshSrc.mShader,AI_MATKEY_NAME);
nuclear@0 548 }
nuclear@0 549 else mat->AddProperty(&meshSrc.mShader,AI_MATKEY_TEXTURE_DIFFUSE(0));
nuclear@0 550 mesh->mMaterialIndex = n++;
nuclear@0 551 }
nuclear@0 552 #endif
nuclear@0 553 }
nuclear@0 554
nuclear@0 555 // ------------------------------------------------------------------------------------------------
nuclear@0 556 // Load an MD5ANIM file
nuclear@0 557 void MD5Importer::LoadMD5AnimFile ()
nuclear@0 558 {
nuclear@0 559 std::string pFile = mFile + "md5anim";
nuclear@0 560 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
nuclear@0 561
nuclear@0 562 // Check whether we can read from the file
nuclear@0 563 if( !file.get() || !file->FileSize()) {
nuclear@0 564 DefaultLogger::get()->warn("Failed to read MD5ANIM file: " + pFile);
nuclear@0 565 return;
nuclear@0 566 }
nuclear@0 567 LoadFileIntoMemory(file.get());
nuclear@0 568
nuclear@0 569 // parse the basic file structure
nuclear@0 570 MD5::MD5Parser parser(mBuffer,fileSize);
nuclear@0 571
nuclear@0 572 // load the animation information from the parse tree
nuclear@0 573 MD5::MD5AnimParser animParser(parser.mSections);
nuclear@0 574
nuclear@0 575 // generate and fill the output animation
nuclear@0 576 if (animParser.mAnimatedBones.empty() || animParser.mFrames.empty() ||
nuclear@0 577 animParser.mBaseFrames.size() != animParser.mAnimatedBones.size()) {
nuclear@0 578
nuclear@0 579 DefaultLogger::get()->error("MD5ANIM: No frames or animated bones loaded");
nuclear@0 580 }
nuclear@0 581 else {
nuclear@0 582 bHadMD5Anim = true;
nuclear@0 583
nuclear@0 584 pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations = 1];
nuclear@0 585 aiAnimation* anim = pScene->mAnimations[0] = new aiAnimation();
nuclear@0 586 anim->mNumChannels = (unsigned int)animParser.mAnimatedBones.size();
nuclear@0 587 anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
nuclear@0 588 for (unsigned int i = 0; i < anim->mNumChannels;++i) {
nuclear@0 589 aiNodeAnim* node = anim->mChannels[i] = new aiNodeAnim();
nuclear@0 590 node->mNodeName = aiString( animParser.mAnimatedBones[i].mName );
nuclear@0 591
nuclear@0 592 // allocate storage for the keyframes
nuclear@0 593 node->mPositionKeys = new aiVectorKey[animParser.mFrames.size()];
nuclear@0 594 node->mRotationKeys = new aiQuatKey[animParser.mFrames.size()];
nuclear@0 595 }
nuclear@0 596
nuclear@0 597 // 1 tick == 1 frame
nuclear@0 598 anim->mTicksPerSecond = animParser.fFrameRate;
nuclear@0 599
nuclear@0 600 for (FrameList::const_iterator iter = animParser.mFrames.begin(), iterEnd = animParser.mFrames.end();iter != iterEnd;++iter){
nuclear@0 601 double dTime = (double)(*iter).iIndex;
nuclear@0 602 aiNodeAnim** pcAnimNode = anim->mChannels;
nuclear@0 603 if (!(*iter).mValues.empty() || iter == animParser.mFrames.begin()) /* be sure we have at least one frame */
nuclear@0 604 {
nuclear@0 605 // now process all values in there ... read all joints
nuclear@0 606 MD5::BaseFrameDesc* pcBaseFrame = &animParser.mBaseFrames[0];
nuclear@0 607 for (AnimBoneList::const_iterator iter2 = animParser.mAnimatedBones.begin(); iter2 != animParser.mAnimatedBones.end();++iter2,
nuclear@0 608 ++pcAnimNode,++pcBaseFrame)
nuclear@0 609 {
nuclear@0 610 if((*iter2).iFirstKeyIndex >= (*iter).mValues.size()) {
nuclear@0 611
nuclear@0 612 // Allow for empty frames
nuclear@0 613 if ((*iter2).iFlags != 0) {
nuclear@0 614 throw DeadlyImportError("MD5: Keyframe index is out of range");
nuclear@0 615
nuclear@0 616 }
nuclear@0 617 continue;
nuclear@0 618 }
nuclear@0 619 const float* fpCur = &(*iter).mValues[(*iter2).iFirstKeyIndex];
nuclear@0 620 aiNodeAnim* pcCurAnimBone = *pcAnimNode;
nuclear@0 621
nuclear@0 622 aiVectorKey* vKey = &pcCurAnimBone->mPositionKeys[pcCurAnimBone->mNumPositionKeys++];
nuclear@0 623 aiQuatKey* qKey = &pcCurAnimBone->mRotationKeys [pcCurAnimBone->mNumRotationKeys++];
nuclear@0 624 aiVector3D vTemp;
nuclear@0 625
nuclear@0 626 // translational component
nuclear@0 627 for (unsigned int i = 0; i < 3; ++i) {
nuclear@0 628 if ((*iter2).iFlags & (1u << i)) {
nuclear@0 629 vKey->mValue[i] = *fpCur++;
nuclear@0 630 }
nuclear@0 631 else vKey->mValue[i] = pcBaseFrame->vPositionXYZ[i];
nuclear@0 632 }
nuclear@0 633
nuclear@0 634 // orientation component
nuclear@0 635 for (unsigned int i = 0; i < 3; ++i) {
nuclear@0 636 if ((*iter2).iFlags & (8u << i)) {
nuclear@0 637 vTemp[i] = *fpCur++;
nuclear@0 638 }
nuclear@0 639 else vTemp[i] = pcBaseFrame->vRotationQuat[i];
nuclear@0 640 }
nuclear@0 641
nuclear@0 642 MD5::ConvertQuaternion(vTemp, qKey->mValue);
nuclear@0 643 qKey->mTime = vKey->mTime = dTime;
nuclear@0 644
nuclear@0 645 // we need this to get to Assimp quaternion conventions
nuclear@0 646 qKey->mValue.w *= -1.f;
nuclear@0 647 }
nuclear@0 648 }
nuclear@0 649
nuclear@0 650 // compute the duration of the animation
nuclear@0 651 anim->mDuration = std::max(dTime,anim->mDuration);
nuclear@0 652 }
nuclear@0 653
nuclear@0 654 // If we didn't build the hierarchy yet (== we didn't load a MD5MESH),
nuclear@0 655 // construct it now from the data given in the MD5ANIM.
nuclear@0 656 if (!pScene->mRootNode) {
nuclear@0 657 pScene->mRootNode = new aiNode();
nuclear@0 658 pScene->mRootNode->mName.Set("<MD5_Hierarchy>");
nuclear@0 659
nuclear@0 660 AttachChilds_Anim(-1,pScene->mRootNode,animParser.mAnimatedBones,(const aiNodeAnim**)anim->mChannels);
nuclear@0 661
nuclear@0 662 // Call SkeletonMeshBuilder to construct a mesh to represent the shape
nuclear@0 663 if (pScene->mRootNode->mNumChildren) {
nuclear@0 664 SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[0]);
nuclear@0 665 }
nuclear@0 666 }
nuclear@0 667 }
nuclear@0 668 }
nuclear@0 669
nuclear@0 670 // ------------------------------------------------------------------------------------------------
nuclear@0 671 // Load an MD5CAMERA file
nuclear@0 672 void MD5Importer::LoadMD5CameraFile ()
nuclear@0 673 {
nuclear@0 674 std::string pFile = mFile + "md5camera";
nuclear@0 675 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
nuclear@0 676
nuclear@0 677 // Check whether we can read from the file
nuclear@0 678 if( !file.get() || !file->FileSize()) {
nuclear@0 679 throw DeadlyImportError("Failed to read MD5CAMERA file: " + pFile);
nuclear@0 680 }
nuclear@0 681 bHadMD5Camera = true;
nuclear@0 682 LoadFileIntoMemory(file.get());
nuclear@0 683
nuclear@0 684 // parse the basic file structure
nuclear@0 685 MD5::MD5Parser parser(mBuffer,fileSize);
nuclear@0 686
nuclear@0 687 // load the camera animation data from the parse tree
nuclear@0 688 MD5::MD5CameraParser cameraParser(parser.mSections);
nuclear@0 689
nuclear@0 690 if (cameraParser.frames.empty()) {
nuclear@0 691 throw DeadlyImportError("MD5CAMERA: No frames parsed");
nuclear@0 692 }
nuclear@0 693
nuclear@0 694 std::vector<unsigned int>& cuts = cameraParser.cuts;
nuclear@0 695 std::vector<MD5::CameraAnimFrameDesc>& frames = cameraParser.frames;
nuclear@0 696
nuclear@0 697 // Construct output graph - a simple root with a dummy child.
nuclear@0 698 // The root node performs the coordinate system conversion
nuclear@0 699 aiNode* root = pScene->mRootNode = new aiNode("<MD5CameraRoot>");
nuclear@0 700 root->mChildren = new aiNode*[root->mNumChildren = 1];
nuclear@0 701 root->mChildren[0] = new aiNode("<MD5Camera>");
nuclear@0 702 root->mChildren[0]->mParent = root;
nuclear@0 703
nuclear@0 704 // ... but with one camera assigned to it
nuclear@0 705 pScene->mCameras = new aiCamera*[pScene->mNumCameras = 1];
nuclear@0 706 aiCamera* cam = pScene->mCameras[0] = new aiCamera();
nuclear@0 707 cam->mName = "<MD5Camera>";
nuclear@0 708
nuclear@0 709 // FIXME: Fov is currently set to the first frame's value
nuclear@0 710 cam->mHorizontalFOV = AI_DEG_TO_RAD( frames.front().fFOV );
nuclear@0 711
nuclear@0 712 // every cut is written to a separate aiAnimation
nuclear@0 713 if (!cuts.size()) {
nuclear@0 714 cuts.push_back(0);
nuclear@0 715 cuts.push_back(frames.size()-1);
nuclear@0 716 }
nuclear@0 717 else {
nuclear@0 718 cuts.insert(cuts.begin(),0);
nuclear@0 719
nuclear@0 720 if (cuts.back() < frames.size()-1)
nuclear@0 721 cuts.push_back(frames.size()-1);
nuclear@0 722 }
nuclear@0 723
nuclear@0 724 pScene->mNumAnimations = cuts.size()-1;
nuclear@0 725 aiAnimation** tmp = pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations];
nuclear@0 726 for (std::vector<unsigned int>::const_iterator it = cuts.begin(); it != cuts.end()-1; ++it) {
nuclear@0 727
nuclear@0 728 aiAnimation* anim = *tmp++ = new aiAnimation();
nuclear@0 729 anim->mName.length = ::sprintf(anim->mName.data,"anim%u_from_%u_to_%u",(unsigned int)(it-cuts.begin()),(*it),*(it+1));
nuclear@0 730
nuclear@0 731 anim->mTicksPerSecond = cameraParser.fFrameRate;
nuclear@0 732 anim->mChannels = new aiNodeAnim*[anim->mNumChannels = 1];
nuclear@0 733 aiNodeAnim* nd = anim->mChannels[0] = new aiNodeAnim();
nuclear@0 734 nd->mNodeName.Set("<MD5Camera>");
nuclear@0 735
nuclear@0 736 nd->mNumPositionKeys = nd->mNumRotationKeys = *(it+1) - (*it);
nuclear@0 737 nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];
nuclear@0 738 nd->mRotationKeys = new aiQuatKey [nd->mNumRotationKeys];
nuclear@0 739 for (unsigned int i = 0; i < nd->mNumPositionKeys; ++i) {
nuclear@0 740
nuclear@0 741 nd->mPositionKeys[i].mValue = frames[*it+i].vPositionXYZ;
nuclear@0 742 MD5::ConvertQuaternion(frames[*it+i].vRotationQuat,nd->mRotationKeys[i].mValue);
nuclear@0 743 nd->mRotationKeys[i].mTime = nd->mPositionKeys[i].mTime = *it+i;
nuclear@0 744 }
nuclear@0 745 }
nuclear@0 746 }
nuclear@0 747
nuclear@0 748 #endif // !! ASSIMP_BUILD_NO_MD5_IMPORTER