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
|