From fcfab2d6163c553227137fd10d3e30327c2e852e Mon Sep 17 00:00:00 2001 From: ponchio Date: Wed, 6 Oct 2004 16:39:38 +0000 Subject: [PATCH] Added. --- apps/nexus/tristripper/graph_array.h | 421 +++++++++++++++++ apps/nexus/tristripper/heap_array.h | 275 +++++++++++ apps/nexus/tristripper/tri_stripper.cpp | 586 ++++++++++++++++++++++++ apps/nexus/tristripper/tri_stripper.h | 372 +++++++++++++++ 4 files changed, 1654 insertions(+) create mode 100644 apps/nexus/tristripper/graph_array.h create mode 100644 apps/nexus/tristripper/heap_array.h create mode 100644 apps/nexus/tristripper/tri_stripper.cpp create mode 100644 apps/nexus/tristripper/tri_stripper.h diff --git a/apps/nexus/tristripper/graph_array.h b/apps/nexus/tristripper/graph_array.h new file mode 100644 index 00000000..1ebf27d3 --- /dev/null +++ b/apps/nexus/tristripper/graph_array.h @@ -0,0 +1,421 @@ +// graph_array.h: interface for the graph_array class. +// +////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 Tanguy Fautré. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Tanguy Fautré +// softdev@pandora.be +// +////////////////////////////////////////////////////////////////////// +// +// Semi-dynamic directed graph +// *************************** +// +// Current version: 3.00 BETA 3 (04/12/2002) +// +// Comment: graph_array is equivalent to an array of nodes linked by +// arcs. +// This means you can't change the size (the number of nodes) +// of the graph once you created it (setsize() will delete +// any previous nodes and arcs). +// But you can add or remove arcs. +// +// History: - 3.00 BETA 3 (04/12/2002) - Added empty() +// - Changed some parameters from copy to reference +// - Fixed a bug with erase_arc +// - Un-inlined external functions +// - Added "insert_arc" which is equivalent to "insert" +// - 3.00 BETA 2 (16/11/2002) - Improved portability +// - 3.00 BETA 1 (27/08/2002) - First public release +// +////////////////////////////////////////////////////////////////////// + +#pragma once + + + +// namespace common_structures +namespace common_structures { + + + + +// graph_array main class +template +class graph_array +{ +public: + + class arc; + class node; + + // New types + typedef size_t nodeid; + typedef std::vector::iterator node_iterator; + typedef std::vector::const_iterator const_node_iterator; + typedef std::vector::reverse_iterator node_reverse_iterator; + typedef std::vector::const_reverse_iterator const_node_reverse_iterator; + + typedef graph_array _mytype; + + + // graph_array::arc class + class arc + { + public: + arc & mark() { m_Marker = true; return (* this); } + arc & unmark() { m_Marker = false; return (* this); } + bool marked() const { return m_Marker; } + + node_iterator initial() const { return m_Initial; } + node_iterator terminal() const { return m_Terminal; } + + arctype & operator * () { return m_Elem; } + arctype * operator -> () { return &m_Elem; } + const arctype & operator * () const { return m_Elem; } + const arctype * operator -> () const { return &m_Elem; } + + protected: + friend class graph_array; + + arc(const node_iterator & Initial, const node_iterator & Terminal) + : m_Initial(Initial), m_Terminal(Terminal), m_Marker(false) { } + + arc(const node_iterator & Initial, const node_iterator & Terminal, const arctype & Elem) + : m_Initial(Initial), m_Terminal(Terminal), m_Elem(Elem), m_Marker(false) { } + + node_iterator m_Initial; + node_iterator m_Terminal; + arctype m_Elem; + bool m_Marker; + }; + + + // New types + typedef std::list::iterator out_arc_iterator; + typedef std::list::const_iterator const_out_arc_iterator; + + + // graph_array::node class + class node + { + public: + node & mark() { m_Marker = true; return (* this); } + node & unmark() { m_Marker = false; return (* this); } + bool marked() const { return m_Marker; } + + bool out_empty() const { return m_OutArcs.empty(); } + size_t number_of_out_arcs() const { return m_OutArcs.size(); } + + out_arc_iterator out_begin() { return m_OutArcs.begin(); } + out_arc_iterator out_end() { return m_OutArcs.end(); } + const_out_arc_iterator out_begin() const { return m_OutArcs.begin(); } + const_out_arc_iterator out_end() const { return m_OutArcs.end(); } + + nodetype & operator * () { return m_Elem; } + nodetype * operator -> () { return &m_Elem; } + const nodetype & operator * () const { return m_Elem; } + const nodetype * operator -> () const { return &m_Elem; } + + nodetype & operator = (const nodetype & Elem) { return (m_Elem = Elem); } + + protected: + friend class graph_array; + friend class std::vector; + + node() : m_Marker(false) { } + + std::list m_OutArcs; + nodetype m_Elem; + bool m_Marker; + }; + + + // Construction/Destruction + graph_array(); + explicit graph_array(const size_t NbNodes); + + // Node related member functions + void clear(); + bool empty() const; + void setsize(const size_t NbNodes); + size_t size() const; + + node & operator [] (const nodeid & i); + const node & operator [] (const nodeid & i) const; + + node_iterator begin(); + node_iterator end(); + const_node_iterator begin() const; + const_node_iterator end() const; + + node_reverse_iterator rbegin(); + node_reverse_iterator rend(); + const_node_reverse_iterator rbegin() const; + const_node_reverse_iterator rend() const; + + // Arc related member functions + size_t number_of_arcs() const; + + void erase_arcs(); + void erase_arcs(const node_iterator & Initial); + out_arc_iterator erase_arc(const out_arc_iterator & Pos); + + out_arc_iterator insert_arc(const nodeid & Initial, const nodeid & Terminal); + out_arc_iterator insert_arc(const nodeid & Initial, const nodeid & Terminal, const arctype & Elem); + out_arc_iterator insert_arc(const node_iterator & Initial, const node_iterator & Terminal); + out_arc_iterator insert_arc(const node_iterator & Initial, const node_iterator & Terminal, const arctype & Elem); + + // Another interface for insert_arc + out_arc_iterator insert(const nodeid & Initial, const nodeid & Terminal) { return insert_arc(Initial, Terminal); } + out_arc_iterator insert(const nodeid & Initial, const nodeid & Terminal, const arctype & Elem) { return insert_arc(Initial, Terminal, Elem); } + out_arc_iterator insert(const node_iterator & Initial, const node_iterator & Terminal) { return insert_arc(Initial, Terminal); } + out_arc_iterator insert(const node_iterator & Initial, const node_iterator & Terminal, const arctype & Elem) { return insert_arc(Initial, Terminal, Elem); } + + // Optimized (overloaded) functions + void swap(_mytype & Right); + friend void swap(_mytype & Left, _mytype & Right) { Left.swap(Right); } + +protected: + size_t m_NbArcs; + std::vector m_Nodes; +}; + + + +// Additional "low level", graph related, functions +template +void unmark_nodes(graph_array & G); + +template +void unmark_arcs_from_node(graph_array::node & N); + +template +void unmark_arcs(graph_array & G); + + + + +////////////////////////////////////////////////////////////////////////// +// graph_array Inline functions +////////////////////////////////////////////////////////////////////////// + +template +inline graph_array::graph_array() : m_NbArcs(0) { } + + +template +inline graph_array::graph_array(const size_t NbNodes) : m_NbArcs(0), m_Nodes(NbNodes) { } + + +template +inline void graph_array::clear() { + m_NbArcs = 0; + m_Nodes.clear(); +} + + + +template +inline bool graph_array::empty() const { + return m_Nodes.empty(); +} + + +template +inline size_t graph_array::size() const { + return m_Nodes.size(); +} + + +template +inline void graph_array::setsize(const size_t NbNodes) { + clear(); + m_Nodes.resize(NbNodes); +} + + +template +inline graph_array::node & graph_array::operator [] (const nodeid & i) { + // Debug check + assert(i < size()); + + return m_Nodes[i]; +} + + +template +inline const graph_array::node & graph_array::operator [] (const nodeid & i) const { + // Debug check + assert(i < size()); + + return m_Nodes[i]; +} + + +template +inline graph_array::node_iterator graph_array::begin() { + return m_Nodes.begin(); +} + + +template +inline graph_array::node_iterator graph_array::end() { + return m_Nodes.end(); +} + + +template +inline graph_array::const_node_iterator graph_array::begin() const { + return m_Nodes.begin(); +} + + +template +inline graph_array::const_node_iterator graph_array::end() const { + return m_Nodes.end(); +} + + +template +inline graph_array::node_reverse_iterator graph_array::rbegin() { + return m_Nodes.rbegin(); +} + + +template +inline graph_array::node_reverse_iterator graph_array::rend() { + return m_Nodes.rend(); +} + + +template +inline graph_array::const_node_reverse_iterator graph_array::rbegin() const { + return m_Nodes.rbegin(); +} + + +template +inline graph_array::const_node_reverse_iterator graph_array::rend() const { + return m_Nodes.rend(); +} + + +template +inline size_t graph_array::number_of_arcs() const { + return m_NbArcs; +} + + +template +inline graph_array::out_arc_iterator graph_array::insert_arc(const nodeid & Initial, const nodeid & Terminal) { + return (insert(begin() + Initial, begin() + Terminal)); +} + + +template +inline graph_array::out_arc_iterator graph_array::insert_arc(const nodeid & Initial, const nodeid & Terminal, const arctype & Elem) { + return (insert(begin() + Initial, begin() + Terminal, Elem)); +} + + +template +inline graph_array::out_arc_iterator graph_array::insert_arc(const node_iterator & Initial, const node_iterator & Terminal) { + ++m_NbArcs; + Initial->m_OutArcs.push_back(arc(Initial, Terminal)); + return (--(Initial->m_OutArcs.end())); +} + + +template +inline graph_array::out_arc_iterator graph_array::insert_arc(const node_iterator & Initial, const node_iterator & Terminal, const arctype & Elem) { + ++m_NbArcs; + Initial->m_OutArcs.push_back(arc(Initial, Terminal, Elem)); + return (--(Initial->m_OutArcs.end())); +} + + +template +inline graph_array::out_arc_iterator graph_array::erase_arc(const out_arc_iterator & Pos) { + --m_NbArcs; + return (Pos->initial()->m_OutArcs.erase(Pos)); +} + + +template +inline void graph_array::erase_arcs(const node_iterator & Initial) { + m_NbArcs -= (Initial->m_OutArcs.size()); + Initial->m_OutArcs.clear(); +} + + +template +inline void graph_array::erase_arcs() { + m_NbArcs = 0; + for (nodeid i = 0; i < Size(); ++i) + m_Nodes[i].m_OutArcs.clear(); +} + + +template +inline void graph_array::swap(_mytype & Right) { + std::swap(m_NbArcs, Right.m_NbArcs); + std::swap(m_Nodes, Right.m_Nodes); +} + + + +////////////////////////////////////////////////////////////////////////// +// additional functions +////////////////////////////////////////////////////////////////////////// + +template +void unmark_nodes(graph_array & G) +{ + typedef graph_array::node_iterator node_it; + + for (node_it NodeIt = G.begin(); NodeIt != G.end(); ++NodeIt) + NodeIt->unmark(); +} + + +template +void unmark_arcs_from_node(graph_array::node & N) +{ + typedef graph_array::out_arc_iterator arc_it; + + for (arc_it ArcIt = N.out_begin(); ArcIt != N.out_end(); ++ArcIt) + ArcIt->unmark(); +} + + +template +void unmark_arcs(graph_array & G) +{ + typedef graph_array::node_iterator node_it; + + for (node_it NodeIt = G.begin(); NodeIt != G.end(); ++NodeIt) + unmark_arcs_from_node(* NodeIt); +} + + + + +}; // namespace common_structures diff --git a/apps/nexus/tristripper/heap_array.h b/apps/nexus/tristripper/heap_array.h new file mode 100644 index 00000000..b164e91f --- /dev/null +++ b/apps/nexus/tristripper/heap_array.h @@ -0,0 +1,275 @@ +// heap_array.h: interface for the heap_array class. +// +////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 Tanguy Fautré. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Tanguy Fautré +// softdev@pandora.be +// +////////////////////////////////////////////////////////////////////// +// +// Semi-dynamic indexed heap +// ************************* +// +// Current version: 1.00 BETA 1 (24/10/2002) +// +// Comment: heap_array acts like a normal heap, you can push elements +// and then get the greatest one. +// However you cannot push any more element once an element +// has been removed (pop, erase, etc...). +// Elements can be modified after they've been pushed into +// the heap via their indice. +// +// History: - +// +////////////////////////////////////////////////////////////////////// + +#pragma once + + + +// namespace common_structures +namespace common_structures { + + + + +template > +class heap_array +{ +public: + + struct heap_is_locked { }; + + + // heap_array main interface. Pre = PreCondition, Post = PostCondition + + heap_array() : m_Locked(false) { } // Post: ((size() == 0) && ! locked()) + + void clear(); // Post: ((size() == 0) && ! locked()) + + void reserve(size_t Size); + size_t size() const; + + bool empty() const; + bool locked() const; + bool removed(size_t i) const; // Pre: (valid(i)) + bool valid(size_t i) const; + + const T & top() const; // Pre: (! empty()) + const T & peek(size_t i) const; // Pre: (valid(i) && ! removed(i)) + const T & operator [] (size_t i) const; // Pre: (valid(i) && ! removed(i)) + + size_t push(const T & Elem); // Pre: (! locked()) else throw (heap_is_locked) + + void pop(); // Pre: (! empty()) Post: (locked()) + void erase(size_t i); // Pre: (valid(i) && ! removed(i)) Post: (locked()) + void update(size_t i, const T & Elem); // Pre: (valid(i) && ! removed(i)) Post: (locked()) + +protected: + + struct linker { + linker(const T & Elem, size_t i) : m_Elem(Elem), m_Indice(i) { } + + T m_Elem; + size_t m_Indice; + }; + + typedef std::vector linked_heap; + typedef std::vector finder; + + void Adjust(size_t i); + void Swap(size_t a, size_t b); + bool Less(const linker & a, const linker & b) const; + + linked_heap m_Heap; + finder m_Finder; + CmpT m_Compare; + bool m_Locked; +}; + + + + +////////////////////////////////////////////////////////////////////////// +// heap_indexed Inline functions +////////////////////////////////////////////////////////////////////////// + +template +inline void heap_array::clear() { + m_Heap.clear(); + m_Finder.clear(); + m_Locked = false; +} + + +template +inline bool heap_array::empty() const { + return m_Heap.empty(); +} + + +template +inline bool heap_array::locked() const { + return m_Locked; +} + + +template +inline void heap_array::reserve(size_t Size) { + m_Heap.reserve(Size); + m_Finder.reserve(Size); +} + + +template +inline size_t heap_array::size() const { + return m_Heap.size(); +} + + +template +inline const T & heap_array::top() const { + // Debug check to ensure heap is not empty + assert(! empty()); + + return m_Heap.front().m_Elem; +} + + +template +inline const T & heap_array::peek(size_t i) const { + // Debug check to ensure element is still present + assert(! removed(i)); + + return (m_Heap[m_Finder[i]].m_Elem); +} + + +template +inline const T & heap_array::operator [] (size_t i) const { + return peek(i); +} + + +template +inline void heap_array::pop() { + m_Locked = true; + + // Debug check to ensure heap is not empty + assert(! empty()); + + Swap(0, size() - 1); + m_Heap.pop_back(); + Adjust(0); +} + + +template +inline size_t heap_array::push(const T & Elem) { + if (m_Locked) + throw heap_is_locked(); + + size_t Id = size(); + m_Finder.push_back(Id); + m_Heap.push_back(linker(Elem, Id)); + Adjust(Id); + + return Id; +} + + +template +inline void heap_array::erase(size_t i) { + m_Locked = true; + + // Debug check to ensure element is still present + assert(! removed(i)); + + size_t j = m_Finder[i]; + Swap(j, size() - 1); + m_Heap.pop_back(); + Adjust(j); +} + + +template +inline bool heap_array::removed(size_t i) const { + return (m_Finder[i] >= m_Heap.size()); +} + + +template +inline bool heap_array::valid(size_t i) const { + return (i < m_Finder.size()); +} + + +template +inline void heap_array::update(size_t i, const T & Elem) { + // Debug check to ensure element is still present + assert(! removed(i)); + + size_t j = m_Finder[i]; + m_Heap[j].m_Elem = Elem; + Adjust(j); +} + + +template +inline void heap_array::Adjust(size_t i) { + size_t j; + + // Check the upper part of the heap + for (j = i; (j > 0) && (Less(m_Heap[(j - 1) / 2], m_Heap[j])); j = ((j - 1) / 2)) + Swap(j, (j - 1) / 2); + + // Check the lower part of the heap + for (i = j; (j = 2 * i + 1) < size(); i = j) { + if ((j + 1 < size()) && (Less(m_Heap[j], m_Heap[j + 1]))) + ++j; + + if (Less(m_Heap[j], m_Heap[i])) + return; + + Swap(i, j); + } +} + + +template +inline void heap_array::Swap(size_t a, size_t b) { + std::swap(m_Heap[a], m_Heap[b]); + + // use (size_t &) to get rid of a bogus compile warning + (size_t &) (m_Finder[(m_Heap[a].m_Indice)]) = a; + (size_t &) (m_Finder[(m_Heap[b].m_Indice)]) = b; +} + + +template +inline bool heap_array::Less(const linker & a, const linker & b) const { + return m_Compare(a.m_Elem, b.m_Elem); +} + + + + +}; // namespace common_structures \ No newline at end of file diff --git a/apps/nexus/tristripper/tri_stripper.cpp b/apps/nexus/tristripper/tri_stripper.cpp new file mode 100644 index 00000000..6e7dbb14 --- /dev/null +++ b/apps/nexus/tristripper/tri_stripper.cpp @@ -0,0 +1,586 @@ +// tri_stripper.cpp: implementation of the Tri Stripper class. +// +// Copyright (C) 2002 Tanguy Fautré. +// For conditions of distribution and use, +// see copyright notice in tri_stripper.h +// +////////////////////////////////////////////////////////////////////// + +#include +#include + +#include +#include +//#include +//#include +#include +#include +#include +#include +using namespace std; +#include "tri_stripper.h" + + + +// namespace triangle_stripper +namespace triangle_stripper { + + + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + + + +////////////////////////////////////////////////////////////////////// +// Members Functions +////////////////////////////////////////////////////////////////////// + +void tri_stripper::Strip(primitives_vector * out_pPrimitivesVector) +{ + // verify that the number of indices is correct + if (m_TriIndices.size() % 3 != 0) + throw triangles_indices_error(); + + // clear possible garbage + m_PrimitivesVector.clear(); + out_pPrimitivesVector->clear(); + + // Initialize the triangle graph + InitTriGraph(); + + // Initialize the triangle priority queue + InitTriHeap(); + + // Initialize the cache simulator + InitCache(); + + // Launch the triangle strip generator + Stripify(); + + // Add the triangles that couldn't be stripped + AddLeftTriangles(); + + // Free ressources + m_Triangles.clear(); + + // Put the results into the user's vector + std::swap(m_PrimitivesVector, (* out_pPrimitivesVector)); +} + + + +void tri_stripper::InitTriGraph() +{ + // Set up the graph size and complete the triangles data + // note: setsize() completely resets the graph as well as the node markers + m_Triangles.setsize(m_TriIndices.size() / 3); + size_t i; + for (i = 0; i < m_Triangles.size(); ++i) + m_Triangles[i] = triangle(m_TriIndices[i * 3 + 0], m_TriIndices[i * 3 + 1], m_TriIndices[i * 3 + 2]); + + // Build the edges lookup table + triangle_edges TriInterface; + TriInterface.reserve(m_Triangles.size() * 3); + + for (i = 0; i < m_Triangles.size(); ++i) { + TriInterface.push_back(triangle_edge(m_Triangles[i]->A(), m_Triangles[i]->B(), i)); + TriInterface.push_back(triangle_edge(m_Triangles[i]->B(), m_Triangles[i]->C(), i)); + TriInterface.push_back(triangle_edge(m_Triangles[i]->C(), m_Triangles[i]->A(), i)); + } + + // Sort the lookup table for faster searches + std::sort(TriInterface.begin(), TriInterface.end(), _cmp_tri_interface_lt()); + + // Link neighbour triangles together using the edges lookup table + for (i = 0; i < m_Triangles.size(); ++i) { + + const triangle_edge EdgeBA(m_Triangles[i]->B(), m_Triangles[i]->A(), i); + const triangle_edge EdgeCB(m_Triangles[i]->C(), m_Triangles[i]->B(), i); + const triangle_edge EdgeAC(m_Triangles[i]->A(), m_Triangles[i]->C(), i); + + LinkNeighboursTri(TriInterface, EdgeBA); + LinkNeighboursTri(TriInterface, EdgeCB); + LinkNeighboursTri(TriInterface, EdgeAC); + } +} + + + +void tri_stripper::LinkNeighboursTri(const triangle_edges & TriInterface, const triangle_edge Edge) +{ + typedef triangle_edges::const_iterator edge_const_it; + + // Find the first edge equal to Edge + edge_const_it It = std::lower_bound(TriInterface.begin(), TriInterface.end(), Edge, _cmp_tri_interface_lt()); + + // See if there are any other edges that are equal + // (if so, it means that more than 2 triangles are sharing the same edge, + // which is unlikely but not impossible) + for (; (It != TriInterface.end()) && ((It->A() == Edge.A()) && (It->B() == Edge.B())); ++It) + m_Triangles.insert(Edge.TriPos(), It->TriPos()); + + // Note: degenerated triangles will also point themselves as neighbour triangles +} + + + +void tri_stripper::InitTriHeap() +{ + m_TriHeap.clear(); + m_TriHeap.reserve(m_Triangles.size()); + + // Set up the triangles priority queue + // The lower the number of available neighbour triangles, the higher the priority. + for (size_t i = 0; i < m_Triangles.size(); ++i) + m_TriHeap.push(triangle_degree(i, m_Triangles[i].number_of_out_arcs())); + + // Remove useless triangles + // (Note: we had to put all of them into the heap before to ensure coherency of the heap_array object) + while ((! m_TriHeap.empty()) && (m_TriHeap.top().Degree() == 0)) + m_TriHeap.pop(); +} + + + +void tri_stripper::InitCache() +{ + m_IndicesCache.clear(); + + if (m_CacheSize > 0) + m_IndicesCache.resize(m_CacheSize, static_cast(-1)); +} + + + +void tri_stripper::Stripify() +{ + // Reset the triangle strip id selector + m_StripID = 0; + + // Reset the candidate list + m_NextCandidates.clear(); + + // Loop untill there is no available candidate triangle left + while (! m_TriHeap.empty()) { + + // There is no triangle in the candidates list, refill it with the loneliest triangle + const size_t HeapTop = m_TriHeap.top().TriPos(); + m_NextCandidates.push_back(HeapTop); + + // Loop while BuildStrip can find good candidates for us + while (! m_NextCandidates.empty()) { + + // Choose the best strip containing that triangle + // Note: FindBestStrip empties m_NextCandidates + const triangle_strip TriStrip = FindBestStrip(); + + // Build it if it's long enough, otherwise discard it + // Note: BuildStrip refills m_NextCandidates + if (TriStrip.Size() >= m_MinStripSize) + BuildStrip(TriStrip); + } + + // We must discard the triangle we inserted in the candidate list from the heap + // if it led to nothing. (We simply removed it if it hasn't been removed by BuildStrip() yet) + if (! m_TriHeap.removed(HeapTop)) + m_TriHeap.erase(HeapTop); + + + // Eliminate all the triangles that have now become useless + while ((! m_TriHeap.empty()) && (m_TriHeap.top().Degree() == 0)) + m_TriHeap.pop(); + } +} + + + +inline tri_stripper::triangle_strip tri_stripper::FindBestStrip() +{ + triangle_strip BestStrip; + size_t BestStripDegree = 0; + size_t BestStripCacheHits = 0; + + // Backup the cache, because it'll be erased during the simulations + indices_cache CacheBackup = m_IndicesCache; + + while (! m_NextCandidates.empty()) { + + // Discard useless triangles from the candidates list + if ((m_Triangles[m_NextCandidates.back()].marked()) || (m_TriHeap[m_NextCandidates.back()].Degree() == 0)) { + m_NextCandidates.pop_back(); + + // "continue" is evil! But it really makes things easier here. + // The useless triangle is discarded, and the "while" just rebegins again + continue; + } + + // Invariant: (CandidateTri's Degree() >= 1) && (CandidateTri is not marked). + // So it can directly be used. + const size_t CandidateTri = m_NextCandidates.back(); + m_NextCandidates.pop_back(); + + // Try to extend the triangle in the 3 possible directions + for (size_t i = 0; i < 3; ++i) { + + // Reset the cache hit count + m_CacheHits = 0; + + // Try a new strip with that triangle in a particular direction + const triangle_strip TempStrip = ExtendTriToStrip(CandidateTri, triangle_strip::start_order(i)); + + // Restore the cache (modified by ExtendTriToStrip) + m_IndicesCache = CacheBackup; + + // We want to keep the best strip + // Discard strips that don't match the minimum required size + if (TempStrip.Size() >= m_MinStripSize) { + + // Cache simulator disabled? + if (m_CacheSize == 0) { + + // Cache is disabled, take the longest strip + if (TempStrip.Size() > BestStrip.Size()) + BestStrip = TempStrip; + + // Cache simulator enabled + // Use other criteria to find the "best" strip + } else { + + // Priority 1: Keep the strip with the best cache hit count + if (m_CacheHits > BestStripCacheHits) { + BestStrip = TempStrip; + BestStripDegree = m_TriHeap[TempStrip.StartTriPos()].Degree(); + BestStripCacheHits = m_CacheHits; + + } else if (m_CacheHits == BestStripCacheHits) { + + // Priority 2: Keep the strip with the loneliest start triangle + if ((BestStrip.Size() != 0) && (m_TriHeap[TempStrip.StartTriPos()].Degree() < BestStripDegree)) { + BestStrip = TempStrip; + BestStripDegree = m_TriHeap[TempStrip.StartTriPos()].Degree(); + + // Priority 3: Keep the longest strip + } else if (TempStrip.Size() > BestStrip.Size()) { + BestStrip = TempStrip; + BestStripDegree = m_TriHeap[TempStrip.StartTriPos()].Degree(); + } + } + } + } + + } + + } + + return BestStrip; +} + + + +tri_stripper::triangle_strip tri_stripper::ExtendTriToStrip(const size_t StartTriPos, const triangle_strip::start_order StartOrder) +{ + typedef triangles_graph::const_out_arc_iterator const_tri_link_iter; + typedef triangles_graph::node_iterator tri_node_iter; + + size_t Size = 1; + bool ClockWise = false; + triangle_strip::start_order Order = StartOrder; + + // Begin a new strip + ++m_StripID; + + // Mark the first triangle as used for this strip + m_Triangles[StartTriPos]->SetStripID(m_StripID); + + // Update the indice cache + AddTriToCache((* m_Triangles[StartTriPos]), Order); + + + // Loop while we can further extend the strip + for (tri_node_iter TriNodeIt = (m_Triangles.begin() + StartTriPos); + (TriNodeIt != m_Triangles.end()) && ((m_CacheSize <= 0) || ((Size + 2) < m_CacheSize)); + ++Size) { + + // Get the triangle edge that would lead to the next triangle + const triangle_edge Edge = GetLatestEdge(** TriNodeIt, Order); + + // Link to a neighbour triangle + const_tri_link_iter LinkIt; + for (LinkIt = TriNodeIt->out_begin(); LinkIt != TriNodeIt->out_end(); ++LinkIt) { + + // Get the reference to the possible next triangle + const triangle & Tri = (** ((*LinkIt).terminal())); + + // Check whether it's already been used + if ((Tri.StripID() != m_StripID) && (! ((*LinkIt).terminal()->marked()))) { + + // Does the current candidate triangle match the required for the strip? + + if ((Edge.B() == Tri.A()) && (Edge.A() == Tri.B())) { + Order = (ClockWise) ? triangle_strip::ABC : triangle_strip::BCA; + AddIndiceToCache(Tri.C(), true); + break; + } + + else if ((Edge.B() == Tri.B()) && (Edge.A() == Tri.C())) { + Order = (ClockWise) ? triangle_strip::BCA : triangle_strip::CAB; + AddIndiceToCache(Tri.A(), true); + break; + } + + else if ((Edge.B() == Tri.C()) && (Edge.A() == Tri.A())) { + Order = (ClockWise) ? triangle_strip::CAB : triangle_strip::ABC; + AddIndiceToCache(Tri.B(), true); + break; + } + } + } + + // Is it the end of the strip? + if (LinkIt == TriNodeIt->out_end()) { + TriNodeIt = m_Triangles.end(); + --Size; + } else { + TriNodeIt = (*LinkIt).terminal(); + + // Setup for the next triangle + (* TriNodeIt)->SetStripID(m_StripID); + ClockWise = ! ClockWise; + } + } + + + return triangle_strip(StartTriPos, StartOrder, Size); +} + + + +inline tri_stripper::triangle_edge tri_stripper::GetLatestEdge(const triangle & Triangle, const triangle_strip::start_order Order) const +{ + switch (Order) { + case triangle_strip::ABC: + return triangle_edge(Triangle.B(), Triangle.C(), 0); + case triangle_strip::BCA: + return triangle_edge(Triangle.C(), Triangle.A(), 0); + case triangle_strip::CAB: + return triangle_edge(Triangle.A(), Triangle.B(), 0); + default: + return triangle_edge(0, 0, 0); + } +} + + + +void tri_stripper::BuildStrip(const triangle_strip TriStrip) +{ + typedef triangles_graph::const_out_arc_iterator const_tri_link_iter; + typedef triangles_graph::node_iterator tri_node_iter; + + const size_t StartTriPos = TriStrip.StartTriPos(); + + bool ClockWise = false; + triangle_strip::start_order Order = TriStrip.StartOrder(); + + // Create a new strip + m_PrimitivesVector.push_back(primitives()); + m_PrimitivesVector.back().m_Type = PT_Triangle_Strip; + + // Put the first triangle into the strip + AddTriToIndices((* m_Triangles[StartTriPos]), Order); + + // Mark the first triangle as used + MarkTriAsTaken(StartTriPos); + + + // Loop while we can further extend the strip + tri_node_iter TriNodeIt = (m_Triangles.begin() + StartTriPos); + + for (size_t Size = 1; Size < TriStrip.Size(); ++Size) { + + // Get the triangle edge that would lead to the next triangle + const triangle_edge Edge = GetLatestEdge(** TriNodeIt, Order); + + // Link to a neighbour triangle + const_tri_link_iter LinkIt; + for (LinkIt = TriNodeIt->out_begin(); LinkIt != TriNodeIt->out_end(); ++LinkIt) { + + // Get the reference to the possible next triangle + const triangle & Tri = (** ((*LinkIt).terminal())); + + // Check whether it's already been used + if (! ((*LinkIt).terminal()->marked())) { + + // Does the current candidate triangle match the required for the strip? + // If it does, then add it to the Indices + if ((Edge.B() == Tri.A()) && (Edge.A() == Tri.B())) { + Order = (ClockWise) ? triangle_strip::ABC : triangle_strip::BCA; + AddIndice(Tri.C()); + break; + } + + else if ((Edge.B() == Tri.B()) && (Edge.A() == Tri.C())) { + Order = (ClockWise) ? triangle_strip::BCA : triangle_strip::CAB; + AddIndice(Tri.A()); + break; + } + + else if ((Edge.B() == Tri.C()) && (Edge.A() == Tri.A())) { + Order = (ClockWise) ? triangle_strip::CAB : triangle_strip::ABC; + AddIndice(Tri.B()); + break; + } + } + } + + // Debug check: we must have found the next triangle + assert(LinkIt != TriNodeIt->out_end()); + + // Go to the next triangle + TriNodeIt = (*LinkIt).terminal(); + MarkTriAsTaken(TriNodeIt - m_Triangles.begin()); + + // Setup for the next triangle + ClockWise = ! ClockWise; + } +} + + + +void tri_stripper::MarkTriAsTaken(const size_t i) +{ + typedef triangles_graph::node_iterator tri_node_iter; + typedef triangles_graph::out_arc_iterator tri_link_iter; + + // Mark the triangle node + m_Triangles[i].mark(); + + // Remove triangle from priority queue if it isn't yet + if (! m_TriHeap.removed(i)) + m_TriHeap.erase(i); + + // Adjust the degree of available neighbour triangles + for (tri_link_iter LinkIt = m_Triangles[i].out_begin(); LinkIt != m_Triangles[i].out_end(); ++LinkIt) { + + const size_t j = (*LinkIt).terminal() - m_Triangles.begin(); + + if ((! m_Triangles[j].marked()) && (! m_TriHeap.removed(j))) { + triangle_degree NewDegree = m_TriHeap.peek(j); + NewDegree.SetDegree(NewDegree.Degree() - 1); + m_TriHeap.update(j, NewDegree); + + // Update the candidate list if cache is enabled + if ((m_CacheSize > 0) && (NewDegree.Degree() > 0)) + m_NextCandidates.push_back(j); + } + } +} + + + +inline void tri_stripper::AddIndiceToCache(const indice i, bool CacheHitCount) +{ + // Cache simulator enabled? + if (m_CacheSize > 0) { + + // Should we simulate the cache hits and count them? + if (CacheHitCount) { + if (std::find(m_IndicesCache.begin(), m_IndicesCache.end(), i) != m_IndicesCache.end()) + ++m_CacheHits; + } + + // Manage the indices cache as a FIFO structure + m_IndicesCache.pop_back(); + m_IndicesCache.push_front(i); + } +} + + + +inline void tri_stripper::AddIndice(const indice i) +{ + // Add the indice to the current indices array + m_PrimitivesVector.back().m_Indices.push_back(i); + + // Run cache simulator + AddIndiceToCache(i); +} + + + +inline void tri_stripper::AddTriToCache(const triangle & Tri, const triangle_strip::start_order Order) +{ + // Add Tri indices in the right order into the indices cache simulator. + // And enable the cache hit count + switch (Order) { + case triangle_strip::ABC: + AddIndiceToCache(Tri.A(), true); + AddIndiceToCache(Tri.B(), true); + AddIndiceToCache(Tri.C(), true); + return; + case triangle_strip::BCA: + AddIndiceToCache(Tri.B(), true); + AddIndiceToCache(Tri.C(), true); + AddIndiceToCache(Tri.A(), true); + return; + case triangle_strip::CAB: + AddIndiceToCache(Tri.C(), true); + AddIndiceToCache(Tri.A(), true); + AddIndiceToCache(Tri.B(), true); + return; + } +} + + + +inline void tri_stripper::AddTriToIndices(const triangle & Tri, const triangle_strip::start_order Order) +{ + // Add Tri indices in the right order into the latest Indices vector. + switch (Order) { + case triangle_strip::ABC: + AddIndice(Tri.A()); + AddIndice(Tri.B()); + AddIndice(Tri.C()); + return; + case triangle_strip::BCA: + AddIndice(Tri.B()); + AddIndice(Tri.C()); + AddIndice(Tri.A()); + return; + case triangle_strip::CAB: + AddIndice(Tri.C()); + AddIndice(Tri.A()); + AddIndice(Tri.B()); + return; + } +} + + + +void tri_stripper::AddLeftTriangles() +{ + // Create the latest indices array + // and fill it with all the triangles that couldn't be stripped + primitives Primitives; + Primitives.m_Type = PT_Triangles; + m_PrimitivesVector.push_back(Primitives); + indices & Indices = m_PrimitivesVector.back().m_Indices; + + for (size_t i = 0; i < m_Triangles.size(); ++i) + if (! m_Triangles[i].marked()) { + Indices.push_back(m_Triangles[i]->A()); + Indices.push_back(m_Triangles[i]->B()); + Indices.push_back(m_Triangles[i]->C()); + } + + // Undo if useless + if (Indices.size() == 0) + m_PrimitivesVector.pop_back(); +} + + + + +}; // namespace triangle_stripper diff --git a/apps/nexus/tristripper/tri_stripper.h b/apps/nexus/tristripper/tri_stripper.h new file mode 100644 index 00000000..80c1701b --- /dev/null +++ b/apps/nexus/tristripper/tri_stripper.h @@ -0,0 +1,372 @@ +// tri_stripper.h: interface for the tri_stripper class. +// +////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2002 Tanguy Fautré. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Tanguy Fautré +// softdev@pandora.be +// +////////////////////////////////////////////////////////////////////// +// +// Tri Stripper +// ************ +// +// Current version: 1.00 BETA 5 (10/12/2002) +// +// Comment: Triangle stripper in O(n.log(n)). +// +// Currently there are no protection against crazy values +// given via SetMinStripSize() and SetCacheSize(). +// So be careful. (Min. strip size should be equal or greater +// than 2, cache size should be about 10 for GeForce 256/2 +// and about 16-18 for GeForce 3/4.) +// +// History: - 1.00 BETA 5 (10/12/2002) - Fixed a bug in Stripify() that could sometimes +// cause it to go into an infinite loop. +// (thanks to Remy for the bug report) +// - 1.00 BETA 4 (18/11/2002) - Removed the dependency on OpenGL: +// modified gl_primitives to primitives, +// and gl_primitives_vector to primitives_vector; +// and added primitive_type. +// (thanks to Patrik for noticing this useless dependency) +// - 1.00 BETA 3 (18/11/2002) - Fixed a bug in LinkNeightboursTri() that could cause a crash +// (thanks to Nicolas for finding it) +// - 1.00 BETA 2 (16/11/2002) - Improved portability +// - 1.00 BETA 1 (27/10/2002) - First public release +// +////////////////////////////////////////////////////////////////////// + +#pragma once + +#include + +// namespace triangle_stripper +namespace triangle_stripper { + + + +//#include "../Common Structures/graph_array.h" +//#include "../Common Structures/heap_array.h" +#include "graph_array.h" +#include "heap_array.h" + + + +class tri_stripper +{ +public: + + // New Public types + typedef unsigned int indice; + typedef std::vector indices; + + enum primitive_type { + PT_Triangles = 0x0004, // = GL_TRIANGLES + PT_Triangle_Strip = 0x0005 // = GL_TRIANGLE_STRIP + }; + + struct primitives + { + indices m_Indices; + primitive_type m_Type; + }; + + typedef std::vector primitives_vector; + + struct triangles_indices_error { }; + + + // constructor/initializer + tri_stripper(const indices & TriIndices); + + // Settings functions + void SetCacheSize(const size_t CacheSize = 16); // = 0 will disable the cache optimizer + void SetMinStripSize(const size_t MinStripSize = 2); + + // Stripper + void Strip(primitives_vector * out_pPrimitivesVector); // throw triangles_indices_error(); + +private: + + friend struct _cmp_tri_interface_lt; + + + class triangle + { + public: + triangle(); + triangle(const indice A, const indice B, const indice C); + + void SetStripID(const size_t StripID); + + indice A() const; + indice B() const; + indice C() const; + size_t StripID() const; + + private: + indice m_A; + indice m_B; + indice m_C; + size_t m_StripID; + }; + + + class triangle_edge + { + public: + triangle_edge(const indice A, const indice B, const size_t TriPos); + + indice A() const; + indice B() const; + size_t TriPos() const; + + private: + indice m_A; + indice m_B; + size_t m_TriPos; + }; + + + class triangle_degree + { + public: + triangle_degree(); + triangle_degree(const size_t TriPos, const size_t Degree); + + size_t Degree() const; + size_t TriPos() const; + + void SetDegree(const size_t Degree); + + private: + size_t m_TriPos; + size_t m_Degree; + }; + + + class triangle_strip + { + public: + enum start_order { ABC = 0, BCA = 1, CAB = 2 }; + + triangle_strip(); + triangle_strip(size_t StartTriPos, start_order StartOrder, size_t Size); + + size_t StartTriPos() const; + start_order StartOrder() const; + size_t Size() const; + + private: + size_t m_StartTriPos; + start_order m_StartOrder; + size_t m_Size; + }; + + + struct _cmp_tri_interface_lt + { + bool operator() (const triangle_edge & a, const triangle_edge & b) const; + }; + + + struct _cmp_tri_degree_gt + { + bool operator () (const triangle_degree & a, const triangle_degree & b) const; + }; + + + typedef common_structures::graph_array triangles_graph; + typedef common_structures::heap_array triangles_heap; + typedef std::vector triangle_edges; + typedef std::vector triangle_indices; + typedef std::deque indices_cache; + + + void InitCache(); + void InitTriGraph(); + void InitTriHeap(); + void Stripify(); + void AddLeftTriangles(); + + void LinkNeighboursTri(const triangle_edges & TriInterface, const triangle_edge Edge); + void MarkTriAsTaken(const size_t i); + + triangle_edge GetLatestEdge(const triangle & Triangle, const triangle_strip::start_order Order) const; + + triangle_strip FindBestStrip(); + triangle_strip ExtendTriToStrip(const size_t StartTriPos, const triangle_strip::start_order StartOrder); + void BuildStrip(const triangle_strip TriStrip); + void AddIndice(const indice i); + void AddIndiceToCache(const indice i, bool CacheHitCount = false); + void AddTriToCache(const triangle & Tri, const triangle_strip::start_order Order); + void AddTriToIndices(const triangle & Tri, const triangle_strip::start_order Order); + + const indices & m_TriIndices; + + size_t m_MinStripSize; + size_t m_CacheSize; + + primitives_vector m_PrimitivesVector; + triangles_graph m_Triangles; + triangles_heap m_TriHeap; + triangle_indices m_NextCandidates; + indices_cache m_IndicesCache; + size_t m_StripID; + size_t m_CacheHits; +}; + + + + +////////////////////////////////////////////////////////////////////////// +// tri_stripper Inline functions +////////////////////////////////////////////////////////////////////////// + +inline tri_stripper::tri_stripper(const indices & TriIndices) : m_TriIndices(TriIndices) { + SetCacheSize(); + SetMinStripSize(); +} + + +inline void tri_stripper::SetCacheSize(const size_t CacheSize) { + m_CacheSize = CacheSize; +} + + +inline void tri_stripper::SetMinStripSize(const size_t MinStripSize) { + m_MinStripSize = MinStripSize; +} + + +inline tri_stripper::triangle::triangle() { } + + +inline tri_stripper::triangle::triangle(const indice A, const indice B, const indice C) : m_A(A), m_B(B), m_C(C), m_StripID(0) { } + + +inline void tri_stripper::triangle::SetStripID(const size_t StripID) { + m_StripID = StripID; +} + + +inline tri_stripper::indice tri_stripper::triangle::A() const { + return m_A; +} + + +inline tri_stripper::indice tri_stripper::triangle::B() const { + return m_B; +} + + +inline tri_stripper::indice tri_stripper::triangle::C() const { + return m_C; +} + + +inline size_t tri_stripper::triangle::StripID() const { + return m_StripID; +} + + +inline tri_stripper::triangle_edge::triangle_edge(const indice A, const indice B, const size_t TriPos) : m_A(A), m_B(B), m_TriPos(TriPos) { } + + +inline tri_stripper::indice tri_stripper::triangle_edge::A() const { + return m_A; +} + + +inline tri_stripper::indice tri_stripper::triangle_edge::B() const { + return m_B; +} + + +inline size_t tri_stripper::triangle_edge::TriPos() const { + return m_TriPos; +} + + +inline tri_stripper::triangle_degree::triangle_degree() { } + + +inline tri_stripper::triangle_degree::triangle_degree(const size_t TriPos, const size_t Degree) : m_TriPos(TriPos), m_Degree(Degree) { } + + +inline size_t tri_stripper::triangle_degree::Degree() const { + return m_Degree; +} + + +inline size_t tri_stripper::triangle_degree::TriPos() const { + return m_TriPos; +} + + +inline void tri_stripper::triangle_degree::SetDegree(const size_t Degree) { + m_Degree = Degree; +} + + +inline tri_stripper::triangle_strip::triangle_strip() : m_StartTriPos(0), m_StartOrder(ABC), m_Size(0) { } + + +inline tri_stripper::triangle_strip::triangle_strip(const size_t StartTriPos, const start_order StartOrder, const size_t Size) + : m_StartTriPos(StartTriPos), m_StartOrder(StartOrder), m_Size(Size) { } + + +inline size_t tri_stripper::triangle_strip::StartTriPos() const { + return m_StartTriPos; +} + + +inline tri_stripper::triangle_strip::start_order tri_stripper::triangle_strip::StartOrder() const { + return m_StartOrder; +} + + +inline size_t tri_stripper::triangle_strip::Size() const { + return m_Size; +} + + +inline bool tri_stripper::_cmp_tri_interface_lt::operator() (const triangle_edge & a, const triangle_edge & b) const { + const tri_stripper::indice A1 = a.A(); + const tri_stripper::indice B1 = a.B(); + const tri_stripper::indice A2 = b.A(); + const tri_stripper::indice B2 = b.B(); + + if ((A1 < A2) || ((A1 == A2) && (B1 < B2))) + return true; + else + return false; +} + + +inline bool tri_stripper::_cmp_tri_degree_gt::operator () (const triangle_degree & a, const triangle_degree & b) const { + // the triangle with a smaller degree has more priority + return a.Degree() > b.Degree(); +} + + + + +}; // namespace triangle_stripper