vrshoot
diff libs/assimp/ValidateDataStructure.cpp @ 0:b2f14e535253
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 01 Feb 2014 19:58:19 +0200 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libs/assimp/ValidateDataStructure.cpp Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,968 @@ 1.4 +/* 1.5 +--------------------------------------------------------------------------- 1.6 +Open Asset Import Library (assimp) 1.7 +--------------------------------------------------------------------------- 1.8 + 1.9 +Copyright (c) 2006-2012, assimp team 1.10 + 1.11 +All rights reserved. 1.12 + 1.13 +Redistribution and use of this software in source and binary forms, 1.14 +with or without modification, are permitted provided that the following 1.15 +conditions are met: 1.16 + 1.17 +* Redistributions of source code must retain the above 1.18 + copyright notice, this list of conditions and the 1.19 + following disclaimer. 1.20 + 1.21 +* Redistributions in binary form must reproduce the above 1.22 + copyright notice, this list of conditions and the 1.23 + following disclaimer in the documentation and/or other 1.24 + materials provided with the distribution. 1.25 + 1.26 +* Neither the name of the assimp team, nor the names of its 1.27 + contributors may be used to endorse or promote products 1.28 + derived from this software without specific prior 1.29 + written permission of the assimp team. 1.30 + 1.31 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.32 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.33 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.34 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.35 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.36 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.37 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.38 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.39 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.40 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.41 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.42 +--------------------------------------------------------------------------- 1.43 +*/ 1.44 + 1.45 +/** @file ValidateDataStructure.cpp 1.46 + * @brief Implementation of the post processing step to validate 1.47 + * the data structure returned by Assimp. 1.48 + */ 1.49 + 1.50 +#include "AssimpPCH.h" 1.51 + 1.52 +// internal headers 1.53 +#include "ValidateDataStructure.h" 1.54 +#include "BaseImporter.h" 1.55 +#include "fast_atof.h" 1.56 +#include "ProcessHelper.h" 1.57 + 1.58 +// CRT headers 1.59 +#include <stdarg.h> 1.60 + 1.61 +using namespace Assimp; 1.62 + 1.63 +// ------------------------------------------------------------------------------------------------ 1.64 +// Constructor to be privately used by Importer 1.65 +ValidateDSProcess::ValidateDSProcess() 1.66 +{} 1.67 + 1.68 +// ------------------------------------------------------------------------------------------------ 1.69 +// Destructor, private as well 1.70 +ValidateDSProcess::~ValidateDSProcess() 1.71 +{} 1.72 + 1.73 +// ------------------------------------------------------------------------------------------------ 1.74 +// Returns whether the processing step is present in the given flag field. 1.75 +bool ValidateDSProcess::IsActive( unsigned int pFlags) const 1.76 +{ 1.77 + return (pFlags & aiProcess_ValidateDataStructure) != 0; 1.78 +} 1.79 +// ------------------------------------------------------------------------------------------------ 1.80 +AI_WONT_RETURN void ValidateDSProcess::ReportError(const char* msg,...) 1.81 +{ 1.82 + ai_assert(NULL != msg); 1.83 + 1.84 + va_list args; 1.85 + va_start(args,msg); 1.86 + 1.87 + char szBuffer[3000]; 1.88 + const int iLen = vsprintf(szBuffer,msg,args); 1.89 + ai_assert(iLen > 0); 1.90 + 1.91 + va_end(args); 1.92 +#ifdef _DEBUG 1.93 + ai_assert( false ); 1.94 +#endif 1.95 + throw DeadlyImportError("Validation failed: " + std::string(szBuffer,iLen)); 1.96 +} 1.97 +// ------------------------------------------------------------------------------------------------ 1.98 +void ValidateDSProcess::ReportWarning(const char* msg,...) 1.99 +{ 1.100 + ai_assert(NULL != msg); 1.101 + 1.102 + va_list args; 1.103 + va_start(args,msg); 1.104 + 1.105 + char szBuffer[3000]; 1.106 + const int iLen = vsprintf(szBuffer,msg,args); 1.107 + ai_assert(iLen > 0); 1.108 + 1.109 + va_end(args); 1.110 + DefaultLogger::get()->warn("Validation warning: " + std::string(szBuffer,iLen)); 1.111 +} 1.112 + 1.113 +// ------------------------------------------------------------------------------------------------ 1.114 +inline int HasNameMatch(const aiString& in, aiNode* node) 1.115 +{ 1.116 + int result = (node->mName == in ? 1 : 0 ); 1.117 + for (unsigned int i = 0; i < node->mNumChildren;++i) { 1.118 + result += HasNameMatch(in,node->mChildren[i]); 1.119 + } 1.120 + return result; 1.121 +} 1.122 + 1.123 +// ------------------------------------------------------------------------------------------------ 1.124 +template <typename T> 1.125 +inline void ValidateDSProcess::DoValidation(T** parray, unsigned int size, 1.126 + const char* firstName, const char* secondName) 1.127 +{ 1.128 + // validate all entries 1.129 + if (size) 1.130 + { 1.131 + if (!parray) 1.132 + { 1.133 + ReportError("aiScene::%s is NULL (aiScene::%s is %i)", 1.134 + firstName, secondName, size); 1.135 + } 1.136 + for (unsigned int i = 0; i < size;++i) 1.137 + { 1.138 + if (!parray[i]) 1.139 + { 1.140 + ReportError("aiScene::%s[%i] is NULL (aiScene::%s is %i)", 1.141 + firstName,i,secondName,size); 1.142 + } 1.143 + Validate(parray[i]); 1.144 + } 1.145 + } 1.146 +} 1.147 + 1.148 +// ------------------------------------------------------------------------------------------------ 1.149 +template <typename T> 1.150 +inline void ValidateDSProcess::DoValidationEx(T** parray, unsigned int size, 1.151 + const char* firstName, const char* secondName) 1.152 +{ 1.153 + // validate all entries 1.154 + if (size) 1.155 + { 1.156 + if (!parray) { 1.157 + ReportError("aiScene::%s is NULL (aiScene::%s is %i)", 1.158 + firstName, secondName, size); 1.159 + } 1.160 + for (unsigned int i = 0; i < size;++i) 1.161 + { 1.162 + if (!parray[i]) 1.163 + { 1.164 + ReportError("aiScene::%s[%i] is NULL (aiScene::%s is %i)", 1.165 + firstName,i,secondName,size); 1.166 + } 1.167 + Validate(parray[i]); 1.168 + 1.169 + // check whether there are duplicate names 1.170 + for (unsigned int a = i+1; a < size;++a) 1.171 + { 1.172 + if (parray[i]->mName == parray[a]->mName) 1.173 + { 1.174 + this->ReportError("aiScene::%s[%i] has the same name as " 1.175 + "aiScene::%s[%i]",firstName, i,secondName, a); 1.176 + } 1.177 + } 1.178 + } 1.179 + } 1.180 +} 1.181 + 1.182 +// ------------------------------------------------------------------------------------------------ 1.183 +template <typename T> 1.184 +inline void ValidateDSProcess::DoValidationWithNameCheck(T** array, 1.185 + unsigned int size, const char* firstName, 1.186 + const char* secondName) 1.187 +{ 1.188 + // validate all entries 1.189 + DoValidationEx(array,size,firstName,secondName); 1.190 + 1.191 + for (unsigned int i = 0; i < size;++i) 1.192 + { 1.193 + int res = HasNameMatch(array[i]->mName,mScene->mRootNode); 1.194 + if (!res) { 1.195 + ReportError("aiScene::%s[%i] has no corresponding node in the scene graph (%s)", 1.196 + firstName,i,array[i]->mName.data); 1.197 + } 1.198 + else if (1 != res) { 1.199 + ReportError("aiScene::%s[%i]: there are more than one nodes with %s as name", 1.200 + firstName,i,array[i]->mName.data); 1.201 + } 1.202 + } 1.203 +} 1.204 + 1.205 +// ------------------------------------------------------------------------------------------------ 1.206 +// Executes the post processing step on the given imported data. 1.207 +void ValidateDSProcess::Execute( aiScene* pScene) 1.208 +{ 1.209 + this->mScene = pScene; 1.210 + DefaultLogger::get()->debug("ValidateDataStructureProcess begin"); 1.211 + 1.212 + // validate the node graph of the scene 1.213 + Validate(pScene->mRootNode); 1.214 + 1.215 + // validate all meshes 1.216 + if (pScene->mNumMeshes) { 1.217 + DoValidation(pScene->mMeshes,pScene->mNumMeshes,"mMeshes","mNumMeshes"); 1.218 + } 1.219 + else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) { 1.220 + ReportError("aiScene::mNumMeshes is 0. At least one mesh must be there"); 1.221 + } 1.222 + else if (pScene->mMeshes) { 1.223 + ReportError("aiScene::mMeshes is non-null although there are no meshes"); 1.224 + } 1.225 + 1.226 + // validate all animations 1.227 + if (pScene->mNumAnimations) { 1.228 + DoValidation(pScene->mAnimations,pScene->mNumAnimations, 1.229 + "mAnimations","mNumAnimations"); 1.230 + } 1.231 + else if (pScene->mAnimations) { 1.232 + ReportError("aiScene::mAnimations is non-null although there are no animations"); 1.233 + } 1.234 + 1.235 + // validate all cameras 1.236 + if (pScene->mNumCameras) { 1.237 + DoValidationWithNameCheck(pScene->mCameras,pScene->mNumCameras, 1.238 + "mCameras","mNumCameras"); 1.239 + } 1.240 + else if (pScene->mCameras) { 1.241 + ReportError("aiScene::mCameras is non-null although there are no cameras"); 1.242 + } 1.243 + 1.244 + // validate all lights 1.245 + if (pScene->mNumLights) { 1.246 + DoValidationWithNameCheck(pScene->mLights,pScene->mNumLights, 1.247 + "mLights","mNumLights"); 1.248 + } 1.249 + else if (pScene->mLights) { 1.250 + ReportError("aiScene::mLights is non-null although there are no lights"); 1.251 + } 1.252 + 1.253 + // validate all textures 1.254 + if (pScene->mNumTextures) { 1.255 + DoValidation(pScene->mTextures,pScene->mNumTextures, 1.256 + "mTextures","mNumTextures"); 1.257 + } 1.258 + else if (pScene->mTextures) { 1.259 + ReportError("aiScene::mTextures is non-null although there are no textures"); 1.260 + } 1.261 + 1.262 + // validate all materials 1.263 + if (pScene->mNumMaterials) { 1.264 + DoValidation(pScene->mMaterials,pScene->mNumMaterials,"mMaterials","mNumMaterials"); 1.265 + } 1.266 +#if 0 1.267 + // NOTE: ScenePreprocessor generates a default material if none is there 1.268 + else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) { 1.269 + ReportError("aiScene::mNumMaterials is 0. At least one material must be there"); 1.270 + } 1.271 +#endif 1.272 + else if (pScene->mMaterials) { 1.273 + ReportError("aiScene::mMaterials is non-null although there are no materials"); 1.274 + } 1.275 + 1.276 +// if (!has)ReportError("The aiScene data structure is empty"); 1.277 + DefaultLogger::get()->debug("ValidateDataStructureProcess end"); 1.278 +} 1.279 + 1.280 +// ------------------------------------------------------------------------------------------------ 1.281 +void ValidateDSProcess::Validate( const aiLight* pLight) 1.282 +{ 1.283 + if (pLight->mType == aiLightSource_UNDEFINED) 1.284 + ReportWarning("aiLight::mType is aiLightSource_UNDEFINED"); 1.285 + 1.286 + if (!pLight->mAttenuationConstant && 1.287 + !pLight->mAttenuationLinear && 1.288 + !pLight->mAttenuationQuadratic) { 1.289 + ReportWarning("aiLight::mAttenuationXXX - all are zero"); 1.290 + } 1.291 + 1.292 + if (pLight->mAngleInnerCone > pLight->mAngleOuterCone) 1.293 + ReportError("aiLight::mAngleInnerCone is larger than aiLight::mAngleOuterCone"); 1.294 + 1.295 + if (pLight->mColorDiffuse.IsBlack() && pLight->mColorAmbient.IsBlack() 1.296 + && pLight->mColorSpecular.IsBlack()) 1.297 + { 1.298 + ReportWarning("aiLight::mColorXXX - all are black and won't have any influence"); 1.299 + } 1.300 +} 1.301 + 1.302 +// ------------------------------------------------------------------------------------------------ 1.303 +void ValidateDSProcess::Validate( const aiCamera* pCamera) 1.304 +{ 1.305 + if (pCamera->mClipPlaneFar <= pCamera->mClipPlaneNear) 1.306 + ReportError("aiCamera::mClipPlaneFar must be >= aiCamera::mClipPlaneNear"); 1.307 + 1.308 + // FIX: there are many 3ds files with invalid FOVs. No reason to 1.309 + // reject them at all ... a warning is appropriate. 1.310 + if (!pCamera->mHorizontalFOV || pCamera->mHorizontalFOV >= (float)AI_MATH_PI) 1.311 + ReportWarning("%f is not a valid value for aiCamera::mHorizontalFOV",pCamera->mHorizontalFOV); 1.312 +} 1.313 + 1.314 +// ------------------------------------------------------------------------------------------------ 1.315 +void ValidateDSProcess::Validate( const aiMesh* pMesh) 1.316 +{ 1.317 + // validate the material index of the mesh 1.318 + if (mScene->mNumMaterials && pMesh->mMaterialIndex >= mScene->mNumMaterials) 1.319 + { 1.320 + ReportError("aiMesh::mMaterialIndex is invalid (value: %i maximum: %i)", 1.321 + pMesh->mMaterialIndex,mScene->mNumMaterials-1); 1.322 + } 1.323 + 1.324 + Validate(&pMesh->mName); 1.325 + 1.326 + for (unsigned int i = 0; i < pMesh->mNumFaces; ++i) 1.327 + { 1.328 + aiFace& face = pMesh->mFaces[i]; 1.329 + 1.330 + if (pMesh->mPrimitiveTypes) 1.331 + { 1.332 + switch (face.mNumIndices) 1.333 + { 1.334 + case 0: 1.335 + ReportError("aiMesh::mFaces[%i].mNumIndices is 0",i); 1.336 + case 1: 1.337 + if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POINT)) 1.338 + { 1.339 + ReportError("aiMesh::mFaces[%i] is a POINT but aiMesh::mPrimtiveTypes " 1.340 + "does not report the POINT flag",i); 1.341 + } 1.342 + break; 1.343 + case 2: 1.344 + if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_LINE)) 1.345 + { 1.346 + ReportError("aiMesh::mFaces[%i] is a LINE but aiMesh::mPrimtiveTypes " 1.347 + "does not report the LINE flag",i); 1.348 + } 1.349 + break; 1.350 + case 3: 1.351 + if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE)) 1.352 + { 1.353 + ReportError("aiMesh::mFaces[%i] is a TRIANGLE but aiMesh::mPrimtiveTypes " 1.354 + "does not report the TRIANGLE flag",i); 1.355 + } 1.356 + break; 1.357 + default: 1.358 + if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) 1.359 + { 1.360 + this->ReportError("aiMesh::mFaces[%i] is a POLYGON but aiMesh::mPrimtiveTypes " 1.361 + "does not report the POLYGON flag",i); 1.362 + } 1.363 + break; 1.364 + }; 1.365 + } 1.366 + 1.367 + if (!face.mIndices) 1.368 + ReportError("aiMesh::mFaces[%i].mIndices is NULL",i); 1.369 + } 1.370 + 1.371 + // positions must always be there ... 1.372 + if (!pMesh->mNumVertices || (!pMesh->mVertices && !mScene->mFlags)) { 1.373 + ReportError("The mesh contains no vertices"); 1.374 + } 1.375 + 1.376 + if (pMesh->mNumVertices > AI_MAX_VERTICES) { 1.377 + ReportError("Mesh has too many vertices: %u, but the limit is %u",pMesh->mNumVertices,AI_MAX_VERTICES); 1.378 + } 1.379 + if (pMesh->mNumFaces > AI_MAX_FACES) { 1.380 + ReportError("Mesh has too many faces: %u, but the limit is %u",pMesh->mNumFaces,AI_MAX_FACES); 1.381 + } 1.382 + 1.383 + // if tangents are there there must also be bitangent vectors ... 1.384 + if ((pMesh->mTangents != NULL) != (pMesh->mBitangents != NULL)) { 1.385 + ReportError("If there are tangents, bitangent vectors must be present as well"); 1.386 + } 1.387 + 1.388 + // faces, too 1.389 + if (!pMesh->mNumFaces || (!pMesh->mFaces && !mScene->mFlags)) { 1.390 + ReportError("Mesh contains no faces"); 1.391 + } 1.392 + 1.393 + // now check whether the face indexing layout is correct: 1.394 + // unique vertices, pseudo-indexed. 1.395 + std::vector<bool> abRefList; 1.396 + abRefList.resize(pMesh->mNumVertices,false); 1.397 + for (unsigned int i = 0; i < pMesh->mNumFaces;++i) 1.398 + { 1.399 + aiFace& face = pMesh->mFaces[i]; 1.400 + if (face.mNumIndices > AI_MAX_FACE_INDICES) { 1.401 + ReportError("Face %u has too many faces: %u, but the limit is %u",i,face.mNumIndices,AI_MAX_FACE_INDICES); 1.402 + } 1.403 + 1.404 + for (unsigned int a = 0; a < face.mNumIndices;++a) 1.405 + { 1.406 + if (face.mIndices[a] >= pMesh->mNumVertices) { 1.407 + ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range",i,a); 1.408 + } 1.409 + // the MSB flag is temporarily used by the extra verbose 1.410 + // mode to tell us that the JoinVerticesProcess might have 1.411 + // been executed already. 1.412 + if ( !(this->mScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT ) && abRefList[face.mIndices[a]]) 1.413 + { 1.414 + ReportError("aiMesh::mVertices[%i] is referenced twice - second " 1.415 + "time by aiMesh::mFaces[%i]::mIndices[%i]",face.mIndices[a],i,a); 1.416 + } 1.417 + abRefList[face.mIndices[a]] = true; 1.418 + } 1.419 + } 1.420 + 1.421 + // check whether there are vertices that aren't referenced by a face 1.422 + bool b = false; 1.423 + for (unsigned int i = 0; i < pMesh->mNumVertices;++i) { 1.424 + if (!abRefList[i])b = true; 1.425 + } 1.426 + abRefList.clear(); 1.427 + if (b)ReportWarning("There are unreferenced vertices"); 1.428 + 1.429 + // texture channel 2 may not be set if channel 1 is zero ... 1.430 + { 1.431 + unsigned int i = 0; 1.432 + for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) 1.433 + { 1.434 + if (!pMesh->HasTextureCoords(i))break; 1.435 + } 1.436 + for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) 1.437 + if (pMesh->HasTextureCoords(i)) 1.438 + { 1.439 + ReportError("Texture coordinate channel %i exists " 1.440 + "although the previous channel was NULL.",i); 1.441 + } 1.442 + } 1.443 + // the same for the vertex colors 1.444 + { 1.445 + unsigned int i = 0; 1.446 + for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i) 1.447 + { 1.448 + if (!pMesh->HasVertexColors(i))break; 1.449 + } 1.450 + for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i) 1.451 + if (pMesh->HasVertexColors(i)) 1.452 + { 1.453 + ReportError("Vertex color channel %i is exists " 1.454 + "although the previous channel was NULL.",i); 1.455 + } 1.456 + } 1.457 + 1.458 + 1.459 + // now validate all bones 1.460 + if (pMesh->mNumBones) 1.461 + { 1.462 + if (!pMesh->mBones) 1.463 + { 1.464 + ReportError("aiMesh::mBones is NULL (aiMesh::mNumBones is %i)", 1.465 + pMesh->mNumBones); 1.466 + } 1.467 + boost::scoped_array<float> afSum(NULL); 1.468 + if (pMesh->mNumVertices) 1.469 + { 1.470 + afSum.reset(new float[pMesh->mNumVertices]); 1.471 + for (unsigned int i = 0; i < pMesh->mNumVertices;++i) 1.472 + afSum[i] = 0.0f; 1.473 + } 1.474 + 1.475 + // check whether there are duplicate bone names 1.476 + for (unsigned int i = 0; i < pMesh->mNumBones;++i) 1.477 + { 1.478 + const aiBone* bone = pMesh->mBones[i]; 1.479 + if (bone->mNumWeights > AI_MAX_BONE_WEIGHTS) { 1.480 + ReportError("Bone %u has too many weights: %u, but the limit is %u",i,bone->mNumWeights,AI_MAX_BONE_WEIGHTS); 1.481 + } 1.482 + 1.483 + if (!pMesh->mBones[i]) 1.484 + { 1.485 + ReportError("aiMesh::mBones[%i] is NULL (aiMesh::mNumBones is %i)", 1.486 + i,pMesh->mNumBones); 1.487 + } 1.488 + Validate(pMesh,pMesh->mBones[i],afSum.get()); 1.489 + 1.490 + for (unsigned int a = i+1; a < pMesh->mNumBones;++a) 1.491 + { 1.492 + if (pMesh->mBones[i]->mName == pMesh->mBones[a]->mName) 1.493 + { 1.494 + ReportError("aiMesh::mBones[%i] has the same name as " 1.495 + "aiMesh::mBones[%i]",i,a); 1.496 + } 1.497 + } 1.498 + } 1.499 + // check whether all bone weights for a vertex sum to 1.0 ... 1.500 + for (unsigned int i = 0; i < pMesh->mNumVertices;++i) 1.501 + { 1.502 + if (afSum[i] && (afSum[i] <= 0.94 || afSum[i] >= 1.05)) { 1.503 + ReportWarning("aiMesh::mVertices[%i]: bone weight sum != 1.0 (sum is %f)",i,afSum[i]); 1.504 + } 1.505 + } 1.506 + } 1.507 + else if (pMesh->mBones) 1.508 + { 1.509 + ReportError("aiMesh::mBones is non-null although there are no bones"); 1.510 + } 1.511 +} 1.512 + 1.513 +// ------------------------------------------------------------------------------------------------ 1.514 +void ValidateDSProcess::Validate( const aiMesh* pMesh, 1.515 + const aiBone* pBone,float* afSum) 1.516 +{ 1.517 + this->Validate(&pBone->mName); 1.518 + 1.519 + if (!pBone->mNumWeights) { 1.520 + ReportError("aiBone::mNumWeights is zero"); 1.521 + } 1.522 + 1.523 + // check whether all vertices affected by this bone are valid 1.524 + for (unsigned int i = 0; i < pBone->mNumWeights;++i) 1.525 + { 1.526 + if (pBone->mWeights[i].mVertexId >= pMesh->mNumVertices) { 1.527 + ReportError("aiBone::mWeights[%i].mVertexId is out of range",i); 1.528 + } 1.529 + else if (!pBone->mWeights[i].mWeight || pBone->mWeights[i].mWeight > 1.0f) { 1.530 + ReportWarning("aiBone::mWeights[%i].mWeight has an invalid value",i); 1.531 + } 1.532 + afSum[pBone->mWeights[i].mVertexId] += pBone->mWeights[i].mWeight; 1.533 + } 1.534 +} 1.535 + 1.536 +// ------------------------------------------------------------------------------------------------ 1.537 +void ValidateDSProcess::Validate( const aiAnimation* pAnimation) 1.538 +{ 1.539 + Validate(&pAnimation->mName); 1.540 + 1.541 + // validate all materials 1.542 + if (pAnimation->mNumChannels) 1.543 + { 1.544 + if (!pAnimation->mChannels) { 1.545 + ReportError("aiAnimation::mChannels is NULL (aiAnimation::mNumChannels is %i)", 1.546 + pAnimation->mNumChannels); 1.547 + } 1.548 + for (unsigned int i = 0; i < pAnimation->mNumChannels;++i) 1.549 + { 1.550 + if (!pAnimation->mChannels[i]) 1.551 + { 1.552 + ReportError("aiAnimation::mChannels[%i] is NULL (aiAnimation::mNumChannels is %i)", 1.553 + i, pAnimation->mNumChannels); 1.554 + } 1.555 + Validate(pAnimation, pAnimation->mChannels[i]); 1.556 + } 1.557 + } 1.558 + else ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there."); 1.559 + 1.560 + // Animation duration is allowed to be zero in cases where the anim contains only a single key frame. 1.561 + // if (!pAnimation->mDuration)this->ReportError("aiAnimation::mDuration is zero"); 1.562 +} 1.563 + 1.564 +// ------------------------------------------------------------------------------------------------ 1.565 +void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial, 1.566 + aiTextureType type) 1.567 +{ 1.568 + const char* szType = TextureTypeToString(type); 1.569 + 1.570 + // **************************************************************************** 1.571 + // Search all keys of the material ... 1.572 + // textures must be specified with ascending indices 1.573 + // (e.g. diffuse #2 may not be specified if diffuse #1 is not there ...) 1.574 + // **************************************************************************** 1.575 + 1.576 + int iNumIndices = 0; 1.577 + int iIndex = -1; 1.578 + for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) 1.579 + { 1.580 + aiMaterialProperty* prop = pMaterial->mProperties[i]; 1.581 + if (!::strcmp(prop->mKey.data,"$tex.file") && prop->mSemantic == type) { 1.582 + iIndex = std::max(iIndex, (int) prop->mIndex); 1.583 + ++iNumIndices; 1.584 + 1.585 + if (aiPTI_String != prop->mType) 1.586 + ReportError("Material property %s is expected to be a string",prop->mKey.data); 1.587 + } 1.588 + } 1.589 + if (iIndex +1 != iNumIndices) { 1.590 + ReportError("%s #%i is set, but there are only %i %s textures", 1.591 + szType,iIndex,iNumIndices,szType); 1.592 + } 1.593 + if (!iNumIndices)return; 1.594 + std::vector<aiTextureMapping> mappings(iNumIndices); 1.595 + 1.596 + // Now check whether all UV indices are valid ... 1.597 + bool bNoSpecified = true; 1.598 + for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) 1.599 + { 1.600 + aiMaterialProperty* prop = pMaterial->mProperties[i]; 1.601 + if (prop->mSemantic != type)continue; 1.602 + 1.603 + if ((int)prop->mIndex >= iNumIndices) 1.604 + { 1.605 + ReportError("Found texture property with index %i, although there " 1.606 + "are only %i textures of type %s", 1.607 + prop->mIndex, iNumIndices, szType); 1.608 + } 1.609 + 1.610 + if (!::strcmp(prop->mKey.data,"$tex.mapping")) { 1.611 + if (aiPTI_Integer != prop->mType || prop->mDataLength < sizeof(aiTextureMapping)) 1.612 + { 1.613 + ReportError("Material property %s%i is expected to be an integer (size is %i)", 1.614 + prop->mKey.data,prop->mIndex,prop->mDataLength); 1.615 + } 1.616 + mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData); 1.617 + } 1.618 + else if (!::strcmp(prop->mKey.data,"$tex.uvtrafo")) { 1.619 + if (aiPTI_Float != prop->mType || prop->mDataLength < sizeof(aiUVTransform)) 1.620 + { 1.621 + ReportError("Material property %s%i is expected to be 5 floats large (size is %i)", 1.622 + prop->mKey.data,prop->mIndex, prop->mDataLength); 1.623 + } 1.624 + mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData); 1.625 + } 1.626 + else if (!::strcmp(prop->mKey.data,"$tex.uvwsrc")) { 1.627 + if (aiPTI_Integer != prop->mType || sizeof(int) > prop->mDataLength) 1.628 + { 1.629 + ReportError("Material property %s%i is expected to be an integer (size is %i)", 1.630 + prop->mKey.data,prop->mIndex,prop->mDataLength); 1.631 + } 1.632 + bNoSpecified = false; 1.633 + 1.634 + // Ignore UV indices for texture channels that are not there ... 1.635 + 1.636 + // Get the value 1.637 + iIndex = *((unsigned int*)prop->mData); 1.638 + 1.639 + // Check whether there is a mesh using this material 1.640 + // which has not enough UV channels ... 1.641 + for (unsigned int a = 0; a < mScene->mNumMeshes;++a) 1.642 + { 1.643 + aiMesh* mesh = this->mScene->mMeshes[a]; 1.644 + if(mesh->mMaterialIndex == (unsigned int)i) 1.645 + { 1.646 + int iChannels = 0; 1.647 + while (mesh->HasTextureCoords(iChannels))++iChannels; 1.648 + if (iIndex >= iChannels) 1.649 + { 1.650 + ReportWarning("Invalid UV index: %i (key %s). Mesh %i has only %i UV channels", 1.651 + iIndex,prop->mKey.data,a,iChannels); 1.652 + } 1.653 + } 1.654 + } 1.655 + } 1.656 + } 1.657 + if (bNoSpecified) 1.658 + { 1.659 + // Assume that all textures are using the first UV channel 1.660 + for (unsigned int a = 0; a < mScene->mNumMeshes;++a) 1.661 + { 1.662 + aiMesh* mesh = mScene->mMeshes[a]; 1.663 + if(mesh->mMaterialIndex == (unsigned int)iIndex && mappings[0] == aiTextureMapping_UV) 1.664 + { 1.665 + if (!mesh->mTextureCoords[0]) 1.666 + { 1.667 + // This is a special case ... it could be that the 1.668 + // original mesh format intended the use of a special 1.669 + // mapping here. 1.670 + ReportWarning("UV-mapped texture, but there are no UV coords"); 1.671 + } 1.672 + } 1.673 + } 1.674 + } 1.675 +} 1.676 +// ------------------------------------------------------------------------------------------------ 1.677 +void ValidateDSProcess::Validate( const aiMaterial* pMaterial) 1.678 +{ 1.679 + // check whether there are material keys that are obviously not legal 1.680 + for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) 1.681 + { 1.682 + const aiMaterialProperty* prop = pMaterial->mProperties[i]; 1.683 + if (!prop) { 1.684 + ReportError("aiMaterial::mProperties[%i] is NULL (aiMaterial::mNumProperties is %i)", 1.685 + i,pMaterial->mNumProperties); 1.686 + } 1.687 + if (!prop->mDataLength || !prop->mData) { 1.688 + ReportError("aiMaterial::mProperties[%i].mDataLength or " 1.689 + "aiMaterial::mProperties[%i].mData is 0",i,i); 1.690 + } 1.691 + // check all predefined types 1.692 + if (aiPTI_String == prop->mType) { 1.693 + // FIX: strings are now stored in a less expensive way, but we can't use the 1.694 + // validation routine for 'normal' aiStrings 1.695 + uint32_t len; 1.696 + if (prop->mDataLength < 5 || prop->mDataLength < 4 + (len=*reinterpret_cast<uint32_t*>(prop->mData)) + 1) { 1.697 + ReportError("aiMaterial::mProperties[%i].mDataLength is " 1.698 + "too small to contain a string (%i, needed: %i)", 1.699 + i,prop->mDataLength,sizeof(aiString)); 1.700 + } 1.701 + if(prop->mData[prop->mDataLength-1]) { 1.702 + ReportError("Missing null-terminator in string material property"); 1.703 + } 1.704 + // Validate((const aiString*)prop->mData); 1.705 + } 1.706 + else if (aiPTI_Float == prop->mType) { 1.707 + if (prop->mDataLength < sizeof(float)) { 1.708 + ReportError("aiMaterial::mProperties[%i].mDataLength is " 1.709 + "too small to contain a float (%i, needed: %i)", 1.710 + i,prop->mDataLength,sizeof(float)); 1.711 + } 1.712 + } 1.713 + else if (aiPTI_Integer == prop->mType) { 1.714 + if (prop->mDataLength < sizeof(int)) { 1.715 + ReportError("aiMaterial::mProperties[%i].mDataLength is " 1.716 + "too small to contain an integer (%i, needed: %i)", 1.717 + i,prop->mDataLength,sizeof(int)); 1.718 + } 1.719 + } 1.720 + // TODO: check whether there is a key with an unknown name ... 1.721 + } 1.722 + 1.723 + // make some more specific tests 1.724 + float fTemp; 1.725 + int iShading; 1.726 + if (AI_SUCCESS == aiGetMaterialInteger( pMaterial,AI_MATKEY_SHADING_MODEL,&iShading)) { 1.727 + switch ((aiShadingMode)iShading) 1.728 + { 1.729 + case aiShadingMode_Blinn: 1.730 + case aiShadingMode_CookTorrance: 1.731 + case aiShadingMode_Phong: 1.732 + 1.733 + if (AI_SUCCESS != aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS,&fTemp)) { 1.734 + ReportWarning("A specular shading model is specified but there is no " 1.735 + "AI_MATKEY_SHININESS key"); 1.736 + } 1.737 + if (AI_SUCCESS == aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS_STRENGTH,&fTemp) && !fTemp) { 1.738 + ReportWarning("A specular shading model is specified but the value of the " 1.739 + "AI_MATKEY_SHININESS_STRENGTH key is 0.0"); 1.740 + } 1.741 + break; 1.742 + default: ; 1.743 + }; 1.744 + } 1.745 + 1.746 + if (AI_SUCCESS == aiGetMaterialFloat( pMaterial,AI_MATKEY_OPACITY,&fTemp) && (!fTemp || fTemp > 1.01f)) { 1.747 + ReportWarning("Invalid opacity value (must be 0 < opacity < 1.0)"); 1.748 + } 1.749 + 1.750 + // Check whether there are invalid texture keys 1.751 + // TODO: that's a relict of the past, where texture type and index were baked 1.752 + // into the material string ... we could do that in one single pass. 1.753 + SearchForInvalidTextures(pMaterial,aiTextureType_DIFFUSE); 1.754 + SearchForInvalidTextures(pMaterial,aiTextureType_SPECULAR); 1.755 + SearchForInvalidTextures(pMaterial,aiTextureType_AMBIENT); 1.756 + SearchForInvalidTextures(pMaterial,aiTextureType_EMISSIVE); 1.757 + SearchForInvalidTextures(pMaterial,aiTextureType_OPACITY); 1.758 + SearchForInvalidTextures(pMaterial,aiTextureType_SHININESS); 1.759 + SearchForInvalidTextures(pMaterial,aiTextureType_HEIGHT); 1.760 + SearchForInvalidTextures(pMaterial,aiTextureType_NORMALS); 1.761 + SearchForInvalidTextures(pMaterial,aiTextureType_DISPLACEMENT); 1.762 + SearchForInvalidTextures(pMaterial,aiTextureType_LIGHTMAP); 1.763 + SearchForInvalidTextures(pMaterial,aiTextureType_REFLECTION); 1.764 +} 1.765 + 1.766 +// ------------------------------------------------------------------------------------------------ 1.767 +void ValidateDSProcess::Validate( const aiTexture* pTexture) 1.768 +{ 1.769 + // the data section may NEVER be NULL 1.770 + if (!pTexture->pcData) { 1.771 + ReportError("aiTexture::pcData is NULL"); 1.772 + } 1.773 + if (pTexture->mHeight) 1.774 + { 1.775 + if (!pTexture->mWidth)ReportError("aiTexture::mWidth is zero " 1.776 + "(aiTexture::mHeight is %i, uncompressed texture)",pTexture->mHeight); 1.777 + } 1.778 + else 1.779 + { 1.780 + if (!pTexture->mWidth) { 1.781 + ReportError("aiTexture::mWidth is zero (compressed texture)"); 1.782 + } 1.783 + if ('\0' != pTexture->achFormatHint[3]) { 1.784 + ReportWarning("aiTexture::achFormatHint must be zero-terminated"); 1.785 + } 1.786 + else if ('.' == pTexture->achFormatHint[0]) { 1.787 + ReportWarning("aiTexture::achFormatHint should contain a file extension " 1.788 + "without a leading dot (format hint: %s).",pTexture->achFormatHint); 1.789 + } 1.790 + } 1.791 + 1.792 + const char* sz = pTexture->achFormatHint; 1.793 + if ((sz[0] >= 'A' && sz[0] <= 'Z') || 1.794 + (sz[1] >= 'A' && sz[1] <= 'Z') || 1.795 + (sz[2] >= 'A' && sz[2] <= 'Z') || 1.796 + (sz[3] >= 'A' && sz[3] <= 'Z')) { 1.797 + ReportError("aiTexture::achFormatHint contains non-lowercase letters"); 1.798 + } 1.799 +} 1.800 + 1.801 +// ------------------------------------------------------------------------------------------------ 1.802 +void ValidateDSProcess::Validate( const aiAnimation* pAnimation, 1.803 + const aiNodeAnim* pNodeAnim) 1.804 +{ 1.805 + Validate(&pNodeAnim->mNodeName); 1.806 + 1.807 + if (!pNodeAnim->mNumPositionKeys && !pNodeAnim->mScalingKeys && !pNodeAnim->mNumRotationKeys) 1.808 + ReportError("Empty node animation channel"); 1.809 + 1.810 + // otherwise check whether one of the keys exceeds the total duration of the animation 1.811 + if (pNodeAnim->mNumPositionKeys) 1.812 + { 1.813 + if (!pNodeAnim->mPositionKeys) 1.814 + { 1.815 + this->ReportError("aiNodeAnim::mPositionKeys is NULL (aiNodeAnim::mNumPositionKeys is %i)", 1.816 + pNodeAnim->mNumPositionKeys); 1.817 + } 1.818 + double dLast = -10e10; 1.819 + for (unsigned int i = 0; i < pNodeAnim->mNumPositionKeys;++i) 1.820 + { 1.821 + // ScenePreprocessor will compute the duration if still the default value 1.822 + // (Aramis) Add small epsilon, comparison tended to fail if max_time == duration, 1.823 + // seems to be due the compilers register usage/width. 1.824 + if (pAnimation->mDuration > 0. && pNodeAnim->mPositionKeys[i].mTime > pAnimation->mDuration+0.001) 1.825 + { 1.826 + ReportError("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is larger " 1.827 + "than aiAnimation::mDuration (which is %.5f)",i, 1.828 + (float)pNodeAnim->mPositionKeys[i].mTime, 1.829 + (float)pAnimation->mDuration); 1.830 + } 1.831 + if (i && pNodeAnim->mPositionKeys[i].mTime <= dLast) 1.832 + { 1.833 + ReportWarning("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is smaller " 1.834 + "than aiAnimation::mPositionKeys[%i] (which is %.5f)",i, 1.835 + (float)pNodeAnim->mPositionKeys[i].mTime, 1.836 + i-1, (float)dLast); 1.837 + } 1.838 + dLast = pNodeAnim->mPositionKeys[i].mTime; 1.839 + } 1.840 + } 1.841 + // rotation keys 1.842 + if (pNodeAnim->mNumRotationKeys) 1.843 + { 1.844 + if (!pNodeAnim->mRotationKeys) 1.845 + { 1.846 + this->ReportError("aiNodeAnim::mRotationKeys is NULL (aiNodeAnim::mNumRotationKeys is %i)", 1.847 + pNodeAnim->mNumRotationKeys); 1.848 + } 1.849 + double dLast = -10e10; 1.850 + for (unsigned int i = 0; i < pNodeAnim->mNumRotationKeys;++i) 1.851 + { 1.852 + if (pAnimation->mDuration > 0. && pNodeAnim->mRotationKeys[i].mTime > pAnimation->mDuration+0.001) 1.853 + { 1.854 + ReportError("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is larger " 1.855 + "than aiAnimation::mDuration (which is %.5f)",i, 1.856 + (float)pNodeAnim->mRotationKeys[i].mTime, 1.857 + (float)pAnimation->mDuration); 1.858 + } 1.859 + if (i && pNodeAnim->mRotationKeys[i].mTime <= dLast) 1.860 + { 1.861 + ReportWarning("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is smaller " 1.862 + "than aiAnimation::mRotationKeys[%i] (which is %.5f)",i, 1.863 + (float)pNodeAnim->mRotationKeys[i].mTime, 1.864 + i-1, (float)dLast); 1.865 + } 1.866 + dLast = pNodeAnim->mRotationKeys[i].mTime; 1.867 + } 1.868 + } 1.869 + // scaling keys 1.870 + if (pNodeAnim->mNumScalingKeys) 1.871 + { 1.872 + if (!pNodeAnim->mScalingKeys) { 1.873 + ReportError("aiNodeAnim::mScalingKeys is NULL (aiNodeAnim::mNumScalingKeys is %i)", 1.874 + pNodeAnim->mNumScalingKeys); 1.875 + } 1.876 + double dLast = -10e10; 1.877 + for (unsigned int i = 0; i < pNodeAnim->mNumScalingKeys;++i) 1.878 + { 1.879 + if (pAnimation->mDuration > 0. && pNodeAnim->mScalingKeys[i].mTime > pAnimation->mDuration+0.001) 1.880 + { 1.881 + ReportError("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is larger " 1.882 + "than aiAnimation::mDuration (which is %.5f)",i, 1.883 + (float)pNodeAnim->mScalingKeys[i].mTime, 1.884 + (float)pAnimation->mDuration); 1.885 + } 1.886 + if (i && pNodeAnim->mScalingKeys[i].mTime <= dLast) 1.887 + { 1.888 + ReportWarning("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is smaller " 1.889 + "than aiAnimation::mScalingKeys[%i] (which is %.5f)",i, 1.890 + (float)pNodeAnim->mScalingKeys[i].mTime, 1.891 + i-1, (float)dLast); 1.892 + } 1.893 + dLast = pNodeAnim->mScalingKeys[i].mTime; 1.894 + } 1.895 + } 1.896 + 1.897 + if (!pNodeAnim->mNumScalingKeys && !pNodeAnim->mNumRotationKeys && 1.898 + !pNodeAnim->mNumPositionKeys) 1.899 + { 1.900 + ReportError("A node animation channel must have at least one subtrack"); 1.901 + } 1.902 +} 1.903 + 1.904 +// ------------------------------------------------------------------------------------------------ 1.905 +void ValidateDSProcess::Validate( const aiNode* pNode) 1.906 +{ 1.907 + if (!pNode)ReportError("A node of the scenegraph is NULL"); 1.908 + if (pNode != mScene->mRootNode && !pNode->mParent) 1.909 + this->ReportError("A node has no valid parent (aiNode::mParent is NULL)"); 1.910 + 1.911 + this->Validate(&pNode->mName); 1.912 + 1.913 + // validate all meshes 1.914 + if (pNode->mNumMeshes) 1.915 + { 1.916 + if (!pNode->mMeshes) 1.917 + { 1.918 + ReportError("aiNode::mMeshes is NULL (aiNode::mNumMeshes is %i)", 1.919 + pNode->mNumMeshes); 1.920 + } 1.921 + std::vector<bool> abHadMesh; 1.922 + abHadMesh.resize(mScene->mNumMeshes,false); 1.923 + for (unsigned int i = 0; i < pNode->mNumMeshes;++i) 1.924 + { 1.925 + if (pNode->mMeshes[i] >= mScene->mNumMeshes) 1.926 + { 1.927 + ReportError("aiNode::mMeshes[%i] is out of range (maximum is %i)", 1.928 + pNode->mMeshes[i],mScene->mNumMeshes-1); 1.929 + } 1.930 + if (abHadMesh[pNode->mMeshes[i]]) 1.931 + { 1.932 + ReportError("aiNode::mMeshes[%i] is already referenced by this node (value: %i)", 1.933 + i,pNode->mMeshes[i]); 1.934 + } 1.935 + abHadMesh[pNode->mMeshes[i]] = true; 1.936 + } 1.937 + } 1.938 + if (pNode->mNumChildren) 1.939 + { 1.940 + if (!pNode->mChildren) { 1.941 + ReportError("aiNode::mChildren is NULL (aiNode::mNumChildren is %i)", 1.942 + pNode->mNumChildren); 1.943 + } 1.944 + for (unsigned int i = 0; i < pNode->mNumChildren;++i) { 1.945 + Validate(pNode->mChildren[i]); 1.946 + } 1.947 + } 1.948 +} 1.949 + 1.950 +// ------------------------------------------------------------------------------------------------ 1.951 +void ValidateDSProcess::Validate( const aiString* pString) 1.952 +{ 1.953 + if (pString->length > MAXLEN) 1.954 + { 1.955 + this->ReportError("aiString::length is too large (%i, maximum is %i)", 1.956 + pString->length,MAXLEN); 1.957 + } 1.958 + const char* sz = pString->data; 1.959 + while (true) 1.960 + { 1.961 + if ('\0' == *sz) 1.962 + { 1.963 + if (pString->length != (unsigned int)(sz-pString->data)) 1.964 + ReportError("aiString::data is invalid: the terminal zero is at a wrong offset"); 1.965 + break; 1.966 + } 1.967 + else if (sz >= &pString->data[MAXLEN]) 1.968 + ReportError("aiString::data is invalid. There is no terminal character"); 1.969 + ++sz; 1.970 + } 1.971 +}