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