rev |
line source |
nuclear@0
|
1 /*
|
nuclear@0
|
2 Open Asset Import Library (assimp)
|
nuclear@0
|
3 ----------------------------------------------------------------------
|
nuclear@0
|
4
|
nuclear@0
|
5 Copyright (c) 2006-2012, assimp team
|
nuclear@0
|
6 All rights reserved.
|
nuclear@0
|
7
|
nuclear@0
|
8 Redistribution and use of this software in source and binary forms,
|
nuclear@0
|
9 with or without modification, are permitted provided that the
|
nuclear@0
|
10 following conditions are met:
|
nuclear@0
|
11
|
nuclear@0
|
12 * Redistributions of source code must retain the above
|
nuclear@0
|
13 copyright notice, this list of conditions and the
|
nuclear@0
|
14 following disclaimer.
|
nuclear@0
|
15
|
nuclear@0
|
16 * Redistributions in binary form must reproduce the above
|
nuclear@0
|
17 copyright notice, this list of conditions and the
|
nuclear@0
|
18 following disclaimer in the documentation and/or other
|
nuclear@0
|
19 materials provided with the distribution.
|
nuclear@0
|
20
|
nuclear@0
|
21 * Neither the name of the assimp team, nor the names of its
|
nuclear@0
|
22 contributors may be used to endorse or promote products
|
nuclear@0
|
23 derived from this software without specific prior
|
nuclear@0
|
24 written permission of the assimp team.
|
nuclear@0
|
25
|
nuclear@0
|
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
nuclear@0
|
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
nuclear@0
|
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
nuclear@0
|
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
nuclear@0
|
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
nuclear@0
|
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
nuclear@0
|
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
nuclear@0
|
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
nuclear@0
|
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
nuclear@0
|
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
nuclear@0
|
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
nuclear@0
|
37
|
nuclear@0
|
38 ----------------------------------------------------------------------
|
nuclear@0
|
39 */
|
nuclear@0
|
40
|
nuclear@0
|
41 #include "AssimpPCH.h"
|
nuclear@0
|
42
|
nuclear@0
|
43 #include "Subdivision.h"
|
nuclear@0
|
44 #include "SceneCombiner.h"
|
nuclear@0
|
45 #include "SpatialSort.h"
|
nuclear@0
|
46 #include "ProcessHelper.h"
|
nuclear@0
|
47 #include "Vertex.h"
|
nuclear@0
|
48
|
nuclear@0
|
49 using namespace Assimp;
|
nuclear@0
|
50 void mydummy() {}
|
nuclear@0
|
51
|
nuclear@0
|
52 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
53 /** Subdivider stub class to implement the Catmull-Clarke subdivision algorithm. The
|
nuclear@0
|
54 * implementation is basing on recursive refinement. Directly evaluating the result is also
|
nuclear@0
|
55 * possible and much quicker, but it depends on lengthy matrix lookup tables. */
|
nuclear@0
|
56 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
57 class CatmullClarkSubdivider : public Subdivider
|
nuclear@0
|
58 {
|
nuclear@0
|
59
|
nuclear@0
|
60 public:
|
nuclear@0
|
61
|
nuclear@0
|
62 void Subdivide (aiMesh* mesh, aiMesh*& out, unsigned int num, bool discard_input);
|
nuclear@0
|
63 void Subdivide (aiMesh** smesh, size_t nmesh,
|
nuclear@0
|
64 aiMesh** out, unsigned int num, bool discard_input);
|
nuclear@0
|
65
|
nuclear@0
|
66 // ---------------------------------------------------------------------------
|
nuclear@0
|
67 /** Intermediate description of an edge between two corners of a polygon*/
|
nuclear@0
|
68 // ---------------------------------------------------------------------------
|
nuclear@0
|
69 struct Edge
|
nuclear@0
|
70 {
|
nuclear@0
|
71 Edge()
|
nuclear@0
|
72 : ref(0)
|
nuclear@0
|
73 {}
|
nuclear@0
|
74 Vertex edge_point, midpoint;
|
nuclear@0
|
75 unsigned int ref;
|
nuclear@0
|
76 };
|
nuclear@0
|
77
|
nuclear@0
|
78
|
nuclear@0
|
79
|
nuclear@0
|
80 typedef std::vector<unsigned int> UIntVector;
|
nuclear@0
|
81 typedef std::map<uint64_t,Edge> EdgeMap;
|
nuclear@0
|
82
|
nuclear@0
|
83 // ---------------------------------------------------------------------------
|
nuclear@0
|
84 // Hashing function to derive an index into an #EdgeMap from two given
|
nuclear@0
|
85 // 'unsigned int' vertex coordinates (!!distinct coordinates - same
|
nuclear@0
|
86 // vertex position == same index!!).
|
nuclear@0
|
87 // NOTE - this leads to rare hash collisions if a) sizeof(unsigned int)>4
|
nuclear@0
|
88 // and (id[0]>2^32-1 or id[0]>2^32-1).
|
nuclear@0
|
89 // MAKE_EDGE_HASH() uses temporaries, so INIT_EDGE_HASH() needs to be put
|
nuclear@0
|
90 // at the head of every function which is about to use MAKE_EDGE_HASH().
|
nuclear@0
|
91 // Reason is that the hash is that hash construction needs to hold the
|
nuclear@0
|
92 // invariant id0<id1 to identify an edge - else two hashes would refer
|
nuclear@0
|
93 // to the same edge.
|
nuclear@0
|
94 // ---------------------------------------------------------------------------
|
nuclear@0
|
95 #define MAKE_EDGE_HASH(id0,id1) (eh_tmp0__=id0,eh_tmp1__=id1,\
|
nuclear@0
|
96 (eh_tmp0__<eh_tmp1__?std::swap(eh_tmp0__,eh_tmp1__):mydummy()),(uint64_t)eh_tmp0__^((uint64_t)eh_tmp1__<<32u))
|
nuclear@0
|
97
|
nuclear@0
|
98
|
nuclear@0
|
99 #define INIT_EDGE_HASH_TEMPORARIES()\
|
nuclear@0
|
100 unsigned int eh_tmp0__, eh_tmp1__;
|
nuclear@0
|
101
|
nuclear@0
|
102 private:
|
nuclear@0
|
103
|
nuclear@0
|
104 void InternSubdivide (const aiMesh* const * smesh,
|
nuclear@0
|
105 size_t nmesh,aiMesh** out, unsigned int num);
|
nuclear@0
|
106 };
|
nuclear@0
|
107
|
nuclear@0
|
108
|
nuclear@0
|
109 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
110 // Construct a subdivider of a specific type
|
nuclear@0
|
111 Subdivider* Subdivider::Create (Algorithm algo)
|
nuclear@0
|
112 {
|
nuclear@0
|
113 switch (algo)
|
nuclear@0
|
114 {
|
nuclear@0
|
115 case CATMULL_CLARKE:
|
nuclear@0
|
116 return new CatmullClarkSubdivider();
|
nuclear@0
|
117 };
|
nuclear@0
|
118
|
nuclear@0
|
119 ai_assert(false);
|
nuclear@0
|
120 return NULL; // shouldn't happen
|
nuclear@0
|
121 }
|
nuclear@0
|
122
|
nuclear@0
|
123 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
124 // Call the Catmull Clark subdivision algorithm for one mesh
|
nuclear@0
|
125 void CatmullClarkSubdivider::Subdivide (
|
nuclear@0
|
126 aiMesh* mesh,
|
nuclear@0
|
127 aiMesh*& out,
|
nuclear@0
|
128 unsigned int num,
|
nuclear@0
|
129 bool discard_input
|
nuclear@0
|
130 )
|
nuclear@0
|
131 {
|
nuclear@0
|
132 assert(mesh != out);
|
nuclear@0
|
133 Subdivide(&mesh,1,&out,num,discard_input);
|
nuclear@0
|
134 }
|
nuclear@0
|
135
|
nuclear@0
|
136 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
137 // Call the Catmull Clark subdivision algorithm for multiple meshes
|
nuclear@0
|
138 void CatmullClarkSubdivider::Subdivide (
|
nuclear@0
|
139 aiMesh** smesh,
|
nuclear@0
|
140 size_t nmesh,
|
nuclear@0
|
141 aiMesh** out,
|
nuclear@0
|
142 unsigned int num,
|
nuclear@0
|
143 bool discard_input
|
nuclear@0
|
144 )
|
nuclear@0
|
145 {
|
nuclear@0
|
146 ai_assert(NULL != smesh && NULL != out);
|
nuclear@0
|
147
|
nuclear@0
|
148 // course, both regions may not overlap
|
nuclear@0
|
149 assert(smesh<out || smesh+nmesh>out+nmesh);
|
nuclear@0
|
150 if (!num) {
|
nuclear@0
|
151
|
nuclear@0
|
152 // No subdivision at all. Need to copy all the meshes .. argh.
|
nuclear@0
|
153 if (discard_input) {
|
nuclear@0
|
154 for (size_t s = 0; s < nmesh; ++s) {
|
nuclear@0
|
155 out[s] = smesh[s];
|
nuclear@0
|
156 smesh[s] = NULL;
|
nuclear@0
|
157 }
|
nuclear@0
|
158 }
|
nuclear@0
|
159 else {
|
nuclear@0
|
160 for (size_t s = 0; s < nmesh; ++s) {
|
nuclear@0
|
161 SceneCombiner::Copy(out+s,smesh[s]);
|
nuclear@0
|
162 }
|
nuclear@0
|
163 }
|
nuclear@0
|
164 return;
|
nuclear@0
|
165 }
|
nuclear@0
|
166
|
nuclear@0
|
167 std::vector<aiMesh*> inmeshes;
|
nuclear@0
|
168 std::vector<aiMesh*> outmeshes;
|
nuclear@0
|
169 std::vector<unsigned int> maptbl;
|
nuclear@0
|
170
|
nuclear@0
|
171 inmeshes.reserve(nmesh);
|
nuclear@0
|
172 outmeshes.reserve(nmesh);
|
nuclear@0
|
173 maptbl.reserve(nmesh);
|
nuclear@0
|
174
|
nuclear@0
|
175 // Remove pure line and point meshes from the working set to reduce the
|
nuclear@0
|
176 // number of edge cases the subdivider is forced to deal with. Line and
|
nuclear@0
|
177 // point meshes are simply passed through.
|
nuclear@0
|
178 for (size_t s = 0; s < nmesh; ++s) {
|
nuclear@0
|
179 aiMesh* i = smesh[s];
|
nuclear@0
|
180 // FIX - mPrimitiveTypes might not yet be initialized
|
nuclear@0
|
181 if (i->mPrimitiveTypes && (i->mPrimitiveTypes & (aiPrimitiveType_LINE|aiPrimitiveType_POINT))==i->mPrimitiveTypes) {
|
nuclear@0
|
182 DefaultLogger::get()->debug("Catmull-Clark Subdivider: Skipping pure line/point mesh");
|
nuclear@0
|
183
|
nuclear@0
|
184 if (discard_input) {
|
nuclear@0
|
185 out[s] = i;
|
nuclear@0
|
186 smesh[s] = NULL;
|
nuclear@0
|
187 }
|
nuclear@0
|
188 else {
|
nuclear@0
|
189 SceneCombiner::Copy(out+s,i);
|
nuclear@0
|
190 }
|
nuclear@0
|
191 continue;
|
nuclear@0
|
192 }
|
nuclear@0
|
193
|
nuclear@0
|
194 outmeshes.push_back(NULL);inmeshes.push_back(i);
|
nuclear@0
|
195 maptbl.push_back(s);
|
nuclear@0
|
196 }
|
nuclear@0
|
197
|
nuclear@0
|
198 // Do the actual subdivision on the preallocated storage. InternSubdivide
|
nuclear@0
|
199 // *always* assumes that enough storage is available, it does not bother
|
nuclear@0
|
200 // checking any ranges.
|
nuclear@0
|
201 ai_assert(inmeshes.size()==outmeshes.size()&&inmeshes.size()==maptbl.size());
|
nuclear@0
|
202 if (inmeshes.empty()) {
|
nuclear@0
|
203 DefaultLogger::get()->warn("Catmull-Clark Subdivider: Pure point/line scene, I can't do anything");
|
nuclear@0
|
204 return;
|
nuclear@0
|
205 }
|
nuclear@0
|
206 InternSubdivide(&inmeshes.front(),inmeshes.size(),&outmeshes.front(),num);
|
nuclear@0
|
207 for (unsigned int i = 0; i < maptbl.size(); ++i) {
|
nuclear@0
|
208 ai_assert(outmeshes[i]);
|
nuclear@0
|
209 out[maptbl[i]] = outmeshes[i];
|
nuclear@0
|
210 }
|
nuclear@0
|
211
|
nuclear@0
|
212 if (discard_input) {
|
nuclear@0
|
213 for (size_t s = 0; s < nmesh; ++s) {
|
nuclear@0
|
214 delete smesh[s];
|
nuclear@0
|
215 }
|
nuclear@0
|
216 }
|
nuclear@0
|
217 }
|
nuclear@0
|
218
|
nuclear@0
|
219 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
220 // Note - this is an implementation of the standard (recursive) Cm-Cl algorithm without further
|
nuclear@0
|
221 // optimizations (except we're using some nice LUTs). A description of the algorithm can be found
|
nuclear@0
|
222 // here: http://en.wikipedia.org/wiki/Catmull-Clark_subdivision_surface
|
nuclear@0
|
223 //
|
nuclear@0
|
224 // The code is mostly O(n), however parts are O(nlogn) which is therefore the algorithm's
|
nuclear@0
|
225 // expected total runtime complexity. The implementation is able to work in-place on the same
|
nuclear@0
|
226 // mesh arrays. Calling #InternSubdivide() directly is not encouraged. The code can operate
|
nuclear@0
|
227 // in-place unless 'smesh' and 'out' are equal (no strange overlaps or reorderings).
|
nuclear@0
|
228 // Previous data is replaced/deleted then.
|
nuclear@0
|
229 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
230 void CatmullClarkSubdivider::InternSubdivide (
|
nuclear@0
|
231 const aiMesh* const * smesh,
|
nuclear@0
|
232 size_t nmesh,
|
nuclear@0
|
233 aiMesh** out,
|
nuclear@0
|
234 unsigned int num
|
nuclear@0
|
235 )
|
nuclear@0
|
236 {
|
nuclear@0
|
237 ai_assert(NULL != smesh && NULL != out);
|
nuclear@0
|
238 INIT_EDGE_HASH_TEMPORARIES();
|
nuclear@0
|
239
|
nuclear@0
|
240 // no subdivision requested or end of recursive refinement
|
nuclear@0
|
241 if (!num) {
|
nuclear@0
|
242 return;
|
nuclear@0
|
243 }
|
nuclear@0
|
244
|
nuclear@0
|
245 UIntVector maptbl;
|
nuclear@0
|
246 SpatialSort spatial;
|
nuclear@0
|
247
|
nuclear@0
|
248 // ---------------------------------------------------------------------
|
nuclear@0
|
249 // 0. Offset table to index all meshes continuously, generate a spatially
|
nuclear@0
|
250 // sorted representation of all vertices in all meshes.
|
nuclear@0
|
251 // ---------------------------------------------------------------------
|
nuclear@0
|
252 typedef std::pair<unsigned int,unsigned int> IntPair;
|
nuclear@0
|
253 std::vector<IntPair> moffsets(nmesh);
|
nuclear@0
|
254 unsigned int totfaces = 0, totvert = 0;
|
nuclear@0
|
255 for (size_t t = 0; t < nmesh; ++t) {
|
nuclear@0
|
256 const aiMesh* mesh = smesh[t];
|
nuclear@0
|
257
|
nuclear@0
|
258 spatial.Append(mesh->mVertices,mesh->mNumVertices,sizeof(aiVector3D),false);
|
nuclear@0
|
259 moffsets[t] = IntPair(totfaces,totvert);
|
nuclear@0
|
260
|
nuclear@0
|
261 totfaces += mesh->mNumFaces;
|
nuclear@0
|
262 totvert += mesh->mNumVertices;
|
nuclear@0
|
263 }
|
nuclear@0
|
264
|
nuclear@0
|
265 spatial.Finalize();
|
nuclear@0
|
266 const unsigned int num_unique = spatial.GenerateMappingTable(maptbl,ComputePositionEpsilon(smesh,nmesh));
|
nuclear@0
|
267
|
nuclear@0
|
268
|
nuclear@0
|
269 #define FLATTEN_VERTEX_IDX(mesh_idx, vert_idx) (moffsets[mesh_idx].second+vert_idx)
|
nuclear@0
|
270 #define FLATTEN_FACE_IDX(mesh_idx, face_idx) (moffsets[mesh_idx].first+face_idx)
|
nuclear@0
|
271
|
nuclear@0
|
272 // ---------------------------------------------------------------------
|
nuclear@0
|
273 // 1. Compute the centroid point for all faces
|
nuclear@0
|
274 // ---------------------------------------------------------------------
|
nuclear@0
|
275 std::vector<Vertex> centroids(totfaces);
|
nuclear@0
|
276 unsigned int nfacesout = 0;
|
nuclear@0
|
277 for (size_t t = 0, n = 0; t < nmesh; ++t) {
|
nuclear@0
|
278 const aiMesh* mesh = smesh[t];
|
nuclear@0
|
279 for (unsigned int i = 0; i < mesh->mNumFaces;++i,++n)
|
nuclear@0
|
280 {
|
nuclear@0
|
281 const aiFace& face = mesh->mFaces[i];
|
nuclear@0
|
282 Vertex& c = centroids[n];
|
nuclear@0
|
283
|
nuclear@0
|
284 for (unsigned int a = 0; a < face.mNumIndices;++a) {
|
nuclear@0
|
285 c += Vertex(mesh,face.mIndices[a]);
|
nuclear@0
|
286 }
|
nuclear@0
|
287
|
nuclear@0
|
288 c /= static_cast<float>(face.mNumIndices);
|
nuclear@0
|
289 nfacesout += face.mNumIndices;
|
nuclear@0
|
290 }
|
nuclear@0
|
291 }
|
nuclear@0
|
292
|
nuclear@0
|
293 EdgeMap edges;
|
nuclear@0
|
294
|
nuclear@0
|
295 // ---------------------------------------------------------------------
|
nuclear@0
|
296 // 2. Set each edge point to be the average of all neighbouring
|
nuclear@0
|
297 // face points and original points. Every edge exists twice
|
nuclear@0
|
298 // if there is a neighboring face.
|
nuclear@0
|
299 // ---------------------------------------------------------------------
|
nuclear@0
|
300 for (size_t t = 0; t < nmesh; ++t) {
|
nuclear@0
|
301 const aiMesh* mesh = smesh[t];
|
nuclear@0
|
302
|
nuclear@0
|
303 for (unsigned int i = 0; i < mesh->mNumFaces;++i) {
|
nuclear@0
|
304 const aiFace& face = mesh->mFaces[i];
|
nuclear@0
|
305
|
nuclear@0
|
306 for (unsigned int p =0; p< face.mNumIndices; ++p) {
|
nuclear@0
|
307 const unsigned int id[] = {
|
nuclear@0
|
308 face.mIndices[p],
|
nuclear@0
|
309 face.mIndices[p==face.mNumIndices-1?0:p+1]
|
nuclear@0
|
310 };
|
nuclear@0
|
311 const unsigned int mp[] = {
|
nuclear@0
|
312 maptbl[FLATTEN_VERTEX_IDX(t,id[0])],
|
nuclear@0
|
313 maptbl[FLATTEN_VERTEX_IDX(t,id[1])]
|
nuclear@0
|
314 };
|
nuclear@0
|
315
|
nuclear@0
|
316 Edge& e = edges[MAKE_EDGE_HASH(mp[0],mp[1])];
|
nuclear@0
|
317 e.ref++;
|
nuclear@0
|
318 if (e.ref<=2) {
|
nuclear@0
|
319 if (e.ref==1) { // original points (end points) - add only once
|
nuclear@0
|
320 e.edge_point = e.midpoint = Vertex(mesh,id[0])+Vertex(mesh,id[1]);
|
nuclear@0
|
321 e.midpoint *= 0.5f;
|
nuclear@0
|
322 }
|
nuclear@0
|
323 e.edge_point += centroids[FLATTEN_FACE_IDX(t,i)];
|
nuclear@0
|
324 }
|
nuclear@0
|
325 }
|
nuclear@0
|
326 }
|
nuclear@0
|
327 }
|
nuclear@0
|
328
|
nuclear@0
|
329 // ---------------------------------------------------------------------
|
nuclear@0
|
330 // 3. Normalize edge points
|
nuclear@0
|
331 // ---------------------------------------------------------------------
|
nuclear@0
|
332 {unsigned int bad_cnt = 0;
|
nuclear@0
|
333 for (EdgeMap::iterator it = edges.begin(); it != edges.end(); ++it) {
|
nuclear@0
|
334 if ((*it).second.ref < 2) {
|
nuclear@0
|
335 ai_assert((*it).second.ref);
|
nuclear@0
|
336 ++bad_cnt;
|
nuclear@0
|
337 }
|
nuclear@0
|
338 (*it).second.edge_point *= 1.f/((*it).second.ref+2.f);
|
nuclear@0
|
339 }
|
nuclear@0
|
340
|
nuclear@0
|
341 if (bad_cnt) {
|
nuclear@0
|
342 // Report the number of bad edges. bad edges are referenced by less than two
|
nuclear@0
|
343 // faces in the mesh. They occur at outer model boundaries in non-closed
|
nuclear@0
|
344 // shapes.
|
nuclear@0
|
345 char tmp[512];
|
nuclear@0
|
346 sprintf(tmp,"Catmull-Clark Subdivider: got %u bad edges touching only one face (totally %u edges). ",
|
nuclear@0
|
347 bad_cnt,static_cast<unsigned int>(edges.size()));
|
nuclear@0
|
348
|
nuclear@0
|
349 DefaultLogger::get()->debug(tmp);
|
nuclear@0
|
350 }}
|
nuclear@0
|
351
|
nuclear@0
|
352 // ---------------------------------------------------------------------
|
nuclear@0
|
353 // 4. Compute a vertex-face adjacency table. We can't reuse the code
|
nuclear@0
|
354 // from VertexTriangleAdjacency because we need the table for multiple
|
nuclear@0
|
355 // meshes and out vertex indices need to be mapped to distinct values
|
nuclear@0
|
356 // first.
|
nuclear@0
|
357 // ---------------------------------------------------------------------
|
nuclear@0
|
358 UIntVector faceadjac(nfacesout), cntadjfac(maptbl.size(),0), ofsadjvec(maptbl.size()+1,0); {
|
nuclear@0
|
359 for (size_t t = 0; t < nmesh; ++t) {
|
nuclear@0
|
360 const aiMesh* const minp = smesh[t];
|
nuclear@0
|
361 for (unsigned int i = 0; i < minp->mNumFaces; ++i) {
|
nuclear@0
|
362
|
nuclear@0
|
363 const aiFace& f = minp->mFaces[i];
|
nuclear@0
|
364 for (unsigned int n = 0; n < f.mNumIndices; ++n) {
|
nuclear@0
|
365 ++cntadjfac[maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]];
|
nuclear@0
|
366 }
|
nuclear@0
|
367 }
|
nuclear@0
|
368 }
|
nuclear@0
|
369 unsigned int cur = 0;
|
nuclear@0
|
370 for (size_t i = 0; i < cntadjfac.size(); ++i) {
|
nuclear@0
|
371 ofsadjvec[i+1] = cur;
|
nuclear@0
|
372 cur += cntadjfac[i];
|
nuclear@0
|
373 }
|
nuclear@0
|
374 for (size_t t = 0; t < nmesh; ++t) {
|
nuclear@0
|
375 const aiMesh* const minp = smesh[t];
|
nuclear@0
|
376 for (unsigned int i = 0; i < minp->mNumFaces; ++i) {
|
nuclear@0
|
377
|
nuclear@0
|
378 const aiFace& f = minp->mFaces[i];
|
nuclear@0
|
379 for (unsigned int n = 0; n < f.mNumIndices; ++n) {
|
nuclear@0
|
380 faceadjac[ofsadjvec[1+maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]]++] = FLATTEN_FACE_IDX(t,i);
|
nuclear@0
|
381 }
|
nuclear@0
|
382 }
|
nuclear@0
|
383 }
|
nuclear@0
|
384
|
nuclear@0
|
385 // check the other way round for consistency
|
nuclear@0
|
386 #ifdef _DEBUG
|
nuclear@0
|
387
|
nuclear@0
|
388 for (size_t t = 0; t < ofsadjvec.size()-1; ++t) {
|
nuclear@0
|
389 for (unsigned int m = 0; m < cntadjfac[t]; ++m) {
|
nuclear@0
|
390 const unsigned int fidx = faceadjac[ofsadjvec[t]+m];
|
nuclear@0
|
391 ai_assert(fidx < totfaces);
|
nuclear@0
|
392 for (size_t n = 1; n < nmesh; ++n) {
|
nuclear@0
|
393
|
nuclear@0
|
394 if (moffsets[n].first > fidx) {
|
nuclear@0
|
395 const aiMesh* msh = smesh[--n];
|
nuclear@0
|
396 const aiFace& f = msh->mFaces[fidx-moffsets[n].first];
|
nuclear@0
|
397
|
nuclear@0
|
398 bool haveit = false;
|
nuclear@0
|
399 for (unsigned int i = 0; i < f.mNumIndices; ++i) {
|
nuclear@0
|
400 if (maptbl[FLATTEN_VERTEX_IDX(n,f.mIndices[i])]==(unsigned int)t) {
|
nuclear@0
|
401 haveit = true; break;
|
nuclear@0
|
402 }
|
nuclear@0
|
403 }
|
nuclear@0
|
404 ai_assert(haveit);
|
nuclear@0
|
405 break;
|
nuclear@0
|
406 }
|
nuclear@0
|
407 }
|
nuclear@0
|
408 }
|
nuclear@0
|
409 }
|
nuclear@0
|
410
|
nuclear@0
|
411 #endif
|
nuclear@0
|
412 }
|
nuclear@0
|
413
|
nuclear@0
|
414 #define GET_ADJACENT_FACES_AND_CNT(vidx,fstartout,numout) \
|
nuclear@0
|
415 fstartout = &faceadjac[ofsadjvec[vidx]], numout = cntadjfac[vidx]
|
nuclear@0
|
416
|
nuclear@0
|
417 typedef std::pair<bool,Vertex> TouchedOVertex;
|
nuclear@0
|
418 std::vector<TouchedOVertex > new_points(num_unique,TouchedOVertex(false,Vertex()));
|
nuclear@0
|
419 // ---------------------------------------------------------------------
|
nuclear@0
|
420 // 5. Spawn a quad from each face point to the corresponding edge points
|
nuclear@0
|
421 // the original points being the fourth quad points.
|
nuclear@0
|
422 // ---------------------------------------------------------------------
|
nuclear@0
|
423 for (size_t t = 0; t < nmesh; ++t) {
|
nuclear@0
|
424 const aiMesh* const minp = smesh[t];
|
nuclear@0
|
425 aiMesh* const mout = out[t] = new aiMesh();
|
nuclear@0
|
426
|
nuclear@0
|
427 for (unsigned int a = 0; a < minp->mNumFaces; ++a) {
|
nuclear@0
|
428 mout->mNumFaces += minp->mFaces[a].mNumIndices;
|
nuclear@0
|
429 }
|
nuclear@0
|
430
|
nuclear@0
|
431 // We need random access to the old face buffer, so reuse is not possible.
|
nuclear@0
|
432 mout->mFaces = new aiFace[mout->mNumFaces];
|
nuclear@0
|
433
|
nuclear@0
|
434 mout->mNumVertices = mout->mNumFaces*4;
|
nuclear@0
|
435 mout->mVertices = new aiVector3D[mout->mNumVertices];
|
nuclear@0
|
436
|
nuclear@0
|
437 // quads only, keep material index
|
nuclear@0
|
438 mout->mPrimitiveTypes = aiPrimitiveType_POLYGON;
|
nuclear@0
|
439 mout->mMaterialIndex = minp->mMaterialIndex;
|
nuclear@0
|
440
|
nuclear@0
|
441 if (minp->HasNormals()) {
|
nuclear@0
|
442 mout->mNormals = new aiVector3D[mout->mNumVertices];
|
nuclear@0
|
443 }
|
nuclear@0
|
444
|
nuclear@0
|
445 if (minp->HasTangentsAndBitangents()) {
|
nuclear@0
|
446 mout->mTangents = new aiVector3D[mout->mNumVertices];
|
nuclear@0
|
447 mout->mBitangents = new aiVector3D[mout->mNumVertices];
|
nuclear@0
|
448 }
|
nuclear@0
|
449
|
nuclear@0
|
450 for(unsigned int i = 0; minp->HasTextureCoords(i); ++i) {
|
nuclear@0
|
451 mout->mTextureCoords[i] = new aiVector3D[mout->mNumVertices];
|
nuclear@0
|
452 mout->mNumUVComponents[i] = minp->mNumUVComponents[i];
|
nuclear@0
|
453 }
|
nuclear@0
|
454
|
nuclear@0
|
455 for(unsigned int i = 0; minp->HasVertexColors(i); ++i) {
|
nuclear@0
|
456 mout->mColors[i] = new aiColor4D[mout->mNumVertices];
|
nuclear@0
|
457 }
|
nuclear@0
|
458
|
nuclear@0
|
459 mout->mNumVertices = mout->mNumFaces<<2u;
|
nuclear@0
|
460 for (unsigned int i = 0, v = 0, n = 0; i < minp->mNumFaces;++i) {
|
nuclear@0
|
461
|
nuclear@0
|
462 const aiFace& face = minp->mFaces[i];
|
nuclear@0
|
463 for (unsigned int a = 0; a < face.mNumIndices;++a) {
|
nuclear@0
|
464
|
nuclear@0
|
465 // Get a clean new face.
|
nuclear@0
|
466 aiFace& faceOut = mout->mFaces[n++];
|
nuclear@0
|
467 faceOut.mIndices = new unsigned int [faceOut.mNumIndices = 4];
|
nuclear@0
|
468
|
nuclear@0
|
469 // Spawn a new quadrilateral (ccw winding) for this original point between:
|
nuclear@0
|
470 // a) face centroid
|
nuclear@0
|
471 centroids[FLATTEN_FACE_IDX(t,i)].SortBack(mout,faceOut.mIndices[0]=v++);
|
nuclear@0
|
472
|
nuclear@0
|
473 // b) adjacent edge on the left, seen from the centroid
|
nuclear@0
|
474 const Edge& e0 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])],
|
nuclear@0
|
475 maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a==face.mNumIndices-1?0:a+1])
|
nuclear@0
|
476 ])]; // fixme: replace with mod face.mNumIndices?
|
nuclear@0
|
477
|
nuclear@0
|
478 // c) adjacent edge on the right, seen from the centroid
|
nuclear@0
|
479 const Edge& e1 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])],
|
nuclear@0
|
480 maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[!a?face.mNumIndices-1:a-1])
|
nuclear@0
|
481 ])]; // fixme: replace with mod face.mNumIndices?
|
nuclear@0
|
482
|
nuclear@0
|
483 e0.edge_point.SortBack(mout,faceOut.mIndices[3]=v++);
|
nuclear@0
|
484 e1.edge_point.SortBack(mout,faceOut.mIndices[1]=v++);
|
nuclear@0
|
485
|
nuclear@0
|
486 // d= original point P with distinct index i
|
nuclear@0
|
487 // F := 0
|
nuclear@0
|
488 // R := 0
|
nuclear@0
|
489 // n := 0
|
nuclear@0
|
490 // for each face f containing i
|
nuclear@0
|
491 // F := F+ centroid of f
|
nuclear@0
|
492 // R := R+ midpoint of edge of f from i to i+1
|
nuclear@0
|
493 // n := n+1
|
nuclear@0
|
494 //
|
nuclear@0
|
495 // (F+2R+(n-3)P)/n
|
nuclear@0
|
496 const unsigned int org = maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])];
|
nuclear@0
|
497 TouchedOVertex& ov = new_points[org];
|
nuclear@0
|
498
|
nuclear@0
|
499 if (!ov.first) {
|
nuclear@0
|
500 ov.first = true;
|
nuclear@0
|
501
|
nuclear@0
|
502 const unsigned int* adj; unsigned int cnt;
|
nuclear@0
|
503 GET_ADJACENT_FACES_AND_CNT(org,adj,cnt);
|
nuclear@0
|
504
|
nuclear@0
|
505 if (cnt < 3) {
|
nuclear@0
|
506 ov.second = Vertex(minp,face.mIndices[a]);
|
nuclear@0
|
507 }
|
nuclear@0
|
508 else {
|
nuclear@0
|
509
|
nuclear@0
|
510 Vertex F,R;
|
nuclear@0
|
511 for (unsigned int o = 0; o < cnt; ++o) {
|
nuclear@0
|
512 ai_assert(adj[o] < totfaces);
|
nuclear@0
|
513 F += centroids[adj[o]];
|
nuclear@0
|
514
|
nuclear@0
|
515 // adj[0] is a global face index - search the face in the mesh list
|
nuclear@0
|
516 const aiMesh* mp = NULL;
|
nuclear@0
|
517 size_t nidx;
|
nuclear@0
|
518
|
nuclear@0
|
519 if (adj[o] < moffsets[0].first) {
|
nuclear@0
|
520 mp = smesh[nidx=0];
|
nuclear@0
|
521 }
|
nuclear@0
|
522 else {
|
nuclear@0
|
523 for (nidx = 1; nidx<= nmesh; ++nidx) {
|
nuclear@0
|
524 if (nidx == nmesh ||moffsets[nidx].first > adj[o]) {
|
nuclear@0
|
525 mp = smesh[--nidx];
|
nuclear@0
|
526 break;
|
nuclear@0
|
527 }
|
nuclear@0
|
528 }
|
nuclear@0
|
529 }
|
nuclear@0
|
530
|
nuclear@0
|
531 ai_assert(adj[o]-moffsets[nidx].first < mp->mNumFaces);
|
nuclear@0
|
532 const aiFace& f = mp->mFaces[adj[o]-moffsets[nidx].first];
|
nuclear@0
|
533 # ifdef _DEBUG
|
nuclear@0
|
534 bool haveit = false;
|
nuclear@0
|
535 # endif
|
nuclear@0
|
536
|
nuclear@0
|
537 // find our original point in the face
|
nuclear@0
|
538 for (unsigned int m = 0; m < f.mNumIndices; ++m) {
|
nuclear@0
|
539 if (maptbl[FLATTEN_VERTEX_IDX(nidx,f.mIndices[m])] == org) {
|
nuclear@0
|
540
|
nuclear@0
|
541 // add *both* edges. this way, we can be sure that we add
|
nuclear@0
|
542 // *all* adjacent edges to R. In a closed shape, every
|
nuclear@0
|
543 // edge is added twice - so we simply leave out the
|
nuclear@0
|
544 // factor 2.f in the amove formula and get the right
|
nuclear@0
|
545 // result.
|
nuclear@0
|
546
|
nuclear@0
|
547 const Edge& c0 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX(
|
nuclear@0
|
548 nidx,f.mIndices[!m?f.mNumIndices-1:m-1])])];
|
nuclear@0
|
549 // fixme: replace with mod face.mNumIndices?
|
nuclear@0
|
550
|
nuclear@0
|
551 const Edge& c1 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX(
|
nuclear@0
|
552 nidx,f.mIndices[m==f.mNumIndices-1?0:m+1])])];
|
nuclear@0
|
553 // fixme: replace with mod face.mNumIndices?
|
nuclear@0
|
554 R += c0.midpoint+c1.midpoint;
|
nuclear@0
|
555
|
nuclear@0
|
556 # ifdef _DEBUG
|
nuclear@0
|
557 haveit = true;
|
nuclear@0
|
558 # endif
|
nuclear@0
|
559 break;
|
nuclear@0
|
560 }
|
nuclear@0
|
561 }
|
nuclear@0
|
562
|
nuclear@0
|
563 // this invariant *must* hold if the vertex-to-face adjacency table is valid
|
nuclear@0
|
564 ai_assert(haveit);
|
nuclear@0
|
565 }
|
nuclear@0
|
566
|
nuclear@0
|
567 const float div = static_cast<float>(cnt), divsq = 1.f/(div*div);
|
nuclear@0
|
568 ov.second = Vertex(minp,face.mIndices[a])*((div-3.f) / div) + R*divsq + F*divsq;
|
nuclear@0
|
569 }
|
nuclear@0
|
570 }
|
nuclear@0
|
571 ov.second.SortBack(mout,faceOut.mIndices[2]=v++);
|
nuclear@0
|
572 }
|
nuclear@0
|
573 }
|
nuclear@0
|
574 }
|
nuclear@0
|
575
|
nuclear@0
|
576 // ---------------------------------------------------------------------
|
nuclear@0
|
577 // 7. Apply the next subdivision step.
|
nuclear@0
|
578 // ---------------------------------------------------------------------
|
nuclear@0
|
579 if (num != 1) {
|
nuclear@0
|
580 std::vector<aiMesh*> tmp(nmesh);
|
nuclear@0
|
581 InternSubdivide (out,nmesh,&tmp.front(),num-1);
|
nuclear@0
|
582 for (size_t i = 0; i < nmesh; ++i) {
|
nuclear@0
|
583 delete out[i];
|
nuclear@0
|
584 out[i] = tmp[i];
|
nuclear@0
|
585 }
|
nuclear@0
|
586 }
|
nuclear@0
|
587 }
|