1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2017, assimp team
7 
8 
9 All rights reserved.
10 
11 Redistribution and use of this software in source and binary forms,
12 with or without modification, are permitted provided that the following
13 conditions are met:
14 
15 * Redistributions of source code must retain the above
16   copyright notice, this list of conditions and the
17   following disclaimer.
18 
19 * Redistributions in binary form must reproduce the above
20   copyright notice, this list of conditions and the
21   following disclaimer in the documentation and/or other
22   materials provided with the distribution.
23 
24 * Neither the name of the assimp team, nor the names of its
25   contributors may be used to endorse or promote products
26   derived from this software without specific prior
27   written permission of the assimp team.
28 
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 ---------------------------------------------------------------------------
41 */
42 
43 /** @file  FindDegenerates.cpp
44  *  @brief Implementation of the FindDegenerates post-process step.
45 */
46 
47 
48 
49 // internal headers
50 #include "ProcessHelper.h"
51 #include "FindDegenerates.h"
52 #include "Exceptional.h"
53 
54 using namespace Assimp;
55 
56 // ------------------------------------------------------------------------------------------------
57 // Constructor to be privately used by Importer
FindDegeneratesProcess()58 FindDegeneratesProcess::FindDegeneratesProcess()
59 : mConfigRemoveDegenerates( false )
60 , mConfigCheckAreaOfTriangle( false ){
61     // empty
62 }
63 
64 // ------------------------------------------------------------------------------------------------
65 // Destructor, private as well
~FindDegeneratesProcess()66 FindDegeneratesProcess::~FindDegeneratesProcess() {
67     // nothing to do here
68 }
69 
70 // ------------------------------------------------------------------------------------------------
71 // Returns whether the processing step is present in the given flag field.
IsActive(unsigned int pFlags) const72 bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const {
73     return 0 != (pFlags & aiProcess_FindDegenerates);
74 }
75 
76 // ------------------------------------------------------------------------------------------------
77 // Setup import configuration
SetupProperties(const Importer * pImp)78 void FindDegeneratesProcess::SetupProperties(const Importer* pImp) {
79     // Get the current value of AI_CONFIG_PP_FD_REMOVE
80     mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0));
81     mConfigCheckAreaOfTriangle = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA) );
82 }
83 
84 // ------------------------------------------------------------------------------------------------
85 // Executes the post processing step on the given imported data.
Execute(aiScene * pScene)86 void FindDegeneratesProcess::Execute( aiScene* pScene) {
87     DefaultLogger::get()->debug("FindDegeneratesProcess begin");
88     for (unsigned int i = 0; i < pScene->mNumMeshes;++i){
89         ExecuteOnMesh( pScene->mMeshes[ i ] );
90     }
91     DefaultLogger::get()->debug("FindDegeneratesProcess finished");
92 }
93 
heron(ai_real a,ai_real b,ai_real c)94 static ai_real heron( ai_real a, ai_real b, ai_real c ) {
95     ai_real s = (a + b + c) / 2;
96     ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), (ai_real)0.5 );
97     return area;
98 }
99 
distance3D(const aiVector3D & vA,aiVector3D & vB)100 static ai_real distance3D( const aiVector3D &vA, aiVector3D &vB ) {
101     const ai_real lx = ( vB.x - vA.x );
102     const ai_real ly = ( vB.y - vA.y );
103     const ai_real lz = ( vB.z - vA.z );
104     ai_real a = lx*lx + ly*ly + lz*lz;
105     ai_real d = pow( a, (ai_real)0.5 );
106 
107     return d;
108 }
109 
calculateAreaOfTriangle(const aiFace & face,aiMesh * mesh)110 static ai_real calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh ) {
111     ai_real area = 0;
112 
113     aiVector3D vA( mesh->mVertices[ face.mIndices[ 0 ] ] );
114     aiVector3D vB( mesh->mVertices[ face.mIndices[ 1 ] ] );
115     aiVector3D vC( mesh->mVertices[ face.mIndices[ 2 ] ] );
116 
117     ai_real a( distance3D( vA, vB ) );
118     ai_real b( distance3D( vB, vC ) );
119     ai_real c( distance3D( vC, vA ) );
120     area = heron( a, b, c );
121 
122     return area;
123 }
124 
125 // ------------------------------------------------------------------------------------------------
126 // Executes the post processing step on the given imported mesh
ExecuteOnMesh(aiMesh * mesh)127 void FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
128     mesh->mPrimitiveTypes = 0;
129 
130     std::vector<bool> remove_me;
131     if (mConfigRemoveDegenerates) {
132         remove_me.resize( mesh->mNumFaces, false );
133     }
134 
135     unsigned int deg = 0, limit;
136     for ( unsigned int a = 0; a < mesh->mNumFaces; ++a ) {
137         aiFace& face = mesh->mFaces[a];
138         bool first = true;
139 
140         // check whether the face contains degenerated entries
141         for (unsigned int i = 0; i < face.mNumIndices; ++i) {
142             // Polygons with more than 4 points are allowed to have double points, that is
143             // simulating polygons with holes just with concave polygons. However,
144             // double points may not come directly after another.
145             limit = face.mNumIndices;
146             if (face.mNumIndices > 4) {
147                 limit = std::min( limit, i+2 );
148             }
149 
150             for (unsigned int t = i+1; t < limit; ++t) {
151                 if (mesh->mVertices[face.mIndices[ i ] ] == mesh->mVertices[ face.mIndices[ t ] ]) {
152                     // we have found a matching vertex position
153                     // remove the corresponding index from the array
154                     --face.mNumIndices;
155                     --limit;
156                     for (unsigned int m = t; m < face.mNumIndices; ++m) {
157                         face.mIndices[ m ] = face.mIndices[ m+1 ];
158                     }
159                     --t;
160 
161                     // NOTE: we set the removed vertex index to an unique value
162                     // to make sure the developer gets notified when his
163                     // application attemps to access this data.
164                     face.mIndices[ face.mNumIndices ] = 0xdeadbeef;
165 
166                     if(first) {
167                         ++deg;
168                         first = false;
169                     }
170 
171                     if ( mConfigRemoveDegenerates ) {
172                         remove_me[ a ] = true;
173                         goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby!
174                     }
175                 }
176             }
177 
178             if ( mConfigCheckAreaOfTriangle ) {
179                 if ( face.mNumIndices == 3 ) {
180                     ai_real area = calculateAreaOfTriangle( face, mesh );
181                     if ( area < 1e-6 ) {
182                         if ( mConfigRemoveDegenerates ) {
183                             remove_me[ a ] = true;
184                             goto evil_jump_outside;
185                         }
186 
187                         // todo: check for index which is corrupt.
188                     }
189                 }
190             }
191         }
192 
193         // We need to update the primitive flags array of the mesh.
194         switch (face.mNumIndices)
195         {
196         case 1u:
197             mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
198             break;
199         case 2u:
200             mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
201             break;
202         case 3u:
203             mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
204             break;
205         default:
206             mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
207             break;
208         };
209 evil_jump_outside:
210         continue;
211     }
212 
213     // If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import
214     if (mConfigRemoveDegenerates && deg) {
215         unsigned int n = 0;
216         for (unsigned int a = 0; a < mesh->mNumFaces; ++a)
217         {
218             aiFace& face_src = mesh->mFaces[a];
219             if (!remove_me[a]) {
220                 aiFace& face_dest = mesh->mFaces[n++];
221 
222                 // Do a manual copy, keep the index array
223                 face_dest.mNumIndices = face_src.mNumIndices;
224                 face_dest.mIndices    = face_src.mIndices;
225 
226                 if (&face_src != &face_dest) {
227                     // clear source
228                     face_src.mNumIndices = 0;
229                     face_src.mIndices = NULL;
230                 }
231             }
232             else {
233                 // Otherwise delete it if we don't need this face
234                 delete[] face_src.mIndices;
235                 face_src.mIndices = NULL;
236                 face_src.mNumIndices = 0;
237             }
238         }
239         // Just leave the rest of the array unreferenced, we don't care for now
240         mesh->mNumFaces = n;
241         if (!mesh->mNumFaces) {
242             // WTF!?
243             // OK ... for completeness and because I'm not yet tired,
244             // let's write code that willl hopefully never be called
245             // (famous last words)
246 
247             // OK ... bad idea.
248             throw DeadlyImportError("Mesh is empty after removal of degenerated primitives ... WTF!?");
249         }
250     }
251 
252     if (deg && !DefaultLogger::isNullLogger())
253     {
254         char s[64];
255         ASSIMP_itoa10(s,deg);
256         DefaultLogger::get()->warn(std::string("Found ") + s + " degenerated primitives");
257     }
258 }
259