vrshoot

view libs/assimp/3DSLoader.cpp @ 1:e7ca128b8713

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