/src/assimp/code/PostProcessing/TriangulateProcess.cpp
Line | Count | Source |
1 | | /* |
2 | | --------------------------------------------------------------------------- |
3 | | Open Asset Import Library (assimp) |
4 | | --------------------------------------------------------------------------- |
5 | | |
6 | | Copyright (c) 2006-2025, assimp team |
7 | | |
8 | | All rights reserved. |
9 | | |
10 | | Redistribution and use of this software in source and binary forms, |
11 | | with or without modification, are permitted provided that the following |
12 | | conditions are met: |
13 | | |
14 | | * Redistributions of source code must retain the above |
15 | | copyright notice, this list of conditions and the |
16 | | following disclaimer. |
17 | | |
18 | | * Redistributions in binary form must reproduce the above |
19 | | copyright notice, this list of conditions and the |
20 | | following disclaimer in the documentation and/or other |
21 | | materials provided with the distribution. |
22 | | |
23 | | * Neither the name of the assimp team, nor the names of its |
24 | | contributors may be used to endorse or promote products |
25 | | derived from this software without specific prior |
26 | | written permission of the assimp team. |
27 | | |
28 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
29 | | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
30 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
31 | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
32 | | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
33 | | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
34 | | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
35 | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
36 | | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
37 | | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
38 | | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
39 | | --------------------------------------------------------------------------- |
40 | | */ |
41 | | |
42 | | /** @file TriangulateProcess.cpp |
43 | | * @brief Implementation of the post processing step to split up |
44 | | * all faces with more than three indices into triangles. |
45 | | * |
46 | | * |
47 | | * The triangulation algorithm will handle concave or convex polygons. |
48 | | * Self-intersecting or non-planar polygons are not rejected, but |
49 | | * they're probably not triangulated correctly. |
50 | | * |
51 | | * DEBUG SWITCHES - do not enable any of them in release builds: |
52 | | * |
53 | | * AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING |
54 | | * - generates vertex colors to represent the face winding order. |
55 | | * the first vertex of a polygon becomes red, the last blue. |
56 | | * AI_BUILD_TRIANGULATE_DEBUG_POLYS |
57 | | * - dump all polygons and their triangulation sequences to |
58 | | * a file |
59 | | */ |
60 | | #ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS |
61 | | |
62 | | #include "PostProcessing/TriangulateProcess.h" |
63 | | #include "PostProcessing/ProcessHelper.h" |
64 | | #include "Common/PolyTools.h" |
65 | | #include "contrib/earcut-hpp/earcut.hpp" |
66 | | |
67 | | #include <memory> |
68 | | #include <cstdint> |
69 | | |
70 | | //#define AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING |
71 | | //#define AI_BUILD_TRIANGULATE_DEBUG_POLYS |
72 | | |
73 | | #define POLY_GRID_Y 40 |
74 | | #define POLY_GRID_X 70 |
75 | | #define POLY_GRID_XPAD 20 |
76 | | #define POLY_OUTPUT_FILE "assimp_polygons_debug.txt" |
77 | | |
78 | | namespace mapbox::util { |
79 | | |
80 | | template <> |
81 | | struct nth<0, aiVector2D> { |
82 | 463k | inline static auto get(const aiVector2D& t) { |
83 | 463k | return t.x; |
84 | 463k | } |
85 | | }; |
86 | | template <> |
87 | | struct nth<1, aiVector2D> { |
88 | 463k | inline static auto get(const aiVector2D& t) { |
89 | 463k | return t.y; |
90 | 463k | } |
91 | | }; |
92 | | |
93 | | } // namespace mapbox::util |
94 | | |
95 | | using namespace Assimp; |
96 | | |
97 | | namespace { |
98 | | |
99 | | /** |
100 | | * @brief Helper struct used to simplify NGON encoding functions. |
101 | | */ |
102 | | struct NGONEncoder { |
103 | 4.84k | NGONEncoder() : mLastNGONFirstIndex((unsigned int)-1) {} |
104 | | |
105 | | /** |
106 | | * @brief Encode the current triangle, and make sure it is recognized as a triangle. |
107 | | * |
108 | | * This method will rotate indices in tri if needed in order to avoid tri to be considered |
109 | | * part of the previous ngon. This method is to be used whenever you want to emit a real triangle, |
110 | | * and make sure it is seen as a triangle. |
111 | | * |
112 | | * @param tri Triangle to encode. |
113 | | */ |
114 | 25.7k | void ngonEncodeTriangle(aiFace * tri) { |
115 | 25.7k | ai_assert(tri->mNumIndices == 3); |
116 | | |
117 | | // Rotate indices in new triangle to avoid ngon encoding false ngons |
118 | | // Otherwise, the new triangle would be considered part of the previous NGON. |
119 | 25.7k | if (isConsideredSameAsLastNgon(tri)) { |
120 | 1.16k | std::swap(tri->mIndices[0], tri->mIndices[2]); |
121 | 1.16k | std::swap(tri->mIndices[1], tri->mIndices[2]); |
122 | 1.16k | } |
123 | | |
124 | 25.7k | mLastNGONFirstIndex = tri->mIndices[0]; |
125 | 25.7k | } |
126 | | |
127 | | /** |
128 | | * @brief Encode a quad (2 triangles) in ngon encoding, and make sure they are seen as a single ngon. |
129 | | * |
130 | | * @param tri1 First quad triangle |
131 | | * @param tri2 Second quad triangle |
132 | | * |
133 | | * @pre Triangles must be properly fanned from the most appropriate vertex. |
134 | | */ |
135 | 87.3k | void ngonEncodeQuad(aiFace *tri1, aiFace *tri2) { |
136 | 87.3k | ai_assert(tri1->mNumIndices == 3); |
137 | 87.3k | ai_assert(tri2->mNumIndices == 3); |
138 | 87.3k | ai_assert(tri1->mIndices[0] == tri2->mIndices[0]); |
139 | | |
140 | | // If the selected fanning vertex is the same as the previously |
141 | | // emitted ngon, we use the opposite vertex which also happens to work |
142 | | // for tri-fanning a concave quad. |
143 | | // ref: https://github.com/assimp/assimp/pull/3695#issuecomment-805999760 |
144 | 87.3k | if (isConsideredSameAsLastNgon(tri1)) { |
145 | | // Right-rotate indices for tri1 (index 2 becomes the new fanning vertex) |
146 | 0 | std::swap(tri1->mIndices[0], tri1->mIndices[2]); |
147 | 0 | std::swap(tri1->mIndices[1], tri1->mIndices[2]); |
148 | | |
149 | | // Left-rotate indices for tri2 (index 2 becomes the new fanning vertex) |
150 | 0 | std::swap(tri2->mIndices[1], tri2->mIndices[2]); |
151 | 0 | std::swap(tri2->mIndices[0], tri2->mIndices[2]); |
152 | |
|
153 | 0 | ai_assert(tri1->mIndices[0] == tri2->mIndices[0]); |
154 | 0 | } |
155 | | |
156 | 87.3k | mLastNGONFirstIndex = tri1->mIndices[0]; |
157 | 87.3k | } |
158 | | |
159 | | /** |
160 | | * @brief Check whether this triangle would be considered part of the lastly emitted ngon or not. |
161 | | * |
162 | | * @param tri Current triangle. |
163 | | * @return true If used as is, this triangle will be part of last ngon. |
164 | | * @return false If used as is, this triangle is not considered part of the last ngon. |
165 | | */ |
166 | 113k | bool isConsideredSameAsLastNgon(const aiFace * tri) const { |
167 | 113k | ai_assert(tri->mNumIndices == 3); |
168 | 113k | return tri->mIndices[0] == mLastNGONFirstIndex; |
169 | 113k | } |
170 | | |
171 | | private: |
172 | | unsigned int mLastNGONFirstIndex; |
173 | | }; |
174 | | |
175 | | } |
176 | | |
177 | | // ------------------------------------------------------------------------------------------------ |
178 | | // Returns whether the processing step is present in the given flag field. |
179 | 455 | bool TriangulateProcess::IsActive( unsigned int pFlags) const { |
180 | 455 | return (pFlags & aiProcess_Triangulate) != 0; |
181 | 455 | } |
182 | | |
183 | | // ------------------------------------------------------------------------------------------------ |
184 | | // Executes the post processing step on the given imported data. |
185 | 267 | void TriangulateProcess::Execute( aiScene* pScene) { |
186 | 267 | ASSIMP_LOG_DEBUG("TriangulateProcess begin"); |
187 | | |
188 | 267 | bool bHas = false; |
189 | 7.65k | for( unsigned int a = 0; a < pScene->mNumMeshes; a++) |
190 | 7.38k | { |
191 | 7.38k | if (pScene->mMeshes[ a ]) { |
192 | 7.38k | if ( TriangulateMesh( pScene->mMeshes[ a ] ) ) { |
193 | 4.84k | bHas = true; |
194 | 4.84k | } |
195 | 7.38k | } |
196 | 7.38k | } |
197 | 267 | if ( bHas ) { |
198 | 170 | ASSIMP_LOG_INFO( "TriangulateProcess finished. All polygons have been triangulated." ); |
199 | 170 | } else { |
200 | 97 | ASSIMP_LOG_DEBUG( "TriangulateProcess finished. There was nothing to be done." ); |
201 | 97 | } |
202 | 267 | } |
203 | | |
204 | | // ------------------------------------------------------------------------------------------------ |
205 | | // Triangulates the given mesh. |
206 | 7.38k | bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) { |
207 | | // Now we have aiMesh::mPrimitiveTypes, so this is only here for test cases |
208 | 7.38k | if (!pMesh->mPrimitiveTypes) { |
209 | 0 | bool bNeed = false; |
210 | |
|
211 | 0 | for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { |
212 | 0 | const aiFace& face = pMesh->mFaces[a]; |
213 | 0 | if( face.mNumIndices != 3) { |
214 | 0 | bNeed = true; |
215 | 0 | } |
216 | 0 | } |
217 | 0 | if (!bNeed) { |
218 | 0 | return false; |
219 | 0 | } |
220 | 0 | } |
221 | 7.38k | else if (!(pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) { |
222 | 2.53k | return false; |
223 | 2.53k | } |
224 | | |
225 | | // Find out how many output faces we'll get |
226 | 4.84k | uint32_t numOut = 0, max_out = 0; |
227 | 4.84k | bool get_normals = true; |
228 | 111k | for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { |
229 | 106k | aiFace& face = pMesh->mFaces[a]; |
230 | 106k | if (face.mNumIndices <= 4) { |
231 | 101k | get_normals = false; |
232 | 101k | } |
233 | 106k | if( face.mNumIndices <= 3) { |
234 | 13.8k | ++numOut; |
235 | 92.7k | } else { |
236 | 92.7k | numOut += face.mNumIndices-2; |
237 | 92.7k | max_out = std::max(max_out,face.mNumIndices); |
238 | 92.7k | } |
239 | 106k | } |
240 | | |
241 | | // Just another check whether aiMesh::mPrimitiveTypes is correct |
242 | 4.84k | if (numOut == pMesh->mNumFaces) { |
243 | 0 | ASSIMP_LOG_ERROR( "Invalidation detected in the number of indices: does not fit to the primitive type." ); |
244 | 0 | return false; |
245 | 0 | } |
246 | | |
247 | 4.84k | aiVector3D *nor_out = nullptr; |
248 | | |
249 | | // if we don't have normals yet, but expect them to be a cheap side |
250 | | // product of triangulation anyway, allocate storage for them. |
251 | 4.84k | if (!pMesh->mNormals && get_normals) { |
252 | | // XXX need a mechanism to inform the GenVertexNormals process to treat these normals as preprocessed per-face normals |
253 | | // nor_out = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; |
254 | 314 | } |
255 | | |
256 | | // the output mesh will contain triangles, but no polys anymore |
257 | 4.84k | pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; |
258 | 4.84k | pMesh->mPrimitiveTypes &= ~aiPrimitiveType_POLYGON; |
259 | | |
260 | | // The mesh becomes NGON encoded now, during the triangulation process. |
261 | 4.84k | pMesh->mPrimitiveTypes |= aiPrimitiveType_NGONEncodingFlag; |
262 | | |
263 | 4.84k | aiFace* out = new aiFace[numOut](), *curOut = out; |
264 | 4.84k | std::vector<aiVector3D> temp_verts3d(max_out+2); /* temporary storage for vertices */ |
265 | 4.84k | std::vector<std::vector<aiVector2D>> temp_poly(1); /* temporary storage for earcut.hpp */ |
266 | 4.84k | std::vector<aiVector2D>& temp_verts = temp_poly[0]; |
267 | 4.84k | temp_verts.reserve(max_out + 2); |
268 | | |
269 | 4.84k | NGONEncoder ngonEncoder; |
270 | | |
271 | | // Apply vertex colors to represent the face winding? |
272 | | #ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING |
273 | | if (!pMesh->mColors[0]) |
274 | | pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices]; |
275 | | else |
276 | | new(pMesh->mColors[0]) aiColor4D[pMesh->mNumVertices]; |
277 | | |
278 | | aiColor4D* clr = pMesh->mColors[0]; |
279 | | #endif |
280 | | |
281 | | #ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS |
282 | | FILE* fout = fopen(POLY_OUTPUT_FILE,"a"); |
283 | | #endif |
284 | | |
285 | 4.84k | const aiVector3D* verts = pMesh->mVertices; |
286 | | |
287 | 111k | for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { |
288 | 106k | aiFace& face = pMesh->mFaces[a]; |
289 | | |
290 | 106k | unsigned int* idx = face.mIndices; |
291 | 106k | unsigned int num = face.mNumIndices; |
292 | | |
293 | | // Apply vertex colors to represent the face winding? |
294 | | #ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING |
295 | | for (unsigned int i = 0; i < face.mNumIndices; ++i) { |
296 | | aiColor4D& c = clr[idx[i]]; |
297 | | c.r = (i+1) / (float)max; |
298 | | c.b = 1.f - c.r; |
299 | | } |
300 | | #endif |
301 | | |
302 | 106k | aiFace* const last_face = curOut; |
303 | | |
304 | | // if it's a simple point,line or triangle: just copy it |
305 | 106k | if( face.mNumIndices <= 3) |
306 | 13.8k | { |
307 | 13.8k | aiFace& nface = *curOut++; |
308 | 13.8k | nface.mNumIndices = face.mNumIndices; |
309 | 13.8k | nface.mIndices = face.mIndices; |
310 | 13.8k | face.mIndices = nullptr; |
311 | | |
312 | | // points and lines don't require ngon encoding (and are not supported either!) |
313 | 13.8k | if (nface.mNumIndices == 3) ngonEncoder.ngonEncodeTriangle(&nface); |
314 | | |
315 | 13.8k | continue; |
316 | 13.8k | } |
317 | | // optimized code for quadrilaterals |
318 | 92.7k | else if ( face.mNumIndices == 4) { |
319 | | |
320 | | // quads can have at maximum one concave vertex. Determine |
321 | | // this vertex (if it exists) and start tri-fanning from |
322 | | // it. |
323 | 87.3k | unsigned int start_vertex = 0; |
324 | 380k | for (unsigned int i = 0; i < 4; ++i) { |
325 | 313k | const aiVector3D& v0 = verts[face.mIndices[(i+3) % 4]]; |
326 | 313k | const aiVector3D& v1 = verts[face.mIndices[(i+2) % 4]]; |
327 | 313k | const aiVector3D& v2 = verts[face.mIndices[(i+1) % 4]]; |
328 | | |
329 | 313k | const aiVector3D& v = verts[face.mIndices[i]]; |
330 | | |
331 | 313k | aiVector3D left = (v0-v); |
332 | 313k | aiVector3D diag = (v1-v); |
333 | 313k | aiVector3D right = (v2-v); |
334 | | |
335 | 313k | left.Normalize(); |
336 | 313k | diag.Normalize(); |
337 | 313k | right.Normalize(); |
338 | | |
339 | 313k | const float angle = std::acos(left*diag) + std::acos(right*diag); |
340 | 313k | if (angle > AI_MATH_PI_F) { |
341 | | // this is the concave point |
342 | 20.5k | start_vertex = i; |
343 | 20.5k | break; |
344 | 20.5k | } |
345 | 313k | } |
346 | | |
347 | 87.3k | const unsigned int temp[] = {face.mIndices[0], face.mIndices[1], face.mIndices[2], face.mIndices[3]}; |
348 | | |
349 | 87.3k | aiFace& nface = *curOut++; |
350 | 87.3k | nface.mNumIndices = 3; |
351 | 87.3k | nface.mIndices = face.mIndices; |
352 | | |
353 | 87.3k | nface.mIndices[0] = temp[start_vertex]; |
354 | 87.3k | nface.mIndices[1] = temp[(start_vertex + 1) % 4]; |
355 | 87.3k | nface.mIndices[2] = temp[(start_vertex + 2) % 4]; |
356 | | |
357 | 87.3k | aiFace& sface = *curOut++; |
358 | 87.3k | sface.mNumIndices = 3; |
359 | 87.3k | sface.mIndices = new unsigned int[3]; |
360 | | |
361 | 87.3k | sface.mIndices[0] = temp[start_vertex]; |
362 | 87.3k | sface.mIndices[1] = temp[(start_vertex + 2) % 4]; |
363 | 87.3k | sface.mIndices[2] = temp[(start_vertex + 3) % 4]; |
364 | | |
365 | | // prevent double deletion of the indices field |
366 | 87.3k | face.mIndices = nullptr; |
367 | | |
368 | 87.3k | ngonEncoder.ngonEncodeQuad(&nface, &sface); |
369 | | |
370 | 87.3k | continue; |
371 | 87.3k | } |
372 | 5.36k | else |
373 | 5.36k | { |
374 | | // A polygon with more than 3 vertices can be either concave or convex. |
375 | | // Usually everything we're getting is convex and we could easily |
376 | | // triangulate by tri-fanning. However, LightWave is probably the only |
377 | | // modeling suite to make extensive use of highly concave, monster polygons ... |
378 | | // so we need to apply the full 'ear cutting' algorithm to get it right. |
379 | | |
380 | | // REQUIREMENT: polygon is expected to be simple and *nearly* planar. |
381 | | // We project it onto a plane to get a 2d triangle. |
382 | | |
383 | | // Collect all vertices of of the polygon. |
384 | 160k | for (unsigned int tmp = 0; tmp < num; ++tmp) { |
385 | 154k | temp_verts3d[tmp] = verts[idx[tmp]]; |
386 | 154k | } |
387 | | |
388 | | // Get newell normal of the polygon. Store it for future use if it's a polygon-only mesh |
389 | 5.36k | aiVector3D n; |
390 | 5.36k | NewellNormal<3, 3, 3>(n, num, &temp_verts3d.front().x, &temp_verts3d.front().y, &temp_verts3d.front().z); |
391 | 5.36k | if (nor_out) { |
392 | 0 | for (unsigned int tmp = 0; tmp < num; ++tmp) |
393 | 0 | nor_out[idx[tmp]] = n; |
394 | 0 | } |
395 | | |
396 | | // Select largest normal coordinate to ignore for projection |
397 | 5.36k | const float ax = (n.x>0 ? n.x : -n.x); |
398 | 5.36k | const float ay = (n.y>0 ? n.y : -n.y); |
399 | 5.36k | const float az = (n.z>0 ? n.z : -n.z); |
400 | | |
401 | 5.36k | unsigned int ac = 0, bc = 1; /* no z coord. projection to xy */ |
402 | 5.36k | float inv = n.z; |
403 | 5.36k | if (ax > ay) { |
404 | 573 | if (ax > az) { /* no x coord. projection to yz */ |
405 | 7 | ac = 1; bc = 2; |
406 | 7 | inv = n.x; |
407 | 7 | } |
408 | 573 | } |
409 | 4.78k | else if (ay > az) { /* no y coord. projection to zy */ |
410 | 2.39k | ac = 2; bc = 0; |
411 | 2.39k | inv = n.y; |
412 | 2.39k | } |
413 | | |
414 | | // Swap projection axes to take the negated projection vector into account |
415 | 5.36k | if (inv < 0.f) { |
416 | 1.89k | std::swap(ac,bc); |
417 | 1.89k | } |
418 | | |
419 | 5.36k | temp_verts.resize(num); |
420 | 160k | for (unsigned int tmp = 0; tmp < num; ++tmp) { |
421 | 154k | temp_verts[tmp].x = verts[idx[tmp]][ac]; |
422 | 154k | temp_verts[tmp].y = verts[idx[tmp]][bc]; |
423 | 154k | } |
424 | | |
425 | 5.36k | auto indices = mapbox::earcut(temp_poly); |
426 | 17.4k | for (size_t i = 0; i < indices.size(); i += 3) { |
427 | 12.0k | aiFace& nface = *curOut++; |
428 | 12.0k | nface.mIndices = new unsigned int[3]; |
429 | 12.0k | nface.mNumIndices = 3; |
430 | 12.0k | nface.mIndices[0] = indices[i]; |
431 | 12.0k | nface.mIndices[1] = indices[i + 1]; |
432 | 12.0k | nface.mIndices[2] = indices[i + 2]; |
433 | 12.0k | } |
434 | | |
435 | | #ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS |
436 | | // plot the plane onto which we mapped the polygon to a 2D ASCII pic |
437 | | aiVector2D bmin,bmax; |
438 | | ArrayBounds(&temp_verts[0],max,bmin,bmax); |
439 | | |
440 | | char grid[POLY_GRID_Y][POLY_GRID_X+POLY_GRID_XPAD]; |
441 | | std::fill_n((char*)grid,POLY_GRID_Y*(POLY_GRID_X+POLY_GRID_XPAD),' '); |
442 | | |
443 | | for (int i =0; i < max; ++i) { |
444 | | const aiVector2D& v = (temp_verts[i] - bmin) / (bmax-bmin); |
445 | | const size_t x = static_cast<size_t>(v.x*(POLY_GRID_X-1)), y = static_cast<size_t>(v.y*(POLY_GRID_Y-1)); |
446 | | char* loc = grid[y]+x; |
447 | | if (grid[y][x] != ' ') { |
448 | | for(;*loc != ' '; ++loc); |
449 | | *loc++ = '_'; |
450 | | } |
451 | | *(loc+::ai_snprintf(loc, POLY_GRID_XPAD,"%i",i)) = ' '; |
452 | | } |
453 | | |
454 | | |
455 | | for(size_t y = 0; y < POLY_GRID_Y; ++y) { |
456 | | grid[y][POLY_GRID_X+POLY_GRID_XPAD-1] = '\0'; |
457 | | fprintf(fout,"%s\n",grid[y]); |
458 | | } |
459 | | |
460 | | fprintf(fout,"\ntriangulation sequence: "); |
461 | | #endif |
462 | 5.36k | } |
463 | | |
464 | | #ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS |
465 | | |
466 | | for(aiFace* f = last_face; f != curOut; ++f) { |
467 | | unsigned int* i = f->mIndices; |
468 | | fprintf(fout," (%i %i %i)",i[0],i[1],i[2]); |
469 | | } |
470 | | |
471 | | fprintf(fout,"\n*********************************************************************\n"); |
472 | | fflush(fout); |
473 | | |
474 | | #endif |
475 | | |
476 | 17.4k | for(aiFace* f = last_face; f != curOut; ) { |
477 | 12.0k | unsigned int* i = f->mIndices; |
478 | | |
479 | 12.0k | i[0] = idx[i[0]]; |
480 | 12.0k | i[1] = idx[i[1]]; |
481 | 12.0k | i[2] = idx[i[2]]; |
482 | | |
483 | | // IMPROVEMENT: Polygons are not supported yet by this ngon encoding + triangulation step. |
484 | | // So we encode polygons as regular triangles. No way to reconstruct the original |
485 | | // polygon in this case. |
486 | 12.0k | ngonEncoder.ngonEncodeTriangle(f); |
487 | 12.0k | ++f; |
488 | 12.0k | } |
489 | | |
490 | 5.36k | delete[] face.mIndices; |
491 | 5.36k | face.mIndices = nullptr; |
492 | 5.36k | } |
493 | | |
494 | | #ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS |
495 | | fclose(fout); |
496 | | #endif |
497 | | |
498 | | // kill the old faces |
499 | 4.84k | delete [] pMesh->mFaces; |
500 | | |
501 | | // ... and store the new ones |
502 | 4.84k | pMesh->mFaces = out; |
503 | 4.84k | pMesh->mNumFaces = (unsigned int)(curOut-out); /* not necessarily equal to numOut */ |
504 | 4.84k | return true; |
505 | 4.84k | } |
506 | | |
507 | | #endif // !! ASSIMP_BUILD_NO_TRIANGULATE_PROCESS |