1 | /* |
2 | --------------------------------------------------------------------------- |
3 | Open Asset Import Library (assimp) |
4 | --------------------------------------------------------------------------- |
5 | |
6 | Copyright (c) 2006-2017, assimp team |
7 | |
8 | |
9 | All rights reserved. |
10 | |
11 | Redistribution and use of this software in source and binary forms, |
12 | with or without modification, are permitted provided that the following |
13 | conditions are met: |
14 | |
15 | * Redistributions of source code must retain the above |
16 | copyright notice, this list of conditions and the |
17 | following disclaimer. |
18 | |
19 | * Redistributions in binary form must reproduce the above |
20 | copyright notice, this list of conditions and the |
21 | following disclaimer in the documentation and/or other |
22 | materials provided with the distribution. |
23 | |
24 | * Neither the name of the assimp team, nor the names of its |
25 | contributors may be used to endorse or promote products |
26 | derived from this software without specific prior |
27 | written permission of the assimp team. |
28 | |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
39 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
40 | --------------------------------------------------------------------------- |
41 | */ |
42 | |
43 | /** @file Implementation of the XFile parser helper class */ |
44 | |
45 | |
46 | #ifndef ASSIMP_BUILD_NO_X_IMPORTER |
47 | |
48 | #include "XFileParser.h" |
49 | #include "XFileHelper.h" |
50 | #include "fast_atof.h" |
51 | #include "Exceptional.h" |
52 | #include "TinyFormatter.h" |
53 | #include "ByteSwapper.h" |
54 | #include "StringUtils.h" |
55 | #include <assimp/DefaultLogger.hpp> |
56 | |
57 | |
58 | using namespace Assimp; |
59 | using namespace Assimp::XFile; |
60 | using namespace Assimp::Formatter; |
61 | |
62 | #ifndef ASSIMP_BUILD_NO_COMPRESSED_X |
63 | |
64 | # ifdef ASSIMP_BUILD_NO_OWN_ZLIB |
65 | # include <zlib.h> |
66 | # else |
67 | # include "../contrib/zlib/zlib.h" |
68 | # endif |
69 | |
70 | // Magic identifier for MSZIP compressed data |
71 | #define MSZIP_MAGIC 0x4B43 |
72 | #define MSZIP_BLOCK 32786 |
73 | |
74 | // ------------------------------------------------------------------------------------------------ |
75 | // Dummy memory wrappers for use with zlib |
76 | static void* dummy_alloc (void* /*opaque*/, unsigned int items, unsigned int size) { |
77 | return ::operator new(items*size); |
78 | } |
79 | |
80 | static void dummy_free (void* /*opaque*/, void* address) { |
81 | return ::operator delete(address); |
82 | } |
83 | |
84 | #endif // !! ASSIMP_BUILD_NO_COMPRESSED_X |
85 | |
86 | // ------------------------------------------------------------------------------------------------ |
87 | // Constructor. Creates a data structure out of the XFile given in the memory block. |
88 | XFileParser::XFileParser( const std::vector<char>& pBuffer) |
89 | { |
90 | mMajorVersion = mMinorVersion = 0; |
91 | mIsBinaryFormat = false; |
92 | mBinaryNumCount = 0; |
93 | P = End = NULL; |
94 | mLineNumber = 0; |
95 | mScene = NULL; |
96 | |
97 | // vector to store uncompressed file for INFLATE'd X files |
98 | std::vector<char> uncompressed; |
99 | |
100 | // set up memory pointers |
101 | P = &pBuffer.front(); |
102 | End = P + pBuffer.size() - 1; |
103 | |
104 | // check header |
105 | if( strncmp( P, "xof " , 4) != 0) |
106 | throw DeadlyImportError( "Header mismatch, file is not an XFile." ); |
107 | |
108 | // read version. It comes in a four byte format such as "0302" |
109 | mMajorVersion = (unsigned int)(P[4] - 48) * 10 + (unsigned int)(P[5] - 48); |
110 | mMinorVersion = (unsigned int)(P[6] - 48) * 10 + (unsigned int)(P[7] - 48); |
111 | |
112 | bool compressed = false; |
113 | |
114 | // txt - pure ASCII text format |
115 | if( strncmp( P + 8, "txt " , 4) == 0) |
116 | mIsBinaryFormat = false; |
117 | |
118 | // bin - Binary format |
119 | else if( strncmp( P + 8, "bin " , 4) == 0) |
120 | mIsBinaryFormat = true; |
121 | |
122 | // tzip - Inflate compressed text format |
123 | else if( strncmp( P + 8, "tzip" , 4) == 0) |
124 | { |
125 | mIsBinaryFormat = false; |
126 | compressed = true; |
127 | } |
128 | // bzip - Inflate compressed binary format |
129 | else if( strncmp( P + 8, "bzip" , 4) == 0) |
130 | { |
131 | mIsBinaryFormat = true; |
132 | compressed = true; |
133 | } |
134 | else ThrowException( format() << "Unsupported xfile format '" << |
135 | P[8] << P[9] << P[10] << P[11] << "'" ); |
136 | |
137 | // float size |
138 | mBinaryFloatSize = (unsigned int)(P[12] - 48) * 1000 |
139 | + (unsigned int)(P[13] - 48) * 100 |
140 | + (unsigned int)(P[14] - 48) * 10 |
141 | + (unsigned int)(P[15] - 48); |
142 | |
143 | if( mBinaryFloatSize != 32 && mBinaryFloatSize != 64) |
144 | ThrowException( format() << "Unknown float size " << mBinaryFloatSize << " specified in xfile header." ); |
145 | |
146 | // The x format specifies size in bits, but we work in bytes |
147 | mBinaryFloatSize /= 8; |
148 | |
149 | P += 16; |
150 | |
151 | // If this is a compressed X file, apply the inflate algorithm to it |
152 | if (compressed) |
153 | { |
154 | #ifdef ASSIMP_BUILD_NO_COMPRESSED_X |
155 | throw DeadlyImportError("Assimp was built without compressed X support" ); |
156 | #else |
157 | /* /////////////////////////////////////////////////////////////////////// |
158 | * COMPRESSED X FILE FORMAT |
159 | * /////////////////////////////////////////////////////////////////////// |
160 | * [xhead] |
161 | * 2 major |
162 | * 2 minor |
163 | * 4 type // bzip,tzip |
164 | * [mszip_master_head] |
165 | * 4 unkn // checksum? |
166 | * 2 unkn // flags? (seems to be constant) |
167 | * [mszip_head] |
168 | * 2 ofs // offset to next section |
169 | * 2 magic // 'CK' |
170 | * ... ofs bytes of data |
171 | * ... next mszip_head |
172 | * |
173 | * http://www.kdedevelopers.org/node/3181 has been very helpful. |
174 | * /////////////////////////////////////////////////////////////////////// |
175 | */ |
176 | |
177 | // build a zlib stream |
178 | z_stream stream; |
179 | stream.opaque = NULL; |
180 | stream.zalloc = &dummy_alloc; |
181 | stream.zfree = &dummy_free; |
182 | stream.data_type = (mIsBinaryFormat ? Z_BINARY : Z_ASCII); |
183 | |
184 | // initialize the inflation algorithm |
185 | ::inflateInit2(&stream, -MAX_WBITS); |
186 | |
187 | // skip unknown data (checksum, flags?) |
188 | P += 6; |
189 | |
190 | // First find out how much storage we'll need. Count sections. |
191 | const char* P1 = P; |
192 | unsigned int est_out = 0; |
193 | |
194 | while (P1 + 3 < End) |
195 | { |
196 | // read next offset |
197 | uint16_t ofs = *((uint16_t*)P1); |
198 | AI_SWAP2(ofs); P1 += 2; |
199 | |
200 | if (ofs >= MSZIP_BLOCK) |
201 | throw DeadlyImportError("X: Invalid offset to next MSZIP compressed block" ); |
202 | |
203 | // check magic word |
204 | uint16_t magic = *((uint16_t*)P1); |
205 | AI_SWAP2(magic); P1 += 2; |
206 | |
207 | if (magic != MSZIP_MAGIC) |
208 | throw DeadlyImportError("X: Unsupported compressed format, expected MSZIP header" ); |
209 | |
210 | // and advance to the next offset |
211 | P1 += ofs; |
212 | est_out += MSZIP_BLOCK; // one decompressed block is 32786 in size |
213 | } |
214 | |
215 | // Allocate storage and terminating zero and do the actual uncompressing |
216 | uncompressed.resize(est_out + 1); |
217 | char* out = &uncompressed.front(); |
218 | while (P + 3 < End) |
219 | { |
220 | uint16_t ofs = *((uint16_t*)P); |
221 | AI_SWAP2(ofs); |
222 | P += 4; |
223 | |
224 | if (P + ofs > End + 2) { |
225 | throw DeadlyImportError("X: Unexpected EOF in compressed chunk" ); |
226 | } |
227 | |
228 | // push data to the stream |
229 | stream.next_in = (Bytef*)P; |
230 | stream.avail_in = ofs; |
231 | stream.next_out = (Bytef*)out; |
232 | stream.avail_out = MSZIP_BLOCK; |
233 | |
234 | // and decompress the data .... |
235 | int ret = ::inflate( &stream, Z_SYNC_FLUSH ); |
236 | if (ret != Z_OK && ret != Z_STREAM_END) |
237 | throw DeadlyImportError("X: Failed to decompress MSZIP-compressed data" ); |
238 | |
239 | ::inflateReset( &stream ); |
240 | ::inflateSetDictionary( &stream, (const Bytef*)out , MSZIP_BLOCK - stream.avail_out ); |
241 | |
242 | // and advance to the next offset |
243 | out += MSZIP_BLOCK - stream.avail_out; |
244 | P += ofs; |
245 | } |
246 | |
247 | // terminate zlib |
248 | ::inflateEnd(&stream); |
249 | |
250 | // ok, update pointers to point to the uncompressed file data |
251 | P = &uncompressed[0]; |
252 | End = out; |
253 | |
254 | // FIXME: we don't need the compressed data anymore, could release |
255 | // it already for better memory usage. Consider breaking const-co. |
256 | DefaultLogger::get()->info("Successfully decompressed MSZIP-compressed file" ); |
257 | #endif // !! ASSIMP_BUILD_NO_COMPRESSED_X |
258 | } |
259 | else |
260 | { |
261 | // start reading here |
262 | ReadUntilEndOfLine(); |
263 | } |
264 | |
265 | mScene = new Scene; |
266 | ParseFile(); |
267 | |
268 | // filter the imported hierarchy for some degenerated cases |
269 | if( mScene->mRootNode) { |
270 | FilterHierarchy( mScene->mRootNode); |
271 | } |
272 | } |
273 | |
274 | // ------------------------------------------------------------------------------------------------ |
275 | // Destructor. Destroys all imported data along with it |
276 | XFileParser::~XFileParser() |
277 | { |
278 | // kill everything we created |
279 | delete mScene; |
280 | } |
281 | |
282 | // ------------------------------------------------------------------------------------------------ |
283 | void XFileParser::ParseFile() |
284 | { |
285 | bool running = true; |
286 | while( running ) |
287 | { |
288 | // read name of next object |
289 | std::string objectName = GetNextToken(); |
290 | if (objectName.length() == 0) |
291 | break; |
292 | |
293 | // parse specific object |
294 | if( objectName == "template" ) |
295 | ParseDataObjectTemplate(); |
296 | else |
297 | if( objectName == "Frame" ) |
298 | ParseDataObjectFrame( NULL); |
299 | else |
300 | if( objectName == "Mesh" ) |
301 | { |
302 | // some meshes have no frames at all |
303 | Mesh* mesh = new Mesh; |
304 | ParseDataObjectMesh( mesh); |
305 | mScene->mGlobalMeshes.push_back( mesh); |
306 | } else |
307 | if( objectName == "AnimTicksPerSecond" ) |
308 | ParseDataObjectAnimTicksPerSecond(); |
309 | else |
310 | if( objectName == "AnimationSet" ) |
311 | ParseDataObjectAnimationSet(); |
312 | else |
313 | if( objectName == "Material" ) |
314 | { |
315 | // Material outside of a mesh or node |
316 | Material material; |
317 | ParseDataObjectMaterial( &material); |
318 | mScene->mGlobalMaterials.push_back( material); |
319 | } else |
320 | if( objectName == "}" ) |
321 | { |
322 | // whatever? |
323 | DefaultLogger::get()->warn("} found in dataObject" ); |
324 | } else |
325 | { |
326 | // unknown format |
327 | DefaultLogger::get()->warn("Unknown data object in animation of .x file" ); |
328 | ParseUnknownDataObject(); |
329 | } |
330 | } |
331 | } |
332 | |
333 | // ------------------------------------------------------------------------------------------------ |
334 | void XFileParser::ParseDataObjectTemplate() |
335 | { |
336 | // parse a template data object. Currently not stored. |
337 | std::string name; |
338 | readHeadOfDataObject( &name); |
339 | |
340 | // read GUID |
341 | std::string guid = GetNextToken(); |
342 | |
343 | // read and ignore data members |
344 | bool running = true; |
345 | while ( running ) |
346 | { |
347 | std::string s = GetNextToken(); |
348 | |
349 | if( s == "}" ) |
350 | break; |
351 | |
352 | if( s.length() == 0) |
353 | ThrowException( "Unexpected end of file reached while parsing template definition" ); |
354 | } |
355 | } |
356 | |
357 | // ------------------------------------------------------------------------------------------------ |
358 | void XFileParser::ParseDataObjectFrame( Node* pParent) |
359 | { |
360 | // A coordinate frame, or "frame of reference." The Frame template |
361 | // is open and can contain any object. The Direct3D extensions (D3DX) |
362 | // mesh-loading functions recognize Mesh, FrameTransformMatrix, and |
363 | // Frame template instances as child objects when loading a Frame |
364 | // instance. |
365 | std::string name; |
366 | readHeadOfDataObject(&name); |
367 | |
368 | // create a named node and place it at its parent, if given |
369 | Node* node = new Node( pParent); |
370 | node->mName = name; |
371 | if( pParent) |
372 | { |
373 | pParent->mChildren.push_back( node); |
374 | } else |
375 | { |
376 | // there might be multiple root nodes |
377 | if( mScene->mRootNode != NULL) |
378 | { |
379 | // place a dummy root if not there |
380 | if( mScene->mRootNode->mName != "$dummy_root" ) |
381 | { |
382 | Node* exroot = mScene->mRootNode; |
383 | mScene->mRootNode = new Node( NULL); |
384 | mScene->mRootNode->mName = "$dummy_root" ; |
385 | mScene->mRootNode->mChildren.push_back( exroot); |
386 | exroot->mParent = mScene->mRootNode; |
387 | } |
388 | // put the new node as its child instead |
389 | mScene->mRootNode->mChildren.push_back( node); |
390 | node->mParent = mScene->mRootNode; |
391 | } else |
392 | { |
393 | // it's the first node imported. place it as root |
394 | mScene->mRootNode = node; |
395 | } |
396 | } |
397 | |
398 | // Now inside a frame. |
399 | // read tokens until closing brace is reached. |
400 | bool running = true; |
401 | while ( running ) |
402 | { |
403 | std::string objectName = GetNextToken(); |
404 | if (objectName.size() == 0) |
405 | ThrowException( "Unexpected end of file reached while parsing frame" ); |
406 | |
407 | if( objectName == "}" ) |
408 | break; // frame finished |
409 | else |
410 | if( objectName == "Frame" ) |
411 | ParseDataObjectFrame( node); // child frame |
412 | else |
413 | if( objectName == "FrameTransformMatrix" ) |
414 | ParseDataObjectTransformationMatrix( node->mTrafoMatrix); |
415 | else |
416 | if( objectName == "Mesh" ) |
417 | { |
418 | Mesh* mesh = new Mesh(name); |
419 | node->mMeshes.push_back( mesh); |
420 | ParseDataObjectMesh( mesh); |
421 | } else |
422 | { |
423 | DefaultLogger::get()->warn("Unknown data object in frame in x file" ); |
424 | ParseUnknownDataObject(); |
425 | } |
426 | } |
427 | } |
428 | |
429 | // ------------------------------------------------------------------------------------------------ |
430 | void XFileParser::ParseDataObjectTransformationMatrix( aiMatrix4x4& pMatrix) |
431 | { |
432 | // read header, we're not interested if it has a name |
433 | readHeadOfDataObject(); |
434 | |
435 | // read its components |
436 | pMatrix.a1 = ReadFloat(); pMatrix.b1 = ReadFloat(); |
437 | pMatrix.c1 = ReadFloat(); pMatrix.d1 = ReadFloat(); |
438 | pMatrix.a2 = ReadFloat(); pMatrix.b2 = ReadFloat(); |
439 | pMatrix.c2 = ReadFloat(); pMatrix.d2 = ReadFloat(); |
440 | pMatrix.a3 = ReadFloat(); pMatrix.b3 = ReadFloat(); |
441 | pMatrix.c3 = ReadFloat(); pMatrix.d3 = ReadFloat(); |
442 | pMatrix.a4 = ReadFloat(); pMatrix.b4 = ReadFloat(); |
443 | pMatrix.c4 = ReadFloat(); pMatrix.d4 = ReadFloat(); |
444 | |
445 | // trailing symbols |
446 | CheckForSemicolon(); |
447 | CheckForClosingBrace(); |
448 | } |
449 | |
450 | // ------------------------------------------------------------------------------------------------ |
451 | void XFileParser::ParseDataObjectMesh( Mesh* pMesh) |
452 | { |
453 | std::string name; |
454 | readHeadOfDataObject( &name); |
455 | |
456 | // read vertex count |
457 | unsigned int numVertices = ReadInt(); |
458 | pMesh->mPositions.resize( numVertices); |
459 | |
460 | // read vertices |
461 | for( unsigned int a = 0; a < numVertices; a++) |
462 | pMesh->mPositions[a] = ReadVector3(); |
463 | |
464 | // read position faces |
465 | unsigned int numPosFaces = ReadInt(); |
466 | pMesh->mPosFaces.resize( numPosFaces); |
467 | for( unsigned int a = 0; a < numPosFaces; a++) |
468 | { |
469 | // read indices |
470 | unsigned int numIndices = ReadInt(); |
471 | Face& face = pMesh->mPosFaces[a]; |
472 | for (unsigned int b = 0; b < numIndices; b++) { |
473 | face.mIndices.push_back( ReadInt() ); |
474 | } |
475 | TestForSeparator(); |
476 | } |
477 | |
478 | // here, other data objects may follow |
479 | bool running = true; |
480 | while ( running ) |
481 | { |
482 | std::string objectName = GetNextToken(); |
483 | |
484 | if( objectName.size() == 0) |
485 | ThrowException( "Unexpected end of file while parsing mesh structure" ); |
486 | else |
487 | if( objectName == "}" ) |
488 | break; // mesh finished |
489 | else |
490 | if( objectName == "MeshNormals" ) |
491 | ParseDataObjectMeshNormals( pMesh); |
492 | else |
493 | if( objectName == "MeshTextureCoords" ) |
494 | ParseDataObjectMeshTextureCoords( pMesh); |
495 | else |
496 | if( objectName == "MeshVertexColors" ) |
497 | ParseDataObjectMeshVertexColors( pMesh); |
498 | else |
499 | if( objectName == "MeshMaterialList" ) |
500 | ParseDataObjectMeshMaterialList( pMesh); |
501 | else |
502 | if( objectName == "VertexDuplicationIndices" ) |
503 | ParseUnknownDataObject(); // we'll ignore vertex duplication indices |
504 | else |
505 | if( objectName == "XSkinMeshHeader" ) |
506 | ParseDataObjectSkinMeshHeader( pMesh); |
507 | else |
508 | if( objectName == "SkinWeights" ) |
509 | ParseDataObjectSkinWeights( pMesh); |
510 | else |
511 | { |
512 | DefaultLogger::get()->warn("Unknown data object in mesh in x file" ); |
513 | ParseUnknownDataObject(); |
514 | } |
515 | } |
516 | } |
517 | |
518 | // ------------------------------------------------------------------------------------------------ |
519 | void XFileParser::ParseDataObjectSkinWeights( Mesh *pMesh) |
520 | { |
521 | readHeadOfDataObject(); |
522 | |
523 | std::string transformNodeName; |
524 | GetNextTokenAsString( transformNodeName); |
525 | |
526 | pMesh->mBones.push_back( Bone()); |
527 | Bone& bone = pMesh->mBones.back(); |
528 | bone.mName = transformNodeName; |
529 | |
530 | // read vertex weights |
531 | unsigned int numWeights = ReadInt(); |
532 | bone.mWeights.reserve( numWeights); |
533 | |
534 | for( unsigned int a = 0; a < numWeights; a++) |
535 | { |
536 | BoneWeight weight; |
537 | weight.mVertex = ReadInt(); |
538 | bone.mWeights.push_back( weight); |
539 | } |
540 | |
541 | // read vertex weights |
542 | for( unsigned int a = 0; a < numWeights; a++) |
543 | bone.mWeights[a].mWeight = ReadFloat(); |
544 | |
545 | // read matrix offset |
546 | bone.mOffsetMatrix.a1 = ReadFloat(); bone.mOffsetMatrix.b1 = ReadFloat(); |
547 | bone.mOffsetMatrix.c1 = ReadFloat(); bone.mOffsetMatrix.d1 = ReadFloat(); |
548 | bone.mOffsetMatrix.a2 = ReadFloat(); bone.mOffsetMatrix.b2 = ReadFloat(); |
549 | bone.mOffsetMatrix.c2 = ReadFloat(); bone.mOffsetMatrix.d2 = ReadFloat(); |
550 | bone.mOffsetMatrix.a3 = ReadFloat(); bone.mOffsetMatrix.b3 = ReadFloat(); |
551 | bone.mOffsetMatrix.c3 = ReadFloat(); bone.mOffsetMatrix.d3 = ReadFloat(); |
552 | bone.mOffsetMatrix.a4 = ReadFloat(); bone.mOffsetMatrix.b4 = ReadFloat(); |
553 | bone.mOffsetMatrix.c4 = ReadFloat(); bone.mOffsetMatrix.d4 = ReadFloat(); |
554 | |
555 | CheckForSemicolon(); |
556 | CheckForClosingBrace(); |
557 | } |
558 | |
559 | // ------------------------------------------------------------------------------------------------ |
560 | void XFileParser::( Mesh* /*pMesh*/ ) |
561 | { |
562 | readHeadOfDataObject(); |
563 | |
564 | /*unsigned int maxSkinWeightsPerVertex =*/ ReadInt(); |
565 | /*unsigned int maxSkinWeightsPerFace =*/ ReadInt(); |
566 | /*unsigned int numBonesInMesh = */ReadInt(); |
567 | |
568 | CheckForClosingBrace(); |
569 | } |
570 | |
571 | // ------------------------------------------------------------------------------------------------ |
572 | void XFileParser::ParseDataObjectMeshNormals( Mesh* pMesh) |
573 | { |
574 | readHeadOfDataObject(); |
575 | |
576 | // read count |
577 | unsigned int numNormals = ReadInt(); |
578 | pMesh->mNormals.resize( numNormals); |
579 | |
580 | // read normal vectors |
581 | for( unsigned int a = 0; a < numNormals; a++) |
582 | pMesh->mNormals[a] = ReadVector3(); |
583 | |
584 | // read normal indices |
585 | unsigned int numFaces = ReadInt(); |
586 | if( numFaces != pMesh->mPosFaces.size()) |
587 | ThrowException( "Normal face count does not match vertex face count." ); |
588 | |
589 | for( unsigned int a = 0; a < numFaces; a++) |
590 | { |
591 | unsigned int numIndices = ReadInt(); |
592 | pMesh->mNormFaces.push_back( Face()); |
593 | Face& face = pMesh->mNormFaces.back(); |
594 | |
595 | for( unsigned int b = 0; b < numIndices; b++) |
596 | face.mIndices.push_back( ReadInt()); |
597 | |
598 | TestForSeparator(); |
599 | } |
600 | |
601 | CheckForClosingBrace(); |
602 | } |
603 | |
604 | // ------------------------------------------------------------------------------------------------ |
605 | void XFileParser::ParseDataObjectMeshTextureCoords( Mesh* pMesh) |
606 | { |
607 | readHeadOfDataObject(); |
608 | if( pMesh->mNumTextures + 1 > AI_MAX_NUMBER_OF_TEXTURECOORDS) |
609 | ThrowException( "Too many sets of texture coordinates" ); |
610 | |
611 | std::vector<aiVector2D>& coords = pMesh->mTexCoords[pMesh->mNumTextures++]; |
612 | |
613 | unsigned int numCoords = ReadInt(); |
614 | if( numCoords != pMesh->mPositions.size()) |
615 | ThrowException( "Texture coord count does not match vertex count" ); |
616 | |
617 | coords.resize( numCoords); |
618 | for( unsigned int a = 0; a < numCoords; a++) |
619 | coords[a] = ReadVector2(); |
620 | |
621 | CheckForClosingBrace(); |
622 | } |
623 | |
624 | // ------------------------------------------------------------------------------------------------ |
625 | void XFileParser::ParseDataObjectMeshVertexColors( Mesh* pMesh) |
626 | { |
627 | readHeadOfDataObject(); |
628 | if( pMesh->mNumColorSets + 1 > AI_MAX_NUMBER_OF_COLOR_SETS) |
629 | ThrowException( "Too many colorsets" ); |
630 | std::vector<aiColor4D>& colors = pMesh->mColors[pMesh->mNumColorSets++]; |
631 | |
632 | unsigned int numColors = ReadInt(); |
633 | if( numColors != pMesh->mPositions.size()) |
634 | ThrowException( "Vertex color count does not match vertex count" ); |
635 | |
636 | colors.resize( numColors, aiColor4D( 0, 0, 0, 1)); |
637 | for( unsigned int a = 0; a < numColors; a++) |
638 | { |
639 | unsigned int index = ReadInt(); |
640 | if( index >= pMesh->mPositions.size()) |
641 | ThrowException( "Vertex color index out of bounds" ); |
642 | |
643 | colors[index] = ReadRGBA(); |
644 | // HACK: (thom) Maxon Cinema XPort plugin puts a third separator here, kwxPort puts a comma. |
645 | // Ignore gracefully. |
646 | if( !mIsBinaryFormat) |
647 | { |
648 | FindNextNoneWhiteSpace(); |
649 | if( *P == ';' || *P == ',') |
650 | P++; |
651 | } |
652 | } |
653 | |
654 | CheckForClosingBrace(); |
655 | } |
656 | |
657 | // ------------------------------------------------------------------------------------------------ |
658 | void XFileParser::ParseDataObjectMeshMaterialList( Mesh* pMesh) |
659 | { |
660 | readHeadOfDataObject(); |
661 | |
662 | // read material count |
663 | /*unsigned int numMaterials =*/ ReadInt(); |
664 | // read non triangulated face material index count |
665 | unsigned int numMatIndices = ReadInt(); |
666 | |
667 | // some models have a material index count of 1... to be able to read them we |
668 | // replicate this single material index on every face |
669 | if( numMatIndices != pMesh->mPosFaces.size() && numMatIndices != 1) |
670 | ThrowException( "Per-Face material index count does not match face count." ); |
671 | |
672 | // read per-face material indices |
673 | for( unsigned int a = 0; a < numMatIndices; a++) |
674 | pMesh->mFaceMaterials.push_back( ReadInt()); |
675 | |
676 | // in version 03.02, the face indices end with two semicolons. |
677 | // commented out version check, as version 03.03 exported from blender also has 2 semicolons |
678 | if( !mIsBinaryFormat) // && MajorVersion == 3 && MinorVersion <= 2) |
679 | { |
680 | if(P < End && *P == ';') |
681 | ++P; |
682 | } |
683 | |
684 | // if there was only a single material index, replicate it on all faces |
685 | while( pMesh->mFaceMaterials.size() < pMesh->mPosFaces.size()) |
686 | pMesh->mFaceMaterials.push_back( pMesh->mFaceMaterials.front()); |
687 | |
688 | // read following data objects |
689 | bool running = true; |
690 | while ( running ) |
691 | { |
692 | std::string objectName = GetNextToken(); |
693 | if( objectName.size() == 0) |
694 | ThrowException( "Unexpected end of file while parsing mesh material list." ); |
695 | else |
696 | if( objectName == "}" ) |
697 | break; // material list finished |
698 | else |
699 | if( objectName == "{" ) |
700 | { |
701 | // template materials |
702 | std::string matName = GetNextToken(); |
703 | Material material; |
704 | material.mIsReference = true; |
705 | material.mName = matName; |
706 | pMesh->mMaterials.push_back( material); |
707 | |
708 | CheckForClosingBrace(); // skip } |
709 | } else |
710 | if( objectName == "Material" ) |
711 | { |
712 | pMesh->mMaterials.push_back( Material()); |
713 | ParseDataObjectMaterial( &pMesh->mMaterials.back()); |
714 | } else |
715 | if( objectName == ";" ) |
716 | { |
717 | // ignore |
718 | } else |
719 | { |
720 | DefaultLogger::get()->warn("Unknown data object in material list in x file" ); |
721 | ParseUnknownDataObject(); |
722 | } |
723 | } |
724 | } |
725 | |
726 | // ------------------------------------------------------------------------------------------------ |
727 | void XFileParser::ParseDataObjectMaterial( Material* pMaterial) |
728 | { |
729 | std::string matName; |
730 | readHeadOfDataObject( &matName); |
731 | if( matName.empty()) |
732 | matName = std::string( "material" ) + to_string( mLineNumber ); |
733 | pMaterial->mName = matName; |
734 | pMaterial->mIsReference = false; |
735 | |
736 | // read material values |
737 | pMaterial->mDiffuse = ReadRGBA(); |
738 | pMaterial->mSpecularExponent = ReadFloat(); |
739 | pMaterial->mSpecular = ReadRGB(); |
740 | pMaterial->mEmissive = ReadRGB(); |
741 | |
742 | // read other data objects |
743 | bool running = true; |
744 | while ( running ) |
745 | { |
746 | std::string objectName = GetNextToken(); |
747 | if( objectName.size() == 0) |
748 | ThrowException( "Unexpected end of file while parsing mesh material" ); |
749 | else |
750 | if( objectName == "}" ) |
751 | break; // material finished |
752 | else |
753 | if( objectName == "TextureFilename" || objectName == "TextureFileName" ) |
754 | { |
755 | // some exporters write "TextureFileName" instead. |
756 | std::string texname; |
757 | ParseDataObjectTextureFilename( texname); |
758 | pMaterial->mTextures.push_back( TexEntry( texname)); |
759 | } else |
760 | if( objectName == "NormalmapFilename" || objectName == "NormalmapFileName" ) |
761 | { |
762 | // one exporter writes out the normal map in a separate filename tag |
763 | std::string texname; |
764 | ParseDataObjectTextureFilename( texname); |
765 | pMaterial->mTextures.push_back( TexEntry( texname, true)); |
766 | } else |
767 | { |
768 | DefaultLogger::get()->warn("Unknown data object in material in x file" ); |
769 | ParseUnknownDataObject(); |
770 | } |
771 | } |
772 | } |
773 | |
774 | // ------------------------------------------------------------------------------------------------ |
775 | void XFileParser::ParseDataObjectAnimTicksPerSecond() |
776 | { |
777 | readHeadOfDataObject(); |
778 | mScene->mAnimTicksPerSecond = ReadInt(); |
779 | CheckForClosingBrace(); |
780 | } |
781 | |
782 | // ------------------------------------------------------------------------------------------------ |
783 | void XFileParser::ParseDataObjectAnimationSet() |
784 | { |
785 | std::string animName; |
786 | readHeadOfDataObject( &animName); |
787 | |
788 | Animation* anim = new Animation; |
789 | mScene->mAnims.push_back( anim); |
790 | anim->mName = animName; |
791 | |
792 | bool running = true; |
793 | while ( running ) |
794 | { |
795 | std::string objectName = GetNextToken(); |
796 | if( objectName.length() == 0) |
797 | ThrowException( "Unexpected end of file while parsing animation set." ); |
798 | else |
799 | if( objectName == "}" ) |
800 | break; // animation set finished |
801 | else |
802 | if( objectName == "Animation" ) |
803 | ParseDataObjectAnimation( anim); |
804 | else |
805 | { |
806 | DefaultLogger::get()->warn("Unknown data object in animation set in x file" ); |
807 | ParseUnknownDataObject(); |
808 | } |
809 | } |
810 | } |
811 | |
812 | // ------------------------------------------------------------------------------------------------ |
813 | void XFileParser::ParseDataObjectAnimation( Animation* pAnim) |
814 | { |
815 | readHeadOfDataObject(); |
816 | AnimBone* banim = new AnimBone; |
817 | pAnim->mAnims.push_back( banim); |
818 | |
819 | bool running = true; |
820 | while( running ) |
821 | { |
822 | std::string objectName = GetNextToken(); |
823 | |
824 | if( objectName.length() == 0) |
825 | ThrowException( "Unexpected end of file while parsing animation." ); |
826 | else |
827 | if( objectName == "}" ) |
828 | break; // animation finished |
829 | else |
830 | if( objectName == "AnimationKey" ) |
831 | ParseDataObjectAnimationKey( banim); |
832 | else |
833 | if( objectName == "AnimationOptions" ) |
834 | ParseUnknownDataObject(); // not interested |
835 | else |
836 | if( objectName == "{" ) |
837 | { |
838 | // read frame name |
839 | banim->mBoneName = GetNextToken(); |
840 | CheckForClosingBrace(); |
841 | } else |
842 | { |
843 | DefaultLogger::get()->warn("Unknown data object in animation in x file" ); |
844 | ParseUnknownDataObject(); |
845 | } |
846 | } |
847 | } |
848 | |
849 | // ------------------------------------------------------------------------------------------------ |
850 | void XFileParser::ParseDataObjectAnimationKey( AnimBone* pAnimBone) |
851 | { |
852 | readHeadOfDataObject(); |
853 | |
854 | // read key type |
855 | unsigned int keyType = ReadInt(); |
856 | |
857 | // read number of keys |
858 | unsigned int numKeys = ReadInt(); |
859 | |
860 | for( unsigned int a = 0; a < numKeys; a++) |
861 | { |
862 | // read time |
863 | unsigned int time = ReadInt(); |
864 | |
865 | // read keys |
866 | switch( keyType) |
867 | { |
868 | case 0: // rotation quaternion |
869 | { |
870 | // read count |
871 | if( ReadInt() != 4) |
872 | ThrowException( "Invalid number of arguments for quaternion key in animation" ); |
873 | |
874 | aiQuatKey key; |
875 | key.mTime = double( time); |
876 | key.mValue.w = ReadFloat(); |
877 | key.mValue.x = ReadFloat(); |
878 | key.mValue.y = ReadFloat(); |
879 | key.mValue.z = ReadFloat(); |
880 | pAnimBone->mRotKeys.push_back( key); |
881 | |
882 | CheckForSemicolon(); |
883 | break; |
884 | } |
885 | |
886 | case 1: // scale vector |
887 | case 2: // position vector |
888 | { |
889 | // read count |
890 | if( ReadInt() != 3) |
891 | ThrowException( "Invalid number of arguments for vector key in animation" ); |
892 | |
893 | aiVectorKey key; |
894 | key.mTime = double( time); |
895 | key.mValue = ReadVector3(); |
896 | |
897 | if( keyType == 2) |
898 | pAnimBone->mPosKeys.push_back( key); |
899 | else |
900 | pAnimBone->mScaleKeys.push_back( key); |
901 | |
902 | break; |
903 | } |
904 | |
905 | case 3: // combined transformation matrix |
906 | case 4: // denoted both as 3 or as 4 |
907 | { |
908 | // read count |
909 | if( ReadInt() != 16) |
910 | ThrowException( "Invalid number of arguments for matrix key in animation" ); |
911 | |
912 | // read matrix |
913 | MatrixKey key; |
914 | key.mTime = double( time); |
915 | key.mMatrix.a1 = ReadFloat(); key.mMatrix.b1 = ReadFloat(); |
916 | key.mMatrix.c1 = ReadFloat(); key.mMatrix.d1 = ReadFloat(); |
917 | key.mMatrix.a2 = ReadFloat(); key.mMatrix.b2 = ReadFloat(); |
918 | key.mMatrix.c2 = ReadFloat(); key.mMatrix.d2 = ReadFloat(); |
919 | key.mMatrix.a3 = ReadFloat(); key.mMatrix.b3 = ReadFloat(); |
920 | key.mMatrix.c3 = ReadFloat(); key.mMatrix.d3 = ReadFloat(); |
921 | key.mMatrix.a4 = ReadFloat(); key.mMatrix.b4 = ReadFloat(); |
922 | key.mMatrix.c4 = ReadFloat(); key.mMatrix.d4 = ReadFloat(); |
923 | pAnimBone->mTrafoKeys.push_back( key); |
924 | |
925 | CheckForSemicolon(); |
926 | break; |
927 | } |
928 | |
929 | default: |
930 | ThrowException( format() << "Unknown key type " << keyType << " in animation." ); |
931 | break; |
932 | } // end switch |
933 | |
934 | // key separator |
935 | CheckForSeparator(); |
936 | } |
937 | |
938 | CheckForClosingBrace(); |
939 | } |
940 | |
941 | // ------------------------------------------------------------------------------------------------ |
942 | void XFileParser::ParseDataObjectTextureFilename( std::string& pName) |
943 | { |
944 | readHeadOfDataObject(); |
945 | GetNextTokenAsString( pName); |
946 | CheckForClosingBrace(); |
947 | |
948 | // FIX: some files (e.g. AnimationTest.x) have "" as texture file name |
949 | if (!pName.length()) |
950 | { |
951 | DefaultLogger::get()->warn("Length of texture file name is zero. Skipping this texture." ); |
952 | } |
953 | |
954 | // some exporters write double backslash paths out. We simply replace them if we find them |
955 | while( pName.find( "\\\\" ) != std::string::npos) |
956 | pName.replace( pName.find( "\\\\" ), 2, "\\" ); |
957 | } |
958 | |
959 | // ------------------------------------------------------------------------------------------------ |
960 | void XFileParser::ParseUnknownDataObject() |
961 | { |
962 | // find opening delimiter |
963 | bool running = true; |
964 | while( running ) |
965 | { |
966 | std::string t = GetNextToken(); |
967 | if( t.length() == 0) |
968 | ThrowException( "Unexpected end of file while parsing unknown segment." ); |
969 | |
970 | if( t == "{" ) |
971 | break; |
972 | } |
973 | |
974 | unsigned int counter = 1; |
975 | |
976 | // parse until closing delimiter |
977 | while( counter > 0) |
978 | { |
979 | std::string t = GetNextToken(); |
980 | |
981 | if( t.length() == 0) |
982 | ThrowException( "Unexpected end of file while parsing unknown segment." ); |
983 | |
984 | if( t == "{" ) |
985 | ++counter; |
986 | else |
987 | if( t == "}" ) |
988 | --counter; |
989 | } |
990 | } |
991 | |
992 | // ------------------------------------------------------------------------------------------------ |
993 | //! checks for closing curly brace |
994 | void XFileParser::CheckForClosingBrace() |
995 | { |
996 | if( GetNextToken() != "}" ) |
997 | ThrowException( "Closing brace expected." ); |
998 | } |
999 | |
1000 | // ------------------------------------------------------------------------------------------------ |
1001 | //! checks for one following semicolon |
1002 | void XFileParser::CheckForSemicolon() |
1003 | { |
1004 | if( mIsBinaryFormat) |
1005 | return; |
1006 | |
1007 | if( GetNextToken() != ";" ) |
1008 | ThrowException( "Semicolon expected." ); |
1009 | } |
1010 | |
1011 | // ------------------------------------------------------------------------------------------------ |
1012 | //! checks for a separator char, either a ',' or a ';' |
1013 | void XFileParser::CheckForSeparator() |
1014 | { |
1015 | if( mIsBinaryFormat) |
1016 | return; |
1017 | |
1018 | std::string token = GetNextToken(); |
1019 | if( token != "," && token != ";" ) |
1020 | ThrowException( "Separator character (';' or ',') expected." ); |
1021 | } |
1022 | |
1023 | // ------------------------------------------------------------------------------------------------ |
1024 | // tests and possibly consumes a separator char, but does nothing if there was no separator |
1025 | void XFileParser::TestForSeparator() |
1026 | { |
1027 | if( mIsBinaryFormat) |
1028 | return; |
1029 | |
1030 | FindNextNoneWhiteSpace(); |
1031 | if( P >= End) |
1032 | return; |
1033 | |
1034 | // test and skip |
1035 | if( *P == ';' || *P == ',') |
1036 | P++; |
1037 | } |
1038 | |
1039 | // ------------------------------------------------------------------------------------------------ |
1040 | void XFileParser::readHeadOfDataObject( std::string* poName) |
1041 | { |
1042 | std::string nameOrBrace = GetNextToken(); |
1043 | if( nameOrBrace != "{" ) |
1044 | { |
1045 | if( poName) |
1046 | *poName = nameOrBrace; |
1047 | |
1048 | if( GetNextToken() != "{" ) |
1049 | ThrowException( "Opening brace expected." ); |
1050 | } |
1051 | } |
1052 | |
1053 | // ------------------------------------------------------------------------------------------------ |
1054 | std::string XFileParser::GetNextToken() |
1055 | { |
1056 | std::string s; |
1057 | |
1058 | // process binary-formatted file |
1059 | if( mIsBinaryFormat) |
1060 | { |
1061 | // in binary mode it will only return NAME and STRING token |
1062 | // and (correctly) skip over other tokens. |
1063 | |
1064 | if( End - P < 2) return s; |
1065 | unsigned int tok = ReadBinWord(); |
1066 | unsigned int len; |
1067 | |
1068 | // standalone tokens |
1069 | switch( tok) |
1070 | { |
1071 | case 1: |
1072 | // name token |
1073 | if( End - P < 4) return s; |
1074 | len = ReadBinDWord(); |
1075 | if( End - P < int(len)) return s; |
1076 | s = std::string(P, len); |
1077 | P += len; |
1078 | return s; |
1079 | case 2: |
1080 | // string token |
1081 | if( End - P < 4) return s; |
1082 | len = ReadBinDWord(); |
1083 | if( End - P < int(len)) return s; |
1084 | s = std::string(P, len); |
1085 | P += (len + 2); |
1086 | return s; |
1087 | case 3: |
1088 | // integer token |
1089 | P += 4; |
1090 | return "<integer>" ; |
1091 | case 5: |
1092 | // GUID token |
1093 | P += 16; |
1094 | return "<guid>" ; |
1095 | case 6: |
1096 | if( End - P < 4) return s; |
1097 | len = ReadBinDWord(); |
1098 | P += (len * 4); |
1099 | return "<int_list>" ; |
1100 | case 7: |
1101 | if( End - P < 4) return s; |
1102 | len = ReadBinDWord(); |
1103 | P += (len * mBinaryFloatSize); |
1104 | return "<flt_list>" ; |
1105 | case 0x0a: |
1106 | return "{" ; |
1107 | case 0x0b: |
1108 | return "}" ; |
1109 | case 0x0c: |
1110 | return "(" ; |
1111 | case 0x0d: |
1112 | return ")" ; |
1113 | case 0x0e: |
1114 | return "[" ; |
1115 | case 0x0f: |
1116 | return "]" ; |
1117 | case 0x10: |
1118 | return "<" ; |
1119 | case 0x11: |
1120 | return ">" ; |
1121 | case 0x12: |
1122 | return "." ; |
1123 | case 0x13: |
1124 | return "," ; |
1125 | case 0x14: |
1126 | return ";" ; |
1127 | case 0x1f: |
1128 | return "template" ; |
1129 | case 0x28: |
1130 | return "WORD" ; |
1131 | case 0x29: |
1132 | return "DWORD" ; |
1133 | case 0x2a: |
1134 | return "FLOAT" ; |
1135 | case 0x2b: |
1136 | return "DOUBLE" ; |
1137 | case 0x2c: |
1138 | return "CHAR" ; |
1139 | case 0x2d: |
1140 | return "UCHAR" ; |
1141 | case 0x2e: |
1142 | return "SWORD" ; |
1143 | case 0x2f: |
1144 | return "SDWORD" ; |
1145 | case 0x30: |
1146 | return "void" ; |
1147 | case 0x31: |
1148 | return "string" ; |
1149 | case 0x32: |
1150 | return "unicode" ; |
1151 | case 0x33: |
1152 | return "cstring" ; |
1153 | case 0x34: |
1154 | return "array" ; |
1155 | } |
1156 | } |
1157 | // process text-formatted file |
1158 | else |
1159 | { |
1160 | FindNextNoneWhiteSpace(); |
1161 | if( P >= End) |
1162 | return s; |
1163 | |
1164 | while( (P < End) && !isspace( (unsigned char) *P)) |
1165 | { |
1166 | // either keep token delimiters when already holding a token, or return if first valid char |
1167 | if( *P == ';' || *P == '}' || *P == '{' || *P == ',') |
1168 | { |
1169 | if( !s.size()) |
1170 | s.append( P++, 1); |
1171 | break; // stop for delimiter |
1172 | } |
1173 | s.append( P++, 1); |
1174 | } |
1175 | } |
1176 | return s; |
1177 | } |
1178 | |
1179 | // ------------------------------------------------------------------------------------------------ |
1180 | void XFileParser::FindNextNoneWhiteSpace() |
1181 | { |
1182 | if( mIsBinaryFormat) |
1183 | return; |
1184 | |
1185 | bool running = true; |
1186 | while( running ) |
1187 | { |
1188 | while( P < End && isspace( (unsigned char) *P)) |
1189 | { |
1190 | if( *P == '\n') |
1191 | mLineNumber++; |
1192 | ++P; |
1193 | } |
1194 | |
1195 | if( P >= End) |
1196 | return; |
1197 | |
1198 | // check if this is a comment |
1199 | if( (P[0] == '/' && P[1] == '/') || P[0] == '#') |
1200 | ReadUntilEndOfLine(); |
1201 | else |
1202 | break; |
1203 | } |
1204 | } |
1205 | |
1206 | // ------------------------------------------------------------------------------------------------ |
1207 | void XFileParser::GetNextTokenAsString( std::string& poString) |
1208 | { |
1209 | if( mIsBinaryFormat) |
1210 | { |
1211 | poString = GetNextToken(); |
1212 | return; |
1213 | } |
1214 | |
1215 | FindNextNoneWhiteSpace(); |
1216 | if( P >= End) |
1217 | ThrowException( "Unexpected end of file while parsing string" ); |
1218 | |
1219 | if( *P != '"') |
1220 | ThrowException( "Expected quotation mark." ); |
1221 | ++P; |
1222 | |
1223 | while( P < End && *P != '"') |
1224 | poString.append( P++, 1); |
1225 | |
1226 | if( P >= End-1) |
1227 | ThrowException( "Unexpected end of file while parsing string" ); |
1228 | |
1229 | if( P[1] != ';' || P[0] != '"') |
1230 | ThrowException( "Expected quotation mark and semicolon at the end of a string." ); |
1231 | P+=2; |
1232 | } |
1233 | |
1234 | // ------------------------------------------------------------------------------------------------ |
1235 | void XFileParser::ReadUntilEndOfLine() |
1236 | { |
1237 | if( mIsBinaryFormat) |
1238 | return; |
1239 | |
1240 | while( P < End) |
1241 | { |
1242 | if( *P == '\n' || *P == '\r') |
1243 | { |
1244 | ++P; mLineNumber++; |
1245 | return; |
1246 | } |
1247 | |
1248 | ++P; |
1249 | } |
1250 | } |
1251 | |
1252 | // ------------------------------------------------------------------------------------------------ |
1253 | unsigned short XFileParser::ReadBinWord() |
1254 | { |
1255 | ai_assert(End - P >= 2); |
1256 | const unsigned char* q = (const unsigned char*) P; |
1257 | unsigned short tmp = q[0] | (q[1] << 8); |
1258 | P += 2; |
1259 | return tmp; |
1260 | } |
1261 | |
1262 | // ------------------------------------------------------------------------------------------------ |
1263 | unsigned int XFileParser::ReadBinDWord() |
1264 | { |
1265 | ai_assert(End - P >= 4); |
1266 | const unsigned char* q = (const unsigned char*) P; |
1267 | unsigned int tmp = q[0] | (q[1] << 8) | (q[2] << 16) | (q[3] << 24); |
1268 | P += 4; |
1269 | return tmp; |
1270 | } |
1271 | |
1272 | // ------------------------------------------------------------------------------------------------ |
1273 | unsigned int XFileParser::ReadInt() |
1274 | { |
1275 | if( mIsBinaryFormat) |
1276 | { |
1277 | if( mBinaryNumCount == 0 && End - P >= 2) |
1278 | { |
1279 | unsigned short tmp = ReadBinWord(); // 0x06 or 0x03 |
1280 | if( tmp == 0x06 && End - P >= 4) // array of ints follows |
1281 | mBinaryNumCount = ReadBinDWord(); |
1282 | else // single int follows |
1283 | mBinaryNumCount = 1; |
1284 | } |
1285 | |
1286 | --mBinaryNumCount; |
1287 | if ( End - P >= 4) { |
1288 | return ReadBinDWord(); |
1289 | } else { |
1290 | P = End; |
1291 | return 0; |
1292 | } |
1293 | } else |
1294 | { |
1295 | FindNextNoneWhiteSpace(); |
1296 | |
1297 | // TODO: consider using strtol10 instead??? |
1298 | |
1299 | // check preceding minus sign |
1300 | bool isNegative = false; |
1301 | if( *P == '-') |
1302 | { |
1303 | isNegative = true; |
1304 | P++; |
1305 | } |
1306 | |
1307 | // at least one digit expected |
1308 | if( !isdigit( *P)) |
1309 | ThrowException( "Number expected." ); |
1310 | |
1311 | // read digits |
1312 | unsigned int number = 0; |
1313 | while( P < End) |
1314 | { |
1315 | if( !isdigit( *P)) |
1316 | break; |
1317 | number = number * 10 + (*P - 48); |
1318 | P++; |
1319 | } |
1320 | |
1321 | CheckForSeparator(); |
1322 | return isNegative ? ((unsigned int) -int( number)) : number; |
1323 | } |
1324 | } |
1325 | |
1326 | // ------------------------------------------------------------------------------------------------ |
1327 | ai_real XFileParser::ReadFloat() |
1328 | { |
1329 | if( mIsBinaryFormat) |
1330 | { |
1331 | if( mBinaryNumCount == 0 && End - P >= 2) |
1332 | { |
1333 | unsigned short tmp = ReadBinWord(); // 0x07 or 0x42 |
1334 | if( tmp == 0x07 && End - P >= 4) // array of floats following |
1335 | mBinaryNumCount = ReadBinDWord(); |
1336 | else // single float following |
1337 | mBinaryNumCount = 1; |
1338 | } |
1339 | |
1340 | --mBinaryNumCount; |
1341 | if( mBinaryFloatSize == 8) |
1342 | { |
1343 | if( End - P >= 8) { |
1344 | ai_real result = (ai_real) (*(double*) P); |
1345 | P += 8; |
1346 | return result; |
1347 | } else { |
1348 | P = End; |
1349 | return 0; |
1350 | } |
1351 | } else |
1352 | { |
1353 | if( End - P >= 4) { |
1354 | ai_real result = *(ai_real*) P; |
1355 | P += 4; |
1356 | return result; |
1357 | } else { |
1358 | P = End; |
1359 | return 0; |
1360 | } |
1361 | } |
1362 | } |
1363 | |
1364 | // text version |
1365 | FindNextNoneWhiteSpace(); |
1366 | // check for various special strings to allow reading files from faulty exporters |
1367 | // I mean you, Blender! |
1368 | // Reading is safe because of the terminating zero |
1369 | if( strncmp( P, "-1.#IND00" , 9) == 0 || strncmp( P, "1.#IND00" , 8) == 0) |
1370 | { |
1371 | P += 9; |
1372 | CheckForSeparator(); |
1373 | return 0.0; |
1374 | } else |
1375 | if( strncmp( P, "1.#QNAN0" , 8) == 0) |
1376 | { |
1377 | P += 8; |
1378 | CheckForSeparator(); |
1379 | return 0.0; |
1380 | } |
1381 | |
1382 | ai_real result = 0.0; |
1383 | P = fast_atoreal_move<ai_real>( P, result); |
1384 | |
1385 | CheckForSeparator(); |
1386 | |
1387 | return result; |
1388 | } |
1389 | |
1390 | // ------------------------------------------------------------------------------------------------ |
1391 | aiVector2D XFileParser::ReadVector2() |
1392 | { |
1393 | aiVector2D vector; |
1394 | vector.x = ReadFloat(); |
1395 | vector.y = ReadFloat(); |
1396 | TestForSeparator(); |
1397 | |
1398 | return vector; |
1399 | } |
1400 | |
1401 | // ------------------------------------------------------------------------------------------------ |
1402 | aiVector3D XFileParser::ReadVector3() |
1403 | { |
1404 | aiVector3D vector; |
1405 | vector.x = ReadFloat(); |
1406 | vector.y = ReadFloat(); |
1407 | vector.z = ReadFloat(); |
1408 | TestForSeparator(); |
1409 | |
1410 | return vector; |
1411 | } |
1412 | |
1413 | // ------------------------------------------------------------------------------------------------ |
1414 | aiColor4D XFileParser::ReadRGBA() |
1415 | { |
1416 | aiColor4D color; |
1417 | color.r = ReadFloat(); |
1418 | color.g = ReadFloat(); |
1419 | color.b = ReadFloat(); |
1420 | color.a = ReadFloat(); |
1421 | TestForSeparator(); |
1422 | |
1423 | return color; |
1424 | } |
1425 | |
1426 | // ------------------------------------------------------------------------------------------------ |
1427 | aiColor3D XFileParser::ReadRGB() |
1428 | { |
1429 | aiColor3D color; |
1430 | color.r = ReadFloat(); |
1431 | color.g = ReadFloat(); |
1432 | color.b = ReadFloat(); |
1433 | TestForSeparator(); |
1434 | |
1435 | return color; |
1436 | } |
1437 | |
1438 | // ------------------------------------------------------------------------------------------------ |
1439 | // Throws an exception with a line number and the given text. |
1440 | AI_WONT_RETURN void XFileParser::ThrowException( const std::string& pText) |
1441 | { |
1442 | if( mIsBinaryFormat) |
1443 | throw DeadlyImportError( pText); |
1444 | else |
1445 | throw DeadlyImportError( format() << "Line " << mLineNumber << ": " << pText ); |
1446 | } |
1447 | |
1448 | |
1449 | // ------------------------------------------------------------------------------------------------ |
1450 | // Filters the imported hierarchy for some degenerated cases that some exporters produce. |
1451 | void XFileParser::FilterHierarchy( XFile::Node* pNode) |
1452 | { |
1453 | // if the node has just a single unnamed child containing a mesh, remove |
1454 | // the anonymous node between. The 3DSMax kwXport plugin seems to produce this |
1455 | // mess in some cases |
1456 | if( pNode->mChildren.size() == 1 && pNode->mMeshes.empty() ) |
1457 | { |
1458 | XFile::Node* child = pNode->mChildren.front(); |
1459 | if( child->mName.length() == 0 && child->mMeshes.size() > 0) |
1460 | { |
1461 | // transfer its meshes to us |
1462 | for( unsigned int a = 0; a < child->mMeshes.size(); a++) |
1463 | pNode->mMeshes.push_back( child->mMeshes[a]); |
1464 | child->mMeshes.clear(); |
1465 | |
1466 | // transfer the transform as well |
1467 | pNode->mTrafoMatrix = pNode->mTrafoMatrix * child->mTrafoMatrix; |
1468 | |
1469 | // then kill it |
1470 | delete child; |
1471 | pNode->mChildren.clear(); |
1472 | } |
1473 | } |
1474 | |
1475 | // recurse |
1476 | for( unsigned int a = 0; a < pNode->mChildren.size(); a++) |
1477 | FilterHierarchy( pNode->mChildren[a]); |
1478 | } |
1479 | |
1480 | #endif // !! ASSIMP_BUILD_NO_X_IMPORTER |
1481 | |