- Added format auto-detection to most loaders
  - Simplified BaseImporter::CanRead() with some utility methods
  - improved fast_atof -> no overruns anymore. Fuck you, irrlicht.
  - added assimp_cmd tool to allow command line model processing. Mainly adebugging tool for internal purposes, but others might find it useful, too.
  - vc8/vc9: revision number is now written to DLL version header
  - mkutil: some batch scripts to simplify tagging & building of release versions
  - some API cleanup
  - fixing some doxygen markup (+now explicit use of @file <filename>)
  - Icon for assimp_view and assimp_cmd

3DS
  - Normal vectors are not anymore inverted in some cases
  - Improved pivot handling
  - Improved handling of x-flipped meshes

Collada
  - fixed a minor bug (visual_scene element)

LWS 
  - WIP implementation. No animations yet, some bugs and crashes.
  - Animation system remains disabled, WIP code
  - many test files for LWS, but most of them test the anim support, which is, read above, currently disabled.

STL
  - fixing a log warning which appears for every model
  - added binary&ascii test spider, exported from truespace

MD3 
  - Cleaning up output tags for automatically joined player models.


IRR
  - Fixing coordinate system issues. 
  - Instance handling improved.
  - Some of the reported crashes not yet fixed.

PretransformVertices
  - Numerous performance improvements.
  - Added config option to preserve the hierarchy during the step.

RemoveRedundantMaterials
  - Added config option to specify a list of materials which are kept in every case.

UNREAL
  - Added support for the old unreal data format (*.a,*.d,*.uc)
  - tested only with exports from Milkshape
  - more Unreal stuff to come soon



git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@356 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
This commit is contained in:
aramis_acg
2009-03-05 22:32:13 +00:00
parent caaf02b0e4
commit 4bbc03332b
237 changed files with 33463 additions and 2245 deletions

View File

@@ -39,8 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the post processing step to validate
* the data structure returned by Assimp
/** @file ValidateDataStructure.cpp
* @brief Implementation of the post processing step to validate
* the data structure returned by Assimp.
*/
#include "AssimpPCH.h"
@@ -50,7 +51,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "BaseImporter.h"
#include "fast_atof.h"
// CRT headers
#include <stdarg.h>
@@ -85,15 +85,9 @@ void ValidateDSProcess::ReportError(const char* msg,...)
va_start(args,msg);
char szBuffer[3000];
const int iLen = vsprintf(szBuffer,msg,args);
ai_assert(iLen > 0);
int iLen;
iLen = vsprintf(szBuffer,msg,args);
if (0 >= iLen)
{
// :-) should not happen ...
throw new ImportErrorException("Idiot ... learn coding!");
}
va_end(args);
#ifdef _DEBUG
aiAssert( false,szBuffer,__LINE__,__FILE__ );
@@ -109,15 +103,9 @@ void ValidateDSProcess::ReportWarning(const char* msg,...)
va_start(args,msg);
char szBuffer[3000];
const int iLen = vsprintf(szBuffer,msg,args);
ai_assert(iLen > 0);
int iLen;
iLen = vsprintf(szBuffer,msg,args);
if (0 >= iLen)
{
// :-) should not happen ...
throw new ImportErrorException("Idiot ... learn coding!");
}
va_end(args);
DefaultLogger::get()->warn("Validation warning: " + std::string(szBuffer,iLen));
}
@@ -126,8 +114,7 @@ void ValidateDSProcess::ReportWarning(const char* msg,...)
inline int HasNameMatch(const aiString& in, aiNode* node)
{
int result = (node->mName == in ? 1 : 0 );
for (unsigned int i = 0; i < node->mNumChildren;++i)
{
for (unsigned int i = 0; i < node->mNumChildren;++i) {
result += HasNameMatch(in,node->mChildren[i]);
}
return result;
@@ -166,8 +153,7 @@ inline void ValidateDSProcess::DoValidationEx(T** parray, unsigned int size,
// validate all entries
if (size)
{
if (!parray)
{
if (!parray) {
ReportError("aiScene::%s is NULL (aiScene::%s is %i)",
firstName, secondName, size);
}
@@ -205,13 +191,11 @@ inline void ValidateDSProcess::DoValidationWithNameCheck(T** array,
for (unsigned int i = 0; i < size;++i)
{
int res = HasNameMatch(array[i]->mName,mScene->mRootNode);
if (!res)
{
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)
{
else if (1 != res) {
ReportError("aiScene::%s[%i]: there are more than one nodes with %s as name",
firstName,i,array[i]->mName.data);
}
@@ -228,79 +212,68 @@ void ValidateDSProcess::Execute( aiScene* pScene)
// validate the node graph of the scene
Validate(pScene->mRootNode);
// at least one of the mXXX arrays must be non-empty or we'll flag
// the sebe as invalid
// At least one of the mXXX arrays must be non-empty or we'll flag
// the scene as invalid
bool has = false;
// validate all meshes
if (pScene->mNumMeshes)
{
has = true;
if (pScene->mNumMeshes) {
// has = true;
DoValidation(pScene->mMeshes,pScene->mNumMeshes,"mMeshes","mNumMeshes");
}
else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE))
{
else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) {
ReportError("aiScene::mNumMeshes is 0. At least one mesh must be there");
}
else if (pScene->mMeshes)
{
else if (pScene->mMeshes) {
ReportError("aiScene::mMeshes is non-null although there are no meshes");
}
// validate all animations
if (pScene->mNumAnimations)
{
has = true;
if (pScene->mNumAnimations) {
// has = true;
DoValidation(pScene->mAnimations,pScene->mNumAnimations,
"mAnimations","mNumAnimations");
}
else if (pScene->mAnimations)
{
else if (pScene->mAnimations) {
ReportError("aiScene::mAnimations is non-null although there are no animations");
}
// validate all cameras
if (pScene->mNumCameras)
{
has = true;
if (pScene->mNumCameras) {
// has = true;
DoValidationWithNameCheck(pScene->mCameras,pScene->mNumCameras,
"mCameras","mNumCameras");
}
else if (pScene->mCameras)
{
else if (pScene->mCameras) {
ReportError("aiScene::mCameras is non-null although there are no cameras");
}
// validate all lights
if (pScene->mNumLights)
{
has = true;
if (pScene->mNumLights) {
// has = true;
DoValidationWithNameCheck(pScene->mLights,pScene->mNumLights,
"mLights","mNumLights");
}
else if (pScene->mLights)
{
else if (pScene->mLights) {
ReportError("aiScene::mLights is non-null although there are no lights");
}
// validate all materials
if (pScene->mNumMaterials)
{
has = true;
if (pScene->mNumMaterials) {
// has = true;
DoValidation(pScene->mMaterials,pScene->mNumMaterials,"mMaterials","mNumMaterials");
}
else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE))
{
// NOTE: ScenePreprocessor generates a default material if none is there
// (and at least one mesh is found). So this should never be triggered ...
#if 0
// NOTE: ScenePreprocessor generates a default material if none is there
else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) {
ReportError("aiScene::mNumMaterials is 0. At least one material must be there");
}
else if (pScene->mMaterials)
{
#endif
else if (pScene->mMaterials) {
ReportError("aiScene::mMaterials is non-null although there are no materials");
}
if (!has)ReportError("The aiScene data structure is empty");
// if (!has)ReportError("The aiScene data structure is empty");
DefaultLogger::get()->debug("ValidateDataStructureProcess end");
}
@@ -312,8 +285,7 @@ void ValidateDSProcess::Validate( const aiLight* pLight)
if (!pLight->mAttenuationConstant &&
!pLight->mAttenuationLinear &&
!pLight->mAttenuationQuadratic)
{
!pLight->mAttenuationQuadratic) {
ReportWarning("aiLight::mAttenuationXXX - all are zero");
}
@@ -333,6 +305,8 @@ void ValidateDSProcess::Validate( const aiCamera* pCamera)
if (pCamera->mClipPlaneFar <= pCamera->mClipPlaneNear)
ReportError("aiCamera::mClipPlaneFar must be >= aiCamera::mClipPlaneNear");
// FIX: there are many 3ds files with invalid FOVs. No reason to
// reject them at all ... a warning is appropriate.
if (!pCamera->mHorizontalFOV || pCamera->mHorizontalFOV >= (float)AI_MATH_PI)
ReportWarning("%f is not a valid value for aiCamera::mHorizontalFOV",pCamera->mHorizontalFOV);
}
@@ -341,10 +315,10 @@ void ValidateDSProcess::Validate( const aiCamera* pCamera)
void ValidateDSProcess::Validate( const aiMesh* pMesh)
{
// validate the material index of the mesh
if (pMesh->mMaterialIndex >= this->mScene->mNumMaterials)
if (mScene->mNumMaterials && pMesh->mMaterialIndex >= mScene->mNumMaterials)
{
this->ReportError("aiMesh::mMaterialIndex is invalid (value: %i maximum: %i)",
pMesh->mMaterialIndex,this->mScene->mNumMaterials-1);
ReportError("aiMesh::mMaterialIndex is invalid (value: %i maximum: %i)",
pMesh->mMaterialIndex,mScene->mNumMaterials-1);
}
for (unsigned int i = 0; i < pMesh->mNumFaces; ++i)
@@ -356,25 +330,25 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
switch (face.mNumIndices)
{
case 0:
this->ReportError("aiMesh::mFaces[%i].mNumIndices is 0",i);
ReportError("aiMesh::mFaces[%i].mNumIndices is 0",i);
case 1:
if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POINT))
{
this->ReportError("aiMesh::mFaces[%i] is a POINT but aiMesh::mPrimtiveTypes "
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))
{
this->ReportError("aiMesh::mFaces[%i] is a LINE but aiMesh::mPrimtiveTypes "
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))
{
this->ReportError("aiMesh::mFaces[%i] is a TRIANGLE but aiMesh::mPrimtiveTypes "
ReportError("aiMesh::mFaces[%i] is a TRIANGLE but aiMesh::mPrimtiveTypes "
"does not report the TRIANGLE flag",i);
}
break;
@@ -388,25 +362,23 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
};
}
if (!face.mIndices)this->ReportError("aiMesh::mFaces[%i].mIndices is NULL",i);
if (!face.mIndices)
ReportError("aiMesh::mFaces[%i].mIndices is NULL",i);
}
// positions must always be there ...
if (!pMesh->mNumVertices || !pMesh->mVertices && !mScene->mFlags)
{
this->ReportError("The mesh contains no vertices");
if (!pMesh->mNumVertices || !pMesh->mVertices && !mScene->mFlags) {
ReportError("The mesh contains no vertices");
}
// if tangents are there there must also be bitangent vectors ...
if ((pMesh->mTangents != NULL) != (pMesh->mBitangents != NULL))
{
this->ReportError("If there are tangents there must also be bitangent vectors");
if ((pMesh->mTangents != NULL) != (pMesh->mBitangents != NULL)) {
ReportError("If there are tangents there must also be bitangent vectors");
}
// faces, too
if (!pMesh->mNumFaces || !pMesh->mFaces && !mScene->mFlags)
{
this->ReportError("The mesh contains no faces");
if (!pMesh->mNumFaces || !pMesh->mFaces && !mScene->mFlags) {
ReportError("The mesh contains no faces");
}
// now check whether the face indexing layout is correct:
@@ -418,8 +390,7 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
aiFace& face = pMesh->mFaces[i];
for (unsigned int a = 0; a < face.mNumIndices;++a)
{
if (face.mIndices[a] >= pMesh->mNumVertices)
{
if (face.mIndices[a] >= pMesh->mNumVertices) {
ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range",i,a);
}
// the MSB flag is temporarily used by the extra verbose
@@ -436,8 +407,7 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
// check whether there are vertices that aren't referenced by a face
bool b = false;
for (unsigned int i = 0; i < pMesh->mNumVertices;++i)
{
for (unsigned int i = 0; i < pMesh->mNumVertices;++i) {
if (!abRefList[i])b = true;
}
abRefList.clear();
@@ -495,7 +465,7 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
if (!pMesh->mBones[i])
{
delete[] afSum;
this->ReportError("aiMesh::mBones[%i] is NULL (aiMesh::mNumBones is %i)",
ReportError("aiMesh::mBones[%i] is NULL (aiMesh::mNumBones is %i)",
i,pMesh->mNumBones);
}
Validate(pMesh,pMesh->mBones[i],afSum);
@@ -505,7 +475,7 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
if (pMesh->mBones[i]->mName == pMesh->mBones[a]->mName)
{
delete[] afSum;
this->ReportError("aiMesh::mBones[%i] has the same name as "
ReportError("aiMesh::mBones[%i] has the same name as "
"aiMesh::mBones[%i]",i,a);
}
}
@@ -513,8 +483,7 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
// check whether all bone weights for a vertex sum to 1.0 ...
for (unsigned int i = 0; i < pMesh->mNumVertices;++i)
{
if (afSum[i] && (afSum[i] <= 0.94 || afSum[i] >= 1.05))
{
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]);
}
}
@@ -532,21 +501,18 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh,
{
this->Validate(&pBone->mName);
if (!pBone->mNumWeights)
{
this->ReportError("aiBone::mNumWeights is zero");
if (!pBone->mNumWeights) {
ReportError("aiBone::mNumWeights is zero");
}
// check whether all vertices affected by this bone are valid
for (unsigned int i = 0; i < pBone->mNumWeights;++i)
{
if (pBone->mWeights[i].mVertexId >= pMesh->mNumVertices)
{
this->ReportError("aiBone::mWeights[%i].mVertexId is out of range",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)
{
this->ReportWarning("aiBone::mWeights[%i].mWeight has an invalid value",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;
}
@@ -555,27 +521,26 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh,
// ------------------------------------------------------------------------------------------------
void ValidateDSProcess::Validate( const aiAnimation* pAnimation)
{
this->Validate(&pAnimation->mName);
Validate(&pAnimation->mName);
// validate all materials
if (pAnimation->mNumChannels)
if (pAnimation->mNumChannels)
{
if (!pAnimation->mChannels)
{
this->ReportError("aiAnimation::mChannels is NULL (aiAnimation::mNumChannels is %i)",
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])
{
this->ReportError("aiAnimation::mChannels[%i] is NULL (aiAnimation::mNumChannels is %i)",
ReportError("aiAnimation::mChannels[%i] is NULL (aiAnimation::mNumChannels is %i)",
i, pAnimation->mNumChannels);
}
this->Validate(pAnimation, pAnimation->mChannels[i]);
Validate(pAnimation, pAnimation->mChannels[i]);
}
}
else this->ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there.");
else ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there.");
// Animation duration is allowed to be zero in cases where the anim contains only a single key frame.
// if (!pAnimation->mDuration)this->ReportError("aiAnimation::mDuration is zero");
@@ -619,7 +584,6 @@ void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial,
case aiTextureType_HEIGHT:
szType = "Height";
break;
default:
break;
};
@@ -746,8 +710,7 @@ void ValidateDSProcess::Validate( const aiMaterial* pMaterial)
"aiMaterial::mProperties[%i].mData is 0",i,i);
}
// check all predefined types
if (aiPTI_String == prop->mType)
{
if (aiPTI_String == prop->mType) {
// FIX: strings are now stored in a less expensive way ...
if (prop->mDataLength < sizeof(size_t) + ((const aiString*)prop->mData)->length + 1) {
ReportError("aiMaterial::mProperties[%i].mDataLength is "
@@ -756,16 +719,14 @@ void ValidateDSProcess::Validate( const aiMaterial* pMaterial)
}
Validate((const aiString*)prop->mData);
}
else if (aiPTI_Float == prop->mType)
{
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)
{
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)",
@@ -822,29 +783,25 @@ void ValidateDSProcess::Validate( const aiMaterial* pMaterial)
void ValidateDSProcess::Validate( const aiTexture* pTexture)
{
// the data section may NEVER be NULL
if (!pTexture->pcData)
{
this->ReportError("aiTexture::pcData is NULL");
if (!pTexture->pcData) {
ReportError("aiTexture::pcData is NULL");
}
if (pTexture->mHeight)
{
if (!pTexture->mWidth)this->ReportError("aiTexture::mWidth is zero "
if (!pTexture->mWidth)ReportError("aiTexture::mWidth is zero "
"(aiTexture::mHeight is %i, uncompressed texture)",pTexture->mHeight);
}
else
{
if (!pTexture->mWidth)this->ReportError("aiTexture::mWidth is zero (compressed texture)");
if ('.' == pTexture->achFormatHint[0])
{
char szTemp[5];
szTemp[0] = pTexture->achFormatHint[0];
szTemp[1] = pTexture->achFormatHint[1];
szTemp[2] = pTexture->achFormatHint[2];
szTemp[3] = pTexture->achFormatHint[3];
szTemp[4] = '\0';
this->ReportWarning("aiTexture::achFormatHint should contain a file extension "
"without a leading dot (format hint: %s).",szTemp);
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);
}
}
@@ -852,9 +809,8 @@ void ValidateDSProcess::Validate( const aiTexture* pTexture)
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')
{
this->ReportError("aiTexture::achFormatHint contains non-lowercase characters");
sz[3] >= 'A' && sz[3] <= 'Z') {
ReportError("aiTexture::achFormatHint contains non-lowercase characters");
}
}
@@ -864,6 +820,9 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
{
Validate(&pNodeAnim->mNodeName);
if (!pNodeAnim->mNumPositionKeys && !pNodeAnim->mScalingKeys && !pNodeAnim->mNumRotationKeys)
ReportError("Empty node animation channel");
// otherwise check whether one of the keys exceeds the total duration of the animation
if (pNodeAnim->mNumPositionKeys)
{
@@ -875,7 +834,8 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
double dLast = -10e10;
for (unsigned int i = 0; i < pNodeAnim->mNumPositionKeys;++i)
{
if (pNodeAnim->mPositionKeys[i].mTime > pAnimation->mDuration)
// ScenePreprocessor will compute the duration if still teh default value
if (-1. != pAnimation->mDuration && pNodeAnim->mPositionKeys[i].mTime > pAnimation->mDuration)
{
ReportError("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is larger "
"than aiAnimation::mDuration (which is %.5f)",i,
@@ -903,7 +863,7 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
double dLast = -10e10;
for (unsigned int i = 0; i < pNodeAnim->mNumRotationKeys;++i)
{
if (pNodeAnim->mRotationKeys[i].mTime > pAnimation->mDuration)
if (-1. != pAnimation->mDuration && pNodeAnim->mRotationKeys[i].mTime > pAnimation->mDuration)
{
ReportError("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is larger "
"than aiAnimation::mDuration (which is %.5f)",i,
@@ -923,15 +883,14 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
// scaling keys
if (pNodeAnim->mNumScalingKeys)
{
if (!pNodeAnim->mScalingKeys)
{
this->ReportError("aiNodeAnim::mScalingKeys is NULL (aiNodeAnim::mNumScalingKeys is %i)",
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 (pNodeAnim->mScalingKeys[i].mTime > pAnimation->mDuration)
if (-1. != pAnimation->mDuration && pNodeAnim->mScalingKeys[i].mTime > pAnimation->mDuration)
{
ReportError("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is larger "
"than aiAnimation::mDuration (which is %.5f)",i,
@@ -992,13 +951,11 @@ void ValidateDSProcess::Validate( const aiNode* pNode)
}
if (pNode->mNumChildren)
{
if (!pNode->mChildren)
{
this->ReportError("aiNode::mChildren is NULL (aiNode::mNumChildren is %i)",
if (!pNode->mChildren) {
ReportError("aiNode::mChildren is NULL (aiNode::mNumChildren is %i)",
pNode->mNumChildren);
}
for (unsigned int i = 0; i < pNode->mNumChildren;++i)
{
for (unsigned int i = 0; i < pNode->mNumChildren;++i) {
Validate(pNode->mChildren[i]);
}
}