rev |
line source |
nuclear@0
|
1 /** Implementation of the BVH loader */
|
nuclear@0
|
2 /*
|
nuclear@0
|
3 ---------------------------------------------------------------------------
|
nuclear@0
|
4 Open Asset Import Library (assimp)
|
nuclear@0
|
5 ---------------------------------------------------------------------------
|
nuclear@0
|
6
|
nuclear@0
|
7 Copyright (c) 2006-2012, assimp team
|
nuclear@0
|
8
|
nuclear@0
|
9 All rights reserved.
|
nuclear@0
|
10
|
nuclear@0
|
11 Redistribution and use of this software in source and binary forms,
|
nuclear@0
|
12 with or without modification, are permitted provided that the following
|
nuclear@0
|
13 conditions are met:
|
nuclear@0
|
14
|
nuclear@0
|
15 * Redistributions of source code must retain the above
|
nuclear@0
|
16 copyright notice, this list of conditions and the
|
nuclear@0
|
17 following disclaimer.
|
nuclear@0
|
18
|
nuclear@0
|
19 * Redistributions in binary form must reproduce the above
|
nuclear@0
|
20 copyright notice, this list of conditions and the
|
nuclear@0
|
21 following disclaimer in the documentation and/or other
|
nuclear@0
|
22 materials provided with the distribution.
|
nuclear@0
|
23
|
nuclear@0
|
24 * Neither the name of the assimp team, nor the names of its
|
nuclear@0
|
25 contributors may be used to endorse or promote products
|
nuclear@0
|
26 derived from this software without specific prior
|
nuclear@0
|
27 written permission of the assimp team.
|
nuclear@0
|
28
|
nuclear@0
|
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
nuclear@0
|
30 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
nuclear@0
|
31 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
nuclear@0
|
32 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
nuclear@0
|
33 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
nuclear@0
|
34 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
nuclear@0
|
35 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
nuclear@0
|
36 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
nuclear@0
|
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
nuclear@0
|
38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
nuclear@0
|
39 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
nuclear@0
|
40 ---------------------------------------------------------------------------
|
nuclear@0
|
41 */
|
nuclear@0
|
42
|
nuclear@0
|
43 #include "AssimpPCH.h"
|
nuclear@0
|
44 #ifndef ASSIMP_BUILD_NO_BVH_IMPORTER
|
nuclear@0
|
45
|
nuclear@0
|
46 #include "BVHLoader.h"
|
nuclear@0
|
47 #include "fast_atof.h"
|
nuclear@0
|
48 #include "SkeletonMeshBuilder.h"
|
nuclear@0
|
49
|
nuclear@0
|
50 using namespace Assimp;
|
nuclear@0
|
51
|
nuclear@0
|
52 static const aiImporterDesc desc = {
|
nuclear@0
|
53 "BVH Importer (MoCap)",
|
nuclear@0
|
54 "",
|
nuclear@0
|
55 "",
|
nuclear@0
|
56 "",
|
nuclear@0
|
57 aiImporterFlags_SupportTextFlavour,
|
nuclear@0
|
58 0,
|
nuclear@0
|
59 0,
|
nuclear@0
|
60 0,
|
nuclear@0
|
61 0,
|
nuclear@0
|
62 "bvh"
|
nuclear@0
|
63 };
|
nuclear@0
|
64
|
nuclear@0
|
65 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
66 // Constructor to be privately used by Importer
|
nuclear@0
|
67 BVHLoader::BVHLoader()
|
nuclear@0
|
68 : noSkeletonMesh()
|
nuclear@0
|
69 {}
|
nuclear@0
|
70
|
nuclear@0
|
71 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
72 // Destructor, private as well
|
nuclear@0
|
73 BVHLoader::~BVHLoader()
|
nuclear@0
|
74 {}
|
nuclear@0
|
75
|
nuclear@0
|
76 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
77 // Returns whether the class can handle the format of the given file.
|
nuclear@0
|
78 bool BVHLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const
|
nuclear@0
|
79 {
|
nuclear@0
|
80 // check file extension
|
nuclear@0
|
81 const std::string extension = GetExtension(pFile);
|
nuclear@0
|
82
|
nuclear@0
|
83 if( extension == "bvh")
|
nuclear@0
|
84 return true;
|
nuclear@0
|
85
|
nuclear@0
|
86 if ((!extension.length() || cs) && pIOHandler) {
|
nuclear@0
|
87 const char* tokens[] = {"HIERARCHY"};
|
nuclear@0
|
88 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
|
nuclear@0
|
89 }
|
nuclear@0
|
90 return false;
|
nuclear@0
|
91 }
|
nuclear@0
|
92
|
nuclear@0
|
93 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
94 void BVHLoader::SetupProperties(const Importer* pImp)
|
nuclear@0
|
95 {
|
nuclear@0
|
96 noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0;
|
nuclear@0
|
97 }
|
nuclear@0
|
98
|
nuclear@0
|
99 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
100 // Loader meta information
|
nuclear@0
|
101 const aiImporterDesc* BVHLoader::GetInfo () const
|
nuclear@0
|
102 {
|
nuclear@0
|
103 return &desc;
|
nuclear@0
|
104 }
|
nuclear@0
|
105
|
nuclear@0
|
106 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
107 // Imports the given file into the given scene structure.
|
nuclear@0
|
108 void BVHLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
|
nuclear@0
|
109 {
|
nuclear@0
|
110 mFileName = pFile;
|
nuclear@0
|
111
|
nuclear@0
|
112 // read file into memory
|
nuclear@0
|
113 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
|
nuclear@0
|
114 if( file.get() == NULL)
|
nuclear@0
|
115 throw DeadlyImportError( "Failed to open file " + pFile + ".");
|
nuclear@0
|
116
|
nuclear@0
|
117 size_t fileSize = file->FileSize();
|
nuclear@0
|
118 if( fileSize == 0)
|
nuclear@0
|
119 throw DeadlyImportError( "File is too small.");
|
nuclear@0
|
120
|
nuclear@0
|
121 mBuffer.resize( fileSize);
|
nuclear@0
|
122 file->Read( &mBuffer.front(), 1, fileSize);
|
nuclear@0
|
123
|
nuclear@0
|
124 // start reading
|
nuclear@0
|
125 mReader = mBuffer.begin();
|
nuclear@0
|
126 mLine = 1;
|
nuclear@0
|
127 ReadStructure( pScene);
|
nuclear@0
|
128
|
nuclear@0
|
129 if (!noSkeletonMesh) {
|
nuclear@0
|
130 // build a dummy mesh for the skeleton so that we see something at least
|
nuclear@0
|
131 SkeletonMeshBuilder meshBuilder( pScene);
|
nuclear@0
|
132 }
|
nuclear@0
|
133
|
nuclear@0
|
134 // construct an animation from all the motion data we read
|
nuclear@0
|
135 CreateAnimation( pScene);
|
nuclear@0
|
136 }
|
nuclear@0
|
137
|
nuclear@0
|
138 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
139 // Reads the file
|
nuclear@0
|
140 void BVHLoader::ReadStructure( aiScene* pScene)
|
nuclear@0
|
141 {
|
nuclear@0
|
142 // first comes hierarchy
|
nuclear@0
|
143 std::string header = GetNextToken();
|
nuclear@0
|
144 if( header != "HIERARCHY")
|
nuclear@0
|
145 ThrowException( "Expected header string \"HIERARCHY\".");
|
nuclear@0
|
146 ReadHierarchy( pScene);
|
nuclear@0
|
147
|
nuclear@0
|
148 // then comes the motion data
|
nuclear@0
|
149 std::string motion = GetNextToken();
|
nuclear@0
|
150 if( motion != "MOTION")
|
nuclear@0
|
151 ThrowException( "Expected beginning of motion data \"MOTION\".");
|
nuclear@0
|
152 ReadMotion( pScene);
|
nuclear@0
|
153 }
|
nuclear@0
|
154
|
nuclear@0
|
155 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
156 // Reads the hierarchy
|
nuclear@0
|
157 void BVHLoader::ReadHierarchy( aiScene* pScene)
|
nuclear@0
|
158 {
|
nuclear@0
|
159 std::string root = GetNextToken();
|
nuclear@0
|
160 if( root != "ROOT")
|
nuclear@0
|
161 ThrowException( "Expected root node \"ROOT\".");
|
nuclear@0
|
162
|
nuclear@0
|
163 // Go read the hierarchy from here
|
nuclear@0
|
164 pScene->mRootNode = ReadNode();
|
nuclear@0
|
165 }
|
nuclear@0
|
166
|
nuclear@0
|
167 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
168 // Reads a node and recursively its childs and returns the created node;
|
nuclear@0
|
169 aiNode* BVHLoader::ReadNode()
|
nuclear@0
|
170 {
|
nuclear@0
|
171 // first token is name
|
nuclear@0
|
172 std::string nodeName = GetNextToken();
|
nuclear@0
|
173 if( nodeName.empty() || nodeName == "{")
|
nuclear@0
|
174 ThrowException( boost::str( boost::format( "Expected node name, but found \"%s\".") % nodeName));
|
nuclear@0
|
175
|
nuclear@0
|
176 // then an opening brace should follow
|
nuclear@0
|
177 std::string openBrace = GetNextToken();
|
nuclear@0
|
178 if( openBrace != "{")
|
nuclear@0
|
179 ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace));
|
nuclear@0
|
180
|
nuclear@0
|
181 // Create a node
|
nuclear@0
|
182 aiNode* node = new aiNode( nodeName);
|
nuclear@0
|
183 std::vector<aiNode*> childNodes;
|
nuclear@0
|
184
|
nuclear@0
|
185 // and create an bone entry for it
|
nuclear@0
|
186 mNodes.push_back( Node( node));
|
nuclear@0
|
187 Node& internNode = mNodes.back();
|
nuclear@0
|
188
|
nuclear@0
|
189 // now read the node's contents
|
nuclear@0
|
190 while( 1)
|
nuclear@0
|
191 {
|
nuclear@0
|
192 std::string token = GetNextToken();
|
nuclear@0
|
193
|
nuclear@0
|
194 // node offset to parent node
|
nuclear@0
|
195 if( token == "OFFSET")
|
nuclear@0
|
196 ReadNodeOffset( node);
|
nuclear@0
|
197 else if( token == "CHANNELS")
|
nuclear@0
|
198 ReadNodeChannels( internNode);
|
nuclear@0
|
199 else if( token == "JOINT")
|
nuclear@0
|
200 {
|
nuclear@0
|
201 // child node follows
|
nuclear@0
|
202 aiNode* child = ReadNode();
|
nuclear@0
|
203 child->mParent = node;
|
nuclear@0
|
204 childNodes.push_back( child);
|
nuclear@0
|
205 }
|
nuclear@0
|
206 else if( token == "End")
|
nuclear@0
|
207 {
|
nuclear@0
|
208 // The real symbol is "End Site". Second part comes in a separate token
|
nuclear@0
|
209 std::string siteToken = GetNextToken();
|
nuclear@0
|
210 if( siteToken != "Site")
|
nuclear@0
|
211 ThrowException( boost::str( boost::format( "Expected \"End Site\" keyword, but found \"%s %s\".") % token % siteToken));
|
nuclear@0
|
212
|
nuclear@0
|
213 aiNode* child = ReadEndSite( nodeName);
|
nuclear@0
|
214 child->mParent = node;
|
nuclear@0
|
215 childNodes.push_back( child);
|
nuclear@0
|
216 }
|
nuclear@0
|
217 else if( token == "}")
|
nuclear@0
|
218 {
|
nuclear@0
|
219 // we're done with that part of the hierarchy
|
nuclear@0
|
220 break;
|
nuclear@0
|
221 } else
|
nuclear@0
|
222 {
|
nuclear@0
|
223 // everything else is a parse error
|
nuclear@0
|
224 ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token));
|
nuclear@0
|
225 }
|
nuclear@0
|
226 }
|
nuclear@0
|
227
|
nuclear@0
|
228 // add the child nodes if there are any
|
nuclear@0
|
229 if( childNodes.size() > 0)
|
nuclear@0
|
230 {
|
nuclear@0
|
231 node->mNumChildren = childNodes.size();
|
nuclear@0
|
232 node->mChildren = new aiNode*[node->mNumChildren];
|
nuclear@0
|
233 std::copy( childNodes.begin(), childNodes.end(), node->mChildren);
|
nuclear@0
|
234 }
|
nuclear@0
|
235
|
nuclear@0
|
236 // and return the sub-hierarchy we built here
|
nuclear@0
|
237 return node;
|
nuclear@0
|
238 }
|
nuclear@0
|
239
|
nuclear@0
|
240 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
241 // Reads an end node and returns the created node.
|
nuclear@0
|
242 aiNode* BVHLoader::ReadEndSite( const std::string& pParentName)
|
nuclear@0
|
243 {
|
nuclear@0
|
244 // check opening brace
|
nuclear@0
|
245 std::string openBrace = GetNextToken();
|
nuclear@0
|
246 if( openBrace != "{")
|
nuclear@0
|
247 ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace));
|
nuclear@0
|
248
|
nuclear@0
|
249 // Create a node
|
nuclear@0
|
250 aiNode* node = new aiNode( "EndSite_" + pParentName);
|
nuclear@0
|
251
|
nuclear@0
|
252 // now read the node's contents. Only possible entry is "OFFSET"
|
nuclear@0
|
253 while( 1)
|
nuclear@0
|
254 {
|
nuclear@0
|
255 std::string token = GetNextToken();
|
nuclear@0
|
256
|
nuclear@0
|
257 // end node's offset
|
nuclear@0
|
258 if( token == "OFFSET")
|
nuclear@0
|
259 {
|
nuclear@0
|
260 ReadNodeOffset( node);
|
nuclear@0
|
261 }
|
nuclear@0
|
262 else if( token == "}")
|
nuclear@0
|
263 {
|
nuclear@0
|
264 // we're done with the end node
|
nuclear@0
|
265 break;
|
nuclear@0
|
266 } else
|
nuclear@0
|
267 {
|
nuclear@0
|
268 // everything else is a parse error
|
nuclear@0
|
269 ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token));
|
nuclear@0
|
270 }
|
nuclear@0
|
271 }
|
nuclear@0
|
272
|
nuclear@0
|
273 // and return the sub-hierarchy we built here
|
nuclear@0
|
274 return node;
|
nuclear@0
|
275 }
|
nuclear@0
|
276 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
277 // Reads a node offset for the given node
|
nuclear@0
|
278 void BVHLoader::ReadNodeOffset( aiNode* pNode)
|
nuclear@0
|
279 {
|
nuclear@0
|
280 // Offset consists of three floats to read
|
nuclear@0
|
281 aiVector3D offset;
|
nuclear@0
|
282 offset.x = GetNextTokenAsFloat();
|
nuclear@0
|
283 offset.y = GetNextTokenAsFloat();
|
nuclear@0
|
284 offset.z = GetNextTokenAsFloat();
|
nuclear@0
|
285
|
nuclear@0
|
286 // build a transformation matrix from it
|
nuclear@0
|
287 pNode->mTransformation = aiMatrix4x4( 1.0f, 0.0f, 0.0f, offset.x, 0.0f, 1.0f, 0.0f, offset.y,
|
nuclear@0
|
288 0.0f, 0.0f, 1.0f, offset.z, 0.0f, 0.0f, 0.0f, 1.0f);
|
nuclear@0
|
289 }
|
nuclear@0
|
290
|
nuclear@0
|
291 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
292 // Reads the animation channels for the given node
|
nuclear@0
|
293 void BVHLoader::ReadNodeChannels( BVHLoader::Node& pNode)
|
nuclear@0
|
294 {
|
nuclear@0
|
295 // number of channels. Use the float reader because we're lazy
|
nuclear@0
|
296 float numChannelsFloat = GetNextTokenAsFloat();
|
nuclear@0
|
297 unsigned int numChannels = (unsigned int) numChannelsFloat;
|
nuclear@0
|
298
|
nuclear@0
|
299 for( unsigned int a = 0; a < numChannels; a++)
|
nuclear@0
|
300 {
|
nuclear@0
|
301 std::string channelToken = GetNextToken();
|
nuclear@0
|
302
|
nuclear@0
|
303 if( channelToken == "Xposition")
|
nuclear@0
|
304 pNode.mChannels.push_back( Channel_PositionX);
|
nuclear@0
|
305 else if( channelToken == "Yposition")
|
nuclear@0
|
306 pNode.mChannels.push_back( Channel_PositionY);
|
nuclear@0
|
307 else if( channelToken == "Zposition")
|
nuclear@0
|
308 pNode.mChannels.push_back( Channel_PositionZ);
|
nuclear@0
|
309 else if( channelToken == "Xrotation")
|
nuclear@0
|
310 pNode.mChannels.push_back( Channel_RotationX);
|
nuclear@0
|
311 else if( channelToken == "Yrotation")
|
nuclear@0
|
312 pNode.mChannels.push_back( Channel_RotationY);
|
nuclear@0
|
313 else if( channelToken == "Zrotation")
|
nuclear@0
|
314 pNode.mChannels.push_back( Channel_RotationZ);
|
nuclear@0
|
315 else
|
nuclear@0
|
316 ThrowException( boost::str( boost::format( "Invalid channel specifier \"%s\".") % channelToken));
|
nuclear@0
|
317 }
|
nuclear@0
|
318 }
|
nuclear@0
|
319
|
nuclear@0
|
320 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
321 // Reads the motion data
|
nuclear@0
|
322 void BVHLoader::ReadMotion( aiScene* /*pScene*/)
|
nuclear@0
|
323 {
|
nuclear@0
|
324 // Read number of frames
|
nuclear@0
|
325 std::string tokenFrames = GetNextToken();
|
nuclear@0
|
326 if( tokenFrames != "Frames:")
|
nuclear@0
|
327 ThrowException( boost::str( boost::format( "Expected frame count \"Frames:\", but found \"%s\".") % tokenFrames));
|
nuclear@0
|
328
|
nuclear@0
|
329 float numFramesFloat = GetNextTokenAsFloat();
|
nuclear@0
|
330 mAnimNumFrames = (unsigned int) numFramesFloat;
|
nuclear@0
|
331
|
nuclear@0
|
332 // Read frame duration
|
nuclear@0
|
333 std::string tokenDuration1 = GetNextToken();
|
nuclear@0
|
334 std::string tokenDuration2 = GetNextToken();
|
nuclear@0
|
335 if( tokenDuration1 != "Frame" || tokenDuration2 != "Time:")
|
nuclear@0
|
336 ThrowException( boost::str( boost::format( "Expected frame duration \"Frame Time:\", but found \"%s %s\".") % tokenDuration1 % tokenDuration2));
|
nuclear@0
|
337
|
nuclear@0
|
338 mAnimTickDuration = GetNextTokenAsFloat();
|
nuclear@0
|
339
|
nuclear@0
|
340 // resize value vectors for each node
|
nuclear@0
|
341 for( std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it)
|
nuclear@0
|
342 it->mChannelValues.reserve( it->mChannels.size() * mAnimNumFrames);
|
nuclear@0
|
343
|
nuclear@0
|
344 // now read all the data and store it in the corresponding node's value vector
|
nuclear@0
|
345 for( unsigned int frame = 0; frame < mAnimNumFrames; ++frame)
|
nuclear@0
|
346 {
|
nuclear@0
|
347 // on each line read the values for all nodes
|
nuclear@0
|
348 for( std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it)
|
nuclear@0
|
349 {
|
nuclear@0
|
350 // get as many values as the node has channels
|
nuclear@0
|
351 for( unsigned int c = 0; c < it->mChannels.size(); ++c)
|
nuclear@0
|
352 it->mChannelValues.push_back( GetNextTokenAsFloat());
|
nuclear@0
|
353 }
|
nuclear@0
|
354
|
nuclear@0
|
355 // after one frame worth of values for all nodes there should be a newline, but we better don't rely on it
|
nuclear@0
|
356 }
|
nuclear@0
|
357 }
|
nuclear@0
|
358
|
nuclear@0
|
359 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
360 // Retrieves the next token
|
nuclear@0
|
361 std::string BVHLoader::GetNextToken()
|
nuclear@0
|
362 {
|
nuclear@0
|
363 // skip any preceeding whitespace
|
nuclear@0
|
364 while( mReader != mBuffer.end())
|
nuclear@0
|
365 {
|
nuclear@0
|
366 if( !isspace( *mReader))
|
nuclear@0
|
367 break;
|
nuclear@0
|
368
|
nuclear@0
|
369 // count lines
|
nuclear@0
|
370 if( *mReader == '\n')
|
nuclear@0
|
371 mLine++;
|
nuclear@0
|
372
|
nuclear@0
|
373 ++mReader;
|
nuclear@0
|
374 }
|
nuclear@0
|
375
|
nuclear@0
|
376 // collect all chars till the next whitespace. BVH is easy in respect to that.
|
nuclear@0
|
377 std::string token;
|
nuclear@0
|
378 while( mReader != mBuffer.end())
|
nuclear@0
|
379 {
|
nuclear@0
|
380 if( isspace( *mReader))
|
nuclear@0
|
381 break;
|
nuclear@0
|
382
|
nuclear@0
|
383 token.push_back( *mReader);
|
nuclear@0
|
384 ++mReader;
|
nuclear@0
|
385
|
nuclear@0
|
386 // little extra logic to make sure braces are counted correctly
|
nuclear@0
|
387 if( token == "{" || token == "}")
|
nuclear@0
|
388 break;
|
nuclear@0
|
389 }
|
nuclear@0
|
390
|
nuclear@0
|
391 // empty token means end of file, which is just fine
|
nuclear@0
|
392 return token;
|
nuclear@0
|
393 }
|
nuclear@0
|
394
|
nuclear@0
|
395 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
396 // Reads the next token as a float
|
nuclear@0
|
397 float BVHLoader::GetNextTokenAsFloat()
|
nuclear@0
|
398 {
|
nuclear@0
|
399 std::string token = GetNextToken();
|
nuclear@0
|
400 if( token.empty())
|
nuclear@0
|
401 ThrowException( "Unexpected end of file while trying to read a float");
|
nuclear@0
|
402
|
nuclear@0
|
403 // check if the float is valid by testing if the atof() function consumed every char of the token
|
nuclear@0
|
404 const char* ctoken = token.c_str();
|
nuclear@0
|
405 float result = 0.0f;
|
nuclear@0
|
406 ctoken = fast_atoreal_move<float>( ctoken, result);
|
nuclear@0
|
407
|
nuclear@0
|
408 if( ctoken != token.c_str() + token.length())
|
nuclear@0
|
409 ThrowException( boost::str( boost::format( "Expected a floating point number, but found \"%s\".") % token));
|
nuclear@0
|
410
|
nuclear@0
|
411 return result;
|
nuclear@0
|
412 }
|
nuclear@0
|
413
|
nuclear@0
|
414 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
415 // Aborts the file reading with an exception
|
nuclear@0
|
416 void BVHLoader::ThrowException( const std::string& pError)
|
nuclear@0
|
417 {
|
nuclear@0
|
418 throw DeadlyImportError( boost::str( boost::format( "%s:%d - %s") % mFileName % mLine % pError));
|
nuclear@0
|
419 }
|
nuclear@0
|
420
|
nuclear@0
|
421 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
422 // Constructs an animation for the motion data and stores it in the given scene
|
nuclear@0
|
423 void BVHLoader::CreateAnimation( aiScene* pScene)
|
nuclear@0
|
424 {
|
nuclear@0
|
425 // create the animation
|
nuclear@0
|
426 pScene->mNumAnimations = 1;
|
nuclear@0
|
427 pScene->mAnimations = new aiAnimation*[1];
|
nuclear@0
|
428 aiAnimation* anim = new aiAnimation;
|
nuclear@0
|
429 pScene->mAnimations[0] = anim;
|
nuclear@0
|
430
|
nuclear@0
|
431 // put down the basic parameters
|
nuclear@0
|
432 anim->mName.Set( "Motion");
|
nuclear@0
|
433 anim->mTicksPerSecond = 1.0 / double( mAnimTickDuration);
|
nuclear@0
|
434 anim->mDuration = double( mAnimNumFrames - 1);
|
nuclear@0
|
435
|
nuclear@0
|
436 // now generate the tracks for all nodes
|
nuclear@0
|
437 anim->mNumChannels = mNodes.size();
|
nuclear@0
|
438 anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
|
nuclear@0
|
439
|
nuclear@0
|
440 // FIX: set the array elements to NULL to ensure proper deletion if an exception is thrown
|
nuclear@0
|
441 for (unsigned int i = 0; i < anim->mNumChannels;++i)
|
nuclear@0
|
442 anim->mChannels[i] = NULL;
|
nuclear@0
|
443
|
nuclear@0
|
444 for( unsigned int a = 0; a < anim->mNumChannels; a++)
|
nuclear@0
|
445 {
|
nuclear@0
|
446 const Node& node = mNodes[a];
|
nuclear@0
|
447 const std::string nodeName = std::string( node.mNode->mName.data );
|
nuclear@0
|
448 aiNodeAnim* nodeAnim = new aiNodeAnim;
|
nuclear@0
|
449 anim->mChannels[a] = nodeAnim;
|
nuclear@0
|
450 nodeAnim->mNodeName.Set( nodeName);
|
nuclear@0
|
451
|
nuclear@0
|
452 // translational part, if given
|
nuclear@0
|
453 if( node.mChannels.size() == 6)
|
nuclear@0
|
454 {
|
nuclear@0
|
455 nodeAnim->mNumPositionKeys = mAnimNumFrames;
|
nuclear@0
|
456 nodeAnim->mPositionKeys = new aiVectorKey[mAnimNumFrames];
|
nuclear@0
|
457 aiVectorKey* poskey = nodeAnim->mPositionKeys;
|
nuclear@0
|
458 for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr)
|
nuclear@0
|
459 {
|
nuclear@0
|
460 poskey->mTime = double( fr);
|
nuclear@0
|
461
|
nuclear@0
|
462 // Now compute all translations in the right order
|
nuclear@0
|
463 for( unsigned int channel = 0; channel < 3; ++channel)
|
nuclear@0
|
464 {
|
nuclear@0
|
465 switch( node.mChannels[channel])
|
nuclear@0
|
466 {
|
nuclear@0
|
467 case Channel_PositionX: poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
|
nuclear@0
|
468 case Channel_PositionY: poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
|
nuclear@0
|
469 case Channel_PositionZ: poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
|
nuclear@0
|
470 default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName );
|
nuclear@0
|
471 }
|
nuclear@0
|
472 }
|
nuclear@0
|
473 ++poskey;
|
nuclear@0
|
474 }
|
nuclear@0
|
475 } else
|
nuclear@0
|
476 {
|
nuclear@0
|
477 // if no translation part is given, put a default sequence
|
nuclear@0
|
478 aiVector3D nodePos( node.mNode->mTransformation.a4, node.mNode->mTransformation.b4, node.mNode->mTransformation.c4);
|
nuclear@0
|
479 nodeAnim->mNumPositionKeys = 1;
|
nuclear@0
|
480 nodeAnim->mPositionKeys = new aiVectorKey[1];
|
nuclear@0
|
481 nodeAnim->mPositionKeys[0].mTime = 0.0;
|
nuclear@0
|
482 nodeAnim->mPositionKeys[0].mValue = nodePos;
|
nuclear@0
|
483 }
|
nuclear@0
|
484
|
nuclear@0
|
485 // rotation part. Always present. First find value offsets
|
nuclear@0
|
486 {
|
nuclear@0
|
487 unsigned int rotOffset = 0;
|
nuclear@0
|
488 if( node.mChannels.size() == 6)
|
nuclear@0
|
489 {
|
nuclear@0
|
490 // Offset all further calculations
|
nuclear@0
|
491 rotOffset = 3;
|
nuclear@0
|
492 }
|
nuclear@0
|
493
|
nuclear@0
|
494 // Then create the number of rotation keys
|
nuclear@0
|
495 nodeAnim->mNumRotationKeys = mAnimNumFrames;
|
nuclear@0
|
496 nodeAnim->mRotationKeys = new aiQuatKey[mAnimNumFrames];
|
nuclear@0
|
497 aiQuatKey* rotkey = nodeAnim->mRotationKeys;
|
nuclear@0
|
498 for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr)
|
nuclear@0
|
499 {
|
nuclear@0
|
500 aiMatrix4x4 temp;
|
nuclear@0
|
501 aiMatrix3x3 rotMatrix;
|
nuclear@0
|
502
|
nuclear@0
|
503 for( unsigned int channel = 0; channel < 3; ++channel)
|
nuclear@0
|
504 {
|
nuclear@0
|
505 // translate ZXY euler angels into a quaternion
|
nuclear@0
|
506 const float angle = node.mChannelValues[fr * node.mChannels.size() + rotOffset + channel] * float( AI_MATH_PI) / 180.0f;
|
nuclear@0
|
507
|
nuclear@0
|
508 // Compute rotation transformations in the right order
|
nuclear@0
|
509 switch (node.mChannels[rotOffset+channel])
|
nuclear@0
|
510 {
|
nuclear@0
|
511 case Channel_RotationX: aiMatrix4x4::RotationX( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
|
nuclear@0
|
512 case Channel_RotationY: aiMatrix4x4::RotationY( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
|
nuclear@0
|
513 case Channel_RotationZ: aiMatrix4x4::RotationZ( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
|
nuclear@0
|
514 default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName );
|
nuclear@0
|
515 }
|
nuclear@0
|
516 }
|
nuclear@0
|
517
|
nuclear@0
|
518 rotkey->mTime = double( fr);
|
nuclear@0
|
519 rotkey->mValue = aiQuaternion( rotMatrix);
|
nuclear@0
|
520 ++rotkey;
|
nuclear@0
|
521 }
|
nuclear@0
|
522 }
|
nuclear@0
|
523
|
nuclear@0
|
524 // scaling part. Always just a default track
|
nuclear@0
|
525 {
|
nuclear@0
|
526 nodeAnim->mNumScalingKeys = 1;
|
nuclear@0
|
527 nodeAnim->mScalingKeys = new aiVectorKey[1];
|
nuclear@0
|
528 nodeAnim->mScalingKeys[0].mTime = 0.0;
|
nuclear@0
|
529 nodeAnim->mScalingKeys[0].mValue.Set( 1.0f, 1.0f, 1.0f);
|
nuclear@0
|
530 }
|
nuclear@0
|
531 }
|
nuclear@0
|
532 }
|
nuclear@0
|
533
|
nuclear@0
|
534 #endif // !! ASSIMP_BUILD_NO_BVH_IMPORTER
|