#include "ValidateDataStructure.h"
#include "BaseImporter.h"
#include "fast_atof.h"
#include "ProcessHelper.h"
#include <memory>
#include <stdarg.h>
using namespace Assimp;
ValidateDSProcess::ValidateDSProcess() :
mScene()
{}
ValidateDSProcess::~ValidateDSProcess()
{}
bool ValidateDSProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_ValidateDataStructure) != 0;
}
AI_WONT_RETURN void ValidateDSProcess::ReportError(const char* msg,...)
{
ai_assert(NULL != msg);
va_list args;
va_start(args,msg);
char szBuffer[3000];
const int iLen = vsprintf(szBuffer,msg,args);
ai_assert(iLen > 0);
va_end(args);
#ifdef ASSIMP_BUILD_DEBUG
ai_assert( false );
#endif
throw DeadlyImportError("Validation failed: " + std::string(szBuffer,iLen));
}
void ValidateDSProcess::ReportWarning(const char* msg,...)
{
ai_assert(NULL != msg);
va_list args;
va_start(args,msg);
char szBuffer[3000];
const int iLen = vsprintf(szBuffer,msg,args);
ai_assert(iLen > 0);
va_end(args);
DefaultLogger::get()->warn("Validation warning: " + std::string(szBuffer,iLen));
}
inline int HasNameMatch(const aiString& in, aiNode* node)
{
int result = (node->mName == in ? 1 : 0 );
for (unsigned int i = 0; i < node->mNumChildren;++i) {
result += HasNameMatch(in,node->mChildren[i]);
}
return result;
}
template <typename T>
inline void ValidateDSProcess::DoValidation(T** parray, unsigned int size,
const char* firstName, const char* secondName)
{
if (size)
{
if (!parray)
{
ReportError("aiScene::%s is NULL (aiScene::%s is %i)",
firstName, secondName, size);
}
for (unsigned int i = 0; i < size;++i)
{
if (!parray[i])
{
ReportError("aiScene::%s[%i] is NULL (aiScene::%s is %i)",
firstName,i,secondName,size);
}
Validate(parray[i]);
}
}
}
template <typename T>
inline void ValidateDSProcess::DoValidationEx(T** parray, unsigned int size,
const char* firstName, const char* secondName)
{
if (size)
{
if (!parray) {
ReportError("aiScene::%s is NULL (aiScene::%s is %i)",
firstName, secondName, size);
}
for (unsigned int i = 0; i < size;++i)
{
if (!parray[i])
{
ReportError("aiScene::%s[%i] is NULL (aiScene::%s is %i)",
firstName,i,secondName,size);
}
Validate(parray[i]);
for (unsigned int a = i+1; a < size;++a)
{
if (parray[i]->mName == parray[a]->mName)
{
this->ReportError("aiScene::%s[%i] has the same name as "
"aiScene::%s[%i]",firstName, i,secondName, a);
}
}
}
}
}
template <typename T>
inline void ValidateDSProcess::DoValidationWithNameCheck(T** array,
unsigned int size, const char* firstName,
const char* secondName)
{
DoValidationEx(array,size,firstName,secondName);
for (unsigned int i = 0; i < size;++i)
{
int res = HasNameMatch(array[i]->mName,mScene->mRootNode);
if (!res) {
ReportError("aiScene::%s[%i] has no corresponding node in the scene graph (%s)",
firstName,i,array[i]->mName.data);
}
else if (1 != res) {
ReportError("aiScene::%s[%i]: there are more than one nodes with %s as name",
firstName,i,array[i]->mName.data);
}
}
}
void ValidateDSProcess::Execute( aiScene* pScene)
{
this->mScene = pScene;
DefaultLogger::get()->debug("ValidateDataStructureProcess begin");
Validate(pScene->mRootNode);
if (pScene->mNumMeshes) {
DoValidation(pScene->mMeshes,pScene->mNumMeshes,"mMeshes","mNumMeshes");
}
else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) {
ReportError("aiScene::mNumMeshes is 0. At least one mesh must be there");
}
else if (pScene->mMeshes) {
ReportError("aiScene::mMeshes is non-null although there are no meshes");
}
if (pScene->mNumAnimations) {
DoValidation(pScene->mAnimations,pScene->mNumAnimations,
"mAnimations","mNumAnimations");
}
else if (pScene->mAnimations) {
ReportError("aiScene::mAnimations is non-null although there are no animations");
}
if (pScene->mNumCameras) {
DoValidationWithNameCheck(pScene->mCameras,pScene->mNumCameras,
"mCameras","mNumCameras");
}
else if (pScene->mCameras) {
ReportError("aiScene::mCameras is non-null although there are no cameras");
}
if (pScene->mNumLights) {
DoValidationWithNameCheck(pScene->mLights,pScene->mNumLights,
"mLights","mNumLights");
}
else if (pScene->mLights) {
ReportError("aiScene::mLights is non-null although there are no lights");
}
if (pScene->mNumTextures) {
DoValidation(pScene->mTextures,pScene->mNumTextures,
"mTextures","mNumTextures");
}
else if (pScene->mTextures) {
ReportError("aiScene::mTextures is non-null although there are no textures");
}
if (pScene->mNumMaterials) {
DoValidation(pScene->mMaterials,pScene->mNumMaterials,"mMaterials","mNumMaterials");
}
#if 0#endif
else if (pScene->mMaterials) {
ReportError("aiScene::mMaterials is non-null although there are no materials");
}
DefaultLogger::get()->debug("ValidateDataStructureProcess end");
}
void ValidateDSProcess::Validate( const aiLight* pLight)
{
if (pLight->mType == aiLightSource_UNDEFINED)
ReportWarning("aiLight::mType is aiLightSource_UNDEFINED");
if (!pLight->mAttenuationConstant &&
!pLight->mAttenuationLinear &&
!pLight->mAttenuationQuadratic) {
ReportWarning("aiLight::mAttenuationXXX - all are zero");
}
if (pLight->mAngleInnerCone > pLight->mAngleOuterCone)
ReportError("aiLight::mAngleInnerCone is larger than aiLight::mAngleOuterCone");
if (pLight->mColorDiffuse.IsBlack() && pLight->mColorAmbient.IsBlack()
&& pLight->mColorSpecular.IsBlack())
{
ReportWarning("aiLight::mColorXXX - all are black and won't have any influence");
}
}
void ValidateDSProcess::Validate( const aiCamera* pCamera)
{
if (pCamera->mClipPlaneFar <= pCamera->mClipPlaneNear)
ReportError("aiCamera::mClipPlaneFar must be >= aiCamera::mClipPlaneNear");
if (!pCamera->mHorizontalFOV || pCamera->mHorizontalFOV >= (float)AI_MATH_PI)
ReportWarning("%f is not a valid value for aiCamera::mHorizontalFOV",pCamera->mHorizontalFOV);
}
void ValidateDSProcess::Validate( const aiMesh* pMesh)
{
if (mScene->mNumMaterials && pMesh->mMaterialIndex >= mScene->mNumMaterials)
{
ReportError("aiMesh::mMaterialIndex is invalid (value: %i maximum: %i)",
pMesh->mMaterialIndex,mScene->mNumMaterials-1);
}
Validate(&pMesh->mName);
for (unsigned int i = 0; i < pMesh->mNumFaces; ++i)
{
aiFace& face = pMesh->mFaces[i];
if (pMesh->mPrimitiveTypes)
{
switch (face.mNumIndices)
{
case 0:
ReportError("aiMesh::mFaces[%i].mNumIndices is 0",i);
case 1:
if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POINT))
{
ReportError("aiMesh::mFaces[%i] is a POINT but aiMesh::mPrimtiveTypes "
"does not report the POINT flag",i);
}
break;
case 2:
if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_LINE))
{
ReportError("aiMesh::mFaces[%i] is a LINE but aiMesh::mPrimtiveTypes "
"does not report the LINE flag",i);
}
break;
case 3:
if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE))
{
ReportError("aiMesh::mFaces[%i] is a TRIANGLE but aiMesh::mPrimtiveTypes "
"does not report the TRIANGLE flag",i);
}
break;
default:
if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON))
{
this->ReportError("aiMesh::mFaces[%i] is a POLYGON but aiMesh::mPrimtiveTypes "
"does not report the POLYGON flag",i);
}
break;
};
}
if (!face.mIndices)
ReportError("aiMesh::mFaces[%i].mIndices is NULL",i);
}
if (!pMesh->mNumVertices || (!pMesh->mVertices && !mScene->mFlags)) {
ReportError("The mesh contains no vertices");
}
if (pMesh->mNumVertices > AI_MAX_VERTICES) {
ReportError("Mesh has too many vertices: %u, but the limit is %u",pMesh->mNumVertices,AI_MAX_VERTICES);
}
if (pMesh->mNumFaces > AI_MAX_FACES) {
ReportError("Mesh has too many faces: %u, but the limit is %u",pMesh->mNumFaces,AI_MAX_FACES);
}
if ((pMesh->mTangents != NULL) != (pMesh->mBitangents != NULL)) {
ReportError("If there are tangents, bitangent vectors must be present as well");
}
if (!pMesh->mNumFaces || (!pMesh->mFaces && !mScene->mFlags)) {
ReportError("Mesh contains no faces");
}
std::vector<bool> abRefList;
abRefList.resize(pMesh->mNumVertices,false);
for (unsigned int i = 0; i < pMesh->mNumFaces;++i)
{
aiFace& face = pMesh->mFaces[i];
if (face.mNumIndices > AI_MAX_FACE_INDICES) {
ReportError("Face %u has too many faces: %u, but the limit is %u",i,face.mNumIndices,AI_MAX_FACE_INDICES);
}
for (unsigned int a = 0; a < face.mNumIndices;++a)
{
if (face.mIndices[a] >= pMesh->mNumVertices) {
ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range",i,a);
}
abRefList[face.mIndices[a]] = true;
}
}
bool b = false;
for (unsigned int i = 0; i < pMesh->mNumVertices;++i) {
if (!abRefList[i])b = true;
}
abRefList.clear();
if (b)ReportWarning("There are unreferenced vertices");
{
unsigned int i = 0;
for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
{
if (!pMesh->HasTextureCoords(i))break;
}
for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
if (pMesh->HasTextureCoords(i))
{
ReportError("Texture coordinate channel %i exists "
"although the previous channel was NULL.",i);
}
}
{
unsigned int i = 0;
for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i)
{
if (!pMesh->HasVertexColors(i))break;
}
for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i)
if (pMesh->HasVertexColors(i))
{
ReportError("Vertex color channel %i is exists "
"although the previous channel was NULL.",i);
}
}
if (pMesh->mNumBones)
{
if (!pMesh->mBones)
{
ReportError("aiMesh::mBones is NULL (aiMesh::mNumBones is %i)",
pMesh->mNumBones);
}
std::unique_ptr<float[]> afSum(nullptr);
if (pMesh->mNumVertices)
{
afSum.reset(new float[pMesh->mNumVertices]);
for (unsigned int i = 0; i < pMesh->mNumVertices;++i)
afSum[i] = 0.0f;
}
for (unsigned int i = 0; i < pMesh->mNumBones;++i)
{
const aiBone* bone = pMesh->mBones[i];
if (bone->mNumWeights > AI_MAX_BONE_WEIGHTS) {
ReportError("Bone %u has too many weights: %u, but the limit is %u",i,bone->mNumWeights,AI_MAX_BONE_WEIGHTS);
}
if (!pMesh->mBones[i])
{
ReportError("aiMesh::mBones[%i] is NULL (aiMesh::mNumBones is %i)",
i,pMesh->mNumBones);
}
Validate(pMesh,pMesh->mBones[i],afSum.get());
for (unsigned int a = i+1; a < pMesh->mNumBones;++a)
{
if (pMesh->mBones[i]->mName == pMesh->mBones[a]->mName)
{
ReportError("aiMesh::mBones[%i] has the same name as "
"aiMesh::mBones[%i]",i,a);
}
}
}
for (unsigned int i = 0; i < pMesh->mNumVertices;++i)
{
if (afSum[i] && (afSum[i] <= 0.94 || afSum[i] >= 1.05)) {
ReportWarning("aiMesh::mVertices[%i]: bone weight sum != 1.0 (sum is %f)",i,afSum[i]);
}
}
}
else if (pMesh->mBones)
{
ReportError("aiMesh::mBones is non-null although there are no bones");
}
}
void ValidateDSProcess::Validate( const aiMesh* pMesh,
const aiBone* pBone,float* afSum)
{
this->Validate(&pBone->mName);
if (!pBone->mNumWeights) {
ReportError("aiBone::mNumWeights is zero");
}
for (unsigned int i = 0; i < pBone->mNumWeights;++i)
{
if (pBone->mWeights[i].mVertexId >= pMesh->mNumVertices) {
ReportError("aiBone::mWeights[%i].mVertexId is out of range",i);
}
else if (!pBone->mWeights[i].mWeight || pBone->mWeights[i].mWeight > 1.0f) {
ReportWarning("aiBone::mWeights[%i].mWeight has an invalid value",i);
}
afSum[pBone->mWeights[i].mVertexId] += pBone->mWeights[i].mWeight;
}
}
void ValidateDSProcess::Validate( const aiAnimation* pAnimation)
{
Validate(&pAnimation->mName);
if (pAnimation->mNumChannels)
{
if (!pAnimation->mChannels) {
ReportError("aiAnimation::mChannels is NULL (aiAnimation::mNumChannels is %i)",
pAnimation->mNumChannels);
}
for (unsigned int i = 0; i < pAnimation->mNumChannels;++i)
{
if (!pAnimation->mChannels[i])
{
ReportError("aiAnimation::mChannels[%i] is NULL (aiAnimation::mNumChannels is %i)",
i, pAnimation->mNumChannels);
}
Validate(pAnimation, pAnimation->mChannels[i]);
}
}
else ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there.");
}
void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial,
aiTextureType type)
{
const char* szType = TextureTypeToString(type);
int iNumIndices = 0;
int iIndex = -1;
for (unsigned int i = 0; i < pMaterial->mNumProperties;++i)
{
aiMaterialProperty* prop = pMaterial->mProperties[i];
if (!::strcmp(prop->mKey.data,"$tex.file") && prop->mSemantic == type) {
iIndex = std::max(iIndex, (int) prop->mIndex);
++iNumIndices;
if (aiPTI_String != prop->mType)
ReportError("Material property %s is expected to be a string",prop->mKey.data);
}
}
if (iIndex +1 != iNumIndices) {
ReportError("%s #%i is set, but there are only %i %s textures",
szType,iIndex,iNumIndices,szType);
}
if (!iNumIndices)return;
std::vector<aiTextureMapping> mappings(iNumIndices);
bool bNoSpecified = true;
for (unsigned int i = 0; i < pMaterial->mNumProperties;++i)
{
aiMaterialProperty* prop = pMaterial->mProperties[i];
if (prop->mSemantic != type)continue;
if ((int)prop->mIndex >= iNumIndices)
{
ReportError("Found texture property with index %i, although there "
"are only %i textures of type %s",
prop->mIndex, iNumIndices, szType);
}
if (!::strcmp(prop->mKey.data,"$tex.mapping")) {
if (aiPTI_Integer != prop->mType || prop->mDataLength < sizeof(aiTextureMapping))
{
ReportError("Material property %s%i is expected to be an integer (size is %i)",
prop->mKey.data,prop->mIndex,prop->mDataLength);
}
mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData);
}
else if (!::strcmp(prop->mKey.data,"$tex.uvtrafo")) {
if (aiPTI_Float != prop->mType || prop->mDataLength < sizeof(aiUVTransform))
{
ReportError("Material property %s%i is expected to be 5 floats large (size is %i)",
prop->mKey.data,prop->mIndex, prop->mDataLength);
}
mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData);
}
else if (!::strcmp(prop->mKey.data,"$tex.uvwsrc")) {
if (aiPTI_Integer != prop->mType || sizeof(int) > prop->mDataLength)
{
ReportError("Material property %s%i is expected to be an integer (size is %i)",
prop->mKey.data,prop->mIndex,prop->mDataLength);
}
bNoSpecified = false;
iIndex = *((unsigned int*)prop->mData);
for (unsigned int a = 0; a < mScene->mNumMeshes;++a)
{
aiMesh* mesh = this->mScene->mMeshes[a];
if(mesh->mMaterialIndex == (unsigned int)i)
{
int iChannels = 0;
while (mesh->HasTextureCoords(iChannels))++iChannels;
if (iIndex >= iChannels)
{
ReportWarning("Invalid UV index: %i (key %s). Mesh %i has only %i UV channels",
iIndex,prop->mKey.data,a,iChannels);
}
}
}
}
}
if (bNoSpecified)
{
for (unsigned int a = 0; a < mScene->mNumMeshes;++a)
{
aiMesh* mesh = mScene->mMeshes[a];
if(mesh->mMaterialIndex == (unsigned int)iIndex && mappings[0] == aiTextureMapping_UV)
{
if (!mesh->mTextureCoords[0])
{
ReportWarning("UV-mapped texture, but there are no UV coords");
}
}
}
}
}
void ValidateDSProcess::Validate( const aiMaterial* pMaterial)
{
for (unsigned int i = 0; i < pMaterial->mNumProperties;++i)
{
const aiMaterialProperty* prop = pMaterial->mProperties[i];
if (!prop) {
ReportError("aiMaterial::mProperties[%i] is NULL (aiMaterial::mNumProperties is %i)",
i,pMaterial->mNumProperties);
}
if (!prop->mDataLength || !prop->mData) {
ReportError("aiMaterial::mProperties[%i].mDataLength or "
"aiMaterial::mProperties[%i].mData is 0",i,i);
}
if (aiPTI_String == prop->mType) {
if (prop->mDataLength < 5 || prop->mDataLength < 4 + (*reinterpret_cast<uint32_t*>(prop->mData)) + 1) {
ReportError("aiMaterial::mProperties[%i].mDataLength is "
"too small to contain a string (%i, needed: %i)",
i,prop->mDataLength,sizeof(aiString));
}
if(prop->mData[prop->mDataLength-1]) {
ReportError("Missing null-terminator in string material property");
}
}
else if (aiPTI_Float == prop->mType) {
if (prop->mDataLength < sizeof(float)) {
ReportError("aiMaterial::mProperties[%i].mDataLength is "
"too small to contain a float (%i, needed: %i)",
i,prop->mDataLength,sizeof(float));
}
}
else if (aiPTI_Integer == prop->mType) {
if (prop->mDataLength < sizeof(int)) {
ReportError("aiMaterial::mProperties[%i].mDataLength is "
"too small to contain an integer (%i, needed: %i)",
i,prop->mDataLength,sizeof(int));
}
}
}
ai_real fTemp;
int iShading;
if (AI_SUCCESS == aiGetMaterialInteger( pMaterial,AI_MATKEY_SHADING_MODEL,&iShading)) {
switch ((aiShadingMode)iShading)
{
case aiShadingMode_Blinn:
case aiShadingMode_CookTorrance:
case aiShadingMode_Phong:
if (AI_SUCCESS != aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS,&fTemp)) {
ReportWarning("A specular shading model is specified but there is no "
"AI_MATKEY_SHININESS key");
}
if (AI_SUCCESS == aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS_STRENGTH,&fTemp) && !fTemp) {
ReportWarning("A specular shading model is specified but the value of the "
"AI_MATKEY_SHININESS_STRENGTH key is 0.0");
}
break;
default: ;
};
}
if (AI_SUCCESS == aiGetMaterialFloat( pMaterial,AI_MATKEY_OPACITY,&fTemp) && (!fTemp || fTemp > 1.01)) {
ReportWarning("Invalid opacity value (must be 0 < opacity < 1.0)");
}
SearchForInvalidTextures(pMaterial,aiTextureType_DIFFUSE);
SearchForInvalidTextures(pMaterial,aiTextureType_SPECULAR);
SearchForInvalidTextures(pMaterial,aiTextureType_AMBIENT);
SearchForInvalidTextures(pMaterial,aiTextureType_EMISSIVE);
SearchForInvalidTextures(pMaterial,aiTextureType_OPACITY);
SearchForInvalidTextures(pMaterial,aiTextureType_SHININESS);
SearchForInvalidTextures(pMaterial,aiTextureType_HEIGHT);
SearchForInvalidTextures(pMaterial,aiTextureType_NORMALS);
SearchForInvalidTextures(pMaterial,aiTextureType_DISPLACEMENT);
SearchForInvalidTextures(pMaterial,aiTextureType_LIGHTMAP);
SearchForInvalidTextures(pMaterial,aiTextureType_REFLECTION);
}
void ValidateDSProcess::Validate( const aiTexture* pTexture)
{
if (!pTexture->pcData) {
ReportError("aiTexture::pcData is NULL");
}
if (pTexture->mHeight)
{
if (!pTexture->mWidth)ReportError("aiTexture::mWidth is zero "
"(aiTexture::mHeight is %i, uncompressed texture)",pTexture->mHeight);
}
else
{
if (!pTexture->mWidth) {
ReportError("aiTexture::mWidth is zero (compressed texture)");
}
if ('\0' != pTexture->achFormatHint[3]) {
ReportWarning("aiTexture::achFormatHint must be zero-terminated");
}
else if ('.' == pTexture->achFormatHint[0]) {
ReportWarning("aiTexture::achFormatHint should contain a file extension "
"without a leading dot (format hint: %s).",pTexture->achFormatHint);
}
}
const char* sz = pTexture->achFormatHint;
if ((sz[0] >= 'A' && sz[0] <= 'Z') ||
(sz[1] >= 'A' && sz[1] <= 'Z') ||
(sz[2] >= 'A' && sz[2] <= 'Z') ||
(sz[3] >= 'A' && sz[3] <= 'Z')) {
ReportError("aiTexture::achFormatHint contains non-lowercase letters");
}
}
void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
const aiNodeAnim* pNodeAnim)
{
Validate(&pNodeAnim->mNodeName);
if (!pNodeAnim->mNumPositionKeys && !pNodeAnim->mScalingKeys && !pNodeAnim->mNumRotationKeys)
ReportError("Empty node animation channel");
if (pNodeAnim->mNumPositionKeys)
{
if (!pNodeAnim->mPositionKeys)
{
this->ReportError("aiNodeAnim::mPositionKeys is NULL (aiNodeAnim::mNumPositionKeys is %i)",
pNodeAnim->mNumPositionKeys);
}
double dLast = -10e10;
for (unsigned int i = 0; i < pNodeAnim->mNumPositionKeys;++i)
{
if (pAnimation->mDuration > 0. && pNodeAnim->mPositionKeys[i].mTime > pAnimation->mDuration+0.001)
{
ReportError("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is larger "
"than aiAnimation::mDuration (which is %.5f)",i,
(float)pNodeAnim->mPositionKeys[i].mTime,
(float)pAnimation->mDuration);
}
if (i && pNodeAnim->mPositionKeys[i].mTime <= dLast)
{
ReportWarning("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is smaller "
"than aiAnimation::mPositionKeys[%i] (which is %.5f)",i,
(float)pNodeAnim->mPositionKeys[i].mTime,
i-1, (float)dLast);
}
dLast = pNodeAnim->mPositionKeys[i].mTime;
}
}
if (pNodeAnim->mNumRotationKeys)
{
if (!pNodeAnim->mRotationKeys)
{
this->ReportError("aiNodeAnim::mRotationKeys is NULL (aiNodeAnim::mNumRotationKeys is %i)",
pNodeAnim->mNumRotationKeys);
}
double dLast = -10e10;
for (unsigned int i = 0; i < pNodeAnim->mNumRotationKeys;++i)
{
if (pAnimation->mDuration > 0. && pNodeAnim->mRotationKeys[i].mTime > pAnimation->mDuration+0.001)
{
ReportError("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is larger "
"than aiAnimation::mDuration (which is %.5f)",i,
(float)pNodeAnim->mRotationKeys[i].mTime,
(float)pAnimation->mDuration);
}
if (i && pNodeAnim->mRotationKeys[i].mTime <= dLast)
{
ReportWarning("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is smaller "
"than aiAnimation::mRotationKeys[%i] (which is %.5f)",i,
(float)pNodeAnim->mRotationKeys[i].mTime,
i-1, (float)dLast);
}
dLast = pNodeAnim->mRotationKeys[i].mTime;
}
}
if (pNodeAnim->mNumScalingKeys)
{
if (!pNodeAnim->mScalingKeys) {
ReportError("aiNodeAnim::mScalingKeys is NULL (aiNodeAnim::mNumScalingKeys is %i)",
pNodeAnim->mNumScalingKeys);
}
double dLast = -10e10;
for (unsigned int i = 0; i < pNodeAnim->mNumScalingKeys;++i)
{
if (pAnimation->mDuration > 0. && pNodeAnim->mScalingKeys[i].mTime > pAnimation->mDuration+0.001)
{
ReportError("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is larger "
"than aiAnimation::mDuration (which is %.5f)",i,
(float)pNodeAnim->mScalingKeys[i].mTime,
(float)pAnimation->mDuration);
}
if (i && pNodeAnim->mScalingKeys[i].mTime <= dLast)
{
ReportWarning("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is smaller "
"than aiAnimation::mScalingKeys[%i] (which is %.5f)",i,
(float)pNodeAnim->mScalingKeys[i].mTime,
i-1, (float)dLast);
}
dLast = pNodeAnim->mScalingKeys[i].mTime;
}
}
if (!pNodeAnim->mNumScalingKeys && !pNodeAnim->mNumRotationKeys &&
!pNodeAnim->mNumPositionKeys)
{
ReportError("A node animation channel must have at least one subtrack");
}
}
void ValidateDSProcess::Validate( const aiNode* pNode)
{
if (!pNode)ReportError("A node of the scenegraph is NULL");
if (pNode != mScene->mRootNode && !pNode->mParent)
this->ReportError("A node has no valid parent (aiNode::mParent is NULL)");
this->Validate(&pNode->mName);
if (pNode->mNumMeshes)
{
if (!pNode->mMeshes)
{
ReportError("aiNode::mMeshes is NULL (aiNode::mNumMeshes is %i)",
pNode->mNumMeshes);
}
std::vector<bool> abHadMesh;
abHadMesh.resize(mScene->mNumMeshes,false);
for (unsigned int i = 0; i < pNode->mNumMeshes;++i)
{
if (pNode->mMeshes[i] >= mScene->mNumMeshes)
{
ReportError("aiNode::mMeshes[%i] is out of range (maximum is %i)",
pNode->mMeshes[i],mScene->mNumMeshes-1);
}
if (abHadMesh[pNode->mMeshes[i]])
{
ReportError("aiNode::mMeshes[%i] is already referenced by this node (value: %i)",
i,pNode->mMeshes[i]);
}
abHadMesh[pNode->mMeshes[i]] = true;
}
}
if (pNode->mNumChildren)
{
if (!pNode->mChildren) {
ReportError("aiNode::mChildren is NULL (aiNode::mNumChildren is %i)",
pNode->mNumChildren);
}
for (unsigned int i = 0; i < pNode->mNumChildren;++i) {
Validate(pNode->mChildren[i]);
}
}
}
void ValidateDSProcess::Validate( const aiString* pString)
{
if (pString->length > MAXLEN)
{
this->ReportError("aiString::length is too large (%i, maximum is %i)",
pString->length,MAXLEN);
}
const char* sz = pString->data;
while (true)
{
if ('\0' == *sz)
{
if (pString->length != (unsigned int)(sz-pString->data))
ReportError("aiString::data is invalid: the terminal zero is at a wrong offset");
break;
}
else if (sz >= &pString->data[MAXLEN])
ReportError("aiString::data is invalid. There is no terminal character");
++sz;
}
}