1 //
2 // Copyright (C) 2004 Tanguy Fautré.
3 // For conditions of distribution and use,
4 // see copyright notice in tri_stripper.h
5 //
6 //////////////////////////////////////////////////////////////////////
7 // SVN: $Id: tri_stripper.cpp 86 2005-06-08 17:47:27Z gpsnoopy $
8 //////////////////////////////////////////////////////////////////////
9
10 #include "tri_stripper.h"
11
12 #include "detail/connectivity_graph.h"
13 #include "detail/policy.h"
14
15 #include <cassert>
16
17
18
19
20 namespace triangle_stripper {
21
22 using namespace detail;
23
24
25
26
tri_stripper(const indices & TriIndices)27 tri_stripper::tri_stripper(const indices & TriIndices)
28 : m_Triangles(TriIndices.size() / 3), // Silently ignore extra indices if (Indices.size() % 3 != 0)
29 m_StripID(0),
30 m_FirstRun(true)
31 {
32 SetCacheSize();
33 SetMinStripSize();
34 SetBackwardSearch();
35 SetPushCacheHits();
36
37 make_connectivity_graph(m_Triangles, TriIndices);
38 }
39
40
41
Strip(primitive_vector * out_pPrimitivesVector)42 void tri_stripper::Strip(primitive_vector * out_pPrimitivesVector)
43 {
44 assert(out_pPrimitivesVector);
45
46 if (! m_FirstRun) {
47 unmark_nodes(m_Triangles);
48 ResetStripIDs();
49 m_Cache.reset();
50 m_TriHeap.clear();
51 m_Candidates.clear();
52 m_StripID = 0;
53
54 m_FirstRun = false;
55 }
56
57 out_pPrimitivesVector->clear();
58
59 InitTriHeap();
60
61 Stripify();
62 AddLeftTriangles();
63
64 std::swap(m_PrimitivesVector, (* out_pPrimitivesVector));
65 }
66
67
68
InitTriHeap()69 void tri_stripper::InitTriHeap()
70 {
71 m_TriHeap.reserve(m_Triangles.size());
72
73 // Set up the triangles priority queue
74 // The lower the number of available neighbour triangles, the higher the priority.
75 for (size_t i = 0; i < m_Triangles.size(); ++i)
76 m_TriHeap.push(m_Triangles[i].out_size());
77
78 // We're not going to add new elements anymore
79 m_TriHeap.lock();
80
81 // Remove useless triangles
82 // Note: we had to put all of them into the heap before to ensure coherency of the heap_array object
83 while ((! m_TriHeap.empty()) && (m_TriHeap.top() == 0))
84 m_TriHeap.pop();
85 }
86
87
88
ResetStripIDs()89 void tri_stripper::ResetStripIDs()
90 {
91 for (triangle_graph::node_iterator it = m_Triangles.begin(); it != m_Triangles.end(); ++it)
92 (**it).ResetStripID();
93 }
94
95
96
Stripify()97 void tri_stripper::Stripify()
98 {
99 while (! m_TriHeap.empty()) {
100
101 // There is no triangle in the candidates list, refill it with the loneliest triangle
102 const size_t HeapTop = m_TriHeap.position(0);
103 m_Candidates.push_back(HeapTop);
104
105 while (! m_Candidates.empty()) {
106
107 // Note: FindBestStrip empties the candidate list, while BuildStrip refills it
108 const strip TriStrip = FindBestStrip();
109
110 if (TriStrip.Size() >= m_MinStripSize)
111 BuildStrip(TriStrip);
112 }
113
114 if (! m_TriHeap.removed(HeapTop))
115 m_TriHeap.erase(HeapTop);
116
117 // Eliminate all the triangles that have now become useless
118 while ((! m_TriHeap.empty()) && (m_TriHeap.top() == 0))
119 m_TriHeap.pop();
120 }
121 }
122
123
124
FindBestStrip()125 inline strip tri_stripper::FindBestStrip()
126 {
127 // Allow to restore the cache (modified by ExtendTriToStrip) and implicitly reset the cache hit count
128 const cache_simulator CacheBackup = m_Cache;
129
130 policy Policy(m_MinStripSize, Cache());
131
132 while (! m_Candidates.empty()) {
133
134 const size_t Candidate = m_Candidates.back();
135 m_Candidates.pop_back();
136
137 // Discard useless triangles from the candidate list
138 if ((m_Triangles[Candidate].marked()) || (m_TriHeap[Candidate] == 0))
139 continue;
140
141 // Try to extend the triangle in the 3 possible forward directions
142 for (size_t i = 0; i < 3; ++i) {
143
144 const strip Strip = ExtendToStrip(Candidate, triangle_order(i));
145 Policy.Challenge(Strip, m_TriHeap[Strip.Start()], m_Cache.hitcount());
146
147 m_Cache = CacheBackup;
148 }
149
150 // Try to extend the triangle in the 6 possible backward directions
151 if (m_BackwardSearch) {
152
153 for (size_t i = 0; i < 3; ++i) {
154
155 const strip Strip = BackExtendToStrip(Candidate, triangle_order(i), false);
156 Policy.Challenge(Strip, m_TriHeap[Strip.Start()], m_Cache.hitcount());
157
158 m_Cache = CacheBackup;
159 }
160
161 for (size_t i = 0; i < 3; ++i) {
162
163 const strip Strip = BackExtendToStrip(Candidate, triangle_order(i), true);
164 Policy.Challenge(Strip, m_TriHeap[Strip.Start()], m_Cache.hitcount());
165
166 m_Cache = CacheBackup;
167 }
168 }
169
170 }
171
172 return Policy.BestStrip();
173 }
174
175
176
ExtendToStrip(const size_t Start,triangle_order Order)177 strip tri_stripper::ExtendToStrip(const size_t Start, triangle_order Order)
178 {
179 const triangle_order StartOrder = Order;
180
181 // Begin a new strip
182 m_Triangles[Start]->SetStripID(++m_StripID);
183 AddTriangle(* m_Triangles[Start], Order, false);
184
185 size_t Size = 1;
186 bool ClockWise = false;
187
188 // Loop while we can further extend the strip
189 for (tri_iterator Node = (m_Triangles.begin() + Start);
190 (Node != m_Triangles.end()) && (!Cache() || ((Size + 2) < CacheSize()));
191 ++Size) {
192
193 const const_link_iterator Link = LinkToNeighbour(Node, ClockWise, Order, false);
194
195 // Is it the end of the strip?
196 if (Link == Node->out_end()) {
197
198 Node = m_Triangles.end();
199 --Size;
200
201 } else {
202
203 Node = Link->terminal();
204 (* Node)->SetStripID(m_StripID);
205 ClockWise = ! ClockWise;
206
207 }
208 }
209
210 return strip(Start, StartOrder, Size);
211 }
212
213
214
BackExtendToStrip(size_t Start,triangle_order Order,bool ClockWise)215 strip tri_stripper::BackExtendToStrip(size_t Start, triangle_order Order, bool ClockWise)
216 {
217 // Begin a new strip
218 m_Triangles[Start]->SetStripID(++m_StripID);
219 BackAddIndex(LastEdge(* m_Triangles[Start], Order).B());
220 size_t Size = 1;
221
222 tri_iterator Node;
223
224 // Loop while we can further extend the strip
225 for (Node = (m_Triangles.begin() + Start);
226 !Cache() || ((Size + 2) < CacheSize());
227 ++Size) {
228
229 const const_link_iterator Link = BackLinkToNeighbour(Node, ClockWise, Order);
230
231 // Is it the end of the strip?
232 if (Link == Node->out_end())
233 break;
234
235 else {
236 Node = Link->terminal();
237 (* Node)->SetStripID(m_StripID);
238 ClockWise = ! ClockWise;
239 }
240 }
241
242 // We have to start from a counterclockwise triangle.
243 // Simply return an empty strip in the case where the first triangle is clockwise.
244 // Even though we could discard the first triangle and start from the next counterclockwise triangle,
245 // this often leads to more lonely triangles afterward.
246 if (ClockWise)
247 return strip();
248
249 if (Cache()) {
250 m_Cache.merge(m_BackCache, Size);
251 m_BackCache.reset();
252 }
253
254 return strip(Node - m_Triangles.begin(), Order, Size);
255 }
256
257
258
BuildStrip(const strip Strip)259 void tri_stripper::BuildStrip(const strip Strip)
260 {
261 const size_t Start = Strip.Start();
262
263 bool ClockWise = false;
264 triangle_order Order = Strip.Order();
265
266 // Create a new strip
267 m_PrimitivesVector.push_back(primitive_group());
268 m_PrimitivesVector.back().Type = TRIANGLE_STRIP;
269 AddTriangle(* m_Triangles[Start], Order, true);
270 MarkTriAsTaken(Start);
271
272 // Loop while we can further extend the strip
273 tri_iterator Node = (m_Triangles.begin() + Start);
274
275 for (size_t Size = 1; Size < Strip.Size(); ++Size) {
276
277 const const_link_iterator Link = LinkToNeighbour(Node, ClockWise, Order, true);
278
279 assert(Link != Node->out_end());
280
281 // Go to the next triangle
282 Node = Link->terminal();
283 MarkTriAsTaken(Node - m_Triangles.begin());
284 ClockWise = ! ClockWise;
285 }
286 }
287
288
289
LinkToNeighbour(const const_tri_iterator Node,const bool ClockWise,triangle_order & Order,const bool NotSimulation)290 inline tri_stripper::const_link_iterator tri_stripper::LinkToNeighbour(const const_tri_iterator Node, const bool ClockWise, triangle_order & Order, const bool NotSimulation)
291 {
292 const triangle_edge Edge = LastEdge(** Node, Order);
293
294 for (const_link_iterator Link = Node->out_begin(); Link != Node->out_end(); ++Link) {
295
296 // Get the reference to the possible next triangle
297 const triangle & Tri = ** Link->terminal();
298
299 // Check whether it's already been used
300 if (NotSimulation || (Tri.StripID() != m_StripID)) {
301
302 if (! Link->terminal()->marked()) {
303
304 // Does the current candidate triangle match the required position for the strip?
305
306 if ((Edge.B() == Tri.A()) && (Edge.A() == Tri.B())) {
307 Order = (ClockWise) ? ABC : BCA;
308 AddIndex(Tri.C(), NotSimulation);
309 return Link;
310 }
311
312 else if ((Edge.B() == Tri.B()) && (Edge.A() == Tri.C())) {
313 Order = (ClockWise) ? BCA : CAB;
314 AddIndex(Tri.A(), NotSimulation);
315 return Link;
316 }
317
318 else if ((Edge.B() == Tri.C()) && (Edge.A() == Tri.A())) {
319 Order = (ClockWise) ? CAB : ABC;
320 AddIndex(Tri.B(), NotSimulation);
321 return Link;
322 }
323 }
324 }
325
326 }
327
328 return Node->out_end();
329 }
330
331
332
BackLinkToNeighbour(const_tri_iterator Node,bool ClockWise,triangle_order & Order)333 inline tri_stripper::const_link_iterator tri_stripper::BackLinkToNeighbour(const_tri_iterator Node, bool ClockWise, triangle_order & Order)
334 {
335 const triangle_edge Edge = FirstEdge(** Node, Order);
336
337 for (const_link_iterator Link = Node->out_begin(); Link != Node->out_end(); ++Link) {
338
339 // Get the reference to the possible previous triangle
340 const triangle & Tri = ** Link->terminal();
341
342 // Check whether it's already been used
343 if ((Tri.StripID() != m_StripID) && ! Link->terminal()->marked()) {
344
345 // Does the current candidate triangle match the required position for the strip?
346
347 if ((Edge.B() == Tri.A()) && (Edge.A() == Tri.B())) {
348 Order = (ClockWise) ? CAB : BCA;
349 BackAddIndex(Tri.C());
350 return Link;
351 }
352
353 else if ((Edge.B() == Tri.B()) && (Edge.A() == Tri.C())) {
354 Order = (ClockWise) ? ABC : CAB;
355 BackAddIndex(Tri.A());
356 return Link;
357 }
358
359 else if ((Edge.B() == Tri.C()) && (Edge.A() == Tri.A())) {
360 Order = (ClockWise) ? BCA : ABC;
361 BackAddIndex(Tri.B());
362 return Link;
363 }
364 }
365
366 }
367
368 return Node->out_end();
369 }
370
371
372
MarkTriAsTaken(const size_t i)373 void tri_stripper::MarkTriAsTaken(const size_t i)
374 {
375 typedef triangle_graph::out_arc_iterator tri_link_iter;
376
377 // Mark the triangle node
378 m_Triangles[i].mark();
379
380 // Remove triangle from priority queue if it isn't yet
381 if (! m_TriHeap.removed(i))
382 m_TriHeap.erase(i);
383
384 // Adjust the degree of available neighbour triangles
385 for (tri_link_iter Link = m_Triangles[i].out_begin(); Link != m_Triangles[i].out_end(); ++Link) {
386
387 const size_t j = Link->terminal() - m_Triangles.begin();
388
389 if ((! m_Triangles[j].marked()) && (! m_TriHeap.removed(j))) {
390 size_t NewDegree = m_TriHeap.peek(j);
391 NewDegree = NewDegree - 1;
392 m_TriHeap.update(j, NewDegree);
393
394 // Update the candidate list if cache is enabled
395 if (Cache() && (NewDegree > 0))
396 m_Candidates.push_back(j);
397 }
398 }
399 }
400
401
402
FirstEdge(const triangle & Triangle,const triangle_order Order)403 inline triangle_edge tri_stripper::FirstEdge(const triangle & Triangle, const triangle_order Order)
404 {
405 switch (Order)
406 {
407 case ABC:
408 return triangle_edge(Triangle.A(), Triangle.B());
409
410 case BCA:
411 return triangle_edge(Triangle.B(), Triangle.C());
412
413 case CAB:
414 return triangle_edge(Triangle.C(), Triangle.A());
415
416 default:
417 assert(false);
418 return triangle_edge(0, 0);
419 }
420 }
421
422
423
LastEdge(const triangle & Triangle,const triangle_order Order)424 inline triangle_edge tri_stripper::LastEdge(const triangle & Triangle, const triangle_order Order)
425 {
426 switch (Order)
427 {
428 case ABC:
429 return triangle_edge(Triangle.B(), Triangle.C());
430
431 case BCA:
432 return triangle_edge(Triangle.C(), Triangle.A());
433
434 case CAB:
435 return triangle_edge(Triangle.A(), Triangle.B());
436
437 default:
438 assert(false);
439 return triangle_edge(0, 0);
440 }
441 }
442
443
444
AddIndex(const index i,const bool NotSimulation)445 inline void tri_stripper::AddIndex(const index i, const bool NotSimulation)
446 {
447 if (Cache())
448 m_Cache.push(i, ! NotSimulation);
449
450 if (NotSimulation)
451 m_PrimitivesVector.back().Indices.push_back(i);
452 }
453
454
455
BackAddIndex(const index i)456 inline void tri_stripper::BackAddIndex(const index i)
457 {
458 if (Cache())
459 m_BackCache.push(i, true);
460 }
461
462
463
AddTriangle(const triangle & Tri,const triangle_order Order,const bool NotSimulation)464 inline void tri_stripper::AddTriangle(const triangle & Tri, const triangle_order Order, const bool NotSimulation)
465 {
466 switch (Order)
467 {
468 case ABC:
469 AddIndex(Tri.A(), NotSimulation);
470 AddIndex(Tri.B(), NotSimulation);
471 AddIndex(Tri.C(), NotSimulation);
472 break;
473
474 case BCA:
475 AddIndex(Tri.B(), NotSimulation);
476 AddIndex(Tri.C(), NotSimulation);
477 AddIndex(Tri.A(), NotSimulation);
478 break;
479
480 case CAB:
481 AddIndex(Tri.C(), NotSimulation);
482 AddIndex(Tri.A(), NotSimulation);
483 AddIndex(Tri.B(), NotSimulation);
484 break;
485 }
486 }
487
488
489
BackAddTriangle(const triangle & Tri,const triangle_order Order)490 inline void tri_stripper::BackAddTriangle(const triangle & Tri, const triangle_order Order)
491 {
492 switch (Order)
493 {
494 case ABC:
495 BackAddIndex(Tri.C());
496 BackAddIndex(Tri.B());
497 BackAddIndex(Tri.A());
498 break;
499
500 case BCA:
501 BackAddIndex(Tri.A());
502 BackAddIndex(Tri.C());
503 BackAddIndex(Tri.B());
504 break;
505
506 case CAB:
507 BackAddIndex(Tri.B());
508 BackAddIndex(Tri.A());
509 BackAddIndex(Tri.C());
510 break;
511 }
512 }
513
514
515
AddLeftTriangles()516 void tri_stripper::AddLeftTriangles()
517 {
518 // Create the last indices array and fill it with all the triangles that couldn't be stripped
519 primitive_group Primitives;
520 Primitives.Type = TRIANGLES;
521 m_PrimitivesVector.push_back(Primitives);
522 indices & Indices = m_PrimitivesVector.back().Indices;
523
524 for (size_t i = 0; i < m_Triangles.size(); ++i)
525 if (! m_Triangles[i].marked()) {
526 Indices.push_back(m_Triangles[i]->A());
527 Indices.push_back(m_Triangles[i]->B());
528 Indices.push_back(m_Triangles[i]->C());
529 }
530
531 // Undo if useless
532 if (Indices.size() == 0)
533 m_PrimitivesVector.pop_back();
534 }
535
536
537
Cache() const538 inline bool tri_stripper::Cache() const
539 {
540 return (m_Cache.size() != 0);
541 }
542
543
544
CacheSize() const545 inline size_t tri_stripper::CacheSize() const
546 {
547 return m_Cache.size();
548 }
549
550
551
552
553 } // namespace triangle_stripper
554