1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5
6 Copyright (c) 2006-2012, assimp team
7
8 All rights reserved.
9
10 Redistribution and use of this software in source and binary forms,
11 with or without modification, are permitted provided that the following
12 conditions are met:
13
14 * Redistributions of source code must retain the above
15 copyright notice, this list of conditions and the
16 following disclaimer.
17
18 * Redistributions in binary form must reproduce the above
19 copyright notice, this list of conditions and the
20 following disclaimer in the documentation and/or other
21 materials provided with the distribution.
22
23 * Neither the name of the assimp team, nor the names of its
24 contributors may be used to endorse or promote products
25 derived from this software without specific prior
26 written permission of the assimp team.
27
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 ---------------------------------------------------------------------------
40 */
41
42 /** @file 3DSLoader.cpp
43 * @brief Implementation of the 3ds importer class
44 *
45 * http://www.the-labs.com/Blender/3DS-details.html
46 */
47
48 #include "AssimpPCH.h"
49 #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
50
51 // internal headers
52 #include "3DSLoader.h"
53
54 using namespace Assimp;
55
56 // ------------------------------------------------------------------------------------------------
57 // Begins a new parsing block
58 // - Reads the current chunk and validates it
59 // - computes its length
60 #define ASSIMP_3DS_BEGIN_CHUNK() \
61 while (true) { \
62 if (stream->GetRemainingSizeToLimit() < sizeof(Discreet3DS::Chunk)){ \
63 return; \
64 } \
65 Discreet3DS::Chunk chunk; \
66 ReadChunk(&chunk); \
67 int chunkSize = chunk.Size-sizeof(Discreet3DS::Chunk); \
68 const int oldReadLimit = stream->GetReadLimit(); \
69 stream->SetReadLimit(stream->GetCurrentPos() + chunkSize); \
70
71
72 // ------------------------------------------------------------------------------------------------
73 // End a parsing block
74 // Must follow at the end of each parsing block, reset chunk end marker to previous value
75 #define ASSIMP_3DS_END_CHUNK() \
76 stream->SkipToReadLimit(); \
77 stream->SetReadLimit(oldReadLimit); \
78 if (stream->GetRemainingSizeToLimit() == 0) \
79 return; \
80 }
81
82 // ------------------------------------------------------------------------------------------------
83 // Constructor to be privately used by Importer
Discreet3DSImporter()84 Discreet3DSImporter::Discreet3DSImporter()
85 {}
86
87 // ------------------------------------------------------------------------------------------------
88 // Destructor, private as well
~Discreet3DSImporter()89 Discreet3DSImporter::~Discreet3DSImporter()
90 {}
91
92 // ------------------------------------------------------------------------------------------------
93 // Returns whether the class can handle the format of the given file.
CanRead(const std::string & pFile,IOSystem * pIOHandler,bool checkSig) const94 bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
95 {
96 std::string extension = GetExtension(pFile);
97 if(extension == "3ds" || extension == "prj" ) {
98 return true;
99 }
100 if (!extension.length() || checkSig) {
101 uint16_t token[3];
102 token[0] = 0x4d4d;
103 token[1] = 0x3dc2;
104 //token[2] = 0x3daa;
105 return CheckMagicToken(pIOHandler,pFile,token,2,0,2);
106 }
107 return false;
108 }
109
110 // ------------------------------------------------------------------------------------------------
111 // Get list of all extension supported by this loader
GetExtensionList(std::set<std::string> & extensions)112 void Discreet3DSImporter::GetExtensionList(std::set<std::string>& extensions)
113 {
114 extensions.insert("3ds");
115 extensions.insert("prj");
116 }
117
118 // ------------------------------------------------------------------------------------------------
119 // Setup configuration properties
SetupProperties(const Importer *)120 void Discreet3DSImporter::SetupProperties(const Importer* /*pImp*/)
121 {
122 // nothing to be done for the moment
123 }
124
125 // ------------------------------------------------------------------------------------------------
126 // Imports the given file into the given scene structure.
InternReadFile(const std::string & pFile,aiScene * pScene,IOSystem * pIOHandler)127 void Discreet3DSImporter::InternReadFile( const std::string& pFile,
128 aiScene* pScene, IOSystem* pIOHandler)
129 {
130 StreamReaderLE stream(pIOHandler->Open(pFile,"rb"));
131 this->stream = &stream;
132
133 // We should have at least one chunk
134 if (stream.GetRemainingSize() < 16) {
135 throw DeadlyImportError("3DS file is either empty or corrupt: " + pFile);
136 }
137
138 // Allocate our temporary 3DS representation
139 mScene = new D3DS::Scene();
140
141 // Initialize members
142 mLastNodeIndex = -1;
143 mCurrentNode = new D3DS::Node();
144 mRootNode = mCurrentNode;
145 mRootNode->mHierarchyPos = -1;
146 mRootNode->mHierarchyIndex = -1;
147 mRootNode->mParent = NULL;
148 mMasterScale = 1.0f;
149 mBackgroundImage = "";
150 bHasBG = false;
151 bIsPrj = false;
152
153 // Parse the file
154 ParseMainChunk();
155
156 // Process all meshes in the file. First check whether all
157 // face indices haev valid values. The generate our
158 // internal verbose representation. Finally compute normal
159 // vectors from the smoothing groups we read from the
160 // file.
161 for (std::vector<D3DS::Mesh>::iterator i = mScene->mMeshes.begin(),
162 end = mScene->mMeshes.end(); i != end;++i) {
163 CheckIndices(*i);
164 MakeUnique (*i);
165 ComputeNormalsWithSmoothingsGroups<D3DS::Face>(*i);
166 }
167
168 // Replace all occurences of the default material with a
169 // valid material. Generate it if no material containing
170 // DEFAULT in its name has been found in the file
171 ReplaceDefaultMaterial();
172
173 // Convert the scene from our internal representation to an
174 // aiScene object. This involves copying all meshes, lights
175 // and cameras to the scene
176 ConvertScene(pScene);
177
178 // Generate the node graph for the scene. This is a little bit
179 // tricky since we'll need to split some meshes into submeshes
180 GenerateNodeGraph(pScene);
181
182 // Now apply the master scaling factor to the scene
183 ApplyMasterScale(pScene);
184
185 // Delete our internal scene representation and the root
186 // node, so the whole hierarchy will follow
187 delete mRootNode;
188 delete mScene;
189
190 AI_DEBUG_INVALIDATE_PTR(mRootNode);
191 AI_DEBUG_INVALIDATE_PTR(mScene);
192 AI_DEBUG_INVALIDATE_PTR(this->stream);
193 }
194
195 // ------------------------------------------------------------------------------------------------
196 // Applies a master-scaling factor to the imported scene
ApplyMasterScale(aiScene * pScene)197 void Discreet3DSImporter::ApplyMasterScale(aiScene* pScene)
198 {
199 // There are some 3DS files with a zero scaling factor
200 if (!mMasterScale)mMasterScale = 1.0f;
201 else mMasterScale = 1.0f / mMasterScale;
202
203 // Construct an uniform scaling matrix and multiply with it
204 pScene->mRootNode->mTransformation *= aiMatrix4x4(
205 mMasterScale,0.0f, 0.0f, 0.0f,
206 0.0f, mMasterScale,0.0f, 0.0f,
207 0.0f, 0.0f, mMasterScale,0.0f,
208 0.0f, 0.0f, 0.0f, 1.0f);
209
210 // Check whether a scaling track is assigned to the root node.
211 }
212
213 // ------------------------------------------------------------------------------------------------
214 // Reads a new chunk from the file
ReadChunk(Discreet3DS::Chunk * pcOut)215 void Discreet3DSImporter::ReadChunk(Discreet3DS::Chunk* pcOut)
216 {
217 ai_assert(pcOut != NULL);
218
219 pcOut->Flag = stream->GetU2();
220 pcOut->Size = stream->GetU4();
221
222 if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSize())
223 throw DeadlyImportError("Chunk is too large");
224
225 if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSizeToLimit())
226 DefaultLogger::get()->error("3DS: Chunk overflow");
227 }
228
229 // ------------------------------------------------------------------------------------------------
230 // Skip a chunk
SkipChunk()231 void Discreet3DSImporter::SkipChunk()
232 {
233 Discreet3DS::Chunk psChunk;
234 ReadChunk(&psChunk);
235
236 stream->IncPtr(psChunk.Size-sizeof(Discreet3DS::Chunk));
237 return;
238 }
239
240 // ------------------------------------------------------------------------------------------------
241 // Process the primary chunk of the file
ParseMainChunk()242 void Discreet3DSImporter::ParseMainChunk()
243 {
244 ASSIMP_3DS_BEGIN_CHUNK();
245
246 // get chunk type
247 switch (chunk.Flag)
248 {
249
250 case Discreet3DS::CHUNK_PRJ:
251 bIsPrj = true;
252 case Discreet3DS::CHUNK_MAIN:
253 ParseEditorChunk();
254 break;
255 };
256
257 ASSIMP_3DS_END_CHUNK();
258 // recursively continue processing this hierarchy level
259 return ParseMainChunk();
260 }
261
262 // ------------------------------------------------------------------------------------------------
ParseEditorChunk()263 void Discreet3DSImporter::ParseEditorChunk()
264 {
265 ASSIMP_3DS_BEGIN_CHUNK();
266
267 // get chunk type
268 switch (chunk.Flag)
269 {
270 case Discreet3DS::CHUNK_OBJMESH:
271
272 ParseObjectChunk();
273 break;
274
275 // NOTE: In several documentations in the internet this
276 // chunk appears at different locations
277 case Discreet3DS::CHUNK_KEYFRAMER:
278
279 ParseKeyframeChunk();
280 break;
281
282 case Discreet3DS::CHUNK_VERSION:
283 {
284 // print the version number
285 char buff[10];
286 ASSIMP_itoa10(buff,stream->GetI2());
287 DefaultLogger::get()->info(std::string("3DS file format version: ") + buff);
288 }
289 break;
290 };
291 ASSIMP_3DS_END_CHUNK();
292 }
293
294 // ------------------------------------------------------------------------------------------------
ParseObjectChunk()295 void Discreet3DSImporter::ParseObjectChunk()
296 {
297 ASSIMP_3DS_BEGIN_CHUNK();
298
299 // get chunk type
300 switch (chunk.Flag)
301 {
302 case Discreet3DS::CHUNK_OBJBLOCK:
303 {
304 unsigned int cnt = 0;
305 const char* sz = (const char*)stream->GetPtr();
306
307 // Get the name of the geometry object
308 while (stream->GetI1())++cnt;
309 ParseChunk(sz,cnt);
310 }
311 break;
312
313 case Discreet3DS::CHUNK_MAT_MATERIAL:
314
315 // Add a new material to the list
316 mScene->mMaterials.push_back(D3DS::Material());
317 ParseMaterialChunk();
318 break;
319
320 case Discreet3DS::CHUNK_AMBCOLOR:
321
322 // This is the ambient base color of the scene.
323 // We add it to the ambient color of all materials
324 ParseColorChunk(&mClrAmbient,true);
325 if (is_qnan(mClrAmbient.r))
326 {
327 // We failed to read the ambient base color.
328 DefaultLogger::get()->error("3DS: Failed to read ambient base color");
329 mClrAmbient.r = mClrAmbient.g = mClrAmbient.b = 0.0f;
330 }
331 break;
332
333 case Discreet3DS::CHUNK_BIT_MAP:
334 {
335 // Specifies the background image. The string should already be
336 // properly 0 terminated but we need to be sure
337 unsigned int cnt = 0;
338 const char* sz = (const char*)stream->GetPtr();
339 while (stream->GetI1())++cnt;
340 mBackgroundImage = std::string(sz,cnt);
341 }
342 break;
343
344 case Discreet3DS::CHUNK_BIT_MAP_EXISTS:
345 bHasBG = true;
346 break;
347
348 case Discreet3DS::CHUNK_MASTER_SCALE:
349 // Scene master scaling factor
350 mMasterScale = stream->GetF4();
351 break;
352 };
353 ASSIMP_3DS_END_CHUNK();
354 }
355
356 // ------------------------------------------------------------------------------------------------
ParseChunk(const char * name,unsigned int num)357 void Discreet3DSImporter::ParseChunk(const char* name, unsigned int num)
358 {
359 ASSIMP_3DS_BEGIN_CHUNK();
360
361 // IMPLEMENTATION NOTE;
362 // Cameras or lights define their transformation in their parent node and in the
363 // corresponding light or camera chunks. However, we read and process the latter
364 // to to be able to return valid cameras/lights even if no scenegraph is given.
365
366 // get chunk type
367 switch (chunk.Flag)
368 {
369 case Discreet3DS::CHUNK_TRIMESH:
370 {
371 // this starts a new triangle mesh
372 mScene->mMeshes.push_back(D3DS::Mesh());
373 D3DS::Mesh& m = mScene->mMeshes.back();
374
375 // Setup the name of the mesh
376 m.mName = std::string(name, num);
377
378 // Read mesh chunks
379 ParseMeshChunk();
380 }
381 break;
382
383 case Discreet3DS::CHUNK_LIGHT:
384 {
385 // This starts a new light
386 aiLight* light = new aiLight();
387 mScene->mLights.push_back(light);
388
389 light->mName.Set(std::string(name, num));
390
391 // First read the position of the light
392 light->mPosition.x = stream->GetF4();
393 light->mPosition.y = stream->GetF4();
394 light->mPosition.z = stream->GetF4();
395
396 light->mColorDiffuse = aiColor3D(1.f,1.f,1.f);
397
398 // Now check for further subchunks
399 if (!bIsPrj) /* fixme */
400 ParseLightChunk();
401
402 // The specular light color is identical the the diffuse light color. The ambient light color
403 // is equal to the ambient base color of the whole scene.
404 light->mColorSpecular = light->mColorDiffuse;
405 light->mColorAmbient = mClrAmbient;
406
407 if (light->mType == aiLightSource_UNDEFINED)
408 {
409 // It must be a point light
410 light->mType = aiLightSource_POINT;
411 }}
412 break;
413
414 case Discreet3DS::CHUNK_CAMERA:
415 {
416 // This starts a new camera
417 aiCamera* camera = new aiCamera();
418 mScene->mCameras.push_back(camera);
419 camera->mName.Set(std::string(name, num));
420
421 // First read the position of the camera
422 camera->mPosition.x = stream->GetF4();
423 camera->mPosition.y = stream->GetF4();
424 camera->mPosition.z = stream->GetF4();
425
426 // Then the camera target
427 camera->mLookAt.x = stream->GetF4() - camera->mPosition.x;
428 camera->mLookAt.y = stream->GetF4() - camera->mPosition.y;
429 camera->mLookAt.z = stream->GetF4() - camera->mPosition.z;
430 float len = camera->mLookAt.Length();
431 if (len < 1e-5f) {
432
433 // There are some files with lookat == position. Don't know why or whether it's ok or not.
434 DefaultLogger::get()->error("3DS: Unable to read proper camera look-at vector");
435 camera->mLookAt = aiVector3D(0.f,1.f,0.f);
436
437 }
438 else camera->mLookAt /= len;
439
440 // And finally - the camera rotation angle, in counter clockwise direction
441 const float angle = AI_DEG_TO_RAD( stream->GetF4() );
442 aiQuaternion quat(camera->mLookAt,angle);
443 camera->mUp = quat.GetMatrix() * aiVector3D(0.f,1.f,0.f);
444
445 // Read the lense angle
446 camera->mHorizontalFOV = AI_DEG_TO_RAD ( stream->GetF4() );
447 if (camera->mHorizontalFOV < 0.001f) {
448 camera->mHorizontalFOV = AI_DEG_TO_RAD(45.f);
449 }
450
451 // Now check for further subchunks
452 if (!bIsPrj) /* fixme */ {
453 ParseCameraChunk();
454 }}
455 break;
456 };
457 ASSIMP_3DS_END_CHUNK();
458 }
459
460 // ------------------------------------------------------------------------------------------------
ParseLightChunk()461 void Discreet3DSImporter::ParseLightChunk()
462 {
463 ASSIMP_3DS_BEGIN_CHUNK();
464 aiLight* light = mScene->mLights.back();
465
466 // get chunk type
467 switch (chunk.Flag)
468 {
469 case Discreet3DS::CHUNK_DL_SPOTLIGHT:
470 // Now we can be sure that the light is a spot light
471 light->mType = aiLightSource_SPOT;
472
473 // We wouldn't need to normalize here, but we do it
474 light->mDirection.x = stream->GetF4() - light->mPosition.x;
475 light->mDirection.y = stream->GetF4() - light->mPosition.y;
476 light->mDirection.z = stream->GetF4() - light->mPosition.z;
477 light->mDirection.Normalize();
478
479 // Now the hotspot and falloff angles - in degrees
480 light->mAngleInnerCone = AI_DEG_TO_RAD( stream->GetF4() );
481
482 // FIX: the falloff angle is just an offset
483 light->mAngleOuterCone = light->mAngleInnerCone+AI_DEG_TO_RAD( stream->GetF4() );
484 break;
485
486 // intensity multiplier
487 case Discreet3DS::CHUNK_DL_MULTIPLIER:
488 light->mColorDiffuse = light->mColorDiffuse * stream->GetF4();
489 break;
490
491 // light color
492 case Discreet3DS::CHUNK_RGBF:
493 case Discreet3DS::CHUNK_LINRGBF:
494 light->mColorDiffuse.r *= stream->GetF4();
495 light->mColorDiffuse.g *= stream->GetF4();
496 light->mColorDiffuse.b *= stream->GetF4();
497 break;
498
499 // light attenuation
500 case Discreet3DS::CHUNK_DL_ATTENUATE:
501 light->mAttenuationLinear = stream->GetF4();
502 break;
503 };
504
505 ASSIMP_3DS_END_CHUNK();
506 }
507
508 // ------------------------------------------------------------------------------------------------
ParseCameraChunk()509 void Discreet3DSImporter::ParseCameraChunk()
510 {
511 ASSIMP_3DS_BEGIN_CHUNK();
512 aiCamera* camera = mScene->mCameras.back();
513
514 // get chunk type
515 switch (chunk.Flag)
516 {
517 // near and far clip plane
518 case Discreet3DS::CHUNK_CAM_RANGES:
519 camera->mClipPlaneNear = stream->GetF4();
520 camera->mClipPlaneFar = stream->GetF4();
521 break;
522 }
523
524 ASSIMP_3DS_END_CHUNK();
525 }
526
527 // ------------------------------------------------------------------------------------------------
ParseKeyframeChunk()528 void Discreet3DSImporter::ParseKeyframeChunk()
529 {
530 ASSIMP_3DS_BEGIN_CHUNK();
531
532 // get chunk type
533 switch (chunk.Flag)
534 {
535 case Discreet3DS::CHUNK_TRACKCAMTGT:
536 case Discreet3DS::CHUNK_TRACKSPOTL:
537 case Discreet3DS::CHUNK_TRACKCAMERA:
538 case Discreet3DS::CHUNK_TRACKINFO:
539 case Discreet3DS::CHUNK_TRACKLIGHT:
540 case Discreet3DS::CHUNK_TRACKLIGTGT:
541
542 // this starts a new mesh hierarchy chunk
543 ParseHierarchyChunk(chunk.Flag);
544 break;
545 };
546
547 ASSIMP_3DS_END_CHUNK();
548 }
549
550 // ------------------------------------------------------------------------------------------------
551 // Little helper function for ParseHierarchyChunk
InverseNodeSearch(D3DS::Node * pcNode,D3DS::Node * pcCurrent)552 void Discreet3DSImporter::InverseNodeSearch(D3DS::Node* pcNode,D3DS::Node* pcCurrent)
553 {
554 if (!pcCurrent) {
555 mRootNode->push_back(pcNode);
556 return;
557 }
558
559 if (pcCurrent->mHierarchyPos == pcNode->mHierarchyPos) {
560 if(pcCurrent->mParent) {
561 pcCurrent->mParent->push_back(pcNode);
562 }
563 else pcCurrent->push_back(pcNode);
564 return;
565 }
566 return InverseNodeSearch(pcNode,pcCurrent->mParent);
567 }
568
569 // ------------------------------------------------------------------------------------------------
570 // Find a node with a specific name in the import hierarchy
FindNode(D3DS::Node * root,const std::string & name)571 D3DS::Node* FindNode(D3DS::Node* root, const std::string& name)
572 {
573 if (root->mName == name)
574 return root;
575 for (std::vector<D3DS::Node*>::iterator it = root->mChildren.begin();it != root->mChildren.end(); ++it) {
576 D3DS::Node* nd;
577 if (( nd = FindNode(*it,name)))
578 return nd;
579 }
580 return NULL;
581 }
582
583 // ------------------------------------------------------------------------------------------------
584 // Binary predicate for std::unique()
585 template <class T>
KeyUniqueCompare(const T & first,const T & second)586 bool KeyUniqueCompare(const T& first, const T& second)
587 {
588 return first.mTime == second.mTime;
589 }
590
591 // ------------------------------------------------------------------------------------------------
592 // Skip some additional import data.
SkipTCBInfo()593 void Discreet3DSImporter::SkipTCBInfo()
594 {
595 unsigned int flags = stream->GetI2();
596
597 if (!flags) {
598 // Currently we can't do anything with these values. They occur
599 // quite rare, so it wouldn't be worth the effort implementing
600 // them. 3DS ist not really suitable for complex animations,
601 // so full support is not required.
602 DefaultLogger::get()->warn("3DS: Skipping TCB animation info");
603 }
604
605 if (flags & Discreet3DS::KEY_USE_TENS) {
606 stream->IncPtr(4);
607 }
608 if (flags & Discreet3DS::KEY_USE_BIAS) {
609 stream->IncPtr(4);
610 }
611 if (flags & Discreet3DS::KEY_USE_CONT) {
612 stream->IncPtr(4);
613 }
614 if (flags & Discreet3DS::KEY_USE_EASE_FROM) {
615 stream->IncPtr(4);
616 }
617 if (flags & Discreet3DS::KEY_USE_EASE_TO) {
618 stream->IncPtr(4);
619 }
620 }
621
622 // ------------------------------------------------------------------------------------------------
623 // Read hierarchy and keyframe info
ParseHierarchyChunk(uint16_t parent)624 void Discreet3DSImporter::ParseHierarchyChunk(uint16_t parent)
625 {
626 ASSIMP_3DS_BEGIN_CHUNK();
627
628 // get chunk type
629 switch (chunk.Flag)
630 {
631 case Discreet3DS::CHUNK_TRACKOBJNAME:
632
633 // This is the name of the object to which the track applies. The chunk also
634 // defines the position of this object in the hierarchy.
635 {
636
637 // First of all: get the name of the object
638 unsigned int cnt = 0;
639 const char* sz = (const char*)stream->GetPtr();
640
641 while (stream->GetI1())++cnt;
642 std::string name = std::string(sz,cnt);
643
644 // Now find out whether we have this node already (target animation channels
645 // are stored with a separate object ID)
646 D3DS::Node* pcNode = FindNode(mRootNode,name);
647 if (pcNode)
648 {
649 // Make this node the current node
650 mCurrentNode = pcNode;
651 break;
652 }
653 pcNode = new D3DS::Node();
654 pcNode->mName = name;
655
656 // There are two unknown values which we can safely ignore
657 stream->IncPtr(4);
658
659 // Now read the hierarchy position of the object
660 uint16_t hierarchy = stream->GetI2() + 1;
661 pcNode->mHierarchyPos = hierarchy;
662 pcNode->mHierarchyIndex = mLastNodeIndex;
663
664 // And find a proper position in the graph for it
665 if (mCurrentNode && mCurrentNode->mHierarchyPos == hierarchy) {
666
667 // add to the parent of the last touched node
668 mCurrentNode->mParent->push_back(pcNode);
669 mLastNodeIndex++;
670 }
671 else if(hierarchy >= mLastNodeIndex) {
672
673 // place it at the current position in the hierarchy
674 mCurrentNode->push_back(pcNode);
675 mLastNodeIndex = hierarchy;
676 }
677 else {
678 // need to go back to the specified position in the hierarchy.
679 InverseNodeSearch(pcNode,mCurrentNode);
680 mLastNodeIndex++;
681 }
682 // Make this node the current node
683 mCurrentNode = pcNode;
684 }
685 break;
686
687 case Discreet3DS::CHUNK_TRACKDUMMYOBJNAME:
688
689 // This is the "real" name of a $$$DUMMY object
690 {
691 const char* sz = (const char*) stream->GetPtr();
692 while (stream->GetI1());
693
694 // If object name is DUMMY, take this one instead
695 if (mCurrentNode->mName == "$$$DUMMY") {
696 //DefaultLogger::get()->warn("3DS: Skipping dummy object name for non-dummy object");
697 mCurrentNode->mName = std::string(sz);
698 break;
699 }
700 }
701 break;
702
703 case Discreet3DS::CHUNK_TRACKPIVOT:
704
705 if ( Discreet3DS::CHUNK_TRACKINFO != parent)
706 {
707 DefaultLogger::get()->warn("3DS: Skipping pivot subchunk for non usual object");
708 break;
709 }
710
711 // Pivot = origin of rotation and scaling
712 mCurrentNode->vPivot.x = stream->GetF4();
713 mCurrentNode->vPivot.y = stream->GetF4();
714 mCurrentNode->vPivot.z = stream->GetF4();
715 break;
716
717
718 // ////////////////////////////////////////////////////////////////////
719 // POSITION KEYFRAME
720 case Discreet3DS::CHUNK_TRACKPOS:
721 {
722 stream->IncPtr(10);
723 const unsigned int numFrames = stream->GetI4();
724 bool sortKeys = false;
725
726 // This could also be meant as the target position for
727 // (targeted) lights and cameras
728 std::vector<aiVectorKey>* l;
729 if ( Discreet3DS::CHUNK_TRACKCAMTGT == parent || Discreet3DS::CHUNK_TRACKLIGTGT == parent) {
730 l = & mCurrentNode->aTargetPositionKeys;
731 }
732 else l = & mCurrentNode->aPositionKeys;
733
734 l->reserve(numFrames);
735 for (unsigned int i = 0; i < numFrames;++i) {
736 const unsigned int fidx = stream->GetI4();
737
738 // Setup a new position key
739 aiVectorKey v;
740 v.mTime = (double)fidx;
741
742 SkipTCBInfo();
743 v.mValue.x = stream->GetF4();
744 v.mValue.y = stream->GetF4();
745 v.mValue.z = stream->GetF4();
746
747 // check whether we'll need to sort the keys
748 if (!l->empty() && v.mTime <= l->back().mTime)
749 sortKeys = true;
750
751 // Add the new keyframe to the list
752 l->push_back(v);
753 }
754
755 // Sort all keys with ascending time values and remove duplicates?
756 if (sortKeys) {
757 std::stable_sort(l->begin(),l->end());
758 l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare<aiVectorKey>), l->end() );
759 }}
760
761 break;
762
763 // ////////////////////////////////////////////////////////////////////
764 // CAMERA ROLL KEYFRAME
765 case Discreet3DS::CHUNK_TRACKROLL:
766 {
767 // roll keys are accepted for cameras only
768 if (parent != Discreet3DS::CHUNK_TRACKCAMERA) {
769 DefaultLogger::get()->warn("3DS: Ignoring roll track for non-camera object");
770 break;
771 }
772 bool sortKeys = false;
773 std::vector<aiFloatKey>* l = &mCurrentNode->aCameraRollKeys;
774
775 stream->IncPtr(10);
776 const unsigned int numFrames = stream->GetI4();
777 l->reserve(numFrames);
778 for (unsigned int i = 0; i < numFrames;++i) {
779 const unsigned int fidx = stream->GetI4();
780
781 // Setup a new position key
782 aiFloatKey v;
783 v.mTime = (double)fidx;
784
785 // This is just a single float
786 SkipTCBInfo();
787 v.mValue = stream->GetF4();
788
789 // Check whether we'll need to sort the keys
790 if (!l->empty() && v.mTime <= l->back().mTime)
791 sortKeys = true;
792
793 // Add the new keyframe to the list
794 l->push_back(v);
795 }
796
797 // Sort all keys with ascending time values and remove duplicates?
798 if (sortKeys) {
799 std::stable_sort(l->begin(),l->end());
800 l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare<aiFloatKey>), l->end() );
801 }}
802 break;
803
804
805 // ////////////////////////////////////////////////////////////////////
806 // CAMERA FOV KEYFRAME
807 case Discreet3DS::CHUNK_TRACKFOV:
808 {
809 DefaultLogger::get()->error("3DS: Skipping FOV animation track. "
810 "This is not supported");
811 }
812 break;
813
814
815 // ////////////////////////////////////////////////////////////////////
816 // ROTATION KEYFRAME
817 case Discreet3DS::CHUNK_TRACKROTATE:
818 {
819 stream->IncPtr(10);
820 const unsigned int numFrames = stream->GetI4();
821
822 bool sortKeys = false;
823 std::vector<aiQuatKey>* l = &mCurrentNode->aRotationKeys;
824 l->reserve(numFrames);
825
826 for (unsigned int i = 0; i < numFrames;++i) {
827 const unsigned int fidx = stream->GetI4();
828 SkipTCBInfo();
829
830 aiQuatKey v;
831 v.mTime = (double)fidx;
832
833 // The rotation keyframe is given as an axis-angle pair
834 const float rad = stream->GetF4();
835 aiVector3D axis;
836 axis.x = stream->GetF4();
837 axis.y = stream->GetF4();
838 axis.z = stream->GetF4();
839
840 if (!axis.x && !axis.y && !axis.z)
841 axis.y = 1.f;
842
843 // Construct a rotation quaternion from the axis-angle pair
844 v.mValue = aiQuaternion(axis,rad);
845
846 // Check whether we'll need to sort the keys
847 if (!l->empty() && v.mTime <= l->back().mTime)
848 sortKeys = true;
849
850 // add the new keyframe to the list
851 l->push_back(v);
852 }
853 // Sort all keys with ascending time values and remove duplicates?
854 if (sortKeys) {
855 std::stable_sort(l->begin(),l->end());
856 l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare<aiQuatKey>), l->end() );
857 }}
858 break;
859
860 // ////////////////////////////////////////////////////////////////////
861 // SCALING KEYFRAME
862 case Discreet3DS::CHUNK_TRACKSCALE:
863 {
864 stream->IncPtr(10);
865 const unsigned int numFrames = stream->GetI2();
866 stream->IncPtr(2);
867
868 bool sortKeys = false;
869 std::vector<aiVectorKey>* l = &mCurrentNode->aScalingKeys;
870 l->reserve(numFrames);
871
872 for (unsigned int i = 0; i < numFrames;++i) {
873 const unsigned int fidx = stream->GetI4();
874 SkipTCBInfo();
875
876 // Setup a new key
877 aiVectorKey v;
878 v.mTime = (double)fidx;
879
880 // ... and read its value
881 v.mValue.x = stream->GetF4();
882 v.mValue.y = stream->GetF4();
883 v.mValue.z = stream->GetF4();
884
885 // check whether we'll need to sort the keys
886 if (!l->empty() && v.mTime <= l->back().mTime)
887 sortKeys = true;
888
889 // Remove zero-scalings on singular axes - they've been reported to be there erroneously in some strange files
890 if (!v.mValue.x) v.mValue.x = 1.f;
891 if (!v.mValue.y) v.mValue.y = 1.f;
892 if (!v.mValue.z) v.mValue.z = 1.f;
893
894 l->push_back(v);
895 }
896 // Sort all keys with ascending time values and remove duplicates?
897 if (sortKeys) {
898 std::stable_sort(l->begin(),l->end());
899 l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare<aiVectorKey>), l->end() );
900 }}
901 break;
902 };
903
904 ASSIMP_3DS_END_CHUNK();
905 }
906
907 // ------------------------------------------------------------------------------------------------
908 // Read a face chunk - it contains smoothing groups and material assignments
ParseFaceChunk()909 void Discreet3DSImporter::ParseFaceChunk()
910 {
911 ASSIMP_3DS_BEGIN_CHUNK();
912
913 // Get the mesh we're currently working on
914 D3DS::Mesh& mMesh = mScene->mMeshes.back();
915
916 // Get chunk type
917 switch (chunk.Flag)
918 {
919 case Discreet3DS::CHUNK_SMOOLIST:
920 {
921 // This is the list of smoothing groups - a bitfield for every face.
922 // Up to 32 smoothing groups assigned to a single face.
923 unsigned int num = chunkSize/4, m = 0;
924 for (std::vector<D3DS::Face>::iterator i = mMesh.mFaces.begin(); m != num;++i, ++m) {
925 // nth bit is set for nth smoothing group
926 (*i).iSmoothGroup = stream->GetI4();
927 }}
928 break;
929
930 case Discreet3DS::CHUNK_FACEMAT:
931 {
932 // at fist an asciiz with the material name
933 const char* sz = (const char*)stream->GetPtr();
934 while (stream->GetI1());
935
936 // find the index of the material
937 unsigned int idx = 0xcdcdcdcd, cnt = 0;
938 for (std::vector<D3DS::Material>::const_iterator i = mScene->mMaterials.begin();i != mScene->mMaterials.end();++i,++cnt) {
939 // use case independent comparisons. hopefully it will work.
940 if ((*i).mName.length() && !ASSIMP_stricmp(sz, (*i).mName.c_str())) {
941 idx = cnt;
942 break;
943 }
944 }
945 if (0xcdcdcdcd == idx) {
946 DefaultLogger::get()->error(std::string("3DS: Unknown material: ") + sz);
947 }
948
949 // Now continue and read all material indices
950 cnt = (uint16_t)stream->GetI2();
951 for (unsigned int i = 0; i < cnt;++i) {
952 unsigned int fidx = (uint16_t)stream->GetI2();
953
954 // check range
955 if (fidx >= mMesh.mFaceMaterials.size()) {
956 DefaultLogger::get()->error("3DS: Invalid face index in face material list");
957 }
958 else mMesh.mFaceMaterials[fidx] = idx;
959 }}
960 break;
961 };
962 ASSIMP_3DS_END_CHUNK();
963 }
964
965 // ------------------------------------------------------------------------------------------------
966 // Read a mesh chunk. Here's the actual mesh data
ParseMeshChunk()967 void Discreet3DSImporter::ParseMeshChunk()
968 {
969 ASSIMP_3DS_BEGIN_CHUNK();
970
971 // Get the mesh we're currently working on
972 D3DS::Mesh& mMesh = mScene->mMeshes.back();
973
974 // get chunk type
975 switch (chunk.Flag)
976 {
977 case Discreet3DS::CHUNK_VERTLIST:
978 {
979 // This is the list of all vertices in the current mesh
980 int num = (int)(uint16_t)stream->GetI2();
981 mMesh.mPositions.reserve(num);
982 while (num-- > 0) {
983 aiVector3D v;
984 v.x = stream->GetF4();
985 v.y = stream->GetF4();
986 v.z = stream->GetF4();
987 mMesh.mPositions.push_back(v);
988 }}
989 break;
990 case Discreet3DS::CHUNK_TRMATRIX:
991 {
992 // This is the RLEATIVE transformation matrix of the current mesh. Vertices are
993 // pretransformed by this matrix wonder.
994 mMesh.mMat.a1 = stream->GetF4();
995 mMesh.mMat.b1 = stream->GetF4();
996 mMesh.mMat.c1 = stream->GetF4();
997 mMesh.mMat.a2 = stream->GetF4();
998 mMesh.mMat.b2 = stream->GetF4();
999 mMesh.mMat.c2 = stream->GetF4();
1000 mMesh.mMat.a3 = stream->GetF4();
1001 mMesh.mMat.b3 = stream->GetF4();
1002 mMesh.mMat.c3 = stream->GetF4();
1003 mMesh.mMat.a4 = stream->GetF4();
1004 mMesh.mMat.b4 = stream->GetF4();
1005 mMesh.mMat.c4 = stream->GetF4();
1006 }
1007 break;
1008
1009 case Discreet3DS::CHUNK_MAPLIST:
1010 {
1011 // This is the list of all UV coords in the current mesh
1012 int num = (int)(uint16_t)stream->GetI2();
1013 mMesh.mTexCoords.reserve(num);
1014 while (num-- > 0) {
1015 aiVector3D v;
1016 v.x = stream->GetF4();
1017 v.y = stream->GetF4();
1018 mMesh.mTexCoords.push_back(v);
1019 }}
1020 break;
1021
1022 case Discreet3DS::CHUNK_FACELIST:
1023 {
1024 // This is the list of all faces in the current mesh
1025 int num = (int)(uint16_t)stream->GetI2();
1026 mMesh.mFaces.reserve(num);
1027 while (num-- > 0) {
1028 // 3DS faces are ALWAYS triangles
1029 mMesh.mFaces.push_back(D3DS::Face());
1030 D3DS::Face& sFace = mMesh.mFaces.back();
1031
1032 sFace.mIndices[0] = (uint16_t)stream->GetI2();
1033 sFace.mIndices[1] = (uint16_t)stream->GetI2();
1034 sFace.mIndices[2] = (uint16_t)stream->GetI2();
1035
1036 stream->IncPtr(2); // skip edge visibility flag
1037 }
1038
1039 // Resize the material array (0xcdcdcdcd marks the default material; so if a face is
1040 // not referenced by a material, $$DEFAULT will be assigned to it)
1041 mMesh.mFaceMaterials.resize(mMesh.mFaces.size(),0xcdcdcdcd);
1042
1043 // Larger 3DS files could have multiple FACE chunks here
1044 chunkSize = stream->GetRemainingSizeToLimit();
1045 if ( chunkSize > (int) sizeof(Discreet3DS::Chunk ) )
1046 ParseFaceChunk();
1047 }
1048 break;
1049 };
1050 ASSIMP_3DS_END_CHUNK();
1051 }
1052
1053 // ------------------------------------------------------------------------------------------------
1054 // Read a 3DS material chunk
ParseMaterialChunk()1055 void Discreet3DSImporter::ParseMaterialChunk()
1056 {
1057 ASSIMP_3DS_BEGIN_CHUNK();
1058 switch (chunk.Flag)
1059 {
1060 case Discreet3DS::CHUNK_MAT_MATNAME:
1061
1062 {
1063 // The material name string is already zero-terminated, but we need to be sure ...
1064 const char* sz = (const char*)stream->GetPtr();
1065 unsigned int cnt = 0;
1066 while (stream->GetI1())
1067 ++cnt;
1068
1069 if (!cnt) {
1070 // This may not be, we use the default name instead
1071 DefaultLogger::get()->error("3DS: Empty material name");
1072 }
1073 else mScene->mMaterials.back().mName = std::string(sz,cnt);
1074 }
1075 break;
1076
1077 case Discreet3DS::CHUNK_MAT_DIFFUSE:
1078 {
1079 // This is the diffuse material color
1080 aiColor3D* pc = &mScene->mMaterials.back().mDiffuse;
1081 ParseColorChunk(pc);
1082 if (is_qnan(pc->r)) {
1083 // color chunk is invalid. Simply ignore it
1084 DefaultLogger::get()->error("3DS: Unable to read DIFFUSE chunk");
1085 pc->r = pc->g = pc->b = 1.0f;
1086 }}
1087 break;
1088
1089 case Discreet3DS::CHUNK_MAT_SPECULAR:
1090 {
1091 // This is the specular material color
1092 aiColor3D* pc = &mScene->mMaterials.back().mSpecular;
1093 ParseColorChunk(pc);
1094 if (is_qnan(pc->r)) {
1095 // color chunk is invalid. Simply ignore it
1096 DefaultLogger::get()->error("3DS: Unable to read SPECULAR chunk");
1097 pc->r = pc->g = pc->b = 1.0f;
1098 }}
1099 break;
1100
1101 case Discreet3DS::CHUNK_MAT_AMBIENT:
1102 {
1103 // This is the ambient material color
1104 aiColor3D* pc = &mScene->mMaterials.back().mAmbient;
1105 ParseColorChunk(pc);
1106 if (is_qnan(pc->r)) {
1107 // color chunk is invalid. Simply ignore it
1108 DefaultLogger::get()->error("3DS: Unable to read AMBIENT chunk");
1109 pc->r = pc->g = pc->b = 0.0f;
1110 }}
1111 break;
1112
1113 case Discreet3DS::CHUNK_MAT_SELF_ILLUM:
1114 {
1115 // This is the emissive material color
1116 aiColor3D* pc = &mScene->mMaterials.back().mEmissive;
1117 ParseColorChunk(pc);
1118 if (is_qnan(pc->r)) {
1119 // color chunk is invalid. Simply ignore it
1120 DefaultLogger::get()->error("3DS: Unable to read EMISSIVE chunk");
1121 pc->r = pc->g = pc->b = 0.0f;
1122 }}
1123 break;
1124
1125 case Discreet3DS::CHUNK_MAT_TRANSPARENCY:
1126 {
1127 // This is the material's transparency
1128 float* pcf = &mScene->mMaterials.back().mTransparency;
1129 *pcf = ParsePercentageChunk();
1130
1131 // NOTE: transparency, not opacity
1132 if (is_qnan(*pcf))
1133 *pcf = 1.0f;
1134 else *pcf = 1.0f - *pcf * (float)0xFFFF / 100.0f;
1135 }
1136 break;
1137
1138 case Discreet3DS::CHUNK_MAT_SHADING:
1139 // This is the material shading mode
1140 mScene->mMaterials.back().mShading = (D3DS::Discreet3DS::shadetype3ds)stream->GetI2();
1141 break;
1142
1143 case Discreet3DS::CHUNK_MAT_TWO_SIDE:
1144 // This is the two-sided flag
1145 mScene->mMaterials.back().mTwoSided = true;
1146 break;
1147
1148 case Discreet3DS::CHUNK_MAT_SHININESS:
1149 { // This is the shininess of the material
1150 float* pcf = &mScene->mMaterials.back().mSpecularExponent;
1151 *pcf = ParsePercentageChunk();
1152 if (is_qnan(*pcf))
1153 *pcf = 0.0f;
1154 else *pcf *= (float)0xFFFF;
1155 }
1156 break;
1157
1158 case Discreet3DS::CHUNK_MAT_SHININESS_PERCENT:
1159 { // This is the shininess strength of the material
1160 float* pcf = &mScene->mMaterials.back().mShininessStrength;
1161 *pcf = ParsePercentageChunk();
1162 if (is_qnan(*pcf))
1163 *pcf = 0.0f;
1164 else *pcf *= (float)0xffff / 100.0f;
1165 }
1166 break;
1167
1168 case Discreet3DS::CHUNK_MAT_SELF_ILPCT:
1169 { // This is the self illumination strength of the material
1170 float f = ParsePercentageChunk();
1171 if (is_qnan(f))
1172 f = 0.0f;
1173 else f *= (float)0xFFFF / 100.0f;
1174 mScene->mMaterials.back().mEmissive = aiColor3D(f,f,f);
1175 }
1176 break;
1177
1178 // Parse texture chunks
1179 case Discreet3DS::CHUNK_MAT_TEXTURE:
1180 // Diffuse texture
1181 ParseTextureChunk(&mScene->mMaterials.back().sTexDiffuse);
1182 break;
1183 case Discreet3DS::CHUNK_MAT_BUMPMAP:
1184 // Height map
1185 ParseTextureChunk(&mScene->mMaterials.back().sTexBump);
1186 break;
1187 case Discreet3DS::CHUNK_MAT_OPACMAP:
1188 // Opacity texture
1189 ParseTextureChunk(&mScene->mMaterials.back().sTexOpacity);
1190 break;
1191 case Discreet3DS::CHUNK_MAT_MAT_SHINMAP:
1192 // Shininess map
1193 ParseTextureChunk(&mScene->mMaterials.back().sTexShininess);
1194 break;
1195 case Discreet3DS::CHUNK_MAT_SPECMAP:
1196 // Specular map
1197 ParseTextureChunk(&mScene->mMaterials.back().sTexSpecular);
1198 break;
1199 case Discreet3DS::CHUNK_MAT_SELFIMAP:
1200 // Self-illumination (emissive) map
1201 ParseTextureChunk(&mScene->mMaterials.back().sTexEmissive);
1202 break;
1203 case Discreet3DS::CHUNK_MAT_REFLMAP:
1204 // Reflection map
1205 ParseTextureChunk(&mScene->mMaterials.back().sTexReflective);
1206 break;
1207 };
1208 ASSIMP_3DS_END_CHUNK();
1209 }
1210
1211 // ------------------------------------------------------------------------------------------------
ParseTextureChunk(D3DS::Texture * pcOut)1212 void Discreet3DSImporter::ParseTextureChunk(D3DS::Texture* pcOut)
1213 {
1214 ASSIMP_3DS_BEGIN_CHUNK();
1215
1216 // get chunk type
1217 switch (chunk.Flag)
1218 {
1219 case Discreet3DS::CHUNK_MAPFILE:
1220 {
1221 // The material name string is already zero-terminated, but we need to be sure ...
1222 const char* sz = (const char*)stream->GetPtr();
1223 unsigned int cnt = 0;
1224 while (stream->GetI1())
1225 ++cnt;
1226 pcOut->mMapName = std::string(sz,cnt);
1227 }
1228 break;
1229
1230
1231 case Discreet3DS::CHUNK_PERCENTF:
1232 // Manually parse the blend factor
1233 pcOut->mTextureBlend = stream->GetF4();
1234 break;
1235
1236 case Discreet3DS::CHUNK_PERCENTW:
1237 // Manually parse the blend factor
1238 pcOut->mTextureBlend = (float)((uint16_t)stream->GetI2()) / 100.0f;
1239 break;
1240
1241 case Discreet3DS::CHUNK_MAT_MAP_USCALE:
1242 // Texture coordinate scaling in the U direction
1243 pcOut->mScaleU = stream->GetF4();
1244 if (0.0f == pcOut->mScaleU)
1245 {
1246 DefaultLogger::get()->warn("Texture coordinate scaling in the x direction is zero. Assuming 1.");
1247 pcOut->mScaleU = 1.0f;
1248 }
1249 break;
1250 case Discreet3DS::CHUNK_MAT_MAP_VSCALE:
1251 // Texture coordinate scaling in the V direction
1252 pcOut->mScaleV = stream->GetF4();
1253 if (0.0f == pcOut->mScaleV)
1254 {
1255 DefaultLogger::get()->warn("Texture coordinate scaling in the y direction is zero. Assuming 1.");
1256 pcOut->mScaleV = 1.0f;
1257 }
1258 break;
1259
1260 case Discreet3DS::CHUNK_MAT_MAP_UOFFSET:
1261 // Texture coordinate offset in the U direction
1262 pcOut->mOffsetU = -stream->GetF4();
1263 break;
1264
1265 case Discreet3DS::CHUNK_MAT_MAP_VOFFSET:
1266 // Texture coordinate offset in the V direction
1267 pcOut->mOffsetV = stream->GetF4();
1268 break;
1269
1270 case Discreet3DS::CHUNK_MAT_MAP_ANG:
1271 // Texture coordinate rotation, CCW in DEGREES
1272 pcOut->mRotation = -AI_DEG_TO_RAD( stream->GetF4() );
1273 break;
1274
1275 case Discreet3DS::CHUNK_MAT_MAP_TILING:
1276 {
1277 const uint16_t iFlags = stream->GetI2();
1278
1279 // Get the mapping mode (for both axes)
1280 if (iFlags & 0x2u)
1281 pcOut->mMapMode = aiTextureMapMode_Mirror;
1282
1283 else if (iFlags & 0x10u)
1284 pcOut->mMapMode = aiTextureMapMode_Decal;
1285
1286 // wrapping in all remaining cases
1287 else pcOut->mMapMode = aiTextureMapMode_Wrap;
1288 }
1289 break;
1290 };
1291
1292 ASSIMP_3DS_END_CHUNK();
1293 }
1294
1295 // ------------------------------------------------------------------------------------------------
1296 // Read a percentage chunk
ParsePercentageChunk()1297 float Discreet3DSImporter::ParsePercentageChunk()
1298 {
1299 Discreet3DS::Chunk chunk;
1300 ReadChunk(&chunk);
1301
1302 if (Discreet3DS::CHUNK_PERCENTF == chunk.Flag)
1303 return stream->GetF4();
1304 else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag)
1305 return (float)((uint16_t)stream->GetI2()) / (float)0xFFFF;
1306 return get_qnan();
1307 }
1308
1309 // ------------------------------------------------------------------------------------------------
1310 // Read a color chunk. If a percentage chunk is found instead it is read as a grayscale color
ParseColorChunk(aiColor3D * out,bool acceptPercent)1311 void Discreet3DSImporter::ParseColorChunk(aiColor3D* out,
1312 bool acceptPercent)
1313 {
1314 ai_assert(out != NULL);
1315
1316 // error return value
1317 const float qnan = get_qnan();
1318 static const aiColor3D clrError = aiColor3D(qnan,qnan,qnan);
1319
1320 Discreet3DS::Chunk chunk;
1321 ReadChunk(&chunk);
1322 const unsigned int diff = chunk.Size - sizeof(Discreet3DS::Chunk);
1323
1324 bool bGamma = false;
1325
1326 // Get the type of the chunk
1327 switch(chunk.Flag)
1328 {
1329 case Discreet3DS::CHUNK_LINRGBF:
1330 bGamma = true;
1331
1332 case Discreet3DS::CHUNK_RGBF:
1333 if (sizeof(float) * 3 > diff) {
1334 *out = clrError;
1335 return;
1336 }
1337 out->r = stream->GetF4();
1338 out->g = stream->GetF4();
1339 out->b = stream->GetF4();
1340 break;
1341
1342 case Discreet3DS::CHUNK_LINRGBB:
1343 bGamma = true;
1344 case Discreet3DS::CHUNK_RGBB:
1345 if (sizeof(char) * 3 > diff) {
1346 *out = clrError;
1347 return;
1348 }
1349 out->r = (float)(uint8_t)stream->GetI1() / 255.0f;
1350 out->g = (float)(uint8_t)stream->GetI1() / 255.0f;
1351 out->b = (float)(uint8_t)stream->GetI1() / 255.0f;
1352 break;
1353
1354 // Percentage chunks are accepted, too.
1355 case Discreet3DS::CHUNK_PERCENTF:
1356 if (acceptPercent && 4 <= diff) {
1357 out->g = out->b = out->r = stream->GetF4();
1358 break;
1359 }
1360 *out = clrError;
1361 return;
1362
1363 case Discreet3DS::CHUNK_PERCENTW:
1364 if (acceptPercent && 1 <= diff) {
1365 out->g = out->b = out->r = (float)(uint8_t)stream->GetI1() / 255.0f;
1366 break;
1367 }
1368 *out = clrError;
1369 return;
1370
1371 default:
1372 stream->IncPtr(diff);
1373 // Skip unknown chunks, hope this won't cause any problems.
1374 return ParseColorChunk(out,acceptPercent);
1375 };
1376 (void)bGamma;
1377 }
1378
1379 #endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER
1380