vrshoot

annotate libs/assimp/FindInvalidDataProcess.cpp @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
rev   line source
nuclear@0 1 /*
nuclear@0 2 ---------------------------------------------------------------------------
nuclear@0 3 Open Asset Import Library (assimp)
nuclear@0 4 ---------------------------------------------------------------------------
nuclear@0 5
nuclear@0 6 Copyright (c) 2006-2012, assimp team
nuclear@0 7
nuclear@0 8 All rights reserved.
nuclear@0 9
nuclear@0 10 Redistribution and use of this software in source and binary forms,
nuclear@0 11 with or without modification, are permitted provided that the following
nuclear@0 12 conditions are met:
nuclear@0 13
nuclear@0 14 * Redistributions of source code must retain the above
nuclear@0 15 copyright notice, this list of conditions and the
nuclear@0 16 following disclaimer.
nuclear@0 17
nuclear@0 18 * Redistributions in binary form must reproduce the above
nuclear@0 19 copyright notice, this list of conditions and the
nuclear@0 20 following disclaimer in the documentation and/or other
nuclear@0 21 materials provided with the distribution.
nuclear@0 22
nuclear@0 23 * Neither the name of the assimp team, nor the names of its
nuclear@0 24 contributors may be used to endorse or promote products
nuclear@0 25 derived from this software without specific prior
nuclear@0 26 written permission of the assimp team.
nuclear@0 27
nuclear@0 28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
nuclear@0 29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
nuclear@0 30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
nuclear@0 31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
nuclear@0 32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
nuclear@0 33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
nuclear@0 34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
nuclear@0 35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
nuclear@0 36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
nuclear@0 37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
nuclear@0 38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
nuclear@0 39 ---------------------------------------------------------------------------
nuclear@0 40 */
nuclear@0 41
nuclear@0 42 /** @file Defines a post processing step to search an importer's output
nuclear@0 43 for data that is obviously invalid */
nuclear@0 44
nuclear@0 45 #include "AssimpPCH.h"
nuclear@0 46
nuclear@0 47 #ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS
nuclear@0 48
nuclear@0 49 // internal headers
nuclear@0 50 #include "FindInvalidDataProcess.h"
nuclear@0 51 #include "ProcessHelper.h"
nuclear@0 52
nuclear@0 53 using namespace Assimp;
nuclear@0 54
nuclear@0 55 // ------------------------------------------------------------------------------------------------
nuclear@0 56 // Constructor to be privately used by Importer
nuclear@0 57 FindInvalidDataProcess::FindInvalidDataProcess()
nuclear@0 58 {
nuclear@0 59 // nothing to do here
nuclear@0 60 }
nuclear@0 61
nuclear@0 62 // ------------------------------------------------------------------------------------------------
nuclear@0 63 // Destructor, private as well
nuclear@0 64 FindInvalidDataProcess::~FindInvalidDataProcess()
nuclear@0 65 {
nuclear@0 66 // nothing to do here
nuclear@0 67 }
nuclear@0 68
nuclear@0 69 // ------------------------------------------------------------------------------------------------
nuclear@0 70 // Returns whether the processing step is present in the given flag field.
nuclear@0 71 bool FindInvalidDataProcess::IsActive( unsigned int pFlags) const
nuclear@0 72 {
nuclear@0 73 return 0 != (pFlags & aiProcess_FindInvalidData);
nuclear@0 74 }
nuclear@0 75
nuclear@0 76 // ------------------------------------------------------------------------------------------------
nuclear@0 77 // Setup import configuration
nuclear@0 78 void FindInvalidDataProcess::SetupProperties(const Importer* pImp)
nuclear@0 79 {
nuclear@0 80 // Get the current value of AI_CONFIG_PP_FID_ANIM_ACCURACY
nuclear@0 81 configEpsilon = (0 != pImp->GetPropertyFloat(AI_CONFIG_PP_FID_ANIM_ACCURACY,0.f));
nuclear@0 82 }
nuclear@0 83
nuclear@0 84 // ------------------------------------------------------------------------------------------------
nuclear@0 85 // Update mesh references in the node graph
nuclear@0 86 void UpdateMeshReferences(aiNode* node, const std::vector<unsigned int>& meshMapping)
nuclear@0 87 {
nuclear@0 88 if (node->mNumMeshes) {
nuclear@0 89 unsigned int out = 0;
nuclear@0 90 for (unsigned int a = 0; a < node->mNumMeshes;++a) {
nuclear@0 91
nuclear@0 92 register unsigned int ref = node->mMeshes[a];
nuclear@0 93 if (UINT_MAX != (ref = meshMapping[ref])) {
nuclear@0 94 node->mMeshes[out++] = ref;
nuclear@0 95 }
nuclear@0 96 }
nuclear@0 97 // just let the members that are unused, that's much cheaper
nuclear@0 98 // than a full array realloc'n'copy party ...
nuclear@0 99 if(!(node->mNumMeshes = out)) {
nuclear@0 100
nuclear@0 101 delete[] node->mMeshes;
nuclear@0 102 node->mMeshes = NULL;
nuclear@0 103 }
nuclear@0 104 }
nuclear@0 105 // recursively update all children
nuclear@0 106 for (unsigned int i = 0; i < node->mNumChildren;++i) {
nuclear@0 107 UpdateMeshReferences(node->mChildren[i],meshMapping);
nuclear@0 108 }
nuclear@0 109 }
nuclear@0 110
nuclear@0 111 // ------------------------------------------------------------------------------------------------
nuclear@0 112 // Executes the post processing step on the given imported data.
nuclear@0 113 void FindInvalidDataProcess::Execute( aiScene* pScene)
nuclear@0 114 {
nuclear@0 115 DefaultLogger::get()->debug("FindInvalidDataProcess begin");
nuclear@0 116
nuclear@0 117 bool out = false;
nuclear@0 118 std::vector<unsigned int> meshMapping(pScene->mNumMeshes);
nuclear@0 119 unsigned int real = 0;
nuclear@0 120
nuclear@0 121 // Process meshes
nuclear@0 122 for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
nuclear@0 123
nuclear@0 124 int result;
nuclear@0 125 if ((result = ProcessMesh( pScene->mMeshes[a]))) {
nuclear@0 126 out = true;
nuclear@0 127
nuclear@0 128 if (2 == result) {
nuclear@0 129 // remove this mesh
nuclear@0 130 delete pScene->mMeshes[a];
nuclear@0 131 AI_DEBUG_INVALIDATE_PTR(pScene->mMeshes[a]);
nuclear@0 132
nuclear@0 133 meshMapping[a] = UINT_MAX;
nuclear@0 134 continue;
nuclear@0 135 }
nuclear@0 136 }
nuclear@0 137 pScene->mMeshes[real] = pScene->mMeshes[a];
nuclear@0 138 meshMapping[a] = real++;
nuclear@0 139 }
nuclear@0 140
nuclear@0 141 // Process animations
nuclear@0 142 for (unsigned int a = 0; a < pScene->mNumAnimations;++a) {
nuclear@0 143 ProcessAnimation( pScene->mAnimations[a]);
nuclear@0 144 }
nuclear@0 145
nuclear@0 146
nuclear@0 147 if (out) {
nuclear@0 148 if ( real != pScene->mNumMeshes) {
nuclear@0 149 if (!real) {
nuclear@0 150 throw DeadlyImportError("No meshes remaining");
nuclear@0 151 }
nuclear@0 152
nuclear@0 153 // we need to remove some meshes.
nuclear@0 154 // therefore we'll also need to remove all references
nuclear@0 155 // to them from the scenegraph
nuclear@0 156 UpdateMeshReferences(pScene->mRootNode,meshMapping);
nuclear@0 157 pScene->mNumMeshes = real;
nuclear@0 158 }
nuclear@0 159
nuclear@0 160 DefaultLogger::get()->info("FindInvalidDataProcess finished. Found issues ...");
nuclear@0 161 }
nuclear@0 162 else DefaultLogger::get()->debug("FindInvalidDataProcess finished. Everything seems to be OK.");
nuclear@0 163 }
nuclear@0 164
nuclear@0 165 // ------------------------------------------------------------------------------------------------
nuclear@0 166 template <typename T>
nuclear@0 167 inline const char* ValidateArrayContents(const T* arr, unsigned int size,
nuclear@0 168 const std::vector<bool>& dirtyMask, bool mayBeIdentical = false, bool mayBeZero = true)
nuclear@0 169 {
nuclear@0 170 return NULL;
nuclear@0 171 }
nuclear@0 172
nuclear@0 173 // ------------------------------------------------------------------------------------------------
nuclear@0 174 template <>
nuclear@0 175 inline const char* ValidateArrayContents<aiVector3D>(const aiVector3D* arr, unsigned int size,
nuclear@0 176 const std::vector<bool>& dirtyMask, bool mayBeIdentical , bool mayBeZero )
nuclear@0 177 {
nuclear@0 178 bool b = false;
nuclear@0 179 unsigned int cnt = 0;
nuclear@0 180 for (unsigned int i = 0; i < size;++i) {
nuclear@0 181
nuclear@0 182 if (dirtyMask.size() && dirtyMask[i]) {
nuclear@0 183 continue;
nuclear@0 184 }
nuclear@0 185 ++cnt;
nuclear@0 186
nuclear@0 187 const aiVector3D& v = arr[i];
nuclear@0 188 if (is_special_float(v.x) || is_special_float(v.y) || is_special_float(v.z)) {
nuclear@0 189 return "INF/NAN was found in a vector component";
nuclear@0 190 }
nuclear@0 191 if (!mayBeZero && !v.x && !v.y && !v.z ) {
nuclear@0 192 return "Found zero-length vector";
nuclear@0 193 }
nuclear@0 194 if (i && v != arr[i-1])b = true;
nuclear@0 195 }
nuclear@0 196 if (cnt > 1 && !b && !mayBeIdentical) {
nuclear@0 197 return "All vectors are identical";
nuclear@0 198 }
nuclear@0 199 return NULL;
nuclear@0 200 }
nuclear@0 201
nuclear@0 202 // ------------------------------------------------------------------------------------------------
nuclear@0 203 template <typename T>
nuclear@0 204 inline bool ProcessArray(T*& in, unsigned int num,const char* name,
nuclear@0 205 const std::vector<bool>& dirtyMask, bool mayBeIdentical = false, bool mayBeZero = true)
nuclear@0 206 {
nuclear@0 207 const char* err = ValidateArrayContents(in,num,dirtyMask,mayBeIdentical,mayBeZero);
nuclear@0 208 if (err) {
nuclear@0 209 DefaultLogger::get()->error(std::string("FindInvalidDataProcess fails on mesh ") + name + ": " + err);
nuclear@0 210
nuclear@0 211 delete[] in;
nuclear@0 212 in = NULL;
nuclear@0 213 return true;
nuclear@0 214 }
nuclear@0 215 return false;
nuclear@0 216 }
nuclear@0 217
nuclear@0 218 // ------------------------------------------------------------------------------------------------
nuclear@0 219 template <typename T>
nuclear@0 220 AI_FORCE_INLINE bool EpsilonCompare(const T& n, const T& s, float epsilon);
nuclear@0 221
nuclear@0 222 // ------------------------------------------------------------------------------------------------
nuclear@0 223 AI_FORCE_INLINE bool EpsilonCompare(float n, float s, float epsilon) {
nuclear@0 224 return fabs(n-s)>epsilon;
nuclear@0 225 }
nuclear@0 226
nuclear@0 227 // ------------------------------------------------------------------------------------------------
nuclear@0 228 template <>
nuclear@0 229 bool EpsilonCompare<aiVectorKey>(const aiVectorKey& n, const aiVectorKey& s, float epsilon) {
nuclear@0 230 return
nuclear@0 231 EpsilonCompare(n.mValue.x,s.mValue.x,epsilon) &&
nuclear@0 232 EpsilonCompare(n.mValue.y,s.mValue.y,epsilon) &&
nuclear@0 233 EpsilonCompare(n.mValue.z,s.mValue.z,epsilon);
nuclear@0 234 }
nuclear@0 235
nuclear@0 236 // ------------------------------------------------------------------------------------------------
nuclear@0 237 template <>
nuclear@0 238 bool EpsilonCompare<aiQuatKey>(const aiQuatKey& n, const aiQuatKey& s, float epsilon) {
nuclear@0 239 return
nuclear@0 240 EpsilonCompare(n.mValue.x,s.mValue.x,epsilon) &&
nuclear@0 241 EpsilonCompare(n.mValue.y,s.mValue.y,epsilon) &&
nuclear@0 242 EpsilonCompare(n.mValue.z,s.mValue.z,epsilon) &&
nuclear@0 243 EpsilonCompare(n.mValue.w,s.mValue.w,epsilon);
nuclear@0 244 }
nuclear@0 245
nuclear@0 246 // ------------------------------------------------------------------------------------------------
nuclear@0 247 template <typename T>
nuclear@0 248 inline bool AllIdentical(T* in, unsigned int num, float epsilon)
nuclear@0 249 {
nuclear@0 250 if (num <= 1) {
nuclear@0 251 return true;
nuclear@0 252 }
nuclear@0 253
nuclear@0 254 if (epsilon > 0.f) {
nuclear@0 255 for (unsigned int i = 0; i < num-1;++i) {
nuclear@0 256
nuclear@0 257 if (!EpsilonCompare(in[i],in[i+1],epsilon)) {
nuclear@0 258 return false;
nuclear@0 259 }
nuclear@0 260 }
nuclear@0 261 }
nuclear@0 262 else {
nuclear@0 263 for (unsigned int i = 0; i < num-1;++i) {
nuclear@0 264
nuclear@0 265 if (in[i] != in[i+1]) {
nuclear@0 266 return false;
nuclear@0 267 }
nuclear@0 268 }
nuclear@0 269 }
nuclear@0 270 return true;
nuclear@0 271 }
nuclear@0 272
nuclear@0 273 // ------------------------------------------------------------------------------------------------
nuclear@0 274 // Search an animation for invalid content
nuclear@0 275 void FindInvalidDataProcess::ProcessAnimation (aiAnimation* anim)
nuclear@0 276 {
nuclear@0 277 // Process all animation channels
nuclear@0 278 for (unsigned int a = 0; a < anim->mNumChannels;++a) {
nuclear@0 279 ProcessAnimationChannel( anim->mChannels[a]);
nuclear@0 280 }
nuclear@0 281 }
nuclear@0 282
nuclear@0 283 // ------------------------------------------------------------------------------------------------
nuclear@0 284 void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim)
nuclear@0 285 {
nuclear@0 286 int i = 0;
nuclear@0 287
nuclear@0 288 // ScenePreprocessor's work ...
nuclear@0 289 ai_assert((0 != anim->mPositionKeys && 0 != anim->mRotationKeys && 0 != anim->mScalingKeys));
nuclear@0 290
nuclear@0 291 // Check whether all values in a tracks are identical - in this case
nuclear@0 292 // we can remove al keys except one.
nuclear@0 293 // POSITIONS
nuclear@0 294 if (anim->mNumPositionKeys > 1 && AllIdentical(anim->mPositionKeys,anim->mNumPositionKeys,configEpsilon))
nuclear@0 295 {
nuclear@0 296 aiVectorKey v = anim->mPositionKeys[0];
nuclear@0 297
nuclear@0 298 // Reallocate ... we need just ONE element, it makes no sense to reuse the array
nuclear@0 299 delete[] anim->mPositionKeys;
nuclear@0 300 anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys = 1];
nuclear@0 301 anim->mPositionKeys[0] = v;
nuclear@0 302 i = 1;
nuclear@0 303 }
nuclear@0 304
nuclear@0 305 // ROTATIONS
nuclear@0 306 if (anim->mNumRotationKeys > 1 && AllIdentical(anim->mRotationKeys,anim->mNumRotationKeys,configEpsilon))
nuclear@0 307 {
nuclear@0 308 aiQuatKey v = anim->mRotationKeys[0];
nuclear@0 309
nuclear@0 310 // Reallocate ... we need just ONE element, it makes no sense to reuse the array
nuclear@0 311 delete[] anim->mRotationKeys;
nuclear@0 312 anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys = 1];
nuclear@0 313 anim->mRotationKeys[0] = v;
nuclear@0 314 i = 1;
nuclear@0 315 }
nuclear@0 316
nuclear@0 317 // SCALINGS
nuclear@0 318 if (anim->mNumScalingKeys > 1 && AllIdentical(anim->mScalingKeys,anim->mNumScalingKeys,configEpsilon))
nuclear@0 319 {
nuclear@0 320 aiVectorKey v = anim->mScalingKeys[0];
nuclear@0 321
nuclear@0 322 // Reallocate ... we need just ONE element, it makes no sense to reuse the array
nuclear@0 323 delete[] anim->mScalingKeys;
nuclear@0 324 anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys = 1];
nuclear@0 325 anim->mScalingKeys[0] = v;
nuclear@0 326 i = 1;
nuclear@0 327 }
nuclear@0 328 if (1 == i)
nuclear@0 329 DefaultLogger::get()->warn("Simplified dummy tracks with just one key");
nuclear@0 330 }
nuclear@0 331
nuclear@0 332 // ------------------------------------------------------------------------------------------------
nuclear@0 333 // Search a mesh for invalid contents
nuclear@0 334 int FindInvalidDataProcess::ProcessMesh (aiMesh* pMesh)
nuclear@0 335 {
nuclear@0 336 bool ret = false;
nuclear@0 337 std::vector<bool> dirtyMask(pMesh->mNumVertices,(pMesh->mNumFaces ? true : false));
nuclear@0 338
nuclear@0 339 // Ignore elements that are not referenced by vertices.
nuclear@0 340 // (they are, for example, caused by the FindDegenerates step)
nuclear@0 341 for (unsigned int m = 0; m < pMesh->mNumFaces;++m) {
nuclear@0 342 const aiFace& f = pMesh->mFaces[m];
nuclear@0 343
nuclear@0 344 for (unsigned int i = 0; i < f.mNumIndices;++i) {
nuclear@0 345 dirtyMask[f.mIndices[i]] = false;
nuclear@0 346 }
nuclear@0 347 }
nuclear@0 348
nuclear@0 349 // Process vertex positions
nuclear@0 350 if(pMesh->mVertices && ProcessArray(pMesh->mVertices,pMesh->mNumVertices,"positions",dirtyMask)) {
nuclear@0 351 DefaultLogger::get()->error("Deleting mesh: Unable to continue without vertex positions");
nuclear@0 352 return 2;
nuclear@0 353 }
nuclear@0 354
nuclear@0 355 // process texture coordinates
nuclear@0 356 for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS && pMesh->mTextureCoords[i];++i) {
nuclear@0 357 if (ProcessArray(pMesh->mTextureCoords[i],pMesh->mNumVertices,"uvcoords",dirtyMask)) {
nuclear@0 358
nuclear@0 359 // delete all subsequent texture coordinate sets.
nuclear@0 360 for (unsigned int a = i+1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) {
nuclear@0 361 delete[] pMesh->mTextureCoords[a]; pMesh->mTextureCoords[a] = NULL;
nuclear@0 362 }
nuclear@0 363 ret = true;
nuclear@0 364 }
nuclear@0 365 }
nuclear@0 366
nuclear@0 367 // -- we don't validate vertex colors, it's difficult to say whether
nuclear@0 368 // they are invalid or not.
nuclear@0 369
nuclear@0 370 // Normals and tangents are undefined for point and line faces.
nuclear@0 371 if (pMesh->mNormals || pMesh->mTangents) {
nuclear@0 372
nuclear@0 373 if (aiPrimitiveType_POINT & pMesh->mPrimitiveTypes ||
nuclear@0 374 aiPrimitiveType_LINE & pMesh->mPrimitiveTypes)
nuclear@0 375 {
nuclear@0 376 if (aiPrimitiveType_TRIANGLE & pMesh->mPrimitiveTypes ||
nuclear@0 377 aiPrimitiveType_POLYGON & pMesh->mPrimitiveTypes)
nuclear@0 378 {
nuclear@0 379 // We need to update the lookup-table
nuclear@0 380 for (unsigned int m = 0; m < pMesh->mNumFaces;++m)
nuclear@0 381 {
nuclear@0 382 const aiFace& f = pMesh->mFaces[m];
nuclear@0 383
nuclear@0 384 if (f.mNumIndices < 3) {
nuclear@0 385 dirtyMask[f.mIndices[0]] = true;
nuclear@0 386
nuclear@0 387 if (f.mNumIndices == 2) {
nuclear@0 388 dirtyMask[f.mIndices[1]] = true;
nuclear@0 389 }
nuclear@0 390 }
nuclear@0 391 }
nuclear@0 392 }
nuclear@0 393 // Normals, tangents and bitangents are undefined for
nuclear@0 394 // the whole mesh (and should not even be there)
nuclear@0 395 else return ret;
nuclear@0 396 }
nuclear@0 397
nuclear@0 398 // Process mesh normals
nuclear@0 399 if (pMesh->mNormals && ProcessArray(pMesh->mNormals,pMesh->mNumVertices,
nuclear@0 400 "normals",dirtyMask,true,false))
nuclear@0 401 ret = true;
nuclear@0 402
nuclear@0 403 // Process mesh tangents
nuclear@0 404 if (pMesh->mTangents && ProcessArray(pMesh->mTangents,pMesh->mNumVertices,"tangents",dirtyMask)) {
nuclear@0 405 delete[] pMesh->mBitangents; pMesh->mBitangents = NULL;
nuclear@0 406 ret = true;
nuclear@0 407 }
nuclear@0 408
nuclear@0 409 // Process mesh bitangents
nuclear@0 410 if (pMesh->mBitangents && ProcessArray(pMesh->mBitangents,pMesh->mNumVertices,"bitangents",dirtyMask)) {
nuclear@0 411 delete[] pMesh->mTangents; pMesh->mTangents = NULL;
nuclear@0 412 ret = true;
nuclear@0 413 }
nuclear@0 414 }
nuclear@0 415 return ret ? 1 : 0;
nuclear@0 416 }
nuclear@0 417
nuclear@0 418
nuclear@0 419 #endif // !! ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS