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