587 lines
17 KiB
C++
587 lines
17 KiB
C++
// 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 <cassert>
|
|
#include <cstdlib>
|
|
|
|
#include <algorithm>
|
|
#include <deque>
|
|
//#include <fstream>
|
|
//#include <iostream.h>
|
|
#include <list>
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
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<size_t>(-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
|