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  ValidateDataStructure.cpp
43  *  @brief Implementation of the post processing step to validate
44  *    the data structure returned by Assimp.
45  */
46 
47 #include "AssimpPCH.h"
48 
49 // internal headers
50 #include "ValidateDataStructure.h"
51 #include "BaseImporter.h"
52 #include "fast_atof.h"
53 #include "ProcessHelper.h"
54 
55 // CRT headers
56 #include <stdarg.h>
57 
58 using namespace Assimp;
59 
60 // ------------------------------------------------------------------------------------------------
61 // Constructor to be privately used by Importer
ValidateDSProcess()62 ValidateDSProcess::ValidateDSProcess()
63 {}
64 
65 // ------------------------------------------------------------------------------------------------
66 // Destructor, private as well
~ValidateDSProcess()67 ValidateDSProcess::~ValidateDSProcess()
68 {}
69 
70 // ------------------------------------------------------------------------------------------------
71 // Returns whether the processing step is present in the given flag field.
IsActive(unsigned int pFlags) const72 bool ValidateDSProcess::IsActive( unsigned int pFlags) const
73 {
74 	return (pFlags & aiProcess_ValidateDataStructure) != 0;
75 }
76 // ------------------------------------------------------------------------------------------------
ReportError(const char * msg,...)77 AI_WONT_RETURN void ValidateDSProcess::ReportError(const char* msg,...)
78 {
79 	ai_assert(NULL != msg);
80 
81 	va_list args;
82 	va_start(args,msg);
83 
84 	char szBuffer[3000];
85 	const int iLen = vsprintf(szBuffer,msg,args);
86 	ai_assert(iLen > 0);
87 
88 	va_end(args);
89 #ifdef _DEBUG
90 	ai_assert( false );
91 #endif
92 	throw DeadlyImportError("Validation failed: " + std::string(szBuffer,iLen));
93 }
94 // ------------------------------------------------------------------------------------------------
ReportWarning(const char * msg,...)95 void ValidateDSProcess::ReportWarning(const char* msg,...)
96 {
97 	ai_assert(NULL != msg);
98 
99 	va_list args;
100 	va_start(args,msg);
101 
102 	char szBuffer[3000];
103 	const int iLen = vsprintf(szBuffer,msg,args);
104 	ai_assert(iLen > 0);
105 
106 	va_end(args);
107 	DefaultLogger::get()->warn("Validation warning: " + std::string(szBuffer,iLen));
108 }
109 
110 // ------------------------------------------------------------------------------------------------
HasNameMatch(const aiString & in,aiNode * node)111 inline int HasNameMatch(const aiString& in, aiNode* node)
112 {
113 	int result = (node->mName == in ? 1 : 0 );
114 	for (unsigned int i = 0; i < node->mNumChildren;++i)	{
115 		result += HasNameMatch(in,node->mChildren[i]);
116 	}
117 	return result;
118 }
119 
120 // ------------------------------------------------------------------------------------------------
121 template <typename T>
DoValidation(T ** parray,unsigned int size,const char * firstName,const char * secondName)122 inline void ValidateDSProcess::DoValidation(T** parray, unsigned int size,
123 	const char* firstName, const char* secondName)
124 {
125 	// validate all entries
126 	if (size)
127 	{
128 		if (!parray)
129 		{
130 			ReportError("aiScene::%s is NULL (aiScene::%s is %i)",
131 				firstName, secondName, size);
132 		}
133 		for (unsigned int i = 0; i < size;++i)
134 		{
135 			if (!parray[i])
136 			{
137 				ReportError("aiScene::%s[%i] is NULL (aiScene::%s is %i)",
138 					firstName,i,secondName,size);
139 			}
140 			Validate(parray[i]);
141 		}
142 	}
143 }
144 
145 // ------------------------------------------------------------------------------------------------
146 template <typename T>
DoValidationEx(T ** parray,unsigned int size,const char * firstName,const char * secondName)147 inline void ValidateDSProcess::DoValidationEx(T** parray, unsigned int size,
148 	const char* firstName, const char* secondName)
149 {
150 	// validate all entries
151 	if (size)
152 	{
153 		if (!parray)	{
154 			ReportError("aiScene::%s is NULL (aiScene::%s is %i)",
155 				firstName, secondName, size);
156 		}
157 		for (unsigned int i = 0; i < size;++i)
158 		{
159 			if (!parray[i])
160 			{
161 				ReportError("aiScene::%s[%i] is NULL (aiScene::%s is %i)",
162 					firstName,i,secondName,size);
163 			}
164 			Validate(parray[i]);
165 
166 			// check whether there are duplicate names
167 			for (unsigned int a = i+1; a < size;++a)
168 			{
169 				if (parray[i]->mName == parray[a]->mName)
170 				{
171 					this->ReportError("aiScene::%s[%i] has the same name as "
172 						"aiScene::%s[%i]",firstName, i,secondName, a);
173 				}
174 			}
175 		}
176 	}
177 }
178 
179 // ------------------------------------------------------------------------------------------------
180 template <typename T>
DoValidationWithNameCheck(T ** array,unsigned int size,const char * firstName,const char * secondName)181 inline void ValidateDSProcess::DoValidationWithNameCheck(T** array,
182 	unsigned int size, const char* firstName,
183 	const char* secondName)
184 {
185 	// validate all entries
186 	DoValidationEx(array,size,firstName,secondName);
187 
188 	for (unsigned int i = 0; i < size;++i)
189 	{
190 		int res = HasNameMatch(array[i]->mName,mScene->mRootNode);
191 		if (!res)	{
192 			ReportError("aiScene::%s[%i] has no corresponding node in the scene graph (%s)",
193 				firstName,i,array[i]->mName.data);
194 		}
195 		else if (1 != res)	{
196 			ReportError("aiScene::%s[%i]: there are more than one nodes with %s as name",
197 				firstName,i,array[i]->mName.data);
198 		}
199 	}
200 }
201 
202 // ------------------------------------------------------------------------------------------------
203 // Executes the post processing step on the given imported data.
Execute(aiScene * pScene)204 void ValidateDSProcess::Execute( aiScene* pScene)
205 {
206 	this->mScene = pScene;
207 	DefaultLogger::get()->debug("ValidateDataStructureProcess begin");
208 
209 	// validate the node graph of the scene
210 	Validate(pScene->mRootNode);
211 
212 	// validate all meshes
213 	if (pScene->mNumMeshes) {
214 		DoValidation(pScene->mMeshes,pScene->mNumMeshes,"mMeshes","mNumMeshes");
215 	}
216 	else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE))	{
217 		ReportError("aiScene::mNumMeshes is 0. At least one mesh must be there");
218 	}
219 	else if (pScene->mMeshes)	{
220 		ReportError("aiScene::mMeshes is non-null although there are no meshes");
221 	}
222 
223 	// validate all animations
224 	if (pScene->mNumAnimations) {
225 		DoValidation(pScene->mAnimations,pScene->mNumAnimations,
226 			"mAnimations","mNumAnimations");
227 	}
228 	else if (pScene->mAnimations)	{
229 		ReportError("aiScene::mAnimations is non-null although there are no animations");
230 	}
231 
232 	// validate all cameras
233 	if (pScene->mNumCameras) {
234 		DoValidationWithNameCheck(pScene->mCameras,pScene->mNumCameras,
235 			"mCameras","mNumCameras");
236 	}
237 	else if (pScene->mCameras)	{
238 		ReportError("aiScene::mCameras is non-null although there are no cameras");
239 	}
240 
241 	// validate all lights
242 	if (pScene->mNumLights) {
243 		DoValidationWithNameCheck(pScene->mLights,pScene->mNumLights,
244 			"mLights","mNumLights");
245 	}
246 	else if (pScene->mLights)	{
247 		ReportError("aiScene::mLights is non-null although there are no lights");
248 	}
249 
250 	// validate all textures
251 	if (pScene->mNumTextures) {
252 		DoValidation(pScene->mTextures,pScene->mNumTextures,
253 			"mTextures","mNumTextures");
254 	}
255 	else if (pScene->mTextures)	{
256 		ReportError("aiScene::mTextures is non-null although there are no textures");
257 	}
258 
259 	// validate all materials
260 	if (pScene->mNumMaterials) {
261 		DoValidation(pScene->mMaterials,pScene->mNumMaterials,"mMaterials","mNumMaterials");
262 	}
263 #if 0
264 	// NOTE: ScenePreprocessor generates a default material if none is there
265 	else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE))	{
266 		ReportError("aiScene::mNumMaterials is 0. At least one material must be there");
267 	}
268 #endif
269 	else if (pScene->mMaterials)	{
270 		ReportError("aiScene::mMaterials is non-null although there are no materials");
271 	}
272 
273 //	if (!has)ReportError("The aiScene data structure is empty");
274 	DefaultLogger::get()->debug("ValidateDataStructureProcess end");
275 }
276 
277 // ------------------------------------------------------------------------------------------------
Validate(const aiLight * pLight)278 void ValidateDSProcess::Validate( const aiLight* pLight)
279 {
280 	if (pLight->mType == aiLightSource_UNDEFINED)
281 		ReportWarning("aiLight::mType is aiLightSource_UNDEFINED");
282 
283 	if (!pLight->mAttenuationConstant &&
284 		!pLight->mAttenuationLinear   &&
285 		!pLight->mAttenuationQuadratic)	{
286 		ReportWarning("aiLight::mAttenuationXXX - all are zero");
287 	}
288 
289 	if (pLight->mAngleInnerCone > pLight->mAngleOuterCone)
290 		ReportError("aiLight::mAngleInnerCone is larger than aiLight::mAngleOuterCone");
291 
292 	if (pLight->mColorDiffuse.IsBlack() && pLight->mColorAmbient.IsBlack()
293 		&& pLight->mColorSpecular.IsBlack())
294 	{
295 		ReportWarning("aiLight::mColorXXX - all are black and won't have any influence");
296 	}
297 }
298 
299 // ------------------------------------------------------------------------------------------------
Validate(const aiCamera * pCamera)300 void ValidateDSProcess::Validate( const aiCamera* pCamera)
301 {
302 	if (pCamera->mClipPlaneFar <= pCamera->mClipPlaneNear)
303 		ReportError("aiCamera::mClipPlaneFar must be >= aiCamera::mClipPlaneNear");
304 
305 	// FIX: there are many 3ds files with invalid FOVs. No reason to
306 	// reject them at all ... a warning is appropriate.
307 	if (!pCamera->mHorizontalFOV || pCamera->mHorizontalFOV >= (float)AI_MATH_PI)
308 		ReportWarning("%f is not a valid value for aiCamera::mHorizontalFOV",pCamera->mHorizontalFOV);
309 }
310 
311 // ------------------------------------------------------------------------------------------------
Validate(const aiMesh * pMesh)312 void ValidateDSProcess::Validate( const aiMesh* pMesh)
313 {
314 	// validate the material index of the mesh
315 	if (mScene->mNumMaterials && pMesh->mMaterialIndex >= mScene->mNumMaterials)
316 	{
317 		ReportError("aiMesh::mMaterialIndex is invalid (value: %i maximum: %i)",
318 			pMesh->mMaterialIndex,mScene->mNumMaterials-1);
319 	}
320 
321 	Validate(&pMesh->mName);
322 
323 	for (unsigned int i = 0; i < pMesh->mNumFaces; ++i)
324 	{
325 		aiFace& face = pMesh->mFaces[i];
326 
327 		if (pMesh->mPrimitiveTypes)
328 		{
329 			switch (face.mNumIndices)
330 			{
331 			case 0:
332 				ReportError("aiMesh::mFaces[%i].mNumIndices is 0",i);
333 			case 1:
334 				if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POINT))
335 				{
336 					ReportError("aiMesh::mFaces[%i] is a POINT but aiMesh::mPrimtiveTypes "
337 						"does not report the POINT flag",i);
338 				}
339 				break;
340 			case 2:
341 				if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_LINE))
342 				{
343 					ReportError("aiMesh::mFaces[%i] is a LINE but aiMesh::mPrimtiveTypes "
344 						"does not report the LINE flag",i);
345 				}
346 				break;
347 			case 3:
348 				if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE))
349 				{
350 					ReportError("aiMesh::mFaces[%i] is a TRIANGLE but aiMesh::mPrimtiveTypes "
351 						"does not report the TRIANGLE flag",i);
352 				}
353 				break;
354 			default:
355 				if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON))
356 				{
357 					this->ReportError("aiMesh::mFaces[%i] is a POLYGON but aiMesh::mPrimtiveTypes "
358 						"does not report the POLYGON flag",i);
359 				}
360 				break;
361 			};
362 		}
363 
364 		if (!face.mIndices)
365 			ReportError("aiMesh::mFaces[%i].mIndices is NULL",i);
366 	}
367 
368 	// positions must always be there ...
369 	if (!pMesh->mNumVertices || (!pMesh->mVertices && !mScene->mFlags))	{
370 		ReportError("The mesh contains no vertices");
371 	}
372 
373 	if (pMesh->mNumVertices > AI_MAX_VERTICES) {
374 		ReportError("Mesh has too many vertices: %u, but the limit is %u",pMesh->mNumVertices,AI_MAX_VERTICES);
375 	}
376 	if (pMesh->mNumFaces > AI_MAX_FACES) {
377 		ReportError("Mesh has too many faces: %u, but the limit is %u",pMesh->mNumFaces,AI_MAX_FACES);
378 	}
379 
380 	// if tangents are there there must also be bitangent vectors ...
381 	if ((pMesh->mTangents != NULL) != (pMesh->mBitangents != NULL))	{
382 		ReportError("If there are tangents, bitangent vectors must be present as well");
383 	}
384 
385 	// faces, too
386 	if (!pMesh->mNumFaces || (!pMesh->mFaces && !mScene->mFlags))	{
387 		ReportError("Mesh contains no faces");
388 	}
389 
390 	// now check whether the face indexing layout is correct:
391 	// unique vertices, pseudo-indexed.
392 	std::vector<bool> abRefList;
393 	abRefList.resize(pMesh->mNumVertices,false);
394 	for (unsigned int i = 0; i < pMesh->mNumFaces;++i)
395 	{
396 		aiFace& face = pMesh->mFaces[i];
397 		if (face.mNumIndices > AI_MAX_FACE_INDICES) {
398 			ReportError("Face %u has too many faces: %u, but the limit is %u",i,face.mNumIndices,AI_MAX_FACE_INDICES);
399 		}
400 
401 		for (unsigned int a = 0; a < face.mNumIndices;++a)
402 		{
403 			if (face.mIndices[a] >= pMesh->mNumVertices)	{
404 				ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range",i,a);
405 			}
406 			// the MSB flag is temporarily used by the extra verbose
407 			// mode to tell us that the JoinVerticesProcess might have
408 			// been executed already.
409 			if ( !(this->mScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT ) && abRefList[face.mIndices[a]])
410 			{
411 				ReportError("aiMesh::mVertices[%i] is referenced twice - second "
412 					"time by aiMesh::mFaces[%i]::mIndices[%i]",face.mIndices[a],i,a);
413 			}
414 			abRefList[face.mIndices[a]] = true;
415 		}
416 	}
417 
418 	// check whether there are vertices that aren't referenced by a face
419 	bool b = false;
420 	for (unsigned int i = 0; i < pMesh->mNumVertices;++i)	{
421 		if (!abRefList[i])b = true;
422 	}
423 	abRefList.clear();
424 	if (b)ReportWarning("There are unreferenced vertices");
425 
426 	// texture channel 2 may not be set if channel 1 is zero ...
427 	{
428 		unsigned int i = 0;
429 		for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
430 		{
431 			if (!pMesh->HasTextureCoords(i))break;
432 		}
433 		for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
434 			if (pMesh->HasTextureCoords(i))
435 			{
436 				ReportError("Texture coordinate channel %i exists "
437 					"although the previous channel was NULL.",i);
438 			}
439 	}
440 	// the same for the vertex colors
441 	{
442 		unsigned int i = 0;
443 		for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i)
444 		{
445 			if (!pMesh->HasVertexColors(i))break;
446 		}
447 		for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i)
448 			if (pMesh->HasVertexColors(i))
449 			{
450 				ReportError("Vertex color channel %i is exists "
451 					"although the previous channel was NULL.",i);
452 			}
453 	}
454 
455 
456 	// now validate all bones
457 	if (pMesh->mNumBones)
458 	{
459 		if (!pMesh->mBones)
460 		{
461 			ReportError("aiMesh::mBones is NULL (aiMesh::mNumBones is %i)",
462 				pMesh->mNumBones);
463 		}
464 		boost::scoped_array<float> afSum(NULL);
465 		if (pMesh->mNumVertices)
466 		{
467 			afSum.reset(new float[pMesh->mNumVertices]);
468 			for (unsigned int i = 0; i < pMesh->mNumVertices;++i)
469 				afSum[i] = 0.0f;
470 		}
471 
472 		// check whether there are duplicate bone names
473 		for (unsigned int i = 0; i < pMesh->mNumBones;++i)
474 		{
475 			const aiBone* bone = pMesh->mBones[i];
476 			if (bone->mNumWeights > AI_MAX_BONE_WEIGHTS) {
477 				ReportError("Bone %u has too many weights: %u, but the limit is %u",i,bone->mNumWeights,AI_MAX_BONE_WEIGHTS);
478 			}
479 
480 			if (!pMesh->mBones[i])
481 			{
482 				ReportError("aiMesh::mBones[%i] is NULL (aiMesh::mNumBones is %i)",
483 					i,pMesh->mNumBones);
484 			}
485 			Validate(pMesh,pMesh->mBones[i],afSum.get());
486 
487 			for (unsigned int a = i+1; a < pMesh->mNumBones;++a)
488 			{
489 				if (pMesh->mBones[i]->mName == pMesh->mBones[a]->mName)
490 				{
491 					ReportError("aiMesh::mBones[%i] has the same name as "
492 						"aiMesh::mBones[%i]",i,a);
493 				}
494 			}
495 		}
496 		// check whether all bone weights for a vertex sum to 1.0 ...
497 		for (unsigned int i = 0; i < pMesh->mNumVertices;++i)
498 		{
499 			if (afSum[i] && (afSum[i] <= 0.94 || afSum[i] >= 1.05))	{
500 				ReportWarning("aiMesh::mVertices[%i]: bone weight sum != 1.0 (sum is %f)",i,afSum[i]);
501 			}
502 		}
503 	}
504 	else if (pMesh->mBones)
505 	{
506 		ReportError("aiMesh::mBones is non-null although there are no bones");
507 	}
508 }
509 
510 // ------------------------------------------------------------------------------------------------
Validate(const aiMesh * pMesh,const aiBone * pBone,float * afSum)511 void ValidateDSProcess::Validate( const aiMesh* pMesh,
512 	const aiBone* pBone,float* afSum)
513 {
514 	this->Validate(&pBone->mName);
515 
516    	if (!pBone->mNumWeights)	{
517 		ReportError("aiBone::mNumWeights is zero");
518 	}
519 
520 	// check whether all vertices affected by this bone are valid
521 	for (unsigned int i = 0; i < pBone->mNumWeights;++i)
522 	{
523 		if (pBone->mWeights[i].mVertexId >= pMesh->mNumVertices)	{
524 			ReportError("aiBone::mWeights[%i].mVertexId is out of range",i);
525 		}
526 		else if (!pBone->mWeights[i].mWeight || pBone->mWeights[i].mWeight > 1.0f)	{
527 			ReportWarning("aiBone::mWeights[%i].mWeight has an invalid value",i);
528 		}
529 		afSum[pBone->mWeights[i].mVertexId] += pBone->mWeights[i].mWeight;
530 	}
531 }
532 
533 // ------------------------------------------------------------------------------------------------
Validate(const aiAnimation * pAnimation)534 void ValidateDSProcess::Validate( const aiAnimation* pAnimation)
535 {
536 	Validate(&pAnimation->mName);
537 
538 	// validate all materials
539 	if (pAnimation->mNumChannels)
540 	{
541 		if (!pAnimation->mChannels)	{
542 			ReportError("aiAnimation::mChannels is NULL (aiAnimation::mNumChannels is %i)",
543 				pAnimation->mNumChannels);
544 		}
545 		for (unsigned int i = 0; i < pAnimation->mNumChannels;++i)
546 		{
547 			if (!pAnimation->mChannels[i])
548 			{
549 				ReportError("aiAnimation::mChannels[%i] is NULL (aiAnimation::mNumChannels is %i)",
550 					i, pAnimation->mNumChannels);
551 			}
552 			Validate(pAnimation, pAnimation->mChannels[i]);
553 		}
554 	}
555 	else ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there.");
556 
557 	// Animation duration is allowed to be zero in cases where the anim contains only a single key frame.
558 	// if (!pAnimation->mDuration)this->ReportError("aiAnimation::mDuration is zero");
559 }
560 
561 // ------------------------------------------------------------------------------------------------
SearchForInvalidTextures(const aiMaterial * pMaterial,aiTextureType type)562 void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial,
563 	aiTextureType type)
564 {
565 	const char* szType = TextureTypeToString(type);
566 
567 	// ****************************************************************************
568 	// Search all keys of the material ...
569 	// textures must be specified with ascending indices
570 	// (e.g. diffuse #2 may not be specified if diffuse #1 is not there ...)
571 	// ****************************************************************************
572 
573 	int iNumIndices = 0;
574 	int iIndex = -1;
575 	for (unsigned int i = 0; i < pMaterial->mNumProperties;++i)
576 	{
577 		aiMaterialProperty* prop = pMaterial->mProperties[i];
578 		if (!::strcmp(prop->mKey.data,"$tex.file") && prop->mSemantic == type)	{
579 			iIndex = std::max(iIndex, (int) prop->mIndex);
580 			++iNumIndices;
581 
582 			if (aiPTI_String != prop->mType)
583 				ReportError("Material property %s is expected to be a string",prop->mKey.data);
584 		}
585 	}
586 	if (iIndex +1 != iNumIndices)	{
587 		ReportError("%s #%i is set, but there are only %i %s textures",
588 			szType,iIndex,iNumIndices,szType);
589 	}
590 	if (!iNumIndices)return;
591 	std::vector<aiTextureMapping> mappings(iNumIndices);
592 
593 	// Now check whether all UV indices are valid ...
594 	bool bNoSpecified = true;
595 	for (unsigned int i = 0; i < pMaterial->mNumProperties;++i)
596 	{
597 		aiMaterialProperty* prop = pMaterial->mProperties[i];
598 		if (prop->mSemantic != type)continue;
599 
600 		if ((int)prop->mIndex >= iNumIndices)
601 		{
602 			ReportError("Found texture property with index %i, although there "
603 				"are only %i textures of type %s",
604 				prop->mIndex, iNumIndices, szType);
605 		}
606 
607 		if (!::strcmp(prop->mKey.data,"$tex.mapping"))	{
608 			if (aiPTI_Integer != prop->mType || prop->mDataLength < sizeof(aiTextureMapping))
609 			{
610 				ReportError("Material property %s%i is expected to be an integer (size is %i)",
611 					prop->mKey.data,prop->mIndex,prop->mDataLength);
612 			}
613 			mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData);
614 		}
615 		else if (!::strcmp(prop->mKey.data,"$tex.uvtrafo"))	{
616 			if (aiPTI_Float != prop->mType || prop->mDataLength < sizeof(aiUVTransform))
617 			{
618 				ReportError("Material property %s%i is expected to be 5 floats large (size is %i)",
619 					prop->mKey.data,prop->mIndex, prop->mDataLength);
620 			}
621 			mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData);
622 		}
623 		else if (!::strcmp(prop->mKey.data,"$tex.uvwsrc")) {
624 			if (aiPTI_Integer != prop->mType || sizeof(int) > prop->mDataLength)
625 			{
626 				ReportError("Material property %s%i is expected to be an integer (size is %i)",
627 					prop->mKey.data,prop->mIndex,prop->mDataLength);
628 			}
629 			bNoSpecified = false;
630 
631 			// Ignore UV indices for texture channels that are not there ...
632 
633 			// Get the value
634 			iIndex = *((unsigned int*)prop->mData);
635 
636 			// Check whether there is a mesh using this material
637 			// which has not enough UV channels ...
638 			for (unsigned int a = 0; a < mScene->mNumMeshes;++a)
639 			{
640 				aiMesh* mesh = this->mScene->mMeshes[a];
641 				if(mesh->mMaterialIndex == (unsigned int)i)
642 				{
643 					int iChannels = 0;
644 					while (mesh->HasTextureCoords(iChannels))++iChannels;
645 					if (iIndex >= iChannels)
646 					{
647 						ReportWarning("Invalid UV index: %i (key %s). Mesh %i has only %i UV channels",
648 							iIndex,prop->mKey.data,a,iChannels);
649 					}
650 				}
651 			}
652 		}
653 	}
654 	if (bNoSpecified)
655 	{
656 		// Assume that all textures are using the first UV channel
657 		for (unsigned int a = 0; a < mScene->mNumMeshes;++a)
658 		{
659 			aiMesh* mesh = mScene->mMeshes[a];
660 			if(mesh->mMaterialIndex == (unsigned int)iIndex && mappings[0] == aiTextureMapping_UV)
661 			{
662 				if (!mesh->mTextureCoords[0])
663 				{
664 					// This is a special case ... it could be that the
665 					// original mesh format intended the use of a special
666 					// mapping here.
667 					ReportWarning("UV-mapped texture, but there are no UV coords");
668 				}
669 			}
670 		}
671 	}
672 }
673 // ------------------------------------------------------------------------------------------------
Validate(const aiMaterial * pMaterial)674 void ValidateDSProcess::Validate( const aiMaterial* pMaterial)
675 {
676 	// check whether there are material keys that are obviously not legal
677 	for (unsigned int i = 0; i < pMaterial->mNumProperties;++i)
678 	{
679 		const aiMaterialProperty* prop = pMaterial->mProperties[i];
680 		if (!prop)	{
681 			ReportError("aiMaterial::mProperties[%i] is NULL (aiMaterial::mNumProperties is %i)",
682 				i,pMaterial->mNumProperties);
683 		}
684 		if (!prop->mDataLength || !prop->mData)	{
685 			ReportError("aiMaterial::mProperties[%i].mDataLength or "
686 				"aiMaterial::mProperties[%i].mData is 0",i,i);
687 		}
688 		// check all predefined types
689 		if (aiPTI_String == prop->mType)	{
690 			// FIX: strings are now stored in a less expensive way, but we can't use the
691 			// validation routine for 'normal' aiStrings
692 			uint32_t len;
693 			if (prop->mDataLength < 5 || prop->mDataLength < 4 + (len=*reinterpret_cast<uint32_t*>(prop->mData)) + 1)	{
694 				ReportError("aiMaterial::mProperties[%i].mDataLength is "
695 					"too small to contain a string (%i, needed: %i)",
696 					i,prop->mDataLength,sizeof(aiString));
697 			}
698 			if(prop->mData[prop->mDataLength-1]) {
699 				ReportError("Missing null-terminator in string material property");
700 			}
701 		//	Validate((const aiString*)prop->mData);
702 		}
703 		else if (aiPTI_Float == prop->mType)	{
704 			if (prop->mDataLength < sizeof(float))	{
705 				ReportError("aiMaterial::mProperties[%i].mDataLength is "
706 					"too small to contain a float (%i, needed: %i)",
707 					i,prop->mDataLength,sizeof(float));
708 			}
709 		}
710 		else if (aiPTI_Integer == prop->mType)	{
711 			if (prop->mDataLength < sizeof(int))	{
712 				ReportError("aiMaterial::mProperties[%i].mDataLength is "
713 					"too small to contain an integer (%i, needed: %i)",
714 					i,prop->mDataLength,sizeof(int));
715 			}
716 		}
717 		// TODO: check whether there is a key with an unknown name ...
718 	}
719 
720 	// make some more specific tests
721 	float fTemp;
722 	int iShading;
723 	if (AI_SUCCESS == aiGetMaterialInteger( pMaterial,AI_MATKEY_SHADING_MODEL,&iShading))	{
724 		switch ((aiShadingMode)iShading)
725 		{
726 		case aiShadingMode_Blinn:
727 		case aiShadingMode_CookTorrance:
728 		case aiShadingMode_Phong:
729 
730 			if (AI_SUCCESS != aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS,&fTemp))	{
731 				ReportWarning("A specular shading model is specified but there is no "
732 					"AI_MATKEY_SHININESS key");
733 			}
734 			if (AI_SUCCESS == aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS_STRENGTH,&fTemp) && !fTemp)	{
735 				ReportWarning("A specular shading model is specified but the value of the "
736 					"AI_MATKEY_SHININESS_STRENGTH key is 0.0");
737 			}
738 			break;
739 		default: ;
740 		};
741 	}
742 
743 	if (AI_SUCCESS == aiGetMaterialFloat( pMaterial,AI_MATKEY_OPACITY,&fTemp) && (!fTemp || fTemp > 1.01f))	{
744 		ReportWarning("Invalid opacity value (must be 0 < opacity < 1.0)");
745 	}
746 
747 	// Check whether there are invalid texture keys
748 	// TODO: that's a relict of the past, where texture type and index were baked
749 	// into the material string ... we could do that in one single pass.
750 	SearchForInvalidTextures(pMaterial,aiTextureType_DIFFUSE);
751 	SearchForInvalidTextures(pMaterial,aiTextureType_SPECULAR);
752 	SearchForInvalidTextures(pMaterial,aiTextureType_AMBIENT);
753 	SearchForInvalidTextures(pMaterial,aiTextureType_EMISSIVE);
754 	SearchForInvalidTextures(pMaterial,aiTextureType_OPACITY);
755 	SearchForInvalidTextures(pMaterial,aiTextureType_SHININESS);
756 	SearchForInvalidTextures(pMaterial,aiTextureType_HEIGHT);
757 	SearchForInvalidTextures(pMaterial,aiTextureType_NORMALS);
758 	SearchForInvalidTextures(pMaterial,aiTextureType_DISPLACEMENT);
759 	SearchForInvalidTextures(pMaterial,aiTextureType_LIGHTMAP);
760 	SearchForInvalidTextures(pMaterial,aiTextureType_REFLECTION);
761 }
762 
763 // ------------------------------------------------------------------------------------------------
Validate(const aiTexture * pTexture)764 void ValidateDSProcess::Validate( const aiTexture* pTexture)
765 {
766 	// the data section may NEVER be NULL
767 	if (!pTexture->pcData)	{
768 		ReportError("aiTexture::pcData is NULL");
769 	}
770 	if (pTexture->mHeight)
771 	{
772 		if (!pTexture->mWidth)ReportError("aiTexture::mWidth is zero "
773 			"(aiTexture::mHeight is %i, uncompressed texture)",pTexture->mHeight);
774 	}
775 	else
776 	{
777 		if (!pTexture->mWidth) {
778 			ReportError("aiTexture::mWidth is zero (compressed texture)");
779 		}
780 		if ('\0' != pTexture->achFormatHint[3]) {
781 			ReportWarning("aiTexture::achFormatHint must be zero-terminated");
782 		}
783 		else if ('.'  == pTexture->achFormatHint[0])	{
784 			ReportWarning("aiTexture::achFormatHint should contain a file extension "
785 				"without a leading dot (format hint: %s).",pTexture->achFormatHint);
786 		}
787 	}
788 
789 	const char* sz = pTexture->achFormatHint;
790  	if ((sz[0] >= 'A' && sz[0] <= 'Z') ||
791 		(sz[1] >= 'A' && sz[1] <= 'Z') ||
792 		(sz[2] >= 'A' && sz[2] <= 'Z') ||
793 		(sz[3] >= 'A' && sz[3] <= 'Z'))	{
794 		ReportError("aiTexture::achFormatHint contains non-lowercase letters");
795 	}
796 }
797 
798 // ------------------------------------------------------------------------------------------------
Validate(const aiAnimation * pAnimation,const aiNodeAnim * pNodeAnim)799 void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
800 	 const aiNodeAnim* pNodeAnim)
801 {
802 	Validate(&pNodeAnim->mNodeName);
803 
804 	if (!pNodeAnim->mNumPositionKeys && !pNodeAnim->mScalingKeys && !pNodeAnim->mNumRotationKeys)
805 		ReportError("Empty node animation channel");
806 
807 	// otherwise check whether one of the keys exceeds the total duration of the animation
808 	if (pNodeAnim->mNumPositionKeys)
809 	{
810 		if (!pNodeAnim->mPositionKeys)
811 		{
812 			this->ReportError("aiNodeAnim::mPositionKeys is NULL (aiNodeAnim::mNumPositionKeys is %i)",
813 				pNodeAnim->mNumPositionKeys);
814 		}
815 		double dLast = -10e10;
816 		for (unsigned int i = 0; i < pNodeAnim->mNumPositionKeys;++i)
817 		{
818 			// ScenePreprocessor will compute the duration if still the default value
819 			// (Aramis) Add small epsilon, comparison tended to fail if max_time == duration,
820 			//  seems to be due the compilers register usage/width.
821 			if (pAnimation->mDuration > 0. && pNodeAnim->mPositionKeys[i].mTime > pAnimation->mDuration+0.001)
822 			{
823 				ReportError("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is larger "
824 					"than aiAnimation::mDuration (which is %.5f)",i,
825 					(float)pNodeAnim->mPositionKeys[i].mTime,
826 					(float)pAnimation->mDuration);
827 			}
828 			if (i && pNodeAnim->mPositionKeys[i].mTime <= dLast)
829 			{
830 				ReportWarning("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is smaller "
831 					"than aiAnimation::mPositionKeys[%i] (which is %.5f)",i,
832 					(float)pNodeAnim->mPositionKeys[i].mTime,
833 					i-1, (float)dLast);
834 			}
835 			dLast = pNodeAnim->mPositionKeys[i].mTime;
836 		}
837 	}
838 	// rotation keys
839 	if (pNodeAnim->mNumRotationKeys)
840 	{
841 		if (!pNodeAnim->mRotationKeys)
842 		{
843 			this->ReportError("aiNodeAnim::mRotationKeys is NULL (aiNodeAnim::mNumRotationKeys is %i)",
844 				pNodeAnim->mNumRotationKeys);
845 		}
846 		double dLast = -10e10;
847 		for (unsigned int i = 0; i < pNodeAnim->mNumRotationKeys;++i)
848 		{
849 			if (pAnimation->mDuration > 0. && pNodeAnim->mRotationKeys[i].mTime > pAnimation->mDuration+0.001)
850 			{
851 				ReportError("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is larger "
852 					"than aiAnimation::mDuration (which is %.5f)",i,
853 					(float)pNodeAnim->mRotationKeys[i].mTime,
854 					(float)pAnimation->mDuration);
855 			}
856 			if (i && pNodeAnim->mRotationKeys[i].mTime <= dLast)
857 			{
858 				ReportWarning("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is smaller "
859 					"than aiAnimation::mRotationKeys[%i] (which is %.5f)",i,
860 					(float)pNodeAnim->mRotationKeys[i].mTime,
861 					i-1, (float)dLast);
862 			}
863 			dLast = pNodeAnim->mRotationKeys[i].mTime;
864 		}
865 	}
866 	// scaling keys
867 	if (pNodeAnim->mNumScalingKeys)
868 	{
869 		if (!pNodeAnim->mScalingKeys)	{
870 			ReportError("aiNodeAnim::mScalingKeys is NULL (aiNodeAnim::mNumScalingKeys is %i)",
871 				pNodeAnim->mNumScalingKeys);
872 		}
873 		double dLast = -10e10;
874 		for (unsigned int i = 0; i < pNodeAnim->mNumScalingKeys;++i)
875 		{
876 			if (pAnimation->mDuration > 0. && pNodeAnim->mScalingKeys[i].mTime > pAnimation->mDuration+0.001)
877 			{
878 				ReportError("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is larger "
879 					"than aiAnimation::mDuration (which is %.5f)",i,
880 					(float)pNodeAnim->mScalingKeys[i].mTime,
881 					(float)pAnimation->mDuration);
882 			}
883 			if (i && pNodeAnim->mScalingKeys[i].mTime <= dLast)
884 			{
885 				ReportWarning("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is smaller "
886 					"than aiAnimation::mScalingKeys[%i] (which is %.5f)",i,
887 					(float)pNodeAnim->mScalingKeys[i].mTime,
888 					i-1, (float)dLast);
889 			}
890 			dLast = pNodeAnim->mScalingKeys[i].mTime;
891 		}
892 	}
893 
894 	if (!pNodeAnim->mNumScalingKeys && !pNodeAnim->mNumRotationKeys &&
895 		!pNodeAnim->mNumPositionKeys)
896 	{
897 		ReportError("A node animation channel must have at least one subtrack");
898 	}
899 }
900 
901 // ------------------------------------------------------------------------------------------------
Validate(const aiNode * pNode)902 void ValidateDSProcess::Validate( const aiNode* pNode)
903 {
904 	if (!pNode)ReportError("A node of the scenegraph is NULL");
905 	if (pNode != mScene->mRootNode && !pNode->mParent)
906 		this->ReportError("A node has no valid parent (aiNode::mParent is NULL)");
907 
908 	this->Validate(&pNode->mName);
909 
910 	// validate all meshes
911 	if (pNode->mNumMeshes)
912 	{
913 		if (!pNode->mMeshes)
914 		{
915 			ReportError("aiNode::mMeshes is NULL (aiNode::mNumMeshes is %i)",
916 				pNode->mNumMeshes);
917 		}
918 		std::vector<bool> abHadMesh;
919 		abHadMesh.resize(mScene->mNumMeshes,false);
920 		for (unsigned int i = 0; i < pNode->mNumMeshes;++i)
921 		{
922 			if (pNode->mMeshes[i] >= mScene->mNumMeshes)
923 			{
924 				ReportError("aiNode::mMeshes[%i] is out of range (maximum is %i)",
925 					pNode->mMeshes[i],mScene->mNumMeshes-1);
926 			}
927 			if (abHadMesh[pNode->mMeshes[i]])
928 			{
929 				ReportError("aiNode::mMeshes[%i] is already referenced by this node (value: %i)",
930 					i,pNode->mMeshes[i]);
931 			}
932 			abHadMesh[pNode->mMeshes[i]] = true;
933 		}
934 	}
935 	if (pNode->mNumChildren)
936 	{
937 		if (!pNode->mChildren)	{
938 			ReportError("aiNode::mChildren is NULL (aiNode::mNumChildren is %i)",
939 				pNode->mNumChildren);
940 		}
941 		for (unsigned int i = 0; i < pNode->mNumChildren;++i)	{
942 			Validate(pNode->mChildren[i]);
943 		}
944 	}
945 }
946 
947 // ------------------------------------------------------------------------------------------------
Validate(const aiString * pString)948 void ValidateDSProcess::Validate( const aiString* pString)
949 {
950 	if (pString->length > MAXLEN)
951 	{
952 		this->ReportError("aiString::length is too large (%i, maximum is %i)",
953 			pString->length,MAXLEN);
954 	}
955 	const char* sz = pString->data;
956 	while (true)
957 	{
958 		if ('\0' == *sz)
959 		{
960 			if (pString->length != (unsigned int)(sz-pString->data))
961 				ReportError("aiString::data is invalid: the terminal zero is at a wrong offset");
962 			break;
963 		}
964 		else if (sz >= &pString->data[MAXLEN])
965 			ReportError("aiString::data is invalid. There is no terminal character");
966 		++sz;
967 	}
968 }
969