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