1 | /*
|
---|
2 | -----------------------------------------------------------------------------
|
---|
3 | This source file is part of OGRE
|
---|
4 | (Object-oriented Graphics Rendering Engine)
|
---|
5 | For the latest info, see http://www.ogre3d.org/
|
---|
6 |
|
---|
7 | Copyright (c) 2000-2005 The OGRE Team
|
---|
8 | Also see acknowledgements in Readme.html
|
---|
9 |
|
---|
10 | This program is free software; you can redistribute it and/or modify it under
|
---|
11 | the terms of the GNU Lesser General Public License as published by the Free Software
|
---|
12 | Foundation; either version 2 of the License, or (at your option) any later
|
---|
13 | version.
|
---|
14 |
|
---|
15 | This program is distributed in the hope that it will be useful, but WITHOUT
|
---|
16 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
---|
17 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
---|
18 |
|
---|
19 | You should have received a copy of the GNU Lesser General Public License along with
|
---|
20 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
---|
21 | Place - Suite 330, Boston, MA 02111-1307, USA, or go to
|
---|
22 | http://www.gnu.org/copyleft/lesser.txt.
|
---|
23 | -----------------------------------------------------------------------------
|
---|
24 | */
|
---|
25 | #include "OgreStableHeaders.h"
|
---|
26 |
|
---|
27 | // The algorithm in this file is based heavily on:
|
---|
28 | /*
|
---|
29 | * Progressive Mesh type Polygon Reduction Algorithm
|
---|
30 | * by Stan Melax (c) 1998
|
---|
31 | */
|
---|
32 |
|
---|
33 | #include "OgreProgressiveMesh.h"
|
---|
34 | #include "OgreString.h"
|
---|
35 | #include "OgreHardwareBufferManager.h"
|
---|
36 | #include <algorithm>
|
---|
37 |
|
---|
38 | #include <iostream>
|
---|
39 |
|
---|
40 | #if OGRE_DEBUG_MODE
|
---|
41 | std::ofstream ofdebug;
|
---|
42 | #endif
|
---|
43 |
|
---|
44 | namespace Ogre {
|
---|
45 | #define NEVER_COLLAPSE_COST 99999.9f
|
---|
46 |
|
---|
47 |
|
---|
48 | /** Comparator for unique vertex list
|
---|
49 | */
|
---|
50 | struct vectorLess
|
---|
51 | {
|
---|
52 | _OgreExport bool operator()(const Vector3& v1, const Vector3& v2) const
|
---|
53 | {
|
---|
54 | if (v1.x < v2.x) return true;
|
---|
55 | if (v1.x == v2.x && v1.y < v2.y) return true;
|
---|
56 | if (v1.x == v2.x && v1.y == v2.y && v1.z < v2.z) return true;
|
---|
57 |
|
---|
58 | return false;
|
---|
59 | }
|
---|
60 | };
|
---|
61 | //---------------------------------------------------------------------
|
---|
62 | ProgressiveMesh::ProgressiveMesh(const VertexData* vertexData,
|
---|
63 | const IndexData* indexData)
|
---|
64 | {
|
---|
65 | addWorkingData(vertexData, indexData);
|
---|
66 | mpVertexData = vertexData;
|
---|
67 | mpIndexData = indexData;
|
---|
68 | mWorstCosts.resize(vertexData->vertexCount);
|
---|
69 |
|
---|
70 |
|
---|
71 |
|
---|
72 | }
|
---|
73 | //---------------------------------------------------------------------
|
---|
74 | ProgressiveMesh::~ProgressiveMesh()
|
---|
75 | {
|
---|
76 | }
|
---|
77 | //---------------------------------------------------------------------
|
---|
78 | void ProgressiveMesh::addExtraVertexPositionBuffer(const VertexData* vertexData)
|
---|
79 | {
|
---|
80 | addWorkingData(vertexData, mpIndexData);
|
---|
81 | }
|
---|
82 | //---------------------------------------------------------------------
|
---|
83 | void ProgressiveMesh::build(ushort numLevels, LODFaceList* outList,
|
---|
84 | VertexReductionQuota quota, Real reductionValue)
|
---|
85 | {
|
---|
86 | IndexData* newLod;
|
---|
87 |
|
---|
88 | computeAllCosts();
|
---|
89 |
|
---|
90 | #if OGRE_DEBUG_MODE
|
---|
91 | dumpContents("pm_before.log");
|
---|
92 | #endif
|
---|
93 |
|
---|
94 | // Init
|
---|
95 | mCurrNumIndexes = mpIndexData->indexCount;
|
---|
96 | size_t numVerts, numCollapses;
|
---|
97 | // Use COMMON vert count, not original vert count
|
---|
98 | // Since collapsing 1 common vert position is equivalent to collapsing them all
|
---|
99 | numVerts = mNumCommonVertices;
|
---|
100 |
|
---|
101 | #if OGRE_DEBUG_MODE
|
---|
102 | ofdebug.open("progressivemesh.log");
|
---|
103 | #endif
|
---|
104 | numCollapses = 0;
|
---|
105 | bool abandon = false;
|
---|
106 | while (numLevels--)
|
---|
107 | {
|
---|
108 | // NB idf 'abandon' is set, we stop reducing
|
---|
109 | // However, we still bake the number of LODs requested, even if it
|
---|
110 | // means they are the same
|
---|
111 | if (!abandon)
|
---|
112 | {
|
---|
113 | if (quota == VRQ_PROPORTIONAL)
|
---|
114 | {
|
---|
115 | numCollapses = numVerts * reductionValue;
|
---|
116 | }
|
---|
117 | else
|
---|
118 | {
|
---|
119 | numCollapses = reductionValue;
|
---|
120 | }
|
---|
121 | // Minimum 3 verts!
|
---|
122 | if ( (numVerts - numCollapses) < 3)
|
---|
123 | numCollapses = numVerts - 3;
|
---|
124 | // Store new number of verts
|
---|
125 | numVerts = numVerts - numCollapses;
|
---|
126 |
|
---|
127 | while(numCollapses-- && !abandon)
|
---|
128 | {
|
---|
129 | size_t nextIndex = getNextCollapser();
|
---|
130 | // Collapse on every buffer
|
---|
131 | WorkingDataList::iterator idata, idataend;
|
---|
132 | idataend = mWorkingData.end();
|
---|
133 | for (idata = mWorkingData.begin(); idata != idataend; ++idata)
|
---|
134 | {
|
---|
135 | PMVertex* collapser = &( idata->mVertList.at( nextIndex ) );
|
---|
136 | // This will reduce mCurrNumIndexes and recalc costs as required
|
---|
137 | if (collapser->collapseTo == NULL)
|
---|
138 | {
|
---|
139 | // Must have run out of valid collapsables
|
---|
140 | abandon = true;
|
---|
141 | break;
|
---|
142 | }
|
---|
143 | #if OGRE_DEBUG_MODE
|
---|
144 | ofdebug << "Collapsing index " << (unsigned int)collapser->index << "(border: "<< collapser->isBorder() <<
|
---|
145 | ") to " << (unsigned int)collapser->collapseTo->index << "(border: "<< collapser->collapseTo->isBorder() <<
|
---|
146 | ")" << std::endl;
|
---|
147 | #endif
|
---|
148 | assert(collapser->collapseTo->removed == false);
|
---|
149 |
|
---|
150 | collapse(collapser);
|
---|
151 | }
|
---|
152 |
|
---|
153 | }
|
---|
154 | }
|
---|
155 | #if OGRE_DEBUG_MODE
|
---|
156 | StringUtil::StrStreamType logname;
|
---|
157 | logname << "pm_level" << numLevels << ".log";
|
---|
158 | dumpContents(logname.str());
|
---|
159 | #endif
|
---|
160 |
|
---|
161 | // Bake a new LOD and add it to the list
|
---|
162 | newLod = new IndexData();
|
---|
163 | bakeNewLOD(newLod);
|
---|
164 | outList->push_back(newLod);
|
---|
165 |
|
---|
166 | }
|
---|
167 |
|
---|
168 |
|
---|
169 |
|
---|
170 | }
|
---|
171 | //---------------------------------------------------------------------
|
---|
172 | void ProgressiveMesh::addWorkingData(const VertexData * vertexData,
|
---|
173 | const IndexData * indexData)
|
---|
174 | {
|
---|
175 | // Insert blank working data, then fill
|
---|
176 | mWorkingData.push_back(PMWorkingData());
|
---|
177 |
|
---|
178 | PMWorkingData& work = mWorkingData.back();
|
---|
179 |
|
---|
180 | // Build vertex list
|
---|
181 | // Resize face list (this will always be this big)
|
---|
182 | work.mFaceVertList.resize(vertexData->vertexCount);
|
---|
183 | // Also resize common vert list to max, to avoid reallocations
|
---|
184 | work.mVertList.resize(vertexData->vertexCount);
|
---|
185 |
|
---|
186 | // locate position element & hte buffer to go with it
|
---|
187 | const VertexElement* posElem = vertexData->vertexDeclaration->findElementBySemantic(VES_POSITION);
|
---|
188 | HardwareVertexBufferSharedPtr vbuf =
|
---|
189 | vertexData->vertexBufferBinding->getBuffer(posElem->getSource());
|
---|
190 | // lock the buffer for reading
|
---|
191 | unsigned char* pVertex = static_cast<unsigned char*>(
|
---|
192 | vbuf->lock(HardwareBuffer::HBL_READ_ONLY));
|
---|
193 | float* pFloat;
|
---|
194 | Vector3 pos;
|
---|
195 | // Map for identifying duplicate position vertices
|
---|
196 | typedef std::map<Vector3, size_t, vectorLess> CommonVertexMap;
|
---|
197 | CommonVertexMap commonVertexMap;
|
---|
198 | CommonVertexMap::iterator iCommonVertex;
|
---|
199 | size_t numCommon = 0;
|
---|
200 | size_t i = 0;
|
---|
201 | for (i = 0; i < vertexData->vertexCount; ++i, pVertex += vbuf->getVertexSize())
|
---|
202 | {
|
---|
203 | posElem->baseVertexPointerToElement(pVertex, &pFloat);
|
---|
204 |
|
---|
205 | pos.x = *pFloat++;
|
---|
206 | pos.y = *pFloat++;
|
---|
207 | pos.z = *pFloat++;
|
---|
208 |
|
---|
209 | // Try to find this position in the existing map
|
---|
210 | iCommonVertex = commonVertexMap.find(pos);
|
---|
211 | if (iCommonVertex == commonVertexMap.end())
|
---|
212 | {
|
---|
213 | // Doesn't exist, so create it
|
---|
214 | PMVertex* commonVert = &(work.mVertList[numCommon]);
|
---|
215 | commonVert->setDetails(pos, numCommon);
|
---|
216 | commonVert->removed = false;
|
---|
217 | commonVert->toBeRemoved = false;
|
---|
218 | commonVert->seam = false;
|
---|
219 |
|
---|
220 | // Enter it in the map
|
---|
221 | commonVertexMap.insert(CommonVertexMap::value_type(pos, numCommon) );
|
---|
222 | // Increment common index
|
---|
223 | ++numCommon;
|
---|
224 |
|
---|
225 | work.mFaceVertList[i].commonVertex = commonVert;
|
---|
226 | work.mFaceVertList[i].realIndex = i;
|
---|
227 | }
|
---|
228 | else
|
---|
229 | {
|
---|
230 | // Exists already, reference it
|
---|
231 | PMVertex* existingVert = &(work.mVertList[iCommonVertex->second]);
|
---|
232 | work.mFaceVertList[i].commonVertex = existingVert;
|
---|
233 | work.mFaceVertList[i].realIndex = i;
|
---|
234 |
|
---|
235 | // Also tag original as a seam since duplicates at this location
|
---|
236 | work.mFaceVertList[i].commonVertex->seam = true;
|
---|
237 |
|
---|
238 | }
|
---|
239 |
|
---|
240 | }
|
---|
241 | vbuf->unlock();
|
---|
242 |
|
---|
243 | mNumCommonVertices = numCommon;
|
---|
244 |
|
---|
245 | // Build tri list
|
---|
246 | size_t numTris = indexData->indexCount / 3;
|
---|
247 | unsigned short* pShort;
|
---|
248 | unsigned int* pInt;
|
---|
249 | HardwareIndexBufferSharedPtr ibuf = indexData->indexBuffer;
|
---|
250 | bool use32bitindexes = (ibuf->getType() == HardwareIndexBuffer::IT_32BIT);
|
---|
251 | if (use32bitindexes)
|
---|
252 | {
|
---|
253 | pInt = static_cast<unsigned int*>(
|
---|
254 | ibuf->lock(HardwareBuffer::HBL_READ_ONLY));
|
---|
255 | }
|
---|
256 | else
|
---|
257 | {
|
---|
258 | pShort = static_cast<unsigned short*>(
|
---|
259 | ibuf->lock(HardwareBuffer::HBL_READ_ONLY));
|
---|
260 | }
|
---|
261 | work.mTriList.resize(numTris); // assumed tri list
|
---|
262 | for (i = 0; i < numTris; ++i)
|
---|
263 | {
|
---|
264 | PMFaceVertex *v0, *v1, *v2;
|
---|
265 | // use 32-bit index always since we're not storing
|
---|
266 | unsigned int vindex = use32bitindexes? *pInt++ : *pShort++;
|
---|
267 | v0 = &(work.mFaceVertList[vindex]);
|
---|
268 | vindex = use32bitindexes? *pInt++ : *pShort++;
|
---|
269 | v1 = &(work.mFaceVertList[vindex]);
|
---|
270 | vindex = use32bitindexes? *pInt++ : *pShort++;
|
---|
271 | v2 = &(work.mFaceVertList[vindex]);
|
---|
272 |
|
---|
273 | work.mTriList[i].setDetails(i, v0, v1, v2);
|
---|
274 |
|
---|
275 | work.mTriList[i].removed = false;
|
---|
276 |
|
---|
277 | }
|
---|
278 | ibuf->unlock();
|
---|
279 |
|
---|
280 | }
|
---|
281 | //---------------------------------------------------------------------
|
---|
282 | Real ProgressiveMesh::computeEdgeCollapseCost(PMVertex *src, PMVertex *dest)
|
---|
283 | {
|
---|
284 | // if we collapse edge uv by moving src to dest then how
|
---|
285 | // much different will the model change, i.e. how much "error".
|
---|
286 | // The method of determining cost was designed in order
|
---|
287 | // to exploit small and coplanar regions for
|
---|
288 | // effective polygon reduction.
|
---|
289 | Vector3 edgeVector = src->position - dest->position;
|
---|
290 |
|
---|
291 | Real cost;
|
---|
292 | Real curvature = 0.001f;
|
---|
293 |
|
---|
294 | // find the "sides" triangles that are on the edge uv
|
---|
295 | PMVertex::FaceList sides;
|
---|
296 | PMVertex::FaceList::iterator srcface, srcfaceEnd;
|
---|
297 | srcfaceEnd = src->face.end();
|
---|
298 | // Iterate over src's faces and find 'sides' of the shared edge which is being collapsed
|
---|
299 | for(srcface = src->face.begin(); srcface != srcfaceEnd; ++srcface)
|
---|
300 | {
|
---|
301 | // Check if this tri also has dest in it (shared edge)
|
---|
302 | if( (*srcface)->hasCommonVertex(dest) )
|
---|
303 | {
|
---|
304 | sides.insert(*srcface);
|
---|
305 | }
|
---|
306 | }
|
---|
307 |
|
---|
308 | // Special cases
|
---|
309 | // If we're looking at a border vertex
|
---|
310 | if(src->isBorder())
|
---|
311 | {
|
---|
312 | if (sides.size() > 1)
|
---|
313 | {
|
---|
314 | // src is on a border, but the src-dest edge has more than one tri on it
|
---|
315 | // So it must be collapsing inwards
|
---|
316 | // Mark as very high-value cost
|
---|
317 | // curvature = 1.0f;
|
---|
318 | cost = 1.0f;
|
---|
319 | }
|
---|
320 | else
|
---|
321 | {
|
---|
322 | // Collapsing ALONG a border
|
---|
323 | // We can't use curvature to measure the effect on the model
|
---|
324 | // Instead, see what effect it has on 'pulling' the other border edges
|
---|
325 | // The more colinear, the less effect it will have
|
---|
326 | // So measure the 'kinkiness' (for want of a better term)
|
---|
327 | // Normally there can be at most 1 other border edge attached to this
|
---|
328 | // However in weird cases there may be more, so find the worst
|
---|
329 | Vector3 collapseEdge, otherBorderEdge;
|
---|
330 | Real kinkiness, maxKinkiness;
|
---|
331 | PMVertex::NeighborList::iterator n, nend;
|
---|
332 | nend = src->neighbor.end();
|
---|
333 | maxKinkiness = 0.0f;
|
---|
334 | edgeVector.normalise();
|
---|
335 | collapseEdge = edgeVector;
|
---|
336 | for (n = src->neighbor.begin(); n != nend; ++n)
|
---|
337 | {
|
---|
338 | if (*n != dest && (*n)->isManifoldEdgeWith(src))
|
---|
339 | {
|
---|
340 | otherBorderEdge = src->position - (*n)->position;
|
---|
341 | otherBorderEdge.normalise();
|
---|
342 | // This time, the nearer the dot is to -1, the better, because that means
|
---|
343 | // the edges are opposite each other, therefore less kinkiness
|
---|
344 | // Scale into [0..1]
|
---|
345 | kinkiness = (otherBorderEdge.dotProduct(collapseEdge) + 1.002f) * 0.5f;
|
---|
346 | maxKinkiness = std::max(kinkiness, maxKinkiness);
|
---|
347 |
|
---|
348 | }
|
---|
349 | }
|
---|
350 |
|
---|
351 | cost = maxKinkiness;
|
---|
352 |
|
---|
353 | }
|
---|
354 | }
|
---|
355 | else // not a border
|
---|
356 | {
|
---|
357 |
|
---|
358 | // Standard inner vertex
|
---|
359 | // Calculate curvature
|
---|
360 | // use the triangle facing most away from the sides
|
---|
361 | // to determine our curvature term
|
---|
362 | // Iterate over src's faces again
|
---|
363 | for(srcface = src->face.begin(); srcface != srcfaceEnd; ++srcface)
|
---|
364 | {
|
---|
365 | Real mincurv = 1.0f; // curve for face i and closer side to it
|
---|
366 | // Iterate over the sides
|
---|
367 | PMVertex::FaceList::iterator sidesFace, sidesFaceEnd;
|
---|
368 | sidesFaceEnd = sides.end();
|
---|
369 | for(sidesFace = sides.begin(); sidesFace != sidesFaceEnd; ++sidesFace)
|
---|
370 | {
|
---|
371 | // Dot product of face normal gives a good delta angle
|
---|
372 | Real dotprod = (*srcface)->normal.dotProduct( (*sidesFace)->normal );
|
---|
373 | // NB we do (1-..) to invert curvature where 1 is high curvature [0..1]
|
---|
374 | // Whilst dot product is high when angle difference is low
|
---|
375 | mincurv = std::min(mincurv,(1.002f - dotprod) * 0.5f);
|
---|
376 | }
|
---|
377 | curvature = std::max(curvature, mincurv);
|
---|
378 | }
|
---|
379 | cost = curvature;
|
---|
380 | }
|
---|
381 |
|
---|
382 | // check for texture seam ripping
|
---|
383 | if (src->seam && !dest->seam)
|
---|
384 | {
|
---|
385 | cost = 1.0f;
|
---|
386 | }
|
---|
387 |
|
---|
388 | // Check for singular triangle destruction
|
---|
389 | // If src and dest both only have 1 triangle (and it must be a shared one)
|
---|
390 | // then this would destroy the shape, so don't do this
|
---|
391 | if (src->face.size() == 1 && dest->face.size() == 1)
|
---|
392 | {
|
---|
393 | cost = NEVER_COLLAPSE_COST;
|
---|
394 | }
|
---|
395 |
|
---|
396 |
|
---|
397 | // Degenerate case check
|
---|
398 | // Are we going to invert a face normal of one of the neighbouring faces?
|
---|
399 | // Can occur when we have a very small remaining edge and collapse crosses it
|
---|
400 | // Look for a face normal changing by > 90 degrees
|
---|
401 | for(srcface = src->face.begin(); srcface != srcfaceEnd; ++srcface)
|
---|
402 | {
|
---|
403 | // Ignore the deleted faces (those including src & dest)
|
---|
404 | if( !(*srcface)->hasCommonVertex(dest) )
|
---|
405 | {
|
---|
406 | // Test the new face normal
|
---|
407 | PMVertex *v0, *v1, *v2;
|
---|
408 | // Replace src with dest wherever it is
|
---|
409 | v0 = ( (*srcface)->vertex[0]->commonVertex == src) ? dest : (*srcface)->vertex[0]->commonVertex;
|
---|
410 | v1 = ( (*srcface)->vertex[1]->commonVertex == src) ? dest : (*srcface)->vertex[1]->commonVertex;
|
---|
411 | v2 = ( (*srcface)->vertex[2]->commonVertex == src) ? dest : (*srcface)->vertex[2]->commonVertex;
|
---|
412 |
|
---|
413 | // Cross-product 2 edges
|
---|
414 | Vector3 e1 = v1->position - v0->position;
|
---|
415 | Vector3 e2 = v2->position - v1->position;
|
---|
416 |
|
---|
417 | Vector3 newNormal = e1.crossProduct(e2);
|
---|
418 | newNormal.normalise();
|
---|
419 |
|
---|
420 | // Dot old and new face normal
|
---|
421 | // If < 0 then more than 90 degree difference
|
---|
422 | if (newNormal.dotProduct( (*srcface)->normal ) < 0.0f )
|
---|
423 | {
|
---|
424 | // Don't do it!
|
---|
425 | cost = NEVER_COLLAPSE_COST;
|
---|
426 | break; // No point continuing
|
---|
427 | }
|
---|
428 |
|
---|
429 |
|
---|
430 | }
|
---|
431 | }
|
---|
432 |
|
---|
433 |
|
---|
434 | assert (cost >= 0);
|
---|
435 | return cost;
|
---|
436 | }
|
---|
437 | //---------------------------------------------------------------------
|
---|
438 | void ProgressiveMesh::initialiseEdgeCollapseCosts(void)
|
---|
439 | {
|
---|
440 | WorkingDataList::iterator i, iend;
|
---|
441 | iend = mWorkingData.end();
|
---|
442 | for (i = mWorkingData.begin(); i != iend; ++i)
|
---|
443 | {
|
---|
444 | CommonVertexList::iterator v, vend;
|
---|
445 | vend = i->mVertList.end();
|
---|
446 | for (v = i->mVertList.begin(); v != vend; ++v)
|
---|
447 | {
|
---|
448 | v->collapseTo = NULL;
|
---|
449 | v->collapseCost = NEVER_COLLAPSE_COST;
|
---|
450 | }
|
---|
451 | }
|
---|
452 |
|
---|
453 |
|
---|
454 | }
|
---|
455 | //---------------------------------------------------------------------
|
---|
456 | Real ProgressiveMesh::computeEdgeCostAtVertexForBuffer(WorkingDataList::iterator idata, size_t vertIndex)
|
---|
457 | {
|
---|
458 | // compute the edge collapse cost for all edges that start
|
---|
459 | // from vertex v. Since we are only interested in reducing
|
---|
460 | // the object by selecting the min cost edge at each step, we
|
---|
461 | // only cache the cost of the least cost edge at this vertex
|
---|
462 | // (in member variable collapse) as well as the value of the
|
---|
463 | // cost (in member variable objdist).
|
---|
464 |
|
---|
465 | CommonVertexList::iterator v = idata->mVertList.begin();
|
---|
466 | v += vertIndex;
|
---|
467 |
|
---|
468 | if(v->neighbor.empty()) {
|
---|
469 | // v doesn't have neighbors so nothing to collapse
|
---|
470 | v->notifyRemoved();
|
---|
471 | return v->collapseCost;
|
---|
472 | }
|
---|
473 |
|
---|
474 | // Init metrics
|
---|
475 | v->collapseCost = NEVER_COLLAPSE_COST;
|
---|
476 | v->collapseTo = NULL;
|
---|
477 |
|
---|
478 | // search all neighboring edges for "least cost" edge
|
---|
479 | PMVertex::NeighborList::iterator n, nend;
|
---|
480 | nend = v->neighbor.end();
|
---|
481 | Real cost;
|
---|
482 | for(n = v->neighbor.begin(); n != nend; ++n)
|
---|
483 | {
|
---|
484 | cost = computeEdgeCollapseCost(&(*v), *n);
|
---|
485 | if( (!v->collapseTo) || cost < v->collapseCost)
|
---|
486 | {
|
---|
487 | v->collapseTo = *n; // candidate for edge collapse
|
---|
488 | v->collapseCost = cost; // cost of the collapse
|
---|
489 | }
|
---|
490 | }
|
---|
491 |
|
---|
492 | return v->collapseCost;
|
---|
493 | }
|
---|
494 | //---------------------------------------------------------------------
|
---|
495 | void ProgressiveMesh::computeAllCosts(void)
|
---|
496 | {
|
---|
497 | initialiseEdgeCollapseCosts();
|
---|
498 | size_t i;
|
---|
499 | for (i = 0; i < mpVertexData->vertexCount; ++i)
|
---|
500 | {
|
---|
501 | computeEdgeCostAtVertex(i);
|
---|
502 | }
|
---|
503 | }
|
---|
504 | //---------------------------------------------------------------------
|
---|
505 | void ProgressiveMesh::collapse(ProgressiveMesh::PMVertex *src)
|
---|
506 | {
|
---|
507 | PMVertex *dest = src->collapseTo;
|
---|
508 | std::set<PMVertex*> recomputeSet;
|
---|
509 |
|
---|
510 | // Abort if we're never supposed to collapse
|
---|
511 | if (src->collapseCost == NEVER_COLLAPSE_COST)
|
---|
512 | return;
|
---|
513 |
|
---|
514 | // Remove this vertex from the running for the next check
|
---|
515 | src->collapseTo = NULL;
|
---|
516 | src->collapseCost = NEVER_COLLAPSE_COST;
|
---|
517 | mWorstCosts[src->index] = NEVER_COLLAPSE_COST;
|
---|
518 |
|
---|
519 | // Collapse the edge uv by moving vertex u onto v
|
---|
520 | // Actually remove tris on uv, then update tris that
|
---|
521 | // have u to have v, and then remove u.
|
---|
522 | if(!dest) {
|
---|
523 | // src is a vertex all by itself
|
---|
524 | #if OGRE_DEBUG_MODE
|
---|
525 | ofdebug << "Aborting collapse, orphan vertex. " << std::endl;
|
---|
526 | #endif
|
---|
527 | return;
|
---|
528 | }
|
---|
529 |
|
---|
530 | // Add dest and all the neighbours of source and dest to recompute list
|
---|
531 | recomputeSet.insert(dest);
|
---|
532 | PMVertex::NeighborList::iterator n, nend;
|
---|
533 | nend = src->neighbor.end();
|
---|
534 |
|
---|
535 | PMVertex* temp;
|
---|
536 |
|
---|
537 | for(n = src->neighbor.begin(); n != nend; ++n)
|
---|
538 | {
|
---|
539 | temp = *n;
|
---|
540 | recomputeSet.insert( *n );
|
---|
541 | }
|
---|
542 | nend = dest->neighbor.end();
|
---|
543 | for(n = dest->neighbor.begin(); n != nend; ++n)
|
---|
544 | {
|
---|
545 | temp = *n;
|
---|
546 | recomputeSet.insert( *n );
|
---|
547 | }
|
---|
548 |
|
---|
549 | // delete triangles on edge src-dest
|
---|
550 | // Notify others to replace src with dest
|
---|
551 | PMVertex::FaceList::iterator f, fend;
|
---|
552 | fend = src->face.end();
|
---|
553 | // Queue of faces for removal / replacement
|
---|
554 | // prevents us screwing up the iterators while we parse
|
---|
555 | PMVertex::FaceList faceRemovalList, faceReplacementList;
|
---|
556 | for(f = src->face.begin(); f != fend; ++f)
|
---|
557 | {
|
---|
558 | if((*f)->hasCommonVertex(dest))
|
---|
559 | {
|
---|
560 | // Tri is on src-dest therefore is gone
|
---|
561 | faceRemovalList.insert(*f);
|
---|
562 | // Reduce index count by 3 (useful for quick allocation later)
|
---|
563 | mCurrNumIndexes -= 3;
|
---|
564 | }
|
---|
565 | else
|
---|
566 | {
|
---|
567 | // Only src involved, replace with dest
|
---|
568 | faceReplacementList.insert(*f);
|
---|
569 | }
|
---|
570 | }
|
---|
571 |
|
---|
572 | src->toBeRemoved = true;
|
---|
573 | // Replace all the faces queued for replacement
|
---|
574 | for(f = faceReplacementList.begin(); f != faceReplacementList.end(); ++f)
|
---|
575 | {
|
---|
576 | /* Locate the face vertex which corresponds with the common 'dest' vertex
|
---|
577 | To to this, find a removed face which has the FACE vertex corresponding with
|
---|
578 | src, and use it's FACE vertex version of dest.
|
---|
579 | */
|
---|
580 | PMFaceVertex* srcFaceVert = (*f)->getFaceVertexFromCommon(src);
|
---|
581 | PMFaceVertex* destFaceVert = NULL;
|
---|
582 | PMVertex::FaceList::iterator iremoved;
|
---|
583 | for(iremoved = faceRemovalList.begin(); iremoved != faceRemovalList.end(); ++iremoved)
|
---|
584 | {
|
---|
585 | //if ( (*iremoved)->hasFaceVertex(srcFaceVert) )
|
---|
586 | //{
|
---|
587 | destFaceVert = (*iremoved)->getFaceVertexFromCommon(dest);
|
---|
588 | //}
|
---|
589 | }
|
---|
590 |
|
---|
591 | assert(destFaceVert);
|
---|
592 |
|
---|
593 | #if OGRE_DEBUG_MODE
|
---|
594 | ofdebug << "Replacing vertex on face " << (unsigned int)(*f)->index << std::endl;
|
---|
595 | #endif
|
---|
596 | (*f)->replaceVertex(srcFaceVert, destFaceVert);
|
---|
597 | }
|
---|
598 | // Remove all the faces queued for removal
|
---|
599 | for(f = faceRemovalList.begin(); f != faceRemovalList.end(); ++f)
|
---|
600 | {
|
---|
601 | #if OGRE_DEBUG_MODE
|
---|
602 | ofdebug << "Removing face " << (unsigned int)(*f)->index << std::endl;
|
---|
603 | #endif
|
---|
604 | (*f)->notifyRemoved();
|
---|
605 | }
|
---|
606 |
|
---|
607 | // Notify the vertex that it is gone
|
---|
608 | src->notifyRemoved();
|
---|
609 |
|
---|
610 | // recompute costs
|
---|
611 | std::set<PMVertex*>::iterator irecomp, irecompend;
|
---|
612 | irecompend = recomputeSet.end();
|
---|
613 | for (irecomp = recomputeSet.begin(); irecomp != irecompend; ++irecomp)
|
---|
614 | {
|
---|
615 | temp = (*irecomp);
|
---|
616 | computeEdgeCostAtVertex( (*irecomp)->index );
|
---|
617 | }
|
---|
618 |
|
---|
619 | }
|
---|
620 | //---------------------------------------------------------------------
|
---|
621 | void ProgressiveMesh::computeEdgeCostAtVertex(size_t vertIndex)
|
---|
622 | {
|
---|
623 | // Call computer for each buffer on this vertex
|
---|
624 | Real worstCost = -0.01f;
|
---|
625 | WorkingDataList::iterator i, iend;
|
---|
626 | iend = mWorkingData.end();
|
---|
627 | for (i = mWorkingData.begin(); i != iend; ++i)
|
---|
628 | {
|
---|
629 | worstCost = std::max(worstCost,
|
---|
630 | computeEdgeCostAtVertexForBuffer(i, vertIndex));
|
---|
631 | }
|
---|
632 | // Save the worst cost
|
---|
633 | mWorstCosts[vertIndex] = worstCost;
|
---|
634 | }
|
---|
635 | //---------------------------------------------------------------------
|
---|
636 | size_t ProgressiveMesh::getNextCollapser(void)
|
---|
637 | {
|
---|
638 | // Scan
|
---|
639 | // Not done as a sort because want to keep the lookup simple for now
|
---|
640 | Real bestVal = NEVER_COLLAPSE_COST;
|
---|
641 | size_t i, bestIndex;
|
---|
642 | bestIndex = 0; // NB this is ok since if nothing is better than this, nothing will collapse
|
---|
643 | for (i = 0; i < mNumCommonVertices; ++i)
|
---|
644 | {
|
---|
645 | if (mWorstCosts[i] < bestVal)
|
---|
646 | {
|
---|
647 | bestVal = mWorstCosts[i];
|
---|
648 | bestIndex = i;
|
---|
649 | }
|
---|
650 | }
|
---|
651 | return bestIndex;
|
---|
652 | }
|
---|
653 | //---------------------------------------------------------------------
|
---|
654 | void ProgressiveMesh::bakeNewLOD(IndexData* pData)
|
---|
655 | {
|
---|
656 | assert(mCurrNumIndexes > 0 && "No triangles to bake!");
|
---|
657 | // Zip through the tri list of any working data copy and bake
|
---|
658 | pData->indexCount = mCurrNumIndexes;
|
---|
659 | pData->indexStart = 0;
|
---|
660 | // Base size of indexes on original
|
---|
661 | bool use32bitindexes =
|
---|
662 | (mpIndexData->indexBuffer->getType() == HardwareIndexBuffer::IT_32BIT);
|
---|
663 |
|
---|
664 | // Create index buffer, we don't need to read it back or modify it a lot
|
---|
665 | pData->indexBuffer = HardwareBufferManager::getSingleton().createIndexBuffer(
|
---|
666 | use32bitindexes? HardwareIndexBuffer::IT_32BIT : HardwareIndexBuffer::IT_16BIT,
|
---|
667 | pData->indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
|
---|
668 |
|
---|
669 | unsigned short* pShort;
|
---|
670 | unsigned int* pInt;
|
---|
671 | if (use32bitindexes)
|
---|
672 | {
|
---|
673 | pInt = static_cast<unsigned int*>(
|
---|
674 | pData->indexBuffer->lock( 0,
|
---|
675 | pData->indexBuffer->getSizeInBytes(),
|
---|
676 | HardwareBuffer::HBL_DISCARD));
|
---|
677 | }
|
---|
678 | else
|
---|
679 | {
|
---|
680 | pShort = static_cast<unsigned short*>(
|
---|
681 | pData->indexBuffer->lock( 0,
|
---|
682 | pData->indexBuffer->getSizeInBytes(),
|
---|
683 | HardwareBuffer::HBL_DISCARD));
|
---|
684 | }
|
---|
685 | TriangleList::iterator tri, triend;
|
---|
686 | // Use the first working data buffer, they are all the same index-wise
|
---|
687 | WorkingDataList::iterator pWork = mWorkingData.begin();
|
---|
688 | triend = pWork->mTriList.end();
|
---|
689 | for (tri = pWork->mTriList.begin(); tri != triend; ++tri)
|
---|
690 | {
|
---|
691 | if (!tri->removed)
|
---|
692 | {
|
---|
693 | if (use32bitindexes)
|
---|
694 | {
|
---|
695 | *pInt++ = static_cast<unsigned int>(tri->vertex[0]->realIndex);
|
---|
696 | *pInt++ = static_cast<unsigned int>(tri->vertex[1]->realIndex);
|
---|
697 | *pInt++ = static_cast<unsigned int>(tri->vertex[2]->realIndex);
|
---|
698 | }
|
---|
699 | else
|
---|
700 | {
|
---|
701 | *pShort++ = static_cast<unsigned short>(tri->vertex[0]->realIndex);
|
---|
702 | *pShort++ = static_cast<unsigned short>(tri->vertex[1]->realIndex);
|
---|
703 | *pShort++ = static_cast<unsigned short>(tri->vertex[2]->realIndex);
|
---|
704 | }
|
---|
705 | }
|
---|
706 | }
|
---|
707 | pData->indexBuffer->unlock();
|
---|
708 |
|
---|
709 | }
|
---|
710 | //---------------------------------------------------------------------
|
---|
711 | ProgressiveMesh::PMTriangle::PMTriangle() : removed(false)
|
---|
712 | {
|
---|
713 | }
|
---|
714 | //---------------------------------------------------------------------
|
---|
715 | void ProgressiveMesh::PMTriangle::setDetails(size_t newindex,
|
---|
716 | ProgressiveMesh::PMFaceVertex *v0, ProgressiveMesh::PMFaceVertex *v1,
|
---|
717 | ProgressiveMesh::PMFaceVertex *v2)
|
---|
718 | {
|
---|
719 | assert(v0!=v1 && v1!=v2 && v2!=v0);
|
---|
720 |
|
---|
721 | index = newindex;
|
---|
722 | vertex[0]=v0;
|
---|
723 | vertex[1]=v1;
|
---|
724 | vertex[2]=v2;
|
---|
725 |
|
---|
726 | computeNormal();
|
---|
727 |
|
---|
728 | // Add tri to vertices
|
---|
729 | // Also tell vertices they are neighbours
|
---|
730 | for(int i=0;i<3;i++) {
|
---|
731 | vertex[i]->commonVertex->face.insert(this);
|
---|
732 | for(int j=0;j<3;j++) if(i!=j) {
|
---|
733 | vertex[i]->commonVertex->neighbor.insert(vertex[j]->commonVertex);
|
---|
734 | }
|
---|
735 | }
|
---|
736 | }
|
---|
737 | //---------------------------------------------------------------------
|
---|
738 | void ProgressiveMesh::PMTriangle::notifyRemoved(void)
|
---|
739 | {
|
---|
740 | int i;
|
---|
741 | for(i=0; i<3; i++) {
|
---|
742 | // remove this tri from the vertices
|
---|
743 | if(vertex[i]) vertex[i]->commonVertex->face.erase(this);
|
---|
744 | }
|
---|
745 | for(i=0; i<3; i++) {
|
---|
746 | int i2 = (i+1)%3;
|
---|
747 | if(!vertex[i] || !vertex[i2]) continue;
|
---|
748 | // Check remaining vertices and remove if not neighbours anymore
|
---|
749 | // NB May remain neighbours if other tris link them
|
---|
750 | vertex[i ]->commonVertex->removeIfNonNeighbor(vertex[i2]->commonVertex);
|
---|
751 | vertex[i2]->commonVertex->removeIfNonNeighbor(vertex[i ]->commonVertex);
|
---|
752 | }
|
---|
753 |
|
---|
754 | removed = true;
|
---|
755 | }
|
---|
756 | //---------------------------------------------------------------------
|
---|
757 | bool ProgressiveMesh::PMTriangle::hasCommonVertex(ProgressiveMesh::PMVertex *v) const
|
---|
758 | {
|
---|
759 | return (v == vertex[0]->commonVertex ||
|
---|
760 | v == vertex[1]->commonVertex ||
|
---|
761 | v == vertex[2]->commonVertex);
|
---|
762 | }
|
---|
763 | //---------------------------------------------------------------------
|
---|
764 | bool ProgressiveMesh::PMTriangle::hasFaceVertex(ProgressiveMesh::PMFaceVertex *v) const
|
---|
765 | {
|
---|
766 | return (v == vertex[0] ||
|
---|
767 | v == vertex[1] ||
|
---|
768 | v == vertex[2]);
|
---|
769 | }
|
---|
770 | //---------------------------------------------------------------------
|
---|
771 | ProgressiveMesh::PMFaceVertex*
|
---|
772 | ProgressiveMesh::PMTriangle::getFaceVertexFromCommon(ProgressiveMesh::PMVertex* commonVert)
|
---|
773 | {
|
---|
774 | if (vertex[0]->commonVertex == commonVert) return vertex[0];
|
---|
775 | if (vertex[1]->commonVertex == commonVert) return vertex[1];
|
---|
776 | if (vertex[2]->commonVertex == commonVert) return vertex[2];
|
---|
777 |
|
---|
778 | return NULL;
|
---|
779 |
|
---|
780 | }
|
---|
781 | //---------------------------------------------------------------------
|
---|
782 | void ProgressiveMesh::PMTriangle::computeNormal()
|
---|
783 | {
|
---|
784 | Vector3 v0=vertex[0]->commonVertex->position;
|
---|
785 | Vector3 v1=vertex[1]->commonVertex->position;
|
---|
786 | Vector3 v2=vertex[2]->commonVertex->position;
|
---|
787 | // Cross-product 2 edges
|
---|
788 | Vector3 e1 = v1 - v0;
|
---|
789 | Vector3 e2 = v2 - v1;
|
---|
790 |
|
---|
791 | normal = e1.crossProduct(e2);
|
---|
792 | normal.normalise();
|
---|
793 | }
|
---|
794 | //---------------------------------------------------------------------
|
---|
795 | void ProgressiveMesh::PMTriangle::replaceVertex(
|
---|
796 | ProgressiveMesh::PMFaceVertex *vold, ProgressiveMesh::PMFaceVertex *vnew)
|
---|
797 | {
|
---|
798 | assert(vold && vnew);
|
---|
799 | assert(vold==vertex[0] || vold==vertex[1] || vold==vertex[2]);
|
---|
800 | assert(vnew!=vertex[0] && vnew!=vertex[1] && vnew!=vertex[2]);
|
---|
801 | if(vold==vertex[0]){
|
---|
802 | vertex[0]=vnew;
|
---|
803 | }
|
---|
804 | else if(vold==vertex[1]){
|
---|
805 | vertex[1]=vnew;
|
---|
806 | }
|
---|
807 | else {
|
---|
808 | assert(vold==vertex[2]);
|
---|
809 | vertex[2]=vnew;
|
---|
810 | }
|
---|
811 | int i;
|
---|
812 | vold->commonVertex->face.erase(this);
|
---|
813 | vnew->commonVertex->face.insert(this);
|
---|
814 | for(i=0;i<3;i++) {
|
---|
815 | vold->commonVertex->removeIfNonNeighbor(vertex[i]->commonVertex);
|
---|
816 | vertex[i]->commonVertex->removeIfNonNeighbor(vold->commonVertex);
|
---|
817 | }
|
---|
818 | for(i=0;i<3;i++) {
|
---|
819 | assert(vertex[i]->commonVertex->face.find(this) != vertex[i]->commonVertex->face.end());
|
---|
820 | for(int j=0;j<3;j++) if(i!=j) {
|
---|
821 | #if OGRE_DEBUG_MODE
|
---|
822 | ofdebug << "Adding vertex " << (unsigned int)vertex[j]->commonVertex->index << " to the neighbor list "
|
---|
823 | "of vertex " << (unsigned int)vertex[i]->commonVertex->index << std::endl;
|
---|
824 | #endif
|
---|
825 | vertex[i]->commonVertex->neighbor.insert(vertex[j]->commonVertex);
|
---|
826 | }
|
---|
827 | }
|
---|
828 | computeNormal();
|
---|
829 | }
|
---|
830 | //---------------------------------------------------------------------
|
---|
831 | ProgressiveMesh::PMVertex::PMVertex() : removed(false)
|
---|
832 | {
|
---|
833 | }
|
---|
834 | //---------------------------------------------------------------------
|
---|
835 | void ProgressiveMesh::PMVertex::setDetails(const Vector3& v, size_t newindex)
|
---|
836 | {
|
---|
837 | position = v;
|
---|
838 | index = newindex;
|
---|
839 | }
|
---|
840 | //---------------------------------------------------------------------
|
---|
841 | void ProgressiveMesh::PMVertex::notifyRemoved(void)
|
---|
842 | {
|
---|
843 | NeighborList::iterator i, iend;
|
---|
844 | iend = neighbor.end();
|
---|
845 | for (i = neighbor.begin(); i != iend; ++i)
|
---|
846 | {
|
---|
847 | // Remove me from neighbor
|
---|
848 | (*i)->neighbor.erase(this);
|
---|
849 | }
|
---|
850 | removed = true;
|
---|
851 | this->collapseTo = NULL;
|
---|
852 | this->collapseCost = NEVER_COLLAPSE_COST;
|
---|
853 | }
|
---|
854 | //---------------------------------------------------------------------
|
---|
855 | bool ProgressiveMesh::PMVertex::isBorder()
|
---|
856 | {
|
---|
857 | // Look for edges which only have one tri attached, this is a border
|
---|
858 |
|
---|
859 | NeighborList::iterator i, iend;
|
---|
860 | iend = neighbor.end();
|
---|
861 | // Loop for each neighbor
|
---|
862 | for(i = neighbor.begin(); i != iend; ++i)
|
---|
863 | {
|
---|
864 | // Count of tris shared between the edge between this and neighbor
|
---|
865 | ushort count = 0;
|
---|
866 | // Loop over each face, looking for shared ones
|
---|
867 | FaceList::iterator j, jend;
|
---|
868 | jend = face.end();
|
---|
869 | for(j = face.begin(); j != jend; ++j)
|
---|
870 | {
|
---|
871 | if((*j)->hasCommonVertex(*i))
|
---|
872 | {
|
---|
873 | // Shared tri
|
---|
874 | count ++;
|
---|
875 | }
|
---|
876 | }
|
---|
877 | //assert(count>0); // Must be at least one!
|
---|
878 | // This edge has only 1 tri on it, it's a border
|
---|
879 | if(count == 1)
|
---|
880 | return true;
|
---|
881 | }
|
---|
882 | return false;
|
---|
883 | }
|
---|
884 | //---------------------------------------------------------------------
|
---|
885 | bool ProgressiveMesh::PMVertex::isManifoldEdgeWith(ProgressiveMesh::PMVertex* v)
|
---|
886 | {
|
---|
887 | // Check the sides involving both these verts
|
---|
888 | // If there is only 1 this is a manifold edge
|
---|
889 | ushort sidesCount = 0;
|
---|
890 | FaceList::iterator i, iend;
|
---|
891 | iend = face.end();
|
---|
892 | for (i = face.begin(); i != iend; ++i)
|
---|
893 | {
|
---|
894 | if ((*i)->hasCommonVertex(v))
|
---|
895 | {
|
---|
896 | sidesCount++;
|
---|
897 | }
|
---|
898 | }
|
---|
899 |
|
---|
900 | return (sidesCount == 1);
|
---|
901 | }
|
---|
902 | //---------------------------------------------------------------------
|
---|
903 | void ProgressiveMesh::PMVertex::removeIfNonNeighbor(ProgressiveMesh::PMVertex *n)
|
---|
904 | {
|
---|
905 | // removes n from neighbor list if n isn't a neighbor.
|
---|
906 | NeighborList::iterator i = neighbor.find(n);
|
---|
907 | if (i == neighbor.end())
|
---|
908 | return; // Not in neighbor list anyway
|
---|
909 |
|
---|
910 | FaceList::iterator f, fend;
|
---|
911 | fend = face.end();
|
---|
912 | for(f = face.begin(); f != fend; ++f)
|
---|
913 | {
|
---|
914 | if((*f)->hasCommonVertex(n)) return; // Still a neighbor
|
---|
915 | }
|
---|
916 |
|
---|
917 | #if OGRE_DEBUG_MODE
|
---|
918 | ofdebug << "Vertex " << (unsigned int)n->index << " is no longer a neighbour of vertex " << (unsigned int)this->index <<
|
---|
919 | " so has been removed from the latter's neighbor list." << std::endl;
|
---|
920 | #endif
|
---|
921 | neighbor.erase(n);
|
---|
922 |
|
---|
923 | if (neighbor.empty() && !toBeRemoved)
|
---|
924 | {
|
---|
925 | // This vertex has been removed through isolation (collapsing around it)
|
---|
926 | this->notifyRemoved();
|
---|
927 | }
|
---|
928 | }
|
---|
929 | //---------------------------------------------------------------------
|
---|
930 | void ProgressiveMesh::dumpContents(const String& log)
|
---|
931 | {
|
---|
932 | std::ofstream ofdump(log.c_str());
|
---|
933 |
|
---|
934 | // Just dump 1st working data for now
|
---|
935 | WorkingDataList::iterator worki = mWorkingData.begin();
|
---|
936 |
|
---|
937 | CommonVertexList::iterator vi, vend;
|
---|
938 | vend = worki->mVertList.end();
|
---|
939 | ofdump << "-------== VERTEX LIST ==-----------------" << std::endl;
|
---|
940 | size_t i;
|
---|
941 | for (vi = worki->mVertList.begin(), i = 0; i < mNumCommonVertices; ++vi, ++i)
|
---|
942 | {
|
---|
943 | ofdump << "Vertex " << (unsigned int)vi->index << " pos: " << vi->position << " removed: "
|
---|
944 | << vi->removed << " isborder: " << vi->isBorder() << std::endl;
|
---|
945 | ofdump << " Faces:" << std::endl;
|
---|
946 | PMVertex::FaceList::iterator f, fend;
|
---|
947 | fend = vi->face.end();
|
---|
948 | for(f = vi->face.begin(); f != fend; ++f)
|
---|
949 | {
|
---|
950 | ofdump << " Triangle index " << (unsigned int)(*f)->index << std::endl;
|
---|
951 | }
|
---|
952 | ofdump << " Neighbours:" << std::endl;
|
---|
953 | PMVertex::NeighborList::iterator n, nend;
|
---|
954 | nend = vi->neighbor.end();
|
---|
955 | for (n = vi->neighbor.begin(); n != nend; ++n)
|
---|
956 | {
|
---|
957 | ofdump << " Vertex index " << (unsigned int)(*n)->index << std::endl;
|
---|
958 | }
|
---|
959 |
|
---|
960 | }
|
---|
961 |
|
---|
962 | TriangleList::iterator ti, tend;
|
---|
963 | tend = worki->mTriList.end();
|
---|
964 | ofdump << "-------== TRIANGLE LIST ==-----------------" << std::endl;
|
---|
965 | for(ti = worki->mTriList.begin(); ti != tend; ++ti)
|
---|
966 | {
|
---|
967 | ofdump << "Triangle " << (unsigned int)ti->index << " norm: " << ti->normal << " removed: " << ti->removed << std::endl;
|
---|
968 | ofdump << " Vertex 0: " << (unsigned int)ti->vertex[0]->realIndex << std::endl;
|
---|
969 | ofdump << " Vertex 1: " << (unsigned int)ti->vertex[1]->realIndex << std::endl;
|
---|
970 | ofdump << " Vertex 2: " << (unsigned int)ti->vertex[2]->realIndex << std::endl;
|
---|
971 | }
|
---|
972 |
|
---|
973 | ofdump << "-------== COLLAPSE COST LIST ==-----------------" << std::endl;
|
---|
974 | for (size_t ci = 0; ci < mNumCommonVertices; ++ci)
|
---|
975 | {
|
---|
976 | ofdump << "Vertex " << (unsigned int)ci << ": " << mWorstCosts[ci] << std::endl;
|
---|
977 | }
|
---|
978 |
|
---|
979 | ofdump.close();
|
---|
980 | }
|
---|
981 |
|
---|
982 |
|
---|
983 | }
|
---|