vrshoot
diff libs/assimp/Subdivision.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/Subdivision.cpp Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,587 @@ 1.4 +/* 1.5 +Open Asset Import Library (assimp) 1.6 +---------------------------------------------------------------------- 1.7 + 1.8 +Copyright (c) 2006-2012, assimp team 1.9 +All rights reserved. 1.10 + 1.11 +Redistribution and use of this software in source and binary forms, 1.12 +with or without modification, are permitted provided that the 1.13 +following conditions are met: 1.14 + 1.15 +* Redistributions of source code must retain the above 1.16 + copyright notice, this list of conditions and the 1.17 + following disclaimer. 1.18 + 1.19 +* Redistributions in binary form must reproduce the above 1.20 + copyright notice, this list of conditions and the 1.21 + following disclaimer in the documentation and/or other 1.22 + materials provided with the distribution. 1.23 + 1.24 +* Neither the name of the assimp team, nor the names of its 1.25 + contributors may be used to endorse or promote products 1.26 + derived from this software without specific prior 1.27 + written permission of the assimp team. 1.28 + 1.29 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.30 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.31 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.32 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.33 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.34 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.35 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.36 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.37 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.38 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.39 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.40 + 1.41 +---------------------------------------------------------------------- 1.42 +*/ 1.43 + 1.44 +#include "AssimpPCH.h" 1.45 + 1.46 +#include "Subdivision.h" 1.47 +#include "SceneCombiner.h" 1.48 +#include "SpatialSort.h" 1.49 +#include "ProcessHelper.h" 1.50 +#include "Vertex.h" 1.51 + 1.52 +using namespace Assimp; 1.53 +void mydummy() {} 1.54 + 1.55 +// ------------------------------------------------------------------------------------------------ 1.56 +/** Subdivider stub class to implement the Catmull-Clarke subdivision algorithm. The 1.57 + * implementation is basing on recursive refinement. Directly evaluating the result is also 1.58 + * possible and much quicker, but it depends on lengthy matrix lookup tables. */ 1.59 +// ------------------------------------------------------------------------------------------------ 1.60 +class CatmullClarkSubdivider : public Subdivider 1.61 +{ 1.62 + 1.63 +public: 1.64 + 1.65 + void Subdivide (aiMesh* mesh, aiMesh*& out, unsigned int num, bool discard_input); 1.66 + void Subdivide (aiMesh** smesh, size_t nmesh, 1.67 + aiMesh** out, unsigned int num, bool discard_input); 1.68 + 1.69 + // --------------------------------------------------------------------------- 1.70 + /** Intermediate description of an edge between two corners of a polygon*/ 1.71 + // --------------------------------------------------------------------------- 1.72 + struct Edge 1.73 + { 1.74 + Edge() 1.75 + : ref(0) 1.76 + {} 1.77 + Vertex edge_point, midpoint; 1.78 + unsigned int ref; 1.79 + }; 1.80 + 1.81 + 1.82 + 1.83 + typedef std::vector<unsigned int> UIntVector; 1.84 + typedef std::map<uint64_t,Edge> EdgeMap; 1.85 + 1.86 + // --------------------------------------------------------------------------- 1.87 + // Hashing function to derive an index into an #EdgeMap from two given 1.88 + // 'unsigned int' vertex coordinates (!!distinct coordinates - same 1.89 + // vertex position == same index!!). 1.90 + // NOTE - this leads to rare hash collisions if a) sizeof(unsigned int)>4 1.91 + // and (id[0]>2^32-1 or id[0]>2^32-1). 1.92 + // MAKE_EDGE_HASH() uses temporaries, so INIT_EDGE_HASH() needs to be put 1.93 + // at the head of every function which is about to use MAKE_EDGE_HASH(). 1.94 + // Reason is that the hash is that hash construction needs to hold the 1.95 + // invariant id0<id1 to identify an edge - else two hashes would refer 1.96 + // to the same edge. 1.97 + // --------------------------------------------------------------------------- 1.98 +#define MAKE_EDGE_HASH(id0,id1) (eh_tmp0__=id0,eh_tmp1__=id1,\ 1.99 + (eh_tmp0__<eh_tmp1__?std::swap(eh_tmp0__,eh_tmp1__):mydummy()),(uint64_t)eh_tmp0__^((uint64_t)eh_tmp1__<<32u)) 1.100 + 1.101 + 1.102 +#define INIT_EDGE_HASH_TEMPORARIES()\ 1.103 + unsigned int eh_tmp0__, eh_tmp1__; 1.104 + 1.105 +private: 1.106 + 1.107 + void InternSubdivide (const aiMesh* const * smesh, 1.108 + size_t nmesh,aiMesh** out, unsigned int num); 1.109 +}; 1.110 + 1.111 + 1.112 +// ------------------------------------------------------------------------------------------------ 1.113 +// Construct a subdivider of a specific type 1.114 +Subdivider* Subdivider::Create (Algorithm algo) 1.115 +{ 1.116 + switch (algo) 1.117 + { 1.118 + case CATMULL_CLARKE: 1.119 + return new CatmullClarkSubdivider(); 1.120 + }; 1.121 + 1.122 + ai_assert(false); 1.123 + return NULL; // shouldn't happen 1.124 +} 1.125 + 1.126 +// ------------------------------------------------------------------------------------------------ 1.127 +// Call the Catmull Clark subdivision algorithm for one mesh 1.128 +void CatmullClarkSubdivider::Subdivide ( 1.129 + aiMesh* mesh, 1.130 + aiMesh*& out, 1.131 + unsigned int num, 1.132 + bool discard_input 1.133 + ) 1.134 +{ 1.135 + assert(mesh != out); 1.136 + Subdivide(&mesh,1,&out,num,discard_input); 1.137 +} 1.138 + 1.139 +// ------------------------------------------------------------------------------------------------ 1.140 +// Call the Catmull Clark subdivision algorithm for multiple meshes 1.141 +void CatmullClarkSubdivider::Subdivide ( 1.142 + aiMesh** smesh, 1.143 + size_t nmesh, 1.144 + aiMesh** out, 1.145 + unsigned int num, 1.146 + bool discard_input 1.147 + ) 1.148 +{ 1.149 + ai_assert(NULL != smesh && NULL != out); 1.150 + 1.151 + // course, both regions may not overlap 1.152 + assert(smesh<out || smesh+nmesh>out+nmesh); 1.153 + if (!num) { 1.154 + 1.155 + // No subdivision at all. Need to copy all the meshes .. argh. 1.156 + if (discard_input) { 1.157 + for (size_t s = 0; s < nmesh; ++s) { 1.158 + out[s] = smesh[s]; 1.159 + smesh[s] = NULL; 1.160 + } 1.161 + } 1.162 + else { 1.163 + for (size_t s = 0; s < nmesh; ++s) { 1.164 + SceneCombiner::Copy(out+s,smesh[s]); 1.165 + } 1.166 + } 1.167 + return; 1.168 + } 1.169 + 1.170 + std::vector<aiMesh*> inmeshes; 1.171 + std::vector<aiMesh*> outmeshes; 1.172 + std::vector<unsigned int> maptbl; 1.173 + 1.174 + inmeshes.reserve(nmesh); 1.175 + outmeshes.reserve(nmesh); 1.176 + maptbl.reserve(nmesh); 1.177 + 1.178 + // Remove pure line and point meshes from the working set to reduce the 1.179 + // number of edge cases the subdivider is forced to deal with. Line and 1.180 + // point meshes are simply passed through. 1.181 + for (size_t s = 0; s < nmesh; ++s) { 1.182 + aiMesh* i = smesh[s]; 1.183 + // FIX - mPrimitiveTypes might not yet be initialized 1.184 + if (i->mPrimitiveTypes && (i->mPrimitiveTypes & (aiPrimitiveType_LINE|aiPrimitiveType_POINT))==i->mPrimitiveTypes) { 1.185 + DefaultLogger::get()->debug("Catmull-Clark Subdivider: Skipping pure line/point mesh"); 1.186 + 1.187 + if (discard_input) { 1.188 + out[s] = i; 1.189 + smesh[s] = NULL; 1.190 + } 1.191 + else { 1.192 + SceneCombiner::Copy(out+s,i); 1.193 + } 1.194 + continue; 1.195 + } 1.196 + 1.197 + outmeshes.push_back(NULL);inmeshes.push_back(i); 1.198 + maptbl.push_back(s); 1.199 + } 1.200 + 1.201 + // Do the actual subdivision on the preallocated storage. InternSubdivide 1.202 + // *always* assumes that enough storage is available, it does not bother 1.203 + // checking any ranges. 1.204 + ai_assert(inmeshes.size()==outmeshes.size()&&inmeshes.size()==maptbl.size()); 1.205 + if (inmeshes.empty()) { 1.206 + DefaultLogger::get()->warn("Catmull-Clark Subdivider: Pure point/line scene, I can't do anything"); 1.207 + return; 1.208 + } 1.209 + InternSubdivide(&inmeshes.front(),inmeshes.size(),&outmeshes.front(),num); 1.210 + for (unsigned int i = 0; i < maptbl.size(); ++i) { 1.211 + ai_assert(outmeshes[i]); 1.212 + out[maptbl[i]] = outmeshes[i]; 1.213 + } 1.214 + 1.215 + if (discard_input) { 1.216 + for (size_t s = 0; s < nmesh; ++s) { 1.217 + delete smesh[s]; 1.218 + } 1.219 + } 1.220 +} 1.221 + 1.222 +// ------------------------------------------------------------------------------------------------ 1.223 +// Note - this is an implementation of the standard (recursive) Cm-Cl algorithm without further 1.224 +// optimizations (except we're using some nice LUTs). A description of the algorithm can be found 1.225 +// here: http://en.wikipedia.org/wiki/Catmull-Clark_subdivision_surface 1.226 +// 1.227 +// The code is mostly O(n), however parts are O(nlogn) which is therefore the algorithm's 1.228 +// expected total runtime complexity. The implementation is able to work in-place on the same 1.229 +// mesh arrays. Calling #InternSubdivide() directly is not encouraged. The code can operate 1.230 +// in-place unless 'smesh' and 'out' are equal (no strange overlaps or reorderings). 1.231 +// Previous data is replaced/deleted then. 1.232 +// ------------------------------------------------------------------------------------------------ 1.233 +void CatmullClarkSubdivider::InternSubdivide ( 1.234 + const aiMesh* const * smesh, 1.235 + size_t nmesh, 1.236 + aiMesh** out, 1.237 + unsigned int num 1.238 + ) 1.239 +{ 1.240 + ai_assert(NULL != smesh && NULL != out); 1.241 + INIT_EDGE_HASH_TEMPORARIES(); 1.242 + 1.243 + // no subdivision requested or end of recursive refinement 1.244 + if (!num) { 1.245 + return; 1.246 + } 1.247 + 1.248 + UIntVector maptbl; 1.249 + SpatialSort spatial; 1.250 + 1.251 + // --------------------------------------------------------------------- 1.252 + // 0. Offset table to index all meshes continuously, generate a spatially 1.253 + // sorted representation of all vertices in all meshes. 1.254 + // --------------------------------------------------------------------- 1.255 + typedef std::pair<unsigned int,unsigned int> IntPair; 1.256 + std::vector<IntPair> moffsets(nmesh); 1.257 + unsigned int totfaces = 0, totvert = 0; 1.258 + for (size_t t = 0; t < nmesh; ++t) { 1.259 + const aiMesh* mesh = smesh[t]; 1.260 + 1.261 + spatial.Append(mesh->mVertices,mesh->mNumVertices,sizeof(aiVector3D),false); 1.262 + moffsets[t] = IntPair(totfaces,totvert); 1.263 + 1.264 + totfaces += mesh->mNumFaces; 1.265 + totvert += mesh->mNumVertices; 1.266 + } 1.267 + 1.268 + spatial.Finalize(); 1.269 + const unsigned int num_unique = spatial.GenerateMappingTable(maptbl,ComputePositionEpsilon(smesh,nmesh)); 1.270 + 1.271 + 1.272 +#define FLATTEN_VERTEX_IDX(mesh_idx, vert_idx) (moffsets[mesh_idx].second+vert_idx) 1.273 +#define FLATTEN_FACE_IDX(mesh_idx, face_idx) (moffsets[mesh_idx].first+face_idx) 1.274 + 1.275 + // --------------------------------------------------------------------- 1.276 + // 1. Compute the centroid point for all faces 1.277 + // --------------------------------------------------------------------- 1.278 + std::vector<Vertex> centroids(totfaces); 1.279 + unsigned int nfacesout = 0; 1.280 + for (size_t t = 0, n = 0; t < nmesh; ++t) { 1.281 + const aiMesh* mesh = smesh[t]; 1.282 + for (unsigned int i = 0; i < mesh->mNumFaces;++i,++n) 1.283 + { 1.284 + const aiFace& face = mesh->mFaces[i]; 1.285 + Vertex& c = centroids[n]; 1.286 + 1.287 + for (unsigned int a = 0; a < face.mNumIndices;++a) { 1.288 + c += Vertex(mesh,face.mIndices[a]); 1.289 + } 1.290 + 1.291 + c /= static_cast<float>(face.mNumIndices); 1.292 + nfacesout += face.mNumIndices; 1.293 + } 1.294 + } 1.295 + 1.296 + EdgeMap edges; 1.297 + 1.298 + // --------------------------------------------------------------------- 1.299 + // 2. Set each edge point to be the average of all neighbouring 1.300 + // face points and original points. Every edge exists twice 1.301 + // if there is a neighboring face. 1.302 + // --------------------------------------------------------------------- 1.303 + for (size_t t = 0; t < nmesh; ++t) { 1.304 + const aiMesh* mesh = smesh[t]; 1.305 + 1.306 + for (unsigned int i = 0; i < mesh->mNumFaces;++i) { 1.307 + const aiFace& face = mesh->mFaces[i]; 1.308 + 1.309 + for (unsigned int p =0; p< face.mNumIndices; ++p) { 1.310 + const unsigned int id[] = { 1.311 + face.mIndices[p], 1.312 + face.mIndices[p==face.mNumIndices-1?0:p+1] 1.313 + }; 1.314 + const unsigned int mp[] = { 1.315 + maptbl[FLATTEN_VERTEX_IDX(t,id[0])], 1.316 + maptbl[FLATTEN_VERTEX_IDX(t,id[1])] 1.317 + }; 1.318 + 1.319 + Edge& e = edges[MAKE_EDGE_HASH(mp[0],mp[1])]; 1.320 + e.ref++; 1.321 + if (e.ref<=2) { 1.322 + if (e.ref==1) { // original points (end points) - add only once 1.323 + e.edge_point = e.midpoint = Vertex(mesh,id[0])+Vertex(mesh,id[1]); 1.324 + e.midpoint *= 0.5f; 1.325 + } 1.326 + e.edge_point += centroids[FLATTEN_FACE_IDX(t,i)]; 1.327 + } 1.328 + } 1.329 + } 1.330 + } 1.331 + 1.332 + // --------------------------------------------------------------------- 1.333 + // 3. Normalize edge points 1.334 + // --------------------------------------------------------------------- 1.335 + {unsigned int bad_cnt = 0; 1.336 + for (EdgeMap::iterator it = edges.begin(); it != edges.end(); ++it) { 1.337 + if ((*it).second.ref < 2) { 1.338 + ai_assert((*it).second.ref); 1.339 + ++bad_cnt; 1.340 + } 1.341 + (*it).second.edge_point *= 1.f/((*it).second.ref+2.f); 1.342 + } 1.343 + 1.344 + if (bad_cnt) { 1.345 + // Report the number of bad edges. bad edges are referenced by less than two 1.346 + // faces in the mesh. They occur at outer model boundaries in non-closed 1.347 + // shapes. 1.348 + char tmp[512]; 1.349 + sprintf(tmp,"Catmull-Clark Subdivider: got %u bad edges touching only one face (totally %u edges). ", 1.350 + bad_cnt,static_cast<unsigned int>(edges.size())); 1.351 + 1.352 + DefaultLogger::get()->debug(tmp); 1.353 + }} 1.354 + 1.355 + // --------------------------------------------------------------------- 1.356 + // 4. Compute a vertex-face adjacency table. We can't reuse the code 1.357 + // from VertexTriangleAdjacency because we need the table for multiple 1.358 + // meshes and out vertex indices need to be mapped to distinct values 1.359 + // first. 1.360 + // --------------------------------------------------------------------- 1.361 + UIntVector faceadjac(nfacesout), cntadjfac(maptbl.size(),0), ofsadjvec(maptbl.size()+1,0); { 1.362 + for (size_t t = 0; t < nmesh; ++t) { 1.363 + const aiMesh* const minp = smesh[t]; 1.364 + for (unsigned int i = 0; i < minp->mNumFaces; ++i) { 1.365 + 1.366 + const aiFace& f = minp->mFaces[i]; 1.367 + for (unsigned int n = 0; n < f.mNumIndices; ++n) { 1.368 + ++cntadjfac[maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]]; 1.369 + } 1.370 + } 1.371 + } 1.372 + unsigned int cur = 0; 1.373 + for (size_t i = 0; i < cntadjfac.size(); ++i) { 1.374 + ofsadjvec[i+1] = cur; 1.375 + cur += cntadjfac[i]; 1.376 + } 1.377 + for (size_t t = 0; t < nmesh; ++t) { 1.378 + const aiMesh* const minp = smesh[t]; 1.379 + for (unsigned int i = 0; i < minp->mNumFaces; ++i) { 1.380 + 1.381 + const aiFace& f = minp->mFaces[i]; 1.382 + for (unsigned int n = 0; n < f.mNumIndices; ++n) { 1.383 + faceadjac[ofsadjvec[1+maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]]++] = FLATTEN_FACE_IDX(t,i); 1.384 + } 1.385 + } 1.386 + } 1.387 + 1.388 + // check the other way round for consistency 1.389 +#ifdef _DEBUG 1.390 + 1.391 + for (size_t t = 0; t < ofsadjvec.size()-1; ++t) { 1.392 + for (unsigned int m = 0; m < cntadjfac[t]; ++m) { 1.393 + const unsigned int fidx = faceadjac[ofsadjvec[t]+m]; 1.394 + ai_assert(fidx < totfaces); 1.395 + for (size_t n = 1; n < nmesh; ++n) { 1.396 + 1.397 + if (moffsets[n].first > fidx) { 1.398 + const aiMesh* msh = smesh[--n]; 1.399 + const aiFace& f = msh->mFaces[fidx-moffsets[n].first]; 1.400 + 1.401 + bool haveit = false; 1.402 + for (unsigned int i = 0; i < f.mNumIndices; ++i) { 1.403 + if (maptbl[FLATTEN_VERTEX_IDX(n,f.mIndices[i])]==(unsigned int)t) { 1.404 + haveit = true; break; 1.405 + } 1.406 + } 1.407 + ai_assert(haveit); 1.408 + break; 1.409 + } 1.410 + } 1.411 + } 1.412 + } 1.413 + 1.414 +#endif 1.415 + } 1.416 + 1.417 +#define GET_ADJACENT_FACES_AND_CNT(vidx,fstartout,numout) \ 1.418 + fstartout = &faceadjac[ofsadjvec[vidx]], numout = cntadjfac[vidx] 1.419 + 1.420 + typedef std::pair<bool,Vertex> TouchedOVertex; 1.421 + std::vector<TouchedOVertex > new_points(num_unique,TouchedOVertex(false,Vertex())); 1.422 + // --------------------------------------------------------------------- 1.423 + // 5. Spawn a quad from each face point to the corresponding edge points 1.424 + // the original points being the fourth quad points. 1.425 + // --------------------------------------------------------------------- 1.426 + for (size_t t = 0; t < nmesh; ++t) { 1.427 + const aiMesh* const minp = smesh[t]; 1.428 + aiMesh* const mout = out[t] = new aiMesh(); 1.429 + 1.430 + for (unsigned int a = 0; a < minp->mNumFaces; ++a) { 1.431 + mout->mNumFaces += minp->mFaces[a].mNumIndices; 1.432 + } 1.433 + 1.434 + // We need random access to the old face buffer, so reuse is not possible. 1.435 + mout->mFaces = new aiFace[mout->mNumFaces]; 1.436 + 1.437 + mout->mNumVertices = mout->mNumFaces*4; 1.438 + mout->mVertices = new aiVector3D[mout->mNumVertices]; 1.439 + 1.440 + // quads only, keep material index 1.441 + mout->mPrimitiveTypes = aiPrimitiveType_POLYGON; 1.442 + mout->mMaterialIndex = minp->mMaterialIndex; 1.443 + 1.444 + if (minp->HasNormals()) { 1.445 + mout->mNormals = new aiVector3D[mout->mNumVertices]; 1.446 + } 1.447 + 1.448 + if (minp->HasTangentsAndBitangents()) { 1.449 + mout->mTangents = new aiVector3D[mout->mNumVertices]; 1.450 + mout->mBitangents = new aiVector3D[mout->mNumVertices]; 1.451 + } 1.452 + 1.453 + for(unsigned int i = 0; minp->HasTextureCoords(i); ++i) { 1.454 + mout->mTextureCoords[i] = new aiVector3D[mout->mNumVertices]; 1.455 + mout->mNumUVComponents[i] = minp->mNumUVComponents[i]; 1.456 + } 1.457 + 1.458 + for(unsigned int i = 0; minp->HasVertexColors(i); ++i) { 1.459 + mout->mColors[i] = new aiColor4D[mout->mNumVertices]; 1.460 + } 1.461 + 1.462 + mout->mNumVertices = mout->mNumFaces<<2u; 1.463 + for (unsigned int i = 0, v = 0, n = 0; i < minp->mNumFaces;++i) { 1.464 + 1.465 + const aiFace& face = minp->mFaces[i]; 1.466 + for (unsigned int a = 0; a < face.mNumIndices;++a) { 1.467 + 1.468 + // Get a clean new face. 1.469 + aiFace& faceOut = mout->mFaces[n++]; 1.470 + faceOut.mIndices = new unsigned int [faceOut.mNumIndices = 4]; 1.471 + 1.472 + // Spawn a new quadrilateral (ccw winding) for this original point between: 1.473 + // a) face centroid 1.474 + centroids[FLATTEN_FACE_IDX(t,i)].SortBack(mout,faceOut.mIndices[0]=v++); 1.475 + 1.476 + // b) adjacent edge on the left, seen from the centroid 1.477 + const Edge& e0 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])], 1.478 + maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a==face.mNumIndices-1?0:a+1]) 1.479 + ])]; // fixme: replace with mod face.mNumIndices? 1.480 + 1.481 + // c) adjacent edge on the right, seen from the centroid 1.482 + const Edge& e1 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])], 1.483 + maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[!a?face.mNumIndices-1:a-1]) 1.484 + ])]; // fixme: replace with mod face.mNumIndices? 1.485 + 1.486 + e0.edge_point.SortBack(mout,faceOut.mIndices[3]=v++); 1.487 + e1.edge_point.SortBack(mout,faceOut.mIndices[1]=v++); 1.488 + 1.489 + // d= original point P with distinct index i 1.490 + // F := 0 1.491 + // R := 0 1.492 + // n := 0 1.493 + // for each face f containing i 1.494 + // F := F+ centroid of f 1.495 + // R := R+ midpoint of edge of f from i to i+1 1.496 + // n := n+1 1.497 + // 1.498 + // (F+2R+(n-3)P)/n 1.499 + const unsigned int org = maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])]; 1.500 + TouchedOVertex& ov = new_points[org]; 1.501 + 1.502 + if (!ov.first) { 1.503 + ov.first = true; 1.504 + 1.505 + const unsigned int* adj; unsigned int cnt; 1.506 + GET_ADJACENT_FACES_AND_CNT(org,adj,cnt); 1.507 + 1.508 + if (cnt < 3) { 1.509 + ov.second = Vertex(minp,face.mIndices[a]); 1.510 + } 1.511 + else { 1.512 + 1.513 + Vertex F,R; 1.514 + for (unsigned int o = 0; o < cnt; ++o) { 1.515 + ai_assert(adj[o] < totfaces); 1.516 + F += centroids[adj[o]]; 1.517 + 1.518 + // adj[0] is a global face index - search the face in the mesh list 1.519 + const aiMesh* mp = NULL; 1.520 + size_t nidx; 1.521 + 1.522 + if (adj[o] < moffsets[0].first) { 1.523 + mp = smesh[nidx=0]; 1.524 + } 1.525 + else { 1.526 + for (nidx = 1; nidx<= nmesh; ++nidx) { 1.527 + if (nidx == nmesh ||moffsets[nidx].first > adj[o]) { 1.528 + mp = smesh[--nidx]; 1.529 + break; 1.530 + } 1.531 + } 1.532 + } 1.533 + 1.534 + ai_assert(adj[o]-moffsets[nidx].first < mp->mNumFaces); 1.535 + const aiFace& f = mp->mFaces[adj[o]-moffsets[nidx].first]; 1.536 +# ifdef _DEBUG 1.537 + bool haveit = false; 1.538 +# endif 1.539 + 1.540 + // find our original point in the face 1.541 + for (unsigned int m = 0; m < f.mNumIndices; ++m) { 1.542 + if (maptbl[FLATTEN_VERTEX_IDX(nidx,f.mIndices[m])] == org) { 1.543 + 1.544 + // add *both* edges. this way, we can be sure that we add 1.545 + // *all* adjacent edges to R. In a closed shape, every 1.546 + // edge is added twice - so we simply leave out the 1.547 + // factor 2.f in the amove formula and get the right 1.548 + // result. 1.549 + 1.550 + const Edge& c0 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX( 1.551 + nidx,f.mIndices[!m?f.mNumIndices-1:m-1])])]; 1.552 + // fixme: replace with mod face.mNumIndices? 1.553 + 1.554 + const Edge& c1 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX( 1.555 + nidx,f.mIndices[m==f.mNumIndices-1?0:m+1])])]; 1.556 + // fixme: replace with mod face.mNumIndices? 1.557 + R += c0.midpoint+c1.midpoint; 1.558 + 1.559 +# ifdef _DEBUG 1.560 + haveit = true; 1.561 +# endif 1.562 + break; 1.563 + } 1.564 + } 1.565 + 1.566 + // this invariant *must* hold if the vertex-to-face adjacency table is valid 1.567 + ai_assert(haveit); 1.568 + } 1.569 + 1.570 + const float div = static_cast<float>(cnt), divsq = 1.f/(div*div); 1.571 + ov.second = Vertex(minp,face.mIndices[a])*((div-3.f) / div) + R*divsq + F*divsq; 1.572 + } 1.573 + } 1.574 + ov.second.SortBack(mout,faceOut.mIndices[2]=v++); 1.575 + } 1.576 + } 1.577 + } 1.578 + 1.579 + // --------------------------------------------------------------------- 1.580 + // 7. Apply the next subdivision step. 1.581 + // --------------------------------------------------------------------- 1.582 + if (num != 1) { 1.583 + std::vector<aiMesh*> tmp(nmesh); 1.584 + InternSubdivide (out,nmesh,&tmp.front(),num-1); 1.585 + for (size_t i = 0; i < nmesh; ++i) { 1.586 + delete out[i]; 1.587 + out[i] = tmp[i]; 1.588 + } 1.589 + } 1.590 +}