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 +}