vrshoot

diff 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
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/libs/assimp/MD5Loader.cpp	Sat Feb 01 19:58:19 2014 +0200
     1.3 @@ -0,0 +1,748 @@
     1.4 +/*
     1.5 +---------------------------------------------------------------------------
     1.6 +Open Asset Import Library (assimp)
     1.7 +---------------------------------------------------------------------------
     1.8 +
     1.9 +Copyright (c) 2006-2012, assimp team
    1.10 +
    1.11 +All rights reserved.
    1.12 +
    1.13 +Redistribution and use of this software in source and binary forms, 
    1.14 +with or without modification, are permitted provided that the following 
    1.15 +conditions are met:
    1.16 +
    1.17 +* Redistributions of source code must retain the above
    1.18 +  copyright notice, this list of conditions and the
    1.19 +  following disclaimer.
    1.20 +
    1.21 +* Redistributions in binary form must reproduce the above
    1.22 +  copyright notice, this list of conditions and the
    1.23 +  following disclaimer in the documentation and/or other
    1.24 +  materials provided with the distribution.
    1.25 +
    1.26 +* Neither the name of the assimp team, nor the names of its
    1.27 +  contributors may be used to endorse or promote products
    1.28 +  derived from this software without specific prior
    1.29 +  written permission of the assimp team.
    1.30 +
    1.31 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    1.32 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    1.33 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    1.34 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
    1.35 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.36 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
    1.37 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    1.38 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
    1.39 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
    1.40 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
    1.41 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.42 +---------------------------------------------------------------------------
    1.43 +*/
    1.44 +
    1.45 +/** @file  MD5Loader.cpp
    1.46 + *  @brief Implementation of the MD5 importer class 
    1.47 + */
    1.48 +
    1.49 +#include "AssimpPCH.h"
    1.50 +#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER
    1.51 +
    1.52 +// internal headers
    1.53 +#include "RemoveComments.h"
    1.54 +#include "MD5Loader.h"
    1.55 +#include "StringComparison.h"
    1.56 +#include "fast_atof.h"
    1.57 +#include "SkeletonMeshBuilder.h"
    1.58 +
    1.59 +using namespace Assimp;
    1.60 +
    1.61 +// Minimum weight value. Weights inside [-n ... n] are ignored
    1.62 +#define AI_MD5_WEIGHT_EPSILON 1e-5f
    1.63 +
    1.64 +
    1.65 +static const aiImporterDesc desc = {
    1.66 +	"Doom 3 / MD5 Mesh Importer",
    1.67 +	"",
    1.68 +	"",
    1.69 +	"",
    1.70 +	aiImporterFlags_SupportBinaryFlavour,
    1.71 +	0,
    1.72 +	0,
    1.73 +	0,
    1.74 +	0,
    1.75 +	"md5mesh md5camera md5anim"
    1.76 +};
    1.77 +
    1.78 +// ------------------------------------------------------------------------------------------------
    1.79 +// Constructor to be privately used by Importer
    1.80 +MD5Importer::MD5Importer()
    1.81 +: mBuffer()
    1.82 +, configNoAutoLoad (false)
    1.83 +{}
    1.84 +
    1.85 +// ------------------------------------------------------------------------------------------------
    1.86 +// Destructor, private as well 
    1.87 +MD5Importer::~MD5Importer()
    1.88 +{}
    1.89 +
    1.90 +// ------------------------------------------------------------------------------------------------
    1.91 +// Returns whether the class can handle the format of the given file. 
    1.92 +bool MD5Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
    1.93 +{
    1.94 +	const std::string extension = GetExtension(pFile);
    1.95 +
    1.96 +	if (extension == "md5anim" || extension == "md5mesh" || extension == "md5camera")
    1.97 +		return true;
    1.98 +	else if (!extension.length() || checkSig)	{
    1.99 +		if (!pIOHandler) {
   1.100 +			return true;
   1.101 +		}
   1.102 +		const char* tokens[] = {"MD5Version"};
   1.103 +		return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
   1.104 +	}
   1.105 +	return false;
   1.106 +}
   1.107 +
   1.108 +// ------------------------------------------------------------------------------------------------
   1.109 +// Get list of all supported extensions
   1.110 +const aiImporterDesc* MD5Importer::GetInfo () const
   1.111 +{
   1.112 +	return &desc;
   1.113 +}
   1.114 +
   1.115 +// ------------------------------------------------------------------------------------------------
   1.116 +// Setup import properties
   1.117 +void MD5Importer::SetupProperties(const Importer* pImp)
   1.118 +{
   1.119 +	// AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD
   1.120 +	configNoAutoLoad = (0 !=  pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD,0));
   1.121 +}
   1.122 +
   1.123 +// ------------------------------------------------------------------------------------------------
   1.124 +// Imports the given file into the given scene structure. 
   1.125 +void MD5Importer::InternReadFile( const std::string& pFile, 
   1.126 +								 aiScene* _pScene, IOSystem* _pIOHandler)
   1.127 +{
   1.128 +	pIOHandler = _pIOHandler;
   1.129 +	pScene     = _pScene;
   1.130 +	bHadMD5Mesh = bHadMD5Anim = bHadMD5Camera = false;
   1.131 +
   1.132 +	// remove the file extension
   1.133 +	const std::string::size_type pos = pFile.find_last_of('.');
   1.134 +	mFile = (std::string::npos == pos ? pFile : pFile.substr(0,pos+1));
   1.135 +
   1.136 +	const std::string extension = GetExtension(pFile);
   1.137 +	try {
   1.138 +		if (extension == "md5camera") {
   1.139 +			LoadMD5CameraFile();
   1.140 +		}
   1.141 +		else if (configNoAutoLoad || extension == "md5anim") {
   1.142 +			// determine file extension and process just *one* file
   1.143 +			if (extension.length() == 0) {
   1.144 +				throw DeadlyImportError("Failure, need file extension to determine MD5 part type");
   1.145 +			}
   1.146 +			if (extension == "md5anim") {
   1.147 +				LoadMD5AnimFile();
   1.148 +			}
   1.149 +			else if (extension == "md5mesh") {
   1.150 +				LoadMD5MeshFile();
   1.151 +			}
   1.152 +		}
   1.153 +		else {
   1.154 +			LoadMD5MeshFile();
   1.155 +			LoadMD5AnimFile();
   1.156 +		}
   1.157 +	}
   1.158 +	catch ( ... ) { // std::exception, Assimp::DeadlyImportError
   1.159 +		UnloadFileFromMemory();
   1.160 +		throw;
   1.161 +	}
   1.162 +
   1.163 +	// make sure we have at least one file
   1.164 +	if (!bHadMD5Mesh && !bHadMD5Anim && !bHadMD5Camera) {
   1.165 +		throw DeadlyImportError("Failed to read valid contents out of this MD5* file");
   1.166 +	}
   1.167 +
   1.168 +	// Now rotate the whole scene 90 degrees around the x axis to match our internal coordinate system
   1.169 +	pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
   1.170 +		0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
   1.171 +
   1.172 +	// the output scene wouldn't pass the validation without this flag
   1.173 +	if (!bHadMD5Mesh) {
   1.174 +		pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
   1.175 +	}
   1.176 +
   1.177 +	// clean the instance -- the BaseImporter instance may be reused later.
   1.178 +	UnloadFileFromMemory();
   1.179 +}
   1.180 +
   1.181 +// ------------------------------------------------------------------------------------------------
   1.182 +// Load a file into a memory buffer
   1.183 +void MD5Importer::LoadFileIntoMemory (IOStream* file)
   1.184 +{
   1.185 +	// unload the previous buffer, if any
   1.186 +	UnloadFileFromMemory();
   1.187 +
   1.188 +	ai_assert(NULL != file);
   1.189 +	fileSize = (unsigned int)file->FileSize();
   1.190 +	ai_assert(fileSize);
   1.191 +
   1.192 +	// allocate storage and copy the contents of the file to a memory buffer
   1.193 +	mBuffer = new char[fileSize+1];
   1.194 +	file->Read( (void*)mBuffer, 1, fileSize);
   1.195 +	iLineNumber = 1;
   1.196 +
   1.197 +	// append a terminal 0
   1.198 +	mBuffer[fileSize] = '\0';
   1.199 +
   1.200 +	// now remove all line comments from the file
   1.201 +	CommentRemover::RemoveLineComments("//",mBuffer,' ');
   1.202 +}
   1.203 +
   1.204 +// ------------------------------------------------------------------------------------------------
   1.205 +// Unload the current memory buffer
   1.206 +void MD5Importer::UnloadFileFromMemory ()
   1.207 +{
   1.208 +	// delete the file buffer
   1.209 +	delete[] mBuffer;
   1.210 +	mBuffer = NULL;
   1.211 +	fileSize = 0;
   1.212 +}
   1.213 +
   1.214 +// ------------------------------------------------------------------------------------------------
   1.215 +// Build unique vertices
   1.216 +void MD5Importer::MakeDataUnique (MD5::MeshDesc& meshSrc)
   1.217 +{
   1.218 +	std::vector<bool> abHad(meshSrc.mVertices.size(),false);
   1.219 +
   1.220 +	// allocate enough storage to keep the output structures
   1.221 +	const unsigned int iNewNum = meshSrc.mFaces.size()*3;
   1.222 +	unsigned int iNewIndex = meshSrc.mVertices.size();
   1.223 +	meshSrc.mVertices.resize(iNewNum);
   1.224 +
   1.225 +	// try to guess how much storage we'll need for new weights
   1.226 +	const float fWeightsPerVert = meshSrc.mWeights.size() / (float)iNewIndex;
   1.227 +	const unsigned int guess = (unsigned int)(fWeightsPerVert*iNewNum); 
   1.228 +	meshSrc.mWeights.reserve(guess + (guess >> 3)); // + 12.5% as buffer
   1.229 +
   1.230 +	for (FaceList::const_iterator iter = meshSrc.mFaces.begin(),iterEnd = meshSrc.mFaces.end();iter != iterEnd;++iter){
   1.231 +		const aiFace& face = *iter;
   1.232 +		for (unsigned int i = 0; i < 3;++i) {
   1.233 +			if (face.mIndices[0] >= meshSrc.mVertices.size()) {
   1.234 +				throw DeadlyImportError("MD5MESH: Invalid vertex index");
   1.235 +			}
   1.236 +
   1.237 +			if (abHad[face.mIndices[i]])	{
   1.238 +				// generate a new vertex
   1.239 +				meshSrc.mVertices[iNewIndex] = meshSrc.mVertices[face.mIndices[i]];
   1.240 +				face.mIndices[i] = iNewIndex++;
   1.241 +			}
   1.242 +			else abHad[face.mIndices[i]] = true;
   1.243 +		}
   1.244 +		// swap face order
   1.245 +		std::swap(face.mIndices[0],face.mIndices[2]);
   1.246 +	}
   1.247 +}
   1.248 +
   1.249 +// ------------------------------------------------------------------------------------------------
   1.250 +// Recursive node graph construction from a MD5MESH
   1.251 +void MD5Importer::AttachChilds_Mesh(int iParentID,aiNode* piParent, BoneList& bones)
   1.252 +{
   1.253 +	ai_assert(NULL != piParent && !piParent->mNumChildren);
   1.254 +
   1.255 +	// First find out how many children we'll have
   1.256 +	for (int i = 0; i < (int)bones.size();++i)	{
   1.257 +		if (iParentID != i && bones[i].mParentIndex == iParentID)	{
   1.258 +			++piParent->mNumChildren;
   1.259 +		}
   1.260 +	}
   1.261 +	if (piParent->mNumChildren)	{
   1.262 +		piParent->mChildren = new aiNode*[piParent->mNumChildren];
   1.263 +		for (int i = 0; i < (int)bones.size();++i)	{
   1.264 +			// (avoid infinite recursion)
   1.265 +			if (iParentID != i && bones[i].mParentIndex == iParentID)	{
   1.266 +				aiNode* pc;
   1.267 +				// setup a new node
   1.268 +				*piParent->mChildren++ = pc = new aiNode();
   1.269 +				pc->mName = aiString(bones[i].mName); 
   1.270 +				pc->mParent = piParent;
   1.271 +
   1.272 +				// get the transformation matrix from rotation and translational components
   1.273 +				aiQuaternion quat; 
   1.274 +				MD5::ConvertQuaternion ( bones[i].mRotationQuat, quat );
   1.275 +
   1.276 +				// FIX to get to Assimp's quaternion conventions
   1.277 +				quat.w *= -1.f;
   1.278 +
   1.279 +				bones[i].mTransform = aiMatrix4x4 ( quat.GetMatrix());
   1.280 +				bones[i].mTransform.a4 = bones[i].mPositionXYZ.x;
   1.281 +				bones[i].mTransform.b4 = bones[i].mPositionXYZ.y;
   1.282 +				bones[i].mTransform.c4 = bones[i].mPositionXYZ.z;
   1.283 +
   1.284 +				// store it for later use
   1.285 +				pc->mTransformation = bones[i].mInvTransform = bones[i].mTransform;
   1.286 +				bones[i].mInvTransform.Inverse();
   1.287 +
   1.288 +				// the transformations for each bone are absolute, so we need to multiply them
   1.289 +				// with the inverse of the absolute matrix of the parent joint
   1.290 +				if (-1 != iParentID)	{
   1.291 +					pc->mTransformation = bones[iParentID].mInvTransform * pc->mTransformation;
   1.292 +				}
   1.293 +
   1.294 +				// add children to this node, too
   1.295 +				AttachChilds_Mesh( i, pc, bones);
   1.296 +			}
   1.297 +		}
   1.298 +		// undo offset computations
   1.299 +		piParent->mChildren -= piParent->mNumChildren;
   1.300 +	}
   1.301 +}
   1.302 +
   1.303 +// ------------------------------------------------------------------------------------------------
   1.304 +// Recursive node graph construction from a MD5ANIM
   1.305 +void MD5Importer::AttachChilds_Anim(int iParentID,aiNode* piParent, AnimBoneList& bones,const aiNodeAnim** node_anims)
   1.306 +{
   1.307 +	ai_assert(NULL != piParent && !piParent->mNumChildren);
   1.308 +
   1.309 +	// First find out how many children we'll have
   1.310 +	for (int i = 0; i < (int)bones.size();++i)	{
   1.311 +		if (iParentID != i && bones[i].mParentIndex == iParentID)	{
   1.312 +			++piParent->mNumChildren;
   1.313 +		}
   1.314 +	}
   1.315 +	if (piParent->mNumChildren)	{
   1.316 +		piParent->mChildren = new aiNode*[piParent->mNumChildren];
   1.317 +		for (int i = 0; i < (int)bones.size();++i)	{
   1.318 +			// (avoid infinite recursion)
   1.319 +			if (iParentID != i && bones[i].mParentIndex == iParentID)
   1.320 +			{
   1.321 +				aiNode* pc;
   1.322 +				// setup a new node
   1.323 +				*piParent->mChildren++ = pc = new aiNode();
   1.324 +				pc->mName = aiString(bones[i].mName); 
   1.325 +				pc->mParent = piParent;
   1.326 +
   1.327 +				// get the corresponding animation channel and its first frame
   1.328 +				const aiNodeAnim** cur = node_anims;
   1.329 +				while ((**cur).mNodeName != pc->mName)++cur;
   1.330 +
   1.331 +				aiMatrix4x4::Translation((**cur).mPositionKeys[0].mValue,pc->mTransformation);
   1.332 +				pc->mTransformation = pc->mTransformation * aiMatrix4x4((**cur).mRotationKeys[0].mValue.GetMatrix()) ;
   1.333 +
   1.334 +				// add children to this node, too
   1.335 +				AttachChilds_Anim( i, pc, bones,node_anims);
   1.336 +			}
   1.337 +		}
   1.338 +		// undo offset computations
   1.339 +		piParent->mChildren -= piParent->mNumChildren;
   1.340 +	}
   1.341 +}
   1.342 +
   1.343 +// ------------------------------------------------------------------------------------------------
   1.344 +// Load a MD5MESH file
   1.345 +void MD5Importer::LoadMD5MeshFile ()
   1.346 +{
   1.347 +	std::string pFile = mFile + "md5mesh";
   1.348 +	boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
   1.349 +
   1.350 +	// Check whether we can read from the file
   1.351 +	if( file.get() == NULL || !file->FileSize())	{
   1.352 +		DefaultLogger::get()->warn("Failed to access MD5MESH file: " + pFile);
   1.353 +		return;
   1.354 +	}
   1.355 +	bHadMD5Mesh = true;
   1.356 +	LoadFileIntoMemory(file.get());
   1.357 +
   1.358 +	// now construct a parser and parse the file
   1.359 +	MD5::MD5Parser parser(mBuffer,fileSize);
   1.360 +
   1.361 +	// load the mesh information from it
   1.362 +	MD5::MD5MeshParser meshParser(parser.mSections);
   1.363 +
   1.364 +	// create the bone hierarchy - first the root node and dummy nodes for all meshes
   1.365 +	pScene->mRootNode = new aiNode("<MD5_Root>");
   1.366 +	pScene->mRootNode->mNumChildren = 2;
   1.367 +	pScene->mRootNode->mChildren = new aiNode*[2];
   1.368 +
   1.369 +	// build the hierarchy from the MD5MESH file
   1.370 +	aiNode* pcNode = pScene->mRootNode->mChildren[1] = new aiNode();
   1.371 +	pcNode->mName.Set("<MD5_Hierarchy>");
   1.372 +	pcNode->mParent = pScene->mRootNode;
   1.373 +	AttachChilds_Mesh(-1,pcNode,meshParser.mJoints);
   1.374 +
   1.375 +	pcNode = pScene->mRootNode->mChildren[0] = new aiNode();
   1.376 +	pcNode->mName.Set("<MD5_Mesh>");
   1.377 +	pcNode->mParent = pScene->mRootNode;
   1.378 +
   1.379 +#if 0
   1.380 +	if (pScene->mRootNode->mChildren[1]->mNumChildren) /* start at the right hierarchy level */
   1.381 +		SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[1]->mChildren[0]);
   1.382 +#else
   1.383 +
   1.384 +	// FIX: MD5 files exported from Blender can have empty meshes
   1.385 +	for (std::vector<MD5::MeshDesc>::const_iterator it  = meshParser.mMeshes.begin(),end = meshParser.mMeshes.end(); it != end;++it) {
   1.386 +		if (!(*it).mFaces.empty() && !(*it).mVertices.empty())
   1.387 +			++pScene->mNumMaterials;
   1.388 +	}
   1.389 +
   1.390 +	// generate all meshes
   1.391 +	pScene->mNumMeshes = pScene->mNumMaterials;
   1.392 +	pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
   1.393 +	pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
   1.394 +
   1.395 +	//  storage for node mesh indices
   1.396 +	pcNode->mNumMeshes = pScene->mNumMeshes;
   1.397 +	pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
   1.398 +	for (unsigned int m = 0; m < pcNode->mNumMeshes;++m)
   1.399 +		pcNode->mMeshes[m] = m;
   1.400 +
   1.401 +	unsigned int n = 0;
   1.402 +	for (std::vector<MD5::MeshDesc>::iterator it  = meshParser.mMeshes.begin(),end = meshParser.mMeshes.end(); it != end;++it) {
   1.403 +		MD5::MeshDesc& meshSrc = *it;
   1.404 +		if (meshSrc.mFaces.empty() || meshSrc.mVertices.empty())
   1.405 +			continue;
   1.406 +
   1.407 +		aiMesh* mesh = pScene->mMeshes[n] = new aiMesh();
   1.408 +		mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
   1.409 +
   1.410 +		// generate unique vertices in our internal verbose format
   1.411 +		MakeDataUnique(meshSrc);
   1.412 +
   1.413 +		mesh->mNumVertices = (unsigned int) meshSrc.mVertices.size();
   1.414 +		mesh->mVertices = new aiVector3D[mesh->mNumVertices];
   1.415 +		mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
   1.416 +		mesh->mNumUVComponents[0] = 2;
   1.417 +
   1.418 +		// copy texture coordinates
   1.419 +		aiVector3D* pv = mesh->mTextureCoords[0];
   1.420 +		for (MD5::VertexList::const_iterator iter =  meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
   1.421 +			pv->x = (*iter).mUV.x;
   1.422 +			pv->y = 1.0f-(*iter).mUV.y; // D3D to OpenGL
   1.423 +			pv->z = 0.0f;
   1.424 +		}
   1.425 +
   1.426 +		// sort all bone weights - per bone
   1.427 +		unsigned int* piCount = new unsigned int[meshParser.mJoints.size()];
   1.428 +		::memset(piCount,0,sizeof(unsigned int)*meshParser.mJoints.size());
   1.429 +
   1.430 +		for (MD5::VertexList::const_iterator iter =  meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
   1.431 +			for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
   1.432 +			{
   1.433 +				MD5::WeightDesc& desc = meshSrc.mWeights[w];
   1.434 +				/* FIX for some invalid exporters */
   1.435 +				if (!(desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON ))
   1.436 +					++piCount[desc.mBone]; 
   1.437 +			}
   1.438 +		}
   1.439 +
   1.440 +		// check how many we will need
   1.441 +		for (unsigned int p = 0; p < meshParser.mJoints.size();++p)
   1.442 +			if (piCount[p])mesh->mNumBones++;
   1.443 +
   1.444 +		if (mesh->mNumBones) // just for safety
   1.445 +		{
   1.446 +			mesh->mBones = new aiBone*[mesh->mNumBones];
   1.447 +			for (unsigned int q = 0,h = 0; q < meshParser.mJoints.size();++q) 
   1.448 +			{
   1.449 +				if (!piCount[q])continue;
   1.450 +				aiBone* p = mesh->mBones[h] = new aiBone();
   1.451 +				p->mNumWeights = piCount[q];
   1.452 +				p->mWeights = new aiVertexWeight[p->mNumWeights];
   1.453 +				p->mName = aiString(meshParser.mJoints[q].mName);
   1.454 +				p->mOffsetMatrix = meshParser.mJoints[q].mInvTransform;
   1.455 +
   1.456 +				// store the index for later use
   1.457 +				MD5::BoneDesc& boneSrc = meshParser.mJoints[q];
   1.458 +				boneSrc.mMap = h++;
   1.459 +
   1.460 +				// compute w-component of quaternion
   1.461 +				MD5::ConvertQuaternion( boneSrc.mRotationQuat, boneSrc.mRotationQuatConverted );
   1.462 +			}
   1.463 +	
   1.464 +			//unsigned int g = 0;
   1.465 +			pv = mesh->mVertices;
   1.466 +			for (MD5::VertexList::const_iterator iter =  meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
   1.467 +				// compute the final vertex position from all single weights
   1.468 +				*pv = aiVector3D();
   1.469 +
   1.470 +				// there are models which have weights which don't sum to 1 ...
   1.471 +				float fSum = 0.0f;
   1.472 +				for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
   1.473 +					fSum += meshSrc.mWeights[w].mWeight;
   1.474 +				if (!fSum) {
   1.475 +					DefaultLogger::get()->error("MD5MESH: The sum of all vertex bone weights is 0");
   1.476 +					continue;
   1.477 +				}
   1.478 +
   1.479 +				// process bone weights
   1.480 +				for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)	{
   1.481 +					if (w >= meshSrc.mWeights.size())
   1.482 +						throw DeadlyImportError("MD5MESH: Invalid weight index");
   1.483 +
   1.484 +					MD5::WeightDesc& desc = meshSrc.mWeights[w];
   1.485 +					if ( desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON) {
   1.486 +						continue;
   1.487 +					}
   1.488 +
   1.489 +					const float fNewWeight = desc.mWeight / fSum; 
   1.490 +
   1.491 +					// transform the local position into worldspace
   1.492 +					MD5::BoneDesc& boneSrc = meshParser.mJoints[desc.mBone];
   1.493 +					const aiVector3D v = boneSrc.mRotationQuatConverted.Rotate (desc.vOffsetPosition);
   1.494 +
   1.495 +					// use the original weight to compute the vertex position
   1.496 +					// (some MD5s seem to depend on the invalid weight values ...)
   1.497 +					*pv += ((boneSrc.mPositionXYZ+v)* desc.mWeight);
   1.498 +			
   1.499 +					aiBone* bone = mesh->mBones[boneSrc.mMap];
   1.500 +					*bone->mWeights++ = aiVertexWeight((unsigned int)(pv-mesh->mVertices),fNewWeight);
   1.501 +				}
   1.502 +			}
   1.503 +
   1.504 +			// undo our nice offset tricks ...
   1.505 +			for (unsigned int p = 0; p < mesh->mNumBones;++p) {
   1.506 +				mesh->mBones[p]->mWeights -= mesh->mBones[p]->mNumWeights;
   1.507 +			}
   1.508 +		}
   1.509 +
   1.510 +		delete[] piCount;
   1.511 +
   1.512 +		// now setup all faces - we can directly copy the list
   1.513 +		// (however, take care that the aiFace destructor doesn't delete the mIndices array)
   1.514 +		mesh->mNumFaces = (unsigned int)meshSrc.mFaces.size();
   1.515 +		mesh->mFaces = new aiFace[mesh->mNumFaces];
   1.516 +		for (unsigned int c = 0; c < mesh->mNumFaces;++c)	{
   1.517 +			mesh->mFaces[c].mNumIndices = 3;
   1.518 +			mesh->mFaces[c].mIndices = meshSrc.mFaces[c].mIndices;
   1.519 +			meshSrc.mFaces[c].mIndices = NULL;
   1.520 +		}
   1.521 +
   1.522 +		// generate a material for the mesh
   1.523 +		aiMaterial* mat = new aiMaterial();
   1.524 +		pScene->mMaterials[n] = mat;
   1.525 +
   1.526 +		// insert the typical doom3 textures:
   1.527 +		// nnn_local.tga  - normal map
   1.528 +		// nnn_h.tga      - height map
   1.529 +		// nnn_s.tga      - specular map
   1.530 +		// nnn_d.tga      - diffuse map
   1.531 +		if (meshSrc.mShader.length && !strchr(meshSrc.mShader.data,'.')) {
   1.532 +		
   1.533 +			aiString temp(meshSrc.mShader);
   1.534 +			temp.Append("_local.tga");
   1.535 +			mat->AddProperty(&temp,AI_MATKEY_TEXTURE_NORMALS(0));
   1.536 +
   1.537 +			temp =  aiString(meshSrc.mShader);
   1.538 +			temp.Append("_s.tga");
   1.539 +			mat->AddProperty(&temp,AI_MATKEY_TEXTURE_SPECULAR(0));
   1.540 +
   1.541 +			temp =  aiString(meshSrc.mShader);
   1.542 +			temp.Append("_d.tga");
   1.543 +			mat->AddProperty(&temp,AI_MATKEY_TEXTURE_DIFFUSE(0));
   1.544 +
   1.545 +			temp =  aiString(meshSrc.mShader);
   1.546 +			temp.Append("_h.tga");
   1.547 +			mat->AddProperty(&temp,AI_MATKEY_TEXTURE_HEIGHT(0));
   1.548 +
   1.549 +			// set this also as material name
   1.550 +			mat->AddProperty(&meshSrc.mShader,AI_MATKEY_NAME);
   1.551 +		}
   1.552 +		else mat->AddProperty(&meshSrc.mShader,AI_MATKEY_TEXTURE_DIFFUSE(0));
   1.553 +		mesh->mMaterialIndex = n++;
   1.554 +	}
   1.555 +#endif
   1.556 +}
   1.557 +
   1.558 +// ------------------------------------------------------------------------------------------------
   1.559 +// Load an MD5ANIM file
   1.560 +void MD5Importer::LoadMD5AnimFile ()
   1.561 +{
   1.562 +	std::string pFile = mFile + "md5anim";
   1.563 +	boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
   1.564 +
   1.565 +	// Check whether we can read from the file
   1.566 +	if( !file.get() || !file->FileSize())	{
   1.567 +		DefaultLogger::get()->warn("Failed to read MD5ANIM file: " + pFile);
   1.568 +		return;
   1.569 +	}
   1.570 +	LoadFileIntoMemory(file.get());
   1.571 +
   1.572 +	// parse the basic file structure
   1.573 +	MD5::MD5Parser parser(mBuffer,fileSize);
   1.574 +
   1.575 +	// load the animation information from the parse tree
   1.576 +	MD5::MD5AnimParser animParser(parser.mSections);
   1.577 +
   1.578 +	// generate and fill the output animation
   1.579 +	if (animParser.mAnimatedBones.empty() || animParser.mFrames.empty() || 
   1.580 +		animParser.mBaseFrames.size() != animParser.mAnimatedBones.size())	{
   1.581 +		
   1.582 +		DefaultLogger::get()->error("MD5ANIM: No frames or animated bones loaded");
   1.583 +	}
   1.584 +	else {
   1.585 +		bHadMD5Anim = true;
   1.586 +
   1.587 +		pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations = 1];
   1.588 +		aiAnimation* anim = pScene->mAnimations[0] = new aiAnimation();
   1.589 +		anim->mNumChannels = (unsigned int)animParser.mAnimatedBones.size();
   1.590 +		anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
   1.591 +		for (unsigned int i = 0; i < anim->mNumChannels;++i)	{
   1.592 +			aiNodeAnim* node = anim->mChannels[i] = new aiNodeAnim();
   1.593 +			node->mNodeName = aiString( animParser.mAnimatedBones[i].mName );
   1.594 +
   1.595 +			// allocate storage for the keyframes
   1.596 +			node->mPositionKeys = new aiVectorKey[animParser.mFrames.size()];
   1.597 +			node->mRotationKeys = new aiQuatKey[animParser.mFrames.size()];
   1.598 +		}
   1.599 +
   1.600 +		// 1 tick == 1 frame
   1.601 +		anim->mTicksPerSecond = animParser.fFrameRate;
   1.602 +
   1.603 +		for (FrameList::const_iterator iter = animParser.mFrames.begin(), iterEnd = animParser.mFrames.end();iter != iterEnd;++iter){
   1.604 +			double dTime = (double)(*iter).iIndex;
   1.605 +			aiNodeAnim** pcAnimNode = anim->mChannels;
   1.606 +			if (!(*iter).mValues.empty() || iter == animParser.mFrames.begin()) /* be sure we have at least one frame */
   1.607 +			{
   1.608 +				// now process all values in there ... read all joints
   1.609 +				MD5::BaseFrameDesc* pcBaseFrame = &animParser.mBaseFrames[0];
   1.610 +				for (AnimBoneList::const_iterator iter2	= animParser.mAnimatedBones.begin(); iter2 != animParser.mAnimatedBones.end();++iter2,
   1.611 +					++pcAnimNode,++pcBaseFrame)
   1.612 +				{
   1.613 +					if((*iter2).iFirstKeyIndex >= (*iter).mValues.size()) {
   1.614 +
   1.615 +						// Allow for empty frames
   1.616 +						if ((*iter2).iFlags != 0) {
   1.617 +							throw DeadlyImportError("MD5: Keyframe index is out of range");
   1.618 +						
   1.619 +						}
   1.620 +						continue;
   1.621 +					}
   1.622 +					const float* fpCur = &(*iter).mValues[(*iter2).iFirstKeyIndex];
   1.623 +					aiNodeAnim* pcCurAnimBone = *pcAnimNode;
   1.624 +
   1.625 +					aiVectorKey* vKey = &pcCurAnimBone->mPositionKeys[pcCurAnimBone->mNumPositionKeys++];
   1.626 +					aiQuatKey* qKey = &pcCurAnimBone->mRotationKeys  [pcCurAnimBone->mNumRotationKeys++];
   1.627 +					aiVector3D vTemp;
   1.628 +
   1.629 +					// translational component
   1.630 +					for (unsigned int i = 0; i < 3; ++i) {
   1.631 +						if ((*iter2).iFlags & (1u << i)) {
   1.632 +							vKey->mValue[i] =  *fpCur++;
   1.633 +						}
   1.634 +						else vKey->mValue[i] = pcBaseFrame->vPositionXYZ[i];
   1.635 +					}
   1.636 +
   1.637 +					// orientation component
   1.638 +					for (unsigned int i = 0; i < 3; ++i) {
   1.639 +						if ((*iter2).iFlags & (8u << i)) {
   1.640 +							vTemp[i] =  *fpCur++;
   1.641 +						}
   1.642 +						else vTemp[i] = pcBaseFrame->vRotationQuat[i];
   1.643 +					}
   1.644 +
   1.645 +					MD5::ConvertQuaternion(vTemp, qKey->mValue);
   1.646 +					qKey->mTime = vKey->mTime = dTime;
   1.647 +
   1.648 +					// we need this to get to Assimp quaternion conventions
   1.649 +					qKey->mValue.w *= -1.f;
   1.650 +				}
   1.651 +			}
   1.652 +
   1.653 +			// compute the duration of the animation
   1.654 +			anim->mDuration = std::max(dTime,anim->mDuration);
   1.655 +		}
   1.656 +
   1.657 +		// If we didn't build the hierarchy yet (== we didn't load a MD5MESH),
   1.658 +		// construct it now from the data given in the MD5ANIM.
   1.659 +		if (!pScene->mRootNode) {
   1.660 +			pScene->mRootNode = new aiNode();
   1.661 +			pScene->mRootNode->mName.Set("<MD5_Hierarchy>");
   1.662 +
   1.663 +			AttachChilds_Anim(-1,pScene->mRootNode,animParser.mAnimatedBones,(const aiNodeAnim**)anim->mChannels);
   1.664 +
   1.665 +			// Call SkeletonMeshBuilder to construct a mesh to represent the shape
   1.666 +			if (pScene->mRootNode->mNumChildren) {
   1.667 +				SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[0]);
   1.668 +			}
   1.669 +		}
   1.670 +	}
   1.671 +}
   1.672 +
   1.673 +// ------------------------------------------------------------------------------------------------
   1.674 +// Load an MD5CAMERA file
   1.675 +void MD5Importer::LoadMD5CameraFile ()
   1.676 +{
   1.677 +	std::string pFile = mFile + "md5camera";
   1.678 +	boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
   1.679 +
   1.680 +	// Check whether we can read from the file
   1.681 +	if( !file.get() || !file->FileSize())	{
   1.682 +		throw DeadlyImportError("Failed to read MD5CAMERA file: " + pFile);
   1.683 +	}
   1.684 +	bHadMD5Camera = true;
   1.685 +	LoadFileIntoMemory(file.get());
   1.686 +
   1.687 +	// parse the basic file structure
   1.688 +	MD5::MD5Parser parser(mBuffer,fileSize);
   1.689 +
   1.690 +	// load the camera animation data from the parse tree
   1.691 +	MD5::MD5CameraParser cameraParser(parser.mSections);
   1.692 +
   1.693 +	if (cameraParser.frames.empty()) {
   1.694 +		throw DeadlyImportError("MD5CAMERA: No frames parsed");
   1.695 +	}
   1.696 +
   1.697 +	std::vector<unsigned int>& cuts = cameraParser.cuts;
   1.698 +	std::vector<MD5::CameraAnimFrameDesc>& frames = cameraParser.frames;
   1.699 +
   1.700 +	// Construct output graph - a simple root with a dummy child.
   1.701 +	// The root node performs the coordinate system conversion
   1.702 +	aiNode* root = pScene->mRootNode = new aiNode("<MD5CameraRoot>");
   1.703 +	root->mChildren = new aiNode*[root->mNumChildren = 1];
   1.704 +	root->mChildren[0] = new aiNode("<MD5Camera>");
   1.705 +	root->mChildren[0]->mParent = root;
   1.706 +
   1.707 +	// ... but with one camera assigned to it
   1.708 +	pScene->mCameras = new aiCamera*[pScene->mNumCameras = 1];
   1.709 +	aiCamera* cam = pScene->mCameras[0] = new aiCamera();
   1.710 +	cam->mName = "<MD5Camera>";
   1.711 +
   1.712 +	// FIXME: Fov is currently set to the first frame's value
   1.713 +	cam->mHorizontalFOV = AI_DEG_TO_RAD( frames.front().fFOV );
   1.714 +
   1.715 +	// every cut is written to a separate aiAnimation
   1.716 +	if (!cuts.size()) {
   1.717 +		cuts.push_back(0);
   1.718 +		cuts.push_back(frames.size()-1);
   1.719 +	}
   1.720 +	else {		
   1.721 +		cuts.insert(cuts.begin(),0);
   1.722 +
   1.723 +		if (cuts.back() < frames.size()-1)
   1.724 +			cuts.push_back(frames.size()-1);
   1.725 +	}
   1.726 +
   1.727 +	pScene->mNumAnimations = cuts.size()-1;
   1.728 +	aiAnimation** tmp = pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations];
   1.729 +	for (std::vector<unsigned int>::const_iterator it = cuts.begin(); it != cuts.end()-1; ++it) {
   1.730 +	
   1.731 +		aiAnimation* anim = *tmp++ = new aiAnimation();
   1.732 +		anim->mName.length = ::sprintf(anim->mName.data,"anim%u_from_%u_to_%u",(unsigned int)(it-cuts.begin()),(*it),*(it+1));
   1.733 +		
   1.734 +		anim->mTicksPerSecond = cameraParser.fFrameRate;
   1.735 +		anim->mChannels = new aiNodeAnim*[anim->mNumChannels = 1];
   1.736 +		aiNodeAnim* nd  = anim->mChannels[0] = new aiNodeAnim();
   1.737 +		nd->mNodeName.Set("<MD5Camera>");
   1.738 +
   1.739 +		nd->mNumPositionKeys = nd->mNumRotationKeys = *(it+1) - (*it);
   1.740 +		nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];
   1.741 +		nd->mRotationKeys = new aiQuatKey  [nd->mNumRotationKeys];
   1.742 +		for (unsigned int i = 0; i < nd->mNumPositionKeys; ++i) {
   1.743 +
   1.744 +			nd->mPositionKeys[i].mValue = frames[*it+i].vPositionXYZ;
   1.745 +			MD5::ConvertQuaternion(frames[*it+i].vRotationQuat,nd->mRotationKeys[i].mValue);
   1.746 +			nd->mRotationKeys[i].mTime = nd->mPositionKeys[i].mTime = *it+i;
   1.747 +		}
   1.748 +	}
   1.749 +}
   1.750 +
   1.751 +#endif // !! ASSIMP_BUILD_NO_MD5_IMPORTER