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