diff options
Diffstat (limited to 'libs/assimp/code/PostProcessing/ValidateDataStructure.cpp')
-rw-r--r-- | libs/assimp/code/PostProcessing/ValidateDataStructure.cpp | 942 |
1 files changed, 0 insertions, 942 deletions
diff --git a/libs/assimp/code/PostProcessing/ValidateDataStructure.cpp b/libs/assimp/code/PostProcessing/ValidateDataStructure.cpp deleted file mode 100644 index e99f6a6..0000000 --- a/libs/assimp/code/PostProcessing/ValidateDataStructure.cpp +++ /dev/null @@ -1,942 +0,0 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2022, assimp team - - - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file ValidateDataStructure.cpp - * @brief Implementation of the post processing step to validate - * the data structure returned by Assimp. - */ - -// internal headers -#include "ValidateDataStructure.h" -#include "ProcessHelper.h" -#include <assimp/BaseImporter.h> -#include <assimp/fast_atof.h> -#include <memory> - -// CRT headers -#include <stdarg.h> - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -ValidateDSProcess::ValidateDSProcess() : - mScene() {} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -ValidateDSProcess::~ValidateDSProcess() {} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the processing step is present in the given flag field. -bool ValidateDSProcess::IsActive(unsigned int pFlags) const { - return (pFlags & aiProcess_ValidateDataStructure) != 0; -} -// ------------------------------------------------------------------------------------------------ -AI_WONT_RETURN void ValidateDSProcess::ReportError(const char *msg, ...) { - ai_assert(nullptr != 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); - - throw DeadlyImportError("Validation failed: ", std::string(szBuffer, iLen)); -} -// ------------------------------------------------------------------------------------------------ -void ValidateDSProcess::ReportWarning(const char *msg, ...) { - ai_assert(nullptr != 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); - ASSIMP_LOG_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) { - // validate all entries - if (size) { - if (!parray) { - ReportError("aiScene::%s is nullptr (aiScene::%s is %i)", - firstName, secondName, size); - } - for (unsigned int i = 0; i < size; ++i) { - if (!parray[i]) { - ReportError("aiScene::%s[%i] is nullptr (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) { - // validate all entries - if (size) { - if (!parray) { - ReportError("aiScene::%s is nullptr (aiScene::%s is %i)", - firstName, secondName, size); - } - for (unsigned int i = 0; i < size; ++i) { - if (!parray[i]) { - ReportError("aiScene::%s[%u] is nullptr (aiScene::%s is %u)", - firstName, i, secondName, size); - } - Validate(parray[i]); - - // check whether there are duplicate names - for (unsigned int a = i + 1; a < size; ++a) { - if (parray[i]->mName == parray[a]->mName) { - ReportError("aiScene::%s[%u] has the same name as " - "aiScene::%s[%u]", - firstName, i, secondName, a); - } - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -template <typename T> -inline void ValidateDSProcess::DoValidationWithNameCheck(T **array, unsigned int size, const char *firstName, - const char *secondName) { - // validate all entries - DoValidationEx(array, size, firstName, secondName); - - for (unsigned int i = 0; i < size; ++i) { - int res = HasNameMatch(array[i]->mName, mScene->mRootNode); - if (0 == res) { - const std::string name = static_cast<char *>(array[i]->mName.data); - ReportError("aiScene::%s[%i] has no corresponding node in the scene graph (%s)", - firstName, i, name.c_str()); - } else if (1 != res) { - const std::string name = static_cast<char *>(array[i]->mName.data); - ReportError("aiScene::%s[%i]: there are more than one nodes with %s as name", - firstName, i, name.c_str()); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Executes the post processing step on the given imported data. -void ValidateDSProcess::Execute(aiScene *pScene) { - mScene = pScene; - ASSIMP_LOG_DEBUG("ValidateDataStructureProcess begin"); - - // validate the node graph of the scene - Validate(pScene->mRootNode); - - // validate all meshes - 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"); - } - - // validate all animations - 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"); - } - - // validate all cameras - 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"); - } - - // validate all lights - 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"); - } - - // validate all textures - 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"); - } - - // validate all materials - if (pScene->mNumMaterials) { - DoValidation(pScene->mMaterials, pScene->mNumMaterials, "mMaterials", "mNumMaterials"); - } -#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"); - } -#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"); - ASSIMP_LOG_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"); - - // 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); -} - -// ------------------------------------------------------------------------------------------------ -void ValidateDSProcess::Validate(const aiMesh *pMesh) { - // validate the material index of the mesh - 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); - break; - case 1: - if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POINT)) { - ReportError("aiMesh::mFaces[%i] is a POINT but aiMesh::mPrimitiveTypes " - "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::mPrimitiveTypes " - "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::mPrimitiveTypes " - "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::mPrimitiveTypes " - "does not report the POLYGON flag", - i); - } - break; - }; - } - - if (!face.mIndices) - ReportError("aiMesh::mFaces[%i].mIndices is nullptr", i); - } - - // positions must always be there ... - if (!pMesh->mNumVertices || (!pMesh->mVertices && !mScene->mFlags)) { - ReportError("The mesh %s contains no vertices", pMesh->mName.C_Str()); - } - - 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 tangents are there there must also be bitangent vectors ... - if ((pMesh->mTangents != nullptr) != (pMesh->mBitangents != nullptr)) { - ReportError("If there are tangents, bitangent vectors must be present as well"); - } - - // faces, too - if (!pMesh->mNumFaces || (!pMesh->mFaces && !mScene->mFlags)) { - ReportError("Mesh %s contains no faces", pMesh->mName.C_Str()); - } - - // now check whether the face indexing layout is correct: - // unique vertices, pseudo-indexed. - 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); - } - // the MSB flag is temporarily used by the extra verbose - // mode to tell us that the JoinVerticesProcess might have - // been executed already. - /*if ( !(this->mScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT ) && !(this->mScene->mFlags & AI_SCENE_FLAGS_ALLOW_SHARED) && - abRefList[face.mIndices[a]]) - { - ReportError("aiMesh::mVertices[%i] is referenced twice - second " - "time by aiMesh::mFaces[%i]::mIndices[%i]",face.mIndices[a],i,a); - }*/ - abRefList[face.mIndices[a]] = true; - } - } - - // check whether there are vertices that aren't referenced by a face - 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"); - } - - // texture channel 2 may not be set if channel 1 is zero ... - { - 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 nullptr.", - i); - } - } - // the same for the vertex colors - { - 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 nullptr.", - i); - } - } - - // now validate all bones - if (pMesh->mNumBones) { - if (!pMesh->mBones) { - ReportError("aiMesh::mBones is nullptr (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; - } - - // check whether there are duplicate bone names - 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 nullptr (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) { - const char *name = "unknown"; - if (nullptr != pMesh->mBones[i]->mName.C_Str()) { - name = pMesh->mBones[i]->mName.C_Str(); - } - ReportError("aiMesh::mBones[%i], name = \"%s\" has the same name as " - "aiMesh::mBones[%i]", - i, name, a); - } - } - } - // 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)) { - 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"); - } - - // 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) { - 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); - - // validate all animations - if (pAnimation->mNumChannels || pAnimation->mNumMorphMeshChannels) { - if (!pAnimation->mChannels && pAnimation->mNumChannels) { - ReportError("aiAnimation::mChannels is nullptr (aiAnimation::mNumChannels is %i)", - pAnimation->mNumChannels); - } - if (!pAnimation->mMorphMeshChannels && pAnimation->mNumMorphMeshChannels) { - ReportError("aiAnimation::mMorphMeshChannels is nullptr (aiAnimation::mNumMorphMeshChannels is %i)", - pAnimation->mNumMorphMeshChannels); - } - for (unsigned int i = 0; i < pAnimation->mNumChannels; ++i) { - if (!pAnimation->mChannels[i]) { - ReportError("aiAnimation::mChannels[%i] is nullptr (aiAnimation::mNumChannels is %i)", - i, pAnimation->mNumChannels); - } - Validate(pAnimation, pAnimation->mChannels[i]); - } - for (unsigned int i = 0; i < pAnimation->mNumMorphMeshChannels; ++i) { - if (!pAnimation->mMorphMeshChannels[i]) { - ReportError("aiAnimation::mMorphMeshChannels[%i] is nullptr (aiAnimation::mNumMorphMeshChannels is %i)", - i, pAnimation->mNumMorphMeshChannels); - } - Validate(pAnimation, pAnimation->mMorphMeshChannels[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); - - // **************************************************************************** - // Search all keys of the material ... - // textures must be specified with ascending indices - // (e.g. diffuse #2 may not be specified if diffuse #1 is not there ...) - // **************************************************************************** - - int iNumIndices = 0; - int iIndex = -1; - for (unsigned int i = 0; i < pMaterial->mNumProperties; ++i) { - aiMaterialProperty *prop = pMaterial->mProperties[i]; - ai_assert(nullptr != prop); - if (!::strcmp(prop->mKey.data, "$tex.file") && prop->mSemantic == static_cast<unsigned int>(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); - - // Now check whether all UV indices are valid ... - bool bNoSpecified = true; - for (unsigned int i = 0; i < pMaterial->mNumProperties; ++i) { - aiMaterialProperty *prop = pMaterial->mProperties[i]; - if (static_cast<aiTextureType>(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] = ((aiUVTransform*)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; - - // Ignore UV indices for texture channels that are not there ... - - // Get the value - iIndex = *((unsigned int *)prop->mData); - - // Check whether there is a mesh using this material - // which has not enough UV channels ... - 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) { - // Assume that all textures are using the first UV channel - 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]) { - // This is a special case ... it could be that the - // original mesh format intended the use of a special - // mapping here. - ReportWarning("UV-mapped texture, but there are no UV coords"); - } - } - } - } -} -// ------------------------------------------------------------------------------------------------ -void ValidateDSProcess::Validate(const aiMaterial *pMaterial) { - // check whether there are material keys that are obviously not legal - for (unsigned int i = 0; i < pMaterial->mNumProperties; ++i) { - const aiMaterialProperty *prop = pMaterial->mProperties[i]; - if (!prop) { - ReportError("aiMaterial::mProperties[%i] is nullptr (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); - } - // check all predefined types - if (aiPTI_String == prop->mType) { - // FIX: strings are now stored in a less expensive way, but we can't use the - // validation routine for 'normal' aiStrings - 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, static_cast<int>(sizeof(aiString))); - } - if (prop->mData[prop->mDataLength - 1]) { - ReportError("Missing null-terminator in string material property"); - } - // Validate((const aiString*)prop->mData); - } 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, static_cast<int>(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, static_cast<int>(sizeof(int))); - } - } - // TODO: check whether there is a key with an unknown name ... - } - - // make some more specific tests - 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: - break; - } - } - - if (AI_SUCCESS == aiGetMaterialFloat(pMaterial, AI_MATKEY_OPACITY, &fTemp) && (!fTemp || fTemp > 1.01)) { - ReportWarning("Invalid opacity value (must be 0 < opacity < 1.0)"); - } - - // Check whether there are invalid texture keys - // TODO: that's a relict of the past, where texture type and index were baked - // into the material string ... we could do that in one single pass. - 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); - SearchForInvalidTextures(pMaterial, aiTextureType_BASE_COLOR); - SearchForInvalidTextures(pMaterial, aiTextureType_NORMAL_CAMERA); - SearchForInvalidTextures(pMaterial, aiTextureType_EMISSION_COLOR); - SearchForInvalidTextures(pMaterial, aiTextureType_METALNESS); - SearchForInvalidTextures(pMaterial, aiTextureType_DIFFUSE_ROUGHNESS); - SearchForInvalidTextures(pMaterial, aiTextureType_AMBIENT_OCCLUSION); -} - -// ------------------------------------------------------------------------------------------------ -void ValidateDSProcess::Validate(const aiTexture *pTexture) { - // the data section may NEVER be nullptr - if (nullptr == pTexture->pcData) { - ReportError("aiTexture::pcData is nullptr"); - } - 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[HINTMAXTEXTURELEN - 1]) { - 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"); - } - // otherwise check whether one of the keys exceeds the total duration of the animation - if (pNodeAnim->mNumPositionKeys) { - if (!pNodeAnim->mPositionKeys) { - ReportError("aiNodeAnim::mPositionKeys is nullptr (aiNodeAnim::mNumPositionKeys is %i)", - pNodeAnim->mNumPositionKeys); - } - double dLast = -10e10; - for (unsigned int i = 0; i < pNodeAnim->mNumPositionKeys; ++i) { - // ScenePreprocessor will compute the duration if still the default value - // (Aramis) Add small epsilon, comparison tended to fail if max_time == duration, - // seems to be due the compilers register usage/width. - 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; - } - } - // rotation keys - if (pNodeAnim->mNumRotationKeys) { - if (!pNodeAnim->mRotationKeys) { - ReportError("aiNodeAnim::mRotationKeys is nullptr (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; - } - } - // scaling keys - if (pNodeAnim->mNumScalingKeys) { - if (!pNodeAnim->mScalingKeys) { - ReportError("aiNodeAnim::mScalingKeys is nullptr (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 aiAnimation *pAnimation, - const aiMeshMorphAnim *pMeshMorphAnim) { - Validate(&pMeshMorphAnim->mName); - - if (!pMeshMorphAnim->mNumKeys) { - ReportWarning("Empty mesh morph animation channel"); - return; - } - - // otherwise check whether one of the keys exceeds the total duration of the animation - if (pMeshMorphAnim->mNumKeys) { - if (!pMeshMorphAnim->mKeys) { - ReportError("aiMeshMorphAnim::mKeys is nullptr (aiMeshMorphAnim::mNumKeys is %i)", - pMeshMorphAnim->mNumKeys); - } - double dLast = -10e10; - for (unsigned int i = 0; i < pMeshMorphAnim->mNumKeys; ++i) { - // ScenePreprocessor will compute the duration if still the default value - // (Aramis) Add small epsilon, comparison tended to fail if max_time == duration, - // seems to be due the compilers register usage/width. - if (pAnimation->mDuration > 0. && pMeshMorphAnim->mKeys[i].mTime > pAnimation->mDuration + 0.001) { - ReportError("aiMeshMorphAnim::mKeys[%i].mTime (%.5f) is larger " - "than aiAnimation::mDuration (which is %.5f)", - i, - (float)pMeshMorphAnim->mKeys[i].mTime, - (float)pAnimation->mDuration); - } - if (i && pMeshMorphAnim->mKeys[i].mTime <= dLast) { - ReportWarning("aiMeshMorphAnim::mKeys[%i].mTime (%.5f) is smaller " - "than aiMeshMorphAnim::mKeys[%i] (which is %.5f)", - i, - (float)pMeshMorphAnim->mKeys[i].mTime, - i - 1, (float)dLast); - } - dLast = pMeshMorphAnim->mKeys[i].mTime; - } - } -} - -// ------------------------------------------------------------------------------------------------ -void ValidateDSProcess::Validate(const aiNode *pNode) { - if (!pNode) { - ReportError("A node of the scene-graph is nullptr"); - } - // Validate node name string first so that it's safe to use in below expressions - this->Validate(&pNode->mName); - const char *nodeName = (&pNode->mName)->C_Str(); - if (pNode != mScene->mRootNode && !pNode->mParent) { - ReportError("Non-root node %s lacks a valid parent (aiNode::mParent is nullptr) ", nodeName); - } - - // validate all meshes - if (pNode->mNumMeshes) { - if (!pNode->mMeshes) { - ReportError("aiNode::mMeshes is nullptr for node %s (aiNode::mNumMeshes is %i)", - nodeName, 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 for node %s (maximum is %i)", - pNode->mMeshes[i], nodeName, mScene->mNumMeshes - 1); - } - if (abHadMesh[pNode->mMeshes[i]]) { - ReportError("aiNode::mMeshes[%i] is already referenced by this node %s (value: %i)", - i, nodeName, pNode->mMeshes[i]); - } - abHadMesh[pNode->mMeshes[i]] = true; - } - } - if (pNode->mNumChildren) { - if (!pNode->mChildren) { - ReportError("aiNode::mChildren is nullptr for node %s (aiNode::mNumChildren is %i)", - nodeName, 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) { - ReportError("aiString::length is too large (%u, maximum is %lu)", - 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; - } -} |