vrshoot
diff libs/assimp/TriangulateProcess.cpp @ 0:b2f14e535253
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 01 Feb 2014 19:58:19 +0200 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libs/assimp/TriangulateProcess.cpp Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,527 @@ 1.4 +/* 1.5 +--------------------------------------------------------------------------- 1.6 +Open Asset Import Library (assimp) 1.7 +--------------------------------------------------------------------------- 1.8 + 1.9 +Copyright (c) 2006-2012, assimp team 1.10 + 1.11 +All rights reserved. 1.12 + 1.13 +Redistribution and use of this software in source and binary forms, 1.14 +with or without modification, are permitted provided that the following 1.15 +conditions are met: 1.16 + 1.17 +* Redistributions of source code must retain the above 1.18 + copyright notice, this list of conditions and the 1.19 + following disclaimer. 1.20 + 1.21 +* Redistributions in binary form must reproduce the above 1.22 + copyright notice, this list of conditions and the 1.23 + following disclaimer in the documentation and/or other 1.24 + materials provided with the distribution. 1.25 + 1.26 +* Neither the name of the assimp team, nor the names of its 1.27 + contributors may be used to endorse or promote products 1.28 + derived from this software without specific prior 1.29 + written permission of the assimp team. 1.30 + 1.31 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.32 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.33 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.34 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.35 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.36 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.37 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.38 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.39 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.40 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.41 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.42 +--------------------------------------------------------------------------- 1.43 +*/ 1.44 + 1.45 +/** @file TriangulateProcess.cpp 1.46 + * @brief Implementation of the post processing step to split up 1.47 + * all faces with more than three indices into triangles. 1.48 + * 1.49 + * 1.50 + * The triangulation algorithm will handle concave or convex polygons. 1.51 + * Self-intersecting or non-planar polygons are not rejected, but 1.52 + * they're probably not triangulated correctly. 1.53 + * 1.54 + * DEBUG SWITCHES - do not enable any of them in release builds: 1.55 + * 1.56 + * AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING 1.57 + * - generates vertex colors to represent the face winding order. 1.58 + * the first vertex of a polygon becomes red, the last blue. 1.59 + * AI_BUILD_TRIANGULATE_DEBUG_POLYS 1.60 + * - dump all polygons and their triangulation sequences to 1.61 + * a file 1.62 + */ 1.63 + 1.64 +#include "AssimpPCH.h" 1.65 + 1.66 +#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS 1.67 +#include "TriangulateProcess.h" 1.68 +#include "ProcessHelper.h" 1.69 +#include "PolyTools.h" 1.70 + 1.71 +//#define AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING 1.72 +//#define AI_BUILD_TRIANGULATE_DEBUG_POLYS 1.73 + 1.74 +#define POLY_GRID_Y 40 1.75 +#define POLY_GRID_X 70 1.76 +#define POLY_GRID_XPAD 20 1.77 +#define POLY_OUTPUT_FILE "assimp_polygons_debug.txt" 1.78 + 1.79 +using namespace Assimp; 1.80 + 1.81 +// ------------------------------------------------------------------------------------------------ 1.82 +// Constructor to be privately used by Importer 1.83 +TriangulateProcess::TriangulateProcess() 1.84 +{ 1.85 + // nothing to do here 1.86 +} 1.87 + 1.88 +// ------------------------------------------------------------------------------------------------ 1.89 +// Destructor, private as well 1.90 +TriangulateProcess::~TriangulateProcess() 1.91 +{ 1.92 + // nothing to do here 1.93 +} 1.94 + 1.95 +// ------------------------------------------------------------------------------------------------ 1.96 +// Returns whether the processing step is present in the given flag field. 1.97 +bool TriangulateProcess::IsActive( unsigned int pFlags) const 1.98 +{ 1.99 + return (pFlags & aiProcess_Triangulate) != 0; 1.100 +} 1.101 + 1.102 +// ------------------------------------------------------------------------------------------------ 1.103 +// Executes the post processing step on the given imported data. 1.104 +void TriangulateProcess::Execute( aiScene* pScene) 1.105 +{ 1.106 + DefaultLogger::get()->debug("TriangulateProcess begin"); 1.107 + 1.108 + bool bHas = false; 1.109 + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) 1.110 + { 1.111 + if( TriangulateMesh( pScene->mMeshes[a])) 1.112 + bHas = true; 1.113 + } 1.114 + if (bHas)DefaultLogger::get()->info ("TriangulateProcess finished. All polygons have been triangulated."); 1.115 + else DefaultLogger::get()->debug("TriangulateProcess finished. There was nothing to be done."); 1.116 +} 1.117 + 1.118 + 1.119 +// ------------------------------------------------------------------------------------------------ 1.120 +// Triangulates the given mesh. 1.121 +bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) 1.122 +{ 1.123 + // Now we have aiMesh::mPrimitiveTypes, so this is only here for test cases 1.124 + if (!pMesh->mPrimitiveTypes) { 1.125 + bool bNeed = false; 1.126 + 1.127 + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { 1.128 + const aiFace& face = pMesh->mFaces[a]; 1.129 + 1.130 + if( face.mNumIndices != 3) { 1.131 + bNeed = true; 1.132 + } 1.133 + } 1.134 + if (!bNeed) 1.135 + return false; 1.136 + } 1.137 + else if (!(pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) { 1.138 + return false; 1.139 + } 1.140 + 1.141 + // Find out how many output faces we'll get 1.142 + unsigned int numOut = 0, max_out = 0; 1.143 + bool get_normals = true; 1.144 + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { 1.145 + aiFace& face = pMesh->mFaces[a]; 1.146 + if (face.mNumIndices <= 4) { 1.147 + get_normals = false; 1.148 + } 1.149 + if( face.mNumIndices <= 3) { 1.150 + numOut++; 1.151 + 1.152 + } 1.153 + else { 1.154 + numOut += face.mNumIndices-2; 1.155 + max_out = std::max(max_out,face.mNumIndices); 1.156 + } 1.157 + } 1.158 + 1.159 + // Just another check whether aiMesh::mPrimitiveTypes is correct 1.160 + assert(numOut != pMesh->mNumFaces); 1.161 + 1.162 + aiVector3D* nor_out = NULL; 1.163 + 1.164 + // if we don't have normals yet, but expect them to be a cheap side 1.165 + // product of triangulation anyway, allocate storage for them. 1.166 + if (!pMesh->mNormals && get_normals) { 1.167 + // XXX need a mechanism to inform the GenVertexNormals process to treat these normals as preprocessed per-face normals 1.168 + // nor_out = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; 1.169 + } 1.170 + 1.171 + // the output mesh will contain triangles, but no polys anymore 1.172 + pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; 1.173 + pMesh->mPrimitiveTypes &= ~aiPrimitiveType_POLYGON; 1.174 + 1.175 + aiFace* out = new aiFace[numOut](), *curOut = out; 1.176 + std::vector<aiVector3D> temp_verts3d(max_out+2); /* temporary storage for vertices */ 1.177 + std::vector<aiVector2D> temp_verts(max_out+2); 1.178 + 1.179 + // Apply vertex colors to represent the face winding? 1.180 +#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING 1.181 + if (!pMesh->mColors[0]) 1.182 + pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices]; 1.183 + else 1.184 + new(pMesh->mColors[0]) aiColor4D[pMesh->mNumVertices]; 1.185 + 1.186 + aiColor4D* clr = pMesh->mColors[0]; 1.187 +#endif 1.188 + 1.189 + 1.190 +#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS 1.191 + FILE* fout = fopen(POLY_OUTPUT_FILE,"a"); 1.192 +#endif 1.193 + 1.194 + const aiVector3D* verts = pMesh->mVertices; 1.195 + 1.196 + // use boost::scoped_array to avoid slow std::vector<bool> specialiations 1.197 + boost::scoped_array<bool> done(new bool[max_out]); 1.198 + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { 1.199 + aiFace& face = pMesh->mFaces[a]; 1.200 + 1.201 + unsigned int* idx = face.mIndices; 1.202 + int num = (int)face.mNumIndices, ear = 0, tmp, prev = num-1, next = 0, max = num; 1.203 + 1.204 + // Apply vertex colors to represent the face winding? 1.205 +#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING 1.206 + for (unsigned int i = 0; i < face.mNumIndices; ++i) { 1.207 + aiColor4D& c = clr[idx[i]]; 1.208 + c.r = (i+1) / (float)max; 1.209 + c.b = 1.f - c.r; 1.210 + } 1.211 +#endif 1.212 + 1.213 + aiFace* const last_face = curOut; 1.214 + 1.215 + // if it's a simple point,line or triangle: just copy it 1.216 + if( face.mNumIndices <= 3) 1.217 + { 1.218 + aiFace& nface = *curOut++; 1.219 + nface.mNumIndices = face.mNumIndices; 1.220 + nface.mIndices = face.mIndices; 1.221 + 1.222 + face.mIndices = NULL; 1.223 + continue; 1.224 + } 1.225 + // optimized code for quadrilaterals 1.226 + else if ( face.mNumIndices == 4) { 1.227 + 1.228 + // quads can have at maximum one concave vertex. Determine 1.229 + // this vertex (if it exists) and start tri-fanning from 1.230 + // it. 1.231 + unsigned int start_vertex = 0; 1.232 + for (unsigned int i = 0; i < 4; ++i) { 1.233 + const aiVector3D& v0 = verts[face.mIndices[(i+3) % 4]]; 1.234 + const aiVector3D& v1 = verts[face.mIndices[(i+2) % 4]]; 1.235 + const aiVector3D& v2 = verts[face.mIndices[(i+1) % 4]]; 1.236 + 1.237 + const aiVector3D& v = verts[face.mIndices[i]]; 1.238 + 1.239 + aiVector3D left = (v0-v); 1.240 + aiVector3D diag = (v1-v); 1.241 + aiVector3D right = (v2-v); 1.242 + 1.243 + left.Normalize(); 1.244 + diag.Normalize(); 1.245 + right.Normalize(); 1.246 + 1.247 + const float angle = acos(left*diag) + acos(right*diag); 1.248 + if (angle > AI_MATH_PI_F) { 1.249 + // this is the concave point 1.250 + start_vertex = i; 1.251 + break; 1.252 + } 1.253 + } 1.254 + 1.255 + const unsigned int temp[] = {face.mIndices[0], face.mIndices[1], face.mIndices[2], face.mIndices[3]}; 1.256 + 1.257 + aiFace& nface = *curOut++; 1.258 + nface.mNumIndices = 3; 1.259 + nface.mIndices = face.mIndices; 1.260 + 1.261 + nface.mIndices[0] = temp[start_vertex]; 1.262 + nface.mIndices[1] = temp[(start_vertex + 1) % 4]; 1.263 + nface.mIndices[2] = temp[(start_vertex + 2) % 4]; 1.264 + 1.265 + aiFace& sface = *curOut++; 1.266 + sface.mNumIndices = 3; 1.267 + sface.mIndices = new unsigned int[3]; 1.268 + 1.269 + sface.mIndices[0] = temp[start_vertex]; 1.270 + sface.mIndices[1] = temp[(start_vertex + 2) % 4]; 1.271 + sface.mIndices[2] = temp[(start_vertex + 3) % 4]; 1.272 + 1.273 + // prevent double deletion of the indices field 1.274 + face.mIndices = NULL; 1.275 + continue; 1.276 + } 1.277 + else 1.278 + { 1.279 + // A polygon with more than 3 vertices can be either concave or convex. 1.280 + // Usually everything we're getting is convex and we could easily 1.281 + // triangulate by trifanning. However, LightWave is probably the only 1.282 + // modeling suite to make extensive use of highly concave, monster polygons ... 1.283 + // so we need to apply the full 'ear cutting' algorithm to get it right. 1.284 + 1.285 + // RERQUIREMENT: polygon is expected to be simple and *nearly* planar. 1.286 + // We project it onto a plane to get a 2d triangle. 1.287 + 1.288 + // Collect all vertices of of the polygon. 1.289 + for (tmp = 0; tmp < max; ++tmp) { 1.290 + temp_verts3d[tmp] = verts[idx[tmp]]; 1.291 + } 1.292 + 1.293 + // Get newell normal of the polygon. Store it for future use if it's a polygon-only mesh 1.294 + aiVector3D n; 1.295 + NewellNormal<3,3,3>(n,max,&temp_verts3d.front().x,&temp_verts3d.front().y,&temp_verts3d.front().z); 1.296 + if (nor_out) { 1.297 + for (tmp = 0; tmp < max; ++tmp) 1.298 + nor_out[idx[tmp]] = n; 1.299 + } 1.300 + 1.301 + // Select largest normal coordinate to ignore for projection 1.302 + const float ax = (n.x>0 ? n.x : -n.x); 1.303 + const float ay = (n.y>0 ? n.y : -n.y); 1.304 + const float az = (n.z>0 ? n.z : -n.z); 1.305 + 1.306 + unsigned int ac = 0, bc = 1; /* no z coord. projection to xy */ 1.307 + float inv = n.z; 1.308 + if (ax > ay) { 1.309 + if (ax > az) { /* no x coord. projection to yz */ 1.310 + ac = 1; bc = 2; 1.311 + inv = n.x; 1.312 + } 1.313 + } 1.314 + else if (ay > az) { /* no y coord. projection to zy */ 1.315 + ac = 2; bc = 0; 1.316 + inv = n.y; 1.317 + } 1.318 + 1.319 + // Swap projection axes to take the negated projection vector into account 1.320 + if (inv < 0.f) { 1.321 + std::swap(ac,bc); 1.322 + } 1.323 + 1.324 + for (tmp =0; tmp < max; ++tmp) { 1.325 + temp_verts[tmp].x = verts[idx[tmp]][ac]; 1.326 + temp_verts[tmp].y = verts[idx[tmp]][bc]; 1.327 + done[tmp] = false; 1.328 + } 1.329 + 1.330 + 1.331 +#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS 1.332 + // plot the plane onto which we mapped the polygon to a 2D ASCII pic 1.333 + aiVector2D bmin,bmax; 1.334 + ArrayBounds(&temp_verts[0],max,bmin,bmax); 1.335 + 1.336 + char grid[POLY_GRID_Y][POLY_GRID_X+POLY_GRID_XPAD]; 1.337 + std::fill_n((char*)grid,POLY_GRID_Y*(POLY_GRID_X+POLY_GRID_XPAD),' '); 1.338 + 1.339 + for (int i =0; i < max; ++i) { 1.340 + const aiVector2D& v = (temp_verts[i] - bmin) / (bmax-bmin); 1.341 + 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)); 1.342 + char* loc = grid[y]+x; 1.343 + if (grid[y][x] != ' ') { 1.344 + for(;*loc != ' '; ++loc); 1.345 + *loc++ = '_'; 1.346 + } 1.347 + *(loc+sprintf(loc,"%i",i)) = ' '; 1.348 + } 1.349 + 1.350 + 1.351 + for(size_t y = 0; y < POLY_GRID_Y; ++y) { 1.352 + grid[y][POLY_GRID_X+POLY_GRID_XPAD-1] = '\0'; 1.353 + fprintf(fout,"%s\n",grid[y]); 1.354 + } 1.355 + 1.356 + fprintf(fout,"\ntriangulation sequence: "); 1.357 +#endif 1.358 + 1.359 + // 1.360 + // FIXME: currently this is the slow O(kn) variant with a worst case 1.361 + // complexity of O(n^2) (I think). Can be done in O(n). 1.362 + while (num > 3) { 1.363 + 1.364 + // Find the next ear of the polygon 1.365 + int num_found = 0; 1.366 + for (ear = next;;prev = ear,ear = next) { 1.367 + 1.368 + // break after we looped two times without a positive match 1.369 + for (next=ear+1;done[(next>=max?next=0:next)];++next); 1.370 + if (next < ear) { 1.371 + if (++num_found == 2) { 1.372 + break; 1.373 + } 1.374 + } 1.375 + const aiVector2D* pnt1 = &temp_verts[ear], 1.376 + *pnt0 = &temp_verts[prev], 1.377 + *pnt2 = &temp_verts[next]; 1.378 + 1.379 + // Must be a convex point. Assuming ccw winding, it must be on the right of the line between p-1 and p+1. 1.380 + if (OnLeftSideOfLine2D(*pnt0,*pnt2,*pnt1)) { 1.381 + continue; 1.382 + } 1.383 + 1.384 + // and no other point may be contained in this triangle 1.385 + for ( tmp = 0; tmp < max; ++tmp) { 1.386 + 1.387 + // We need to compare the actual values because it's possible that multiple indexes in 1.388 + // the polygon are referring to the same position. concave_polygon.obj is a sample 1.389 + // 1.390 + // FIXME: Use 'epsiloned' comparisons instead? Due to numeric inaccuracies in 1.391 + // PointInTriangle() I'm guessing that it's actually possible to construct 1.392 + // input data that would cause us to end up with no ears. The problem is, 1.393 + // which epsilon? If we chose a too large value, we'd get wrong results 1.394 + const aiVector2D& vtmp = temp_verts[tmp]; 1.395 + if ( vtmp != *pnt1 && vtmp != *pnt2 && vtmp != *pnt0 && PointInTriangle2D(*pnt0,*pnt1,*pnt2,vtmp)) { 1.396 + break; 1.397 + } 1.398 + } 1.399 + if (tmp != max) { 1.400 + continue; 1.401 + } 1.402 + 1.403 + // this vertex is an ear 1.404 + break; 1.405 + } 1.406 + if (num_found == 2) { 1.407 + 1.408 + // Due to the 'two ear theorem', every simple polygon with more than three points must 1.409 + // have 2 'ears'. Here's definitely someting wrong ... but we don't give up yet. 1.410 + // 1.411 + 1.412 + // Instead we're continuting with the standard trifanning algorithm which we'd 1.413 + // use if we had only convex polygons. That's life. 1.414 + DefaultLogger::get()->error("Failed to triangulate polygon (no ear found). Probably not a simple polygon?"); 1.415 + 1.416 + 1.417 +#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS 1.418 + fprintf(fout,"critical error here, no ear found! "); 1.419 +#endif 1.420 + num = 0; 1.421 + break; 1.422 + 1.423 + curOut -= (max-num); /* undo all previous work */ 1.424 + for (tmp = 0; tmp < max-2; ++tmp) { 1.425 + aiFace& nface = *curOut++; 1.426 + 1.427 + nface.mNumIndices = 3; 1.428 + if (!nface.mIndices) 1.429 + nface.mIndices = new unsigned int[3]; 1.430 + 1.431 + nface.mIndices[0] = 0; 1.432 + nface.mIndices[1] = tmp+1; 1.433 + nface.mIndices[2] = tmp+2; 1.434 + 1.435 + } 1.436 + num = 0; 1.437 + break; 1.438 + } 1.439 + 1.440 + aiFace& nface = *curOut++; 1.441 + nface.mNumIndices = 3; 1.442 + 1.443 + if (!nface.mIndices) { 1.444 + nface.mIndices = new unsigned int[3]; 1.445 + } 1.446 + 1.447 + // setup indices for the new triangle ... 1.448 + nface.mIndices[0] = prev; 1.449 + nface.mIndices[1] = ear; 1.450 + nface.mIndices[2] = next; 1.451 + 1.452 + // exclude the ear from most further processing 1.453 + done[ear] = true; 1.454 + --num; 1.455 + } 1.456 + if (num > 0) { 1.457 + // We have three indices forming the last 'ear' remaining. Collect them. 1.458 + aiFace& nface = *curOut++; 1.459 + nface.mNumIndices = 3; 1.460 + if (!nface.mIndices) { 1.461 + nface.mIndices = new unsigned int[3]; 1.462 + } 1.463 + 1.464 + for (tmp = 0; done[tmp]; ++tmp); 1.465 + nface.mIndices[0] = tmp; 1.466 + 1.467 + for (++tmp; done[tmp]; ++tmp); 1.468 + nface.mIndices[1] = tmp; 1.469 + 1.470 + for (++tmp; done[tmp]; ++tmp); 1.471 + nface.mIndices[2] = tmp; 1.472 + 1.473 + } 1.474 + } 1.475 + 1.476 +#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS 1.477 + 1.478 + for(aiFace* f = last_face; f != curOut; ++f) { 1.479 + unsigned int* i = f->mIndices; 1.480 + fprintf(fout," (%i %i %i)",i[0],i[1],i[2]); 1.481 + } 1.482 + 1.483 + fprintf(fout,"\n*********************************************************************\n"); 1.484 + fflush(fout); 1.485 + 1.486 +#endif 1.487 + 1.488 + for(aiFace* f = last_face; f != curOut; ) { 1.489 + unsigned int* i = f->mIndices; 1.490 + 1.491 + // drop dumb 0-area triangles 1.492 + if (fabs(GetArea2D(temp_verts[i[0]],temp_verts[i[1]],temp_verts[i[2]])) < 1e-5f) { 1.493 + DefaultLogger::get()->debug("Dropping triangle with area 0"); 1.494 + --curOut; 1.495 + 1.496 + delete[] f->mIndices; 1.497 + f->mIndices = NULL; 1.498 + 1.499 + for(aiFace* ff = f; ff != curOut; ++ff) { 1.500 + ff->mNumIndices = (ff+1)->mNumIndices; 1.501 + ff->mIndices = (ff+1)->mIndices; 1.502 + (ff+1)->mIndices = NULL; 1.503 + } 1.504 + continue; 1.505 + } 1.506 + 1.507 + i[0] = idx[i[0]]; 1.508 + i[1] = idx[i[1]]; 1.509 + i[2] = idx[i[2]]; 1.510 + ++f; 1.511 + } 1.512 + 1.513 + delete[] face.mIndices; 1.514 + face.mIndices = NULL; 1.515 + } 1.516 + 1.517 +#ifdef AI_BUILD_TRIANGULATE_DEBUG_POLYS 1.518 + fclose(fout); 1.519 +#endif 1.520 + 1.521 + // kill the old faces 1.522 + delete [] pMesh->mFaces; 1.523 + 1.524 + // ... and store the new ones 1.525 + pMesh->mFaces = out; 1.526 + pMesh->mNumFaces = (unsigned int)(curOut-out); /* not necessarily equal to numOut */ 1.527 + return true; 1.528 +} 1.529 + 1.530 +#endif // !! ASSIMP_BUILD_NO_TRIANGULATE_PROCESS