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