1639 lines
48 KiB
C++
1639 lines
48 KiB
C++
|
|
#if defined(_MSC_VER)
|
|
#pragma warning( disable : 4786 )
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <set>
|
|
#include "NvTriStripObjects.h"
|
|
|
|
#include <osg/Notify>
|
|
|
|
VertexCache::VertexCache()
|
|
{
|
|
VertexCache(16);
|
|
}
|
|
|
|
|
|
VertexCache::VertexCache(int size)
|
|
{
|
|
numEntries = size;
|
|
|
|
entries = new int[numEntries];
|
|
|
|
for(int i = 0; i < numEntries; i++)
|
|
entries[i] = -1;
|
|
}
|
|
|
|
|
|
VertexCache::~VertexCache()
|
|
{
|
|
delete[] entries;
|
|
}
|
|
|
|
|
|
int VertexCache::At(int index)
|
|
{
|
|
return entries[index];
|
|
}
|
|
|
|
|
|
void VertexCache::Set(int index, int value)
|
|
{
|
|
entries[index] = value;
|
|
}
|
|
|
|
|
|
void VertexCache::Clear()
|
|
{
|
|
for(int i = 0; i < numEntries; i++)
|
|
entries[i] = -1;
|
|
}
|
|
|
|
|
|
void VertexCache::Copy(VertexCache* inVcache)
|
|
{
|
|
for(int i = 0; i < numEntries; i++)
|
|
{
|
|
inVcache->Set(i, entries[i]);
|
|
}
|
|
}
|
|
|
|
|
|
NvStripifier::NvStripifier()
|
|
{
|
|
|
|
}
|
|
|
|
|
|
NvStripifier::~NvStripifier()
|
|
{
|
|
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// FindEdgeInfo()
|
|
//
|
|
// find the edge info for these two indices
|
|
//
|
|
NvEdgeInfo * NvStripifier::FindEdgeInfo(NvEdgeInfoVec &edgeInfos, int v0, int v1)
|
|
{
|
|
|
|
// we can get to it through either array
|
|
// because the edge infos have a v0 and v1
|
|
// and there is no order except how it was
|
|
// first created.
|
|
NvEdgeInfo *infoIter = edgeInfos[v0];
|
|
while (infoIter != NULL)
|
|
{
|
|
if (infoIter->m_v0 == v0)
|
|
{
|
|
if (infoIter->m_v1 == v1)
|
|
return infoIter;
|
|
else
|
|
infoIter = infoIter->m_nextV0;
|
|
}
|
|
else
|
|
{
|
|
assert(infoIter->m_v1 == v0);
|
|
if (infoIter->m_v0 == v1)
|
|
return infoIter;
|
|
else
|
|
infoIter = infoIter->m_nextV1;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// FindOtherFace
|
|
//
|
|
// find the other face sharing these vertices
|
|
// exactly like the edge info above
|
|
//
|
|
NvFaceInfo * NvStripifier::FindOtherFace(NvEdgeInfoVec &edgeInfos, int v0, int v1, NvFaceInfo *faceInfo)
|
|
{
|
|
NvEdgeInfo *edgeInfo = FindEdgeInfo(edgeInfos, v0, v1);
|
|
assert(edgeInfo != NULL);
|
|
return (edgeInfo->m_face0 == faceInfo ? edgeInfo->m_face1 : edgeInfo->m_face0);
|
|
}
|
|
|
|
|
|
bool NvStripifier::AlreadyExists(NvFaceInfo* faceInfo, NvFaceInfoVec& faceInfos)
|
|
{
|
|
for(unsigned int i = 0; i < faceInfos.size(); i++)
|
|
{
|
|
if( (faceInfos[i]->m_v0 == faceInfo->m_v0) &&
|
|
(faceInfos[i]->m_v1 == faceInfo->m_v1) &&
|
|
(faceInfos[i]->m_v2 == faceInfo->m_v2) )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// BuildStripifyInfo()
|
|
//
|
|
// Builds the list of all face and edge infos
|
|
//
|
|
void NvStripifier::BuildStripifyInfo(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos, const int numVertices)
|
|
{
|
|
|
|
// reserve space for the face infos, but do not resize them.
|
|
int numIndices = indices.size();
|
|
int numTriangles = numIndices / 3;
|
|
|
|
faceInfos.reserve(numTriangles);
|
|
|
|
// we actually resize the edge infos, so we must initialize to NULL
|
|
edgeInfos.resize (numVertices);
|
|
int i;
|
|
for (i = 0; i < numVertices; i++)
|
|
edgeInfos[i] = NULL;
|
|
|
|
|
|
// iterate through the triangles of the triangle list
|
|
int index = 0;
|
|
for (i = 0; i < numTriangles; i++)
|
|
{
|
|
// grab the indices
|
|
int v0 = indices[index++];
|
|
int v1 = indices[index++];
|
|
int v2 = indices[index++];
|
|
|
|
// create the face info and add it to the list of faces, but only if this exact face doesn't already
|
|
// exist in the list
|
|
NvFaceInfo *faceInfo = new NvFaceInfo(v0, v1, v2);
|
|
if(!AlreadyExists(faceInfo, faceInfos))
|
|
{
|
|
faceInfos.push_back(faceInfo);
|
|
|
|
// grab the edge infos, creating them if they do not already exist
|
|
NvEdgeInfo *edgeInfo01 = FindEdgeInfo(edgeInfos, v0, v1);
|
|
if (edgeInfo01 == NULL)
|
|
{
|
|
|
|
// create the info
|
|
edgeInfo01 = new NvEdgeInfo(v0, v1);
|
|
|
|
// update the linked list on both
|
|
edgeInfo01->m_nextV0 = edgeInfos[v0];
|
|
edgeInfo01->m_nextV1 = edgeInfos[v1];
|
|
edgeInfos[v0] = edgeInfo01;
|
|
edgeInfos[v1] = edgeInfo01;
|
|
|
|
// set face 0
|
|
edgeInfo01->m_face0 = faceInfo;
|
|
}
|
|
else
|
|
{
|
|
if (edgeInfo01->m_face1 != NULL)
|
|
osg::notify(osg::WARN)<<"BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n"<< std::endl;
|
|
else
|
|
edgeInfo01->m_face1 = faceInfo;
|
|
}
|
|
|
|
// grab the edge infos, creating them if they do not already exist
|
|
NvEdgeInfo *edgeInfo12 = FindEdgeInfo(edgeInfos, v1, v2);
|
|
if (edgeInfo12 == NULL)
|
|
{
|
|
|
|
// create the info
|
|
edgeInfo12 = new NvEdgeInfo(v1, v2);
|
|
|
|
// update the linked list on both
|
|
edgeInfo12->m_nextV0 = edgeInfos[v1];
|
|
edgeInfo12->m_nextV1 = edgeInfos[v2];
|
|
edgeInfos[v1] = edgeInfo12;
|
|
edgeInfos[v2] = edgeInfo12;
|
|
|
|
// set face 0
|
|
edgeInfo12->m_face0 = faceInfo;
|
|
}
|
|
else
|
|
{
|
|
if (edgeInfo12->m_face1 != NULL)
|
|
osg::notify(osg::WARN)<<"BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n"<< std::endl;
|
|
else
|
|
edgeInfo12->m_face1 = faceInfo;
|
|
}
|
|
|
|
// grab the edge infos, creating them if they do not already exist
|
|
NvEdgeInfo *edgeInfo20 = FindEdgeInfo(edgeInfos, v2, v0);
|
|
if (edgeInfo20 == NULL)
|
|
{
|
|
|
|
// create the info
|
|
edgeInfo20 = new NvEdgeInfo(v2, v0);
|
|
|
|
// update the linked list on both
|
|
edgeInfo20->m_nextV0 = edgeInfos[v2];
|
|
edgeInfo20->m_nextV1 = edgeInfos[v0];
|
|
edgeInfos[v2] = edgeInfo20;
|
|
edgeInfos[v0] = edgeInfo20;
|
|
|
|
// set face 0
|
|
edgeInfo20->m_face0 = faceInfo;
|
|
}
|
|
else
|
|
{
|
|
if (edgeInfo20->m_face1 != NULL)
|
|
osg::notify(osg::WARN)<<"BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n"<< std::endl;
|
|
else
|
|
edgeInfo20->m_face1 = faceInfo;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// FindStartPoint()
|
|
//
|
|
// Finds a good starting point, namely one which has only one neighbor
|
|
//
|
|
int NvStripifier::FindStartPoint(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos)
|
|
{
|
|
for(unsigned int i = 0; i < faceInfos.size(); i++)
|
|
{
|
|
int ctr = 0;
|
|
|
|
if(FindOtherFace(edgeInfos, faceInfos[i]->m_v0, faceInfos[i]->m_v1, faceInfos[i]) == NULL)
|
|
ctr++;
|
|
if(FindOtherFace(edgeInfos, faceInfos[i]->m_v1, faceInfos[i]->m_v2, faceInfos[i]) == NULL)
|
|
ctr++;
|
|
if(FindOtherFace(edgeInfos, faceInfos[i]->m_v2, faceInfos[i]->m_v0, faceInfos[i]) == NULL)
|
|
ctr++;
|
|
if(ctr > 1)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// FindGoodResetPoint()
|
|
//
|
|
// A good reset point is one near other commited areas so that
|
|
// we know that when we've made the longest strips its because
|
|
// we're stripifying in the same general orientation.
|
|
//
|
|
NvFaceInfo* NvStripifier::FindGoodResetPoint(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos)
|
|
{
|
|
// we hop into different areas of the mesh to try to get
|
|
// other large open spans done. Areas of small strips can
|
|
// just be left to triangle lists added at the end.
|
|
NvFaceInfo *result = NULL;
|
|
|
|
if(result == NULL)
|
|
{
|
|
int numFaces = faceInfos.size();
|
|
int startPoint;
|
|
if(bFirstTimeResetPoint)
|
|
{
|
|
//first time, find a face with few neighbors (look for an edge of the mesh)
|
|
startPoint = FindStartPoint(faceInfos, edgeInfos);
|
|
bFirstTimeResetPoint = false;
|
|
}
|
|
else
|
|
startPoint = (int)(((float) numFaces - 1) * meshJump);
|
|
|
|
if(startPoint == -1)
|
|
startPoint = (int)(((float) numFaces - 1) * meshJump);
|
|
|
|
int i = startPoint;
|
|
do
|
|
{
|
|
|
|
// if this guy isn't visited, try him
|
|
if (faceInfos[i]->m_stripId < 0)
|
|
{
|
|
result = faceInfos[i];
|
|
break;
|
|
}
|
|
|
|
// update the index and clamp to 0-(numFaces-1)
|
|
if (++i >= numFaces)
|
|
i = 0;
|
|
|
|
} while (i != startPoint);
|
|
|
|
// update the meshJump
|
|
meshJump += 0.1f;
|
|
if (meshJump > 1.0f)
|
|
meshJump = .05f;
|
|
}
|
|
|
|
// return the best face we found
|
|
return result;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// GetUniqueVertexInB()
|
|
//
|
|
// Returns the vertex unique to faceB
|
|
//
|
|
int NvStripifier::GetUniqueVertexInB(NvFaceInfo *faceA, NvFaceInfo *faceB)
|
|
{
|
|
|
|
int facev0 = faceB->m_v0;
|
|
if (facev0 != faceA->m_v0 &&
|
|
facev0 != faceA->m_v1 &&
|
|
facev0 != faceA->m_v2)
|
|
return facev0;
|
|
|
|
int facev1 = faceB->m_v1;
|
|
if (facev1 != faceA->m_v0 &&
|
|
facev1 != faceA->m_v1 &&
|
|
facev1 != faceA->m_v2)
|
|
return facev1;
|
|
|
|
int facev2 = faceB->m_v2;
|
|
if (facev2 != faceA->m_v0 &&
|
|
facev2 != faceA->m_v1 &&
|
|
facev2 != faceA->m_v2)
|
|
return facev2;
|
|
|
|
// nothing is different
|
|
return -1;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// GetSharedVertex()
|
|
//
|
|
// Returns the vertex shared between the two input faces
|
|
//
|
|
int NvStripifier::GetSharedVertex(NvFaceInfo *faceA, NvFaceInfo *faceB)
|
|
{
|
|
|
|
int facev0 = faceB->m_v0;
|
|
if (facev0 == faceA->m_v0 ||
|
|
facev0 == faceA->m_v1 ||
|
|
facev0 == faceA->m_v2)
|
|
return facev0;
|
|
|
|
int facev1 = faceB->m_v1;
|
|
if (facev1 == faceA->m_v0 ||
|
|
facev1 == faceA->m_v1 ||
|
|
facev1 == faceA->m_v2)
|
|
return facev1;
|
|
|
|
int facev2 = faceB->m_v2;
|
|
if (facev2 == faceA->m_v0 ||
|
|
facev2 == faceA->m_v1 ||
|
|
facev2 == faceA->m_v2)
|
|
return facev2;
|
|
|
|
// nothing is shared
|
|
return -1;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// GetNextIndex()
|
|
//
|
|
// Returns vertex of the input face which is "next" in the input index list
|
|
//
|
|
inline int NvStripifier::GetNextIndex(const WordVec &indices, NvFaceInfo *face)
|
|
{
|
|
|
|
int numIndices = indices.size();
|
|
assert(numIndices >= 2);
|
|
|
|
int v0 = indices[numIndices-2];
|
|
int v1 = indices[numIndices-1];
|
|
|
|
int fv0 = face->m_v0;
|
|
int fv1 = face->m_v1;
|
|
int fv2 = face->m_v2;
|
|
|
|
if (fv0 != v0 && fv0 != v1)
|
|
{
|
|
if ((fv1 != v0 && fv1 != v1) || (fv2 != v0 && fv2 != v1))
|
|
{
|
|
osg::notify(osg::WARN)<<"GetNextIndex: Triangle doesn't have all of its vertices\n"<< std::endl;
|
|
osg::notify(osg::WARN)<<"GetNextIndex: Duplicate triangle probably got us derailed\n"<< std::endl;
|
|
}
|
|
return fv0;
|
|
}
|
|
if (fv1 != v0 && fv1 != v1)
|
|
{
|
|
if ((fv0 != v0 && fv0 != v1) || (fv2 != v0 && fv2 != v1))
|
|
{
|
|
osg::notify(osg::WARN)<<"GetNextIndex: Triangle doesn't have all of its vertices\n"<< std::endl;
|
|
osg::notify(osg::WARN)<<"GetNextIndex: Duplicate triangle probably got us derailed\n"<< std::endl;
|
|
}
|
|
return fv1;
|
|
}
|
|
if (fv2 != v0 && fv2 != v1)
|
|
{
|
|
if ((fv0 != v0 && fv0 != v1) || (fv1 != v0 && fv1 != v1))
|
|
{
|
|
osg::notify(osg::WARN)<<"GetNextIndex: Triangle doesn't have all of its vertices\n"<< std::endl;
|
|
osg::notify(osg::WARN)<<"GetNextIndex: Duplicate triangle probably got us derailed\n"<< std::endl;
|
|
}
|
|
return fv2;
|
|
}
|
|
|
|
// shouldn't get here
|
|
osg::notify(osg::WARN)<<"GetNextIndex: Duplicate triangle sent\n"<< std::endl;
|
|
return -1;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// IsMarked()
|
|
//
|
|
// If either the faceInfo has a real strip index because it is
|
|
// already assign to a committed strip OR it is assigned in an
|
|
// experiment and the experiment index is the one we are building
|
|
// for, then it is marked and unavailable
|
|
inline bool NvStripInfo::IsMarked(NvFaceInfo *faceInfo)
|
|
{
|
|
return (faceInfo->m_stripId >= 0) || (IsExperiment() && faceInfo->m_experimentId == m_experimentId);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// MarkTriangle()
|
|
//
|
|
// Marks the face with the current strip ID
|
|
//
|
|
inline void NvStripInfo::MarkTriangle(NvFaceInfo *faceInfo)
|
|
{
|
|
assert(!IsMarked(faceInfo));
|
|
if (IsExperiment())
|
|
{
|
|
faceInfo->m_experimentId = m_experimentId;
|
|
faceInfo->m_testStripId = m_stripId;
|
|
}
|
|
else
|
|
{
|
|
assert(faceInfo->m_stripId == -1);
|
|
faceInfo->m_experimentId = -1;
|
|
faceInfo->m_stripId = m_stripId;
|
|
}
|
|
}
|
|
|
|
|
|
bool NvStripInfo::Unique(NvFaceInfoVec& faceVec, NvFaceInfo* face)
|
|
{
|
|
bool bv0, bv1, bv2; //bools to indicate whether a vertex is in the faceVec or not
|
|
bv0 = bv1 = bv2 = false;
|
|
|
|
for(unsigned int i = 0; i < faceVec.size(); i++)
|
|
{
|
|
if(!bv0)
|
|
{
|
|
if( (faceVec[i]->m_v0 == face->m_v0) ||
|
|
(faceVec[i]->m_v1 == face->m_v0) ||
|
|
(faceVec[i]->m_v2 == face->m_v0) )
|
|
bv0 = true;
|
|
}
|
|
|
|
if(!bv1)
|
|
{
|
|
if( (faceVec[i]->m_v0 == face->m_v1) ||
|
|
(faceVec[i]->m_v1 == face->m_v1) ||
|
|
(faceVec[i]->m_v2 == face->m_v1) )
|
|
bv1 = true;
|
|
}
|
|
|
|
if(!bv2)
|
|
{
|
|
if( (faceVec[i]->m_v0 == face->m_v2) ||
|
|
(faceVec[i]->m_v1 == face->m_v2) ||
|
|
(faceVec[i]->m_v2 == face->m_v2) )
|
|
bv2 = true;
|
|
}
|
|
|
|
//the face is not unique, all it's vertices exist in the face vector
|
|
if(bv0 && bv1 && bv2)
|
|
return false;
|
|
}
|
|
|
|
//if we get out here, it's unique
|
|
return true;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// Build()
|
|
//
|
|
// Builds a strip forward as far as we can go, then builds backwards, and joins the two lists
|
|
//
|
|
void NvStripInfo::Build(NvEdgeInfoVec &edgeInfos, NvFaceInfoVec &)
|
|
{
|
|
// parameters : void NvStripInfo::Build(NvEdgeInfoVec &edgeInfos, NvFaceInfoVec &faceInfos)
|
|
|
|
// used in building the strips forward and backward
|
|
static WordVec scratchIndices;
|
|
scratchIndices.resize(0);
|
|
|
|
// build forward... start with the initial face
|
|
NvFaceInfoVec forwardFaces, backwardFaces;
|
|
forwardFaces.push_back(m_startInfo.m_startFace);
|
|
|
|
MarkTriangle(m_startInfo.m_startFace);
|
|
|
|
int v0 = (m_startInfo.m_toV1 ? m_startInfo.m_startEdge->m_v0 : m_startInfo.m_startEdge->m_v1);
|
|
int v1 = (m_startInfo.m_toV1 ? m_startInfo.m_startEdge->m_v1 : m_startInfo.m_startEdge->m_v0);
|
|
|
|
// easiest way to get v2 is to use this function which requires the
|
|
// other indices to already be in the list.
|
|
scratchIndices.push_back(v0);
|
|
scratchIndices.push_back(v1);
|
|
int v2 = NvStripifier::GetNextIndex(scratchIndices, m_startInfo.m_startFace);
|
|
scratchIndices.push_back(v2);
|
|
|
|
//
|
|
// build the forward list
|
|
//
|
|
int nv0 = v1;
|
|
int nv1 = v2;
|
|
|
|
NvFaceInfo *nextFace = NvStripifier::FindOtherFace(edgeInfos, nv0, nv1, m_startInfo.m_startFace);
|
|
while (nextFace != NULL && !IsMarked(nextFace))
|
|
{
|
|
//this tests to see if a face is "unique", meaning that its vertices aren't already in the list
|
|
// so, strips which "wrap-around" are not allowed
|
|
if(!Unique(forwardFaces, nextFace))
|
|
break;
|
|
|
|
// add this to the strip
|
|
forwardFaces.push_back(nextFace);
|
|
|
|
MarkTriangle(nextFace);
|
|
|
|
// add the index
|
|
nv0 = nv1;
|
|
nv1 = NvStripifier::GetNextIndex(scratchIndices, nextFace);
|
|
scratchIndices.push_back(nv1);
|
|
|
|
// and get the next face
|
|
nextFace = NvStripifier::FindOtherFace(edgeInfos, nv0, nv1, nextFace);
|
|
|
|
}
|
|
|
|
// tempAllFaces is going to be forwardFaces + backwardFaces
|
|
// it's used for Unique()
|
|
NvFaceInfoVec tempAllFaces;
|
|
for(unsigned int i = 0; i < forwardFaces.size(); i++)
|
|
tempAllFaces.push_back(forwardFaces[i]);
|
|
|
|
//
|
|
// reset the indices for building the strip backwards and do so
|
|
//
|
|
scratchIndices.resize(0);
|
|
scratchIndices.push_back(v2);
|
|
scratchIndices.push_back(v1);
|
|
scratchIndices.push_back(v0);
|
|
nv0 = v1;
|
|
nv1 = v0;
|
|
nextFace = NvStripifier::FindOtherFace(edgeInfos, nv0, nv1, m_startInfo.m_startFace);
|
|
while (nextFace != NULL && !IsMarked(nextFace))
|
|
{
|
|
//this tests to see if a face is "unique", meaning that its vertices aren't already in the list
|
|
// so, strips which "wrap-around" are not allowed
|
|
if(!Unique(tempAllFaces, nextFace))
|
|
break;
|
|
|
|
// add this to the strip
|
|
backwardFaces.push_back(nextFace);
|
|
|
|
//this is just so Unique() will work
|
|
tempAllFaces.push_back(nextFace);
|
|
|
|
MarkTriangle(nextFace);
|
|
|
|
// add the index
|
|
nv0 = nv1;
|
|
nv1 = NvStripifier::GetNextIndex(scratchIndices, nextFace);
|
|
scratchIndices.push_back(nv1);
|
|
|
|
// and get the next face
|
|
nextFace = NvStripifier::FindOtherFace(edgeInfos, nv0, nv1, nextFace);
|
|
}
|
|
|
|
// Combine the forward and backwards stripification lists and put into our own face vector
|
|
Combine(forwardFaces, backwardFaces);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// Combine()
|
|
//
|
|
// Combines the two input face vectors and puts the result into m_faces
|
|
//
|
|
void NvStripInfo::Combine(const NvFaceInfoVec &forward, const NvFaceInfoVec &backward)
|
|
{
|
|
|
|
// add backward faces
|
|
int numFaces = backward.size();
|
|
int i;
|
|
for (i = numFaces - 1; i >= 0; i--)
|
|
m_faces.push_back(backward[i]);
|
|
|
|
// add forward faces
|
|
numFaces = forward.size();
|
|
for (i = 0; i < numFaces; i++)
|
|
m_faces.push_back(forward[i]);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// SharesEdge()
|
|
//
|
|
// Returns true if the input face and the current strip share an edge
|
|
//
|
|
bool NvStripInfo::SharesEdge(const NvFaceInfo* faceInfo, NvEdgeInfoVec &edgeInfos)
|
|
{
|
|
//check v0->v1 edge
|
|
NvEdgeInfo* currEdge = NvStripifier::FindEdgeInfo(edgeInfos, faceInfo->m_v0, faceInfo->m_v1);
|
|
|
|
if(IsInStrip(currEdge->m_face0) || IsInStrip(currEdge->m_face1))
|
|
return true;
|
|
|
|
//check v1->v2 edge
|
|
currEdge = NvStripifier::FindEdgeInfo(edgeInfos, faceInfo->m_v1, faceInfo->m_v2);
|
|
|
|
if(IsInStrip(currEdge->m_face0) || IsInStrip(currEdge->m_face1))
|
|
return true;
|
|
|
|
//check v2->v0 edge
|
|
currEdge = NvStripifier::FindEdgeInfo(edgeInfos, faceInfo->m_v2, faceInfo->m_v0);
|
|
|
|
if(IsInStrip(currEdge->m_face0) || IsInStrip(currEdge->m_face1))
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// CommitStrips()
|
|
//
|
|
// "Commits" the input strips by setting their m_experimentId to -1 and adding to the allStrips
|
|
// vector
|
|
//
|
|
void NvStripifier::CommitStrips(NvStripInfoVec &allStrips, const NvStripInfoVec &strips)
|
|
{
|
|
// Iterate through strips
|
|
int numStrips = strips.size();
|
|
for (int i = 0; i < numStrips; i++)
|
|
{
|
|
|
|
// Tell the strip that it is now real
|
|
NvStripInfo *strip = strips[i];
|
|
strip->m_experimentId = -1;
|
|
|
|
// add to the list of real strips
|
|
allStrips.push_back(strip);
|
|
|
|
// Iterate through the faces of the strip
|
|
// Tell the faces of the strip that they belong to a real strip now
|
|
const NvFaceInfoVec &faces = strips[i]->m_faces;
|
|
int numFaces = faces.size();
|
|
|
|
if( (faces[0]->m_v0 == 2302) &&
|
|
(faces[0]->m_v1 == 3215) &&
|
|
(faces[0]->m_v2 == 2603) )
|
|
osg::notify(osg::WARN)<<"BLEH"<< std::endl;
|
|
|
|
for (int j = 0; j < numFaces; j++)
|
|
{
|
|
strip->MarkTriangle(faces[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// FindTraversal()
|
|
//
|
|
// Finds the next face to start the next strip on.
|
|
//
|
|
bool NvStripifier::FindTraversal(NvFaceInfoVec &,
|
|
NvEdgeInfoVec &edgeInfos,
|
|
NvStripInfo *strip,
|
|
NvStripStartInfo &startInfo)
|
|
{
|
|
|
|
// if the strip was v0->v1 on the edge, then v1 will be a vertex in the next edge.
|
|
int v = (strip->m_startInfo.m_toV1 ? strip->m_startInfo.m_startEdge->m_v1 : strip->m_startInfo.m_startEdge->m_v0);
|
|
|
|
NvFaceInfo *untouchedFace = NULL;
|
|
NvEdgeInfo *edgeIter = edgeInfos[v];
|
|
while (edgeIter != NULL)
|
|
{
|
|
NvFaceInfo *face0 = edgeIter->m_face0;
|
|
NvFaceInfo *face1 = edgeIter->m_face1;
|
|
if ((face0 != NULL && !strip->IsInStrip(face0)) && face1 != NULL && !strip->IsMarked(face1))
|
|
{
|
|
untouchedFace = face1;
|
|
break;
|
|
}
|
|
if ((face1 != NULL && !strip->IsInStrip(face1)) && face0 != NULL && !strip->IsMarked(face0))
|
|
{
|
|
untouchedFace = face0;
|
|
break;
|
|
}
|
|
|
|
// find the next edgeIter
|
|
edgeIter = (edgeIter->m_v0 == v ? edgeIter->m_nextV0 : edgeIter->m_nextV1);
|
|
}
|
|
|
|
startInfo.m_startFace = untouchedFace;
|
|
startInfo.m_startEdge = edgeIter;
|
|
if (edgeIter != NULL)
|
|
{
|
|
if(strip->SharesEdge(startInfo.m_startFace, edgeInfos))
|
|
//note! used to be m_v1
|
|
startInfo.m_toV1 = (edgeIter->m_v0 == v);
|
|
else
|
|
startInfo.m_toV1 = (edgeIter->m_v1 == v);
|
|
}
|
|
return (startInfo.m_startFace != NULL);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
// RemoveSmallStrips()
|
|
//
|
|
// allStrips is the whole strip vector...all small strips will be deleted from this list, to avoid leaking mem
|
|
// allBigStrips is an out parameter which will contain all strips above minStripLength
|
|
// faceList is an out parameter which will contain all faces which were removed from the striplist
|
|
//
|
|
void NvStripifier::RemoveSmallStrips(NvStripInfoVec& allStrips, NvStripInfoVec& allBigStrips, NvFaceInfoVec& faceList)
|
|
{
|
|
faceList.clear();
|
|
allBigStrips.clear(); //make sure these are empty
|
|
NvFaceInfoVec tempFaceList;
|
|
|
|
for(unsigned int i = 0; i < allStrips.size(); i++)
|
|
{
|
|
if(allStrips[i]->m_faces.size() < minStripLength)
|
|
{
|
|
//strip is too small, add faces to faceList
|
|
for(unsigned int j = 0; j < allStrips[i]->m_faces.size(); j++)
|
|
tempFaceList.push_back(allStrips[i]->m_faces[j]);
|
|
|
|
//and free memory
|
|
delete allStrips[i];
|
|
}
|
|
else
|
|
{
|
|
allBigStrips.push_back(allStrips[i]);
|
|
}
|
|
}
|
|
|
|
bool *bVisitedList = new bool[tempFaceList.size()];
|
|
memset(bVisitedList, 0, tempFaceList.size()*sizeof(bool));
|
|
|
|
VertexCache* vcache = new VertexCache(cacheSize);
|
|
|
|
int bestNumHits = -1;
|
|
int numHits;
|
|
int bestIndex = 0;
|
|
|
|
while(1)
|
|
{
|
|
bestNumHits = -1;
|
|
|
|
//find best face to add next, given the current cache
|
|
for(unsigned int i = 0; i < tempFaceList.size(); i++)
|
|
{
|
|
if(bVisitedList[i])
|
|
continue;
|
|
|
|
numHits = CalcNumHitsFace(vcache, tempFaceList[i]);
|
|
if(numHits > bestNumHits)
|
|
{
|
|
bestNumHits = numHits;
|
|
bestIndex = i;
|
|
}
|
|
}
|
|
|
|
if(bestNumHits == -1.0)
|
|
break;
|
|
bVisitedList[bestIndex] = true;
|
|
UpdateCacheFace(vcache, tempFaceList[bestIndex]);
|
|
faceList.push_back(tempFaceList[bestIndex]);
|
|
}
|
|
|
|
delete vcache;
|
|
delete[] bVisitedList;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// Stripify()
|
|
//
|
|
//
|
|
// in_indices are the input indices of the mesh to stripify
|
|
// in_cacheSize is the target cache size
|
|
//
|
|
void NvStripifier::Stripify(const WordVec &in_indices, const int in_numVertices, const int in_cacheSize,
|
|
const int in_minStripLength, NvStripInfoVec &outStrips, NvFaceInfoVec& outFaceList)
|
|
{
|
|
meshJump = 0.0f;
|
|
bFirstTimeResetPoint = true; //used in FindGoodResetPoint()
|
|
|
|
//the number of times to run the experiments
|
|
int numSamples = 10;
|
|
cacheSize = in_cacheSize;
|
|
//this is the strip size threshold below which we dump the strip into a list
|
|
minStripLength = in_minStripLength;
|
|
|
|
indices = in_indices;
|
|
|
|
// build the stripification info
|
|
NvFaceInfoVec allFaceInfos;
|
|
NvEdgeInfoVec allEdgeInfos;
|
|
|
|
BuildStripifyInfo(allFaceInfos, allEdgeInfos, in_numVertices);
|
|
|
|
NvStripInfoVec allStrips;
|
|
|
|
// stripify
|
|
FindAllStrips(allStrips, allFaceInfos, allEdgeInfos, numSamples);
|
|
|
|
//split up the strips into cache friendly pieces, optimize them, then dump these into outStrips
|
|
SplitUpStripsAndOptimize(allStrips, outStrips, allEdgeInfos, outFaceList);
|
|
|
|
//clean up
|
|
int i;
|
|
for(i = 0; i < (int)allStrips.size(); i++)
|
|
{
|
|
delete allStrips[i];
|
|
}
|
|
|
|
for (i = 0; i < (int)allEdgeInfos.size(); i++)
|
|
{
|
|
NvEdgeInfo *info = allEdgeInfos[i];
|
|
while (info != NULL)
|
|
{
|
|
NvEdgeInfo *next = (info->m_v0 == i ? info->m_nextV0 : info->m_nextV1);
|
|
info->Unref();
|
|
info = next;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// SplitUpStripsAndOptimize()
|
|
//
|
|
// Splits the input vector of strips (allBigStrips) into smaller, cache friendly pieces, then
|
|
// reorders these pieces to maximize cache hits
|
|
// The final strips are output through outStrips
|
|
//
|
|
void NvStripifier::SplitUpStripsAndOptimize(NvStripInfoVec &allStrips, NvStripInfoVec &outStrips,
|
|
NvEdgeInfoVec& edgeInfos, NvFaceInfoVec& outFaceList)
|
|
{
|
|
int threshold = cacheSize - 4;
|
|
NvStripInfoVec tempStrips;
|
|
|
|
//split up strips into threshold-sized pieces
|
|
unsigned int i;
|
|
for(i = 0; i < allStrips.size(); i++)
|
|
{
|
|
NvStripInfo* currentStrip;
|
|
NvStripStartInfo startInfo(NULL, NULL, false);
|
|
|
|
if((int)(allStrips[i]->m_faces.size()) > threshold)
|
|
{
|
|
|
|
int numTimes = allStrips[i]->m_faces.size() / threshold;
|
|
int numLeftover = allStrips[i]->m_faces.size() % threshold;
|
|
|
|
int j;
|
|
for(j = 0; j < numTimes; j++)
|
|
{
|
|
currentStrip = new NvStripInfo(startInfo, 0, -1);
|
|
|
|
for(int faceCtr = j*threshold; faceCtr < threshold+(j*threshold); faceCtr++)
|
|
{
|
|
currentStrip->m_faces.push_back(allStrips[i]->m_faces[faceCtr]);
|
|
}
|
|
|
|
tempStrips.push_back(currentStrip);
|
|
}
|
|
|
|
int leftOff = j * threshold;
|
|
|
|
if(numLeftover != 0)
|
|
{
|
|
currentStrip = new NvStripInfo(startInfo, 0, -1);
|
|
|
|
for(int k = 0; k < numLeftover; k++)
|
|
{
|
|
currentStrip->m_faces.push_back(allStrips[i]->m_faces[leftOff++]);
|
|
}
|
|
|
|
tempStrips.push_back(currentStrip);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//we're not just doing a tempStrips.push_back(allBigStrips[i]) because
|
|
// this way we can delete allBigStrips later to free the memory
|
|
currentStrip = new NvStripInfo(startInfo, 0, -1);
|
|
|
|
for(unsigned int j = 0; j < allStrips[i]->m_faces.size(); j++)
|
|
currentStrip->m_faces.push_back(allStrips[i]->m_faces[j]);
|
|
|
|
tempStrips.push_back(currentStrip);
|
|
}
|
|
}
|
|
|
|
//add small strips to face list
|
|
NvStripInfoVec tempStrips2;
|
|
RemoveSmallStrips(tempStrips, tempStrips2, outFaceList);
|
|
|
|
outStrips.clear();
|
|
if(tempStrips2.size() != 0)
|
|
{
|
|
//Optimize for the vertex cache
|
|
VertexCache* vcache = new VertexCache(cacheSize);
|
|
|
|
float bestNumHits = -1.0f;
|
|
float numHits;
|
|
int bestIndex=0;
|
|
|
|
int firstIndex = 0;
|
|
float minCost = 10000.0f;
|
|
|
|
for(i = 0; i < tempStrips2.size(); i++)
|
|
{
|
|
int numNeighbors = 0;
|
|
|
|
//find strip with least number of neighbors per face
|
|
for(unsigned int j = 0; j < tempStrips2[i]->m_faces.size(); j++)
|
|
{
|
|
numNeighbors += NumNeighbors(tempStrips2[i]->m_faces[j], edgeInfos);
|
|
}
|
|
|
|
float currCost = (float)numNeighbors / (float)tempStrips2[i]->m_faces.size();
|
|
if(currCost < minCost)
|
|
{
|
|
minCost = currCost;
|
|
firstIndex = i;
|
|
}
|
|
}
|
|
|
|
UpdateCacheStrip(vcache, tempStrips2[firstIndex]);
|
|
outStrips.push_back(tempStrips2[firstIndex]);
|
|
|
|
tempStrips2[firstIndex]->visited = true;
|
|
|
|
//this n^2 algo is what slows down stripification so much....
|
|
// needs to be improved
|
|
while(1)
|
|
{
|
|
bestNumHits = -1.0f;
|
|
|
|
//find best strip to add next, given the current cache
|
|
for(unsigned int i = 0; i < tempStrips2.size(); i++)
|
|
{
|
|
if(tempStrips2[i]->visited)
|
|
continue;
|
|
|
|
numHits = CalcNumHitsStrip(vcache, tempStrips2[i]);
|
|
if(numHits > bestNumHits)
|
|
{
|
|
bestNumHits = numHits;
|
|
bestIndex = i;
|
|
}
|
|
}
|
|
|
|
if(bestNumHits == -1.0f)
|
|
break;
|
|
tempStrips2[bestIndex]->visited = true;
|
|
UpdateCacheStrip(vcache, tempStrips2[bestIndex]);
|
|
outStrips.push_back(tempStrips2[bestIndex]);
|
|
}
|
|
|
|
delete vcache;
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// UpdateCacheStrip()
|
|
//
|
|
// Updates the input vertex cache with this strip's vertices
|
|
//
|
|
void NvStripifier::UpdateCacheStrip(VertexCache* vcache, NvStripInfo* strip)
|
|
{
|
|
for(unsigned int i = 0; i < strip->m_faces.size(); i++)
|
|
{
|
|
if(!vcache->InCache(strip->m_faces[i]->m_v0))
|
|
vcache->AddEntry(strip->m_faces[i]->m_v0);
|
|
|
|
if(!vcache->InCache(strip->m_faces[i]->m_v1))
|
|
vcache->AddEntry(strip->m_faces[i]->m_v1);
|
|
|
|
if(!vcache->InCache(strip->m_faces[i]->m_v2))
|
|
vcache->AddEntry(strip->m_faces[i]->m_v2);
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// UpdateCacheFace()
|
|
//
|
|
// Updates the input vertex cache with this face's vertices
|
|
//
|
|
void NvStripifier::UpdateCacheFace(VertexCache* vcache, NvFaceInfo* face)
|
|
{
|
|
if(!vcache->InCache(face->m_v0))
|
|
vcache->AddEntry(face->m_v0);
|
|
|
|
if(!vcache->InCache(face->m_v1))
|
|
vcache->AddEntry(face->m_v1);
|
|
|
|
if(!vcache->InCache(face->m_v2))
|
|
vcache->AddEntry(face->m_v2);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// CalcNumHitsStrip()
|
|
//
|
|
// returns the number of cache hits per face in the strip
|
|
//
|
|
float NvStripifier::CalcNumHitsStrip(VertexCache* vcache, NvStripInfo* strip)
|
|
{
|
|
int numHits = 0;
|
|
int numFaces = 0;
|
|
|
|
for(unsigned int i = 0; i < strip->m_faces.size(); i++)
|
|
{
|
|
if(vcache->InCache(strip->m_faces[i]->m_v0))
|
|
numHits++;
|
|
|
|
if(vcache->InCache(strip->m_faces[i]->m_v1))
|
|
numHits++;
|
|
|
|
if(vcache->InCache(strip->m_faces[i]->m_v2))
|
|
numHits++;
|
|
|
|
numFaces++;
|
|
|
|
}
|
|
|
|
return ((float)numHits / (float)numFaces);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// CalcNumHitsFace()
|
|
//
|
|
// returns the number of cache hits in the face
|
|
//
|
|
int NvStripifier::CalcNumHitsFace(VertexCache* vcache, NvFaceInfo* face)
|
|
{
|
|
int numHits = 0;
|
|
|
|
if(vcache->InCache(face->m_v0))
|
|
numHits++;
|
|
|
|
if(vcache->InCache(face->m_v1))
|
|
numHits++;
|
|
|
|
if(vcache->InCache(face->m_v2))
|
|
numHits++;
|
|
|
|
return numHits;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// NumNeighbors()
|
|
//
|
|
// Returns the number of neighbors that this face has
|
|
//
|
|
int NvStripifier::NumNeighbors(NvFaceInfo* face, NvEdgeInfoVec& edgeInfoVec)
|
|
{
|
|
int numNeighbors = 0;
|
|
|
|
if(FindOtherFace(edgeInfoVec, face->m_v0, face->m_v1, face) != NULL)
|
|
{
|
|
numNeighbors++;
|
|
}
|
|
|
|
if(FindOtherFace(edgeInfoVec, face->m_v1, face->m_v2, face) != NULL)
|
|
{
|
|
numNeighbors++;
|
|
}
|
|
|
|
if(FindOtherFace(edgeInfoVec, face->m_v2, face->m_v0, face) != NULL)
|
|
{
|
|
numNeighbors++;
|
|
}
|
|
|
|
return numNeighbors;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// AvgStripSize()
|
|
//
|
|
// Finds the average strip size of the input vector of strips
|
|
//
|
|
float NvStripifier::AvgStripSize(const NvStripInfoVec &strips)
|
|
{
|
|
int sizeAccum = 0;
|
|
int numStrips = strips.size();
|
|
for (int i = 0; i < numStrips; i++)
|
|
{
|
|
NvStripInfo *strip = strips[i];
|
|
sizeAccum += strip->m_faces.size();
|
|
}
|
|
return ((float)sizeAccum) / ((float)numStrips);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// FindAllStrips()
|
|
//
|
|
// Does the stripification, puts output strips into vector allStrips
|
|
//
|
|
// Works by setting runnning a number of experiments in different areas of the mesh, and
|
|
// accepting the one which results in the longest strips. It then accepts this, and moves
|
|
// on to a different area of the mesh. We try to jump around the mesh some, to ensure that
|
|
// large open spans of strips get generated.
|
|
//
|
|
void NvStripifier::FindAllStrips(NvStripInfoVec &allStrips,
|
|
NvFaceInfoVec &allFaceInfos,
|
|
NvEdgeInfoVec &allEdgeInfos,
|
|
int numSamples)
|
|
{
|
|
// the experiments
|
|
int experimentId = 0;
|
|
int stripId = 0;
|
|
bool done = false;
|
|
|
|
int loopCtr = 0;
|
|
|
|
while (!done)
|
|
{
|
|
loopCtr++;
|
|
|
|
//
|
|
// PHASE 1: Set up numSamples * numEdges experiments
|
|
//
|
|
NvStripInfoVec *experiments = new NvStripInfoVec [numSamples * 6];
|
|
int experimentIndex = 0;
|
|
std::set <NvFaceInfo*> resetPoints;
|
|
int i;
|
|
for (i = 0; i < numSamples; i++)
|
|
{
|
|
|
|
// Try to find another good reset point.
|
|
// If there are none to be found, we are done
|
|
NvFaceInfo *nextFace = FindGoodResetPoint(allFaceInfos, allEdgeInfos);
|
|
if (nextFace == NULL)
|
|
{
|
|
done = true;
|
|
break;
|
|
}
|
|
|
|
// If we have already evaluated starting at this face in this slew
|
|
// of experiments, then skip going any further
|
|
else if (resetPoints.find(nextFace) != resetPoints.end())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// trying it now...
|
|
resetPoints.insert(nextFace);
|
|
|
|
// otherwise, we shall now try experiments for starting on the 01,12, and 20 edges
|
|
assert(nextFace->m_stripId < 0);
|
|
|
|
// build the strip off of this face's 0-1 edge
|
|
NvEdgeInfo *edge01 = FindEdgeInfo(allEdgeInfos, nextFace->m_v0, nextFace->m_v1);
|
|
NvStripInfo *strip01 = new NvStripInfo(NvStripStartInfo(nextFace, edge01, true), stripId++, experimentId++);
|
|
experiments[experimentIndex++].push_back(strip01);
|
|
|
|
// build the strip off of this face's 1-0 edge
|
|
NvEdgeInfo *edge10 = FindEdgeInfo(allEdgeInfos, nextFace->m_v0, nextFace->m_v1);
|
|
NvStripInfo *strip10 = new NvStripInfo(NvStripStartInfo(nextFace, edge10, false), stripId++, experimentId++);
|
|
experiments[experimentIndex++].push_back(strip10);
|
|
|
|
// build the strip off of this face's 1-2 edge
|
|
NvEdgeInfo *edge12 = FindEdgeInfo(allEdgeInfos, nextFace->m_v1, nextFace->m_v2);
|
|
NvStripInfo *strip12 = new NvStripInfo(NvStripStartInfo(nextFace, edge12, true), stripId++, experimentId++);
|
|
experiments[experimentIndex++].push_back(strip12);
|
|
|
|
// build the strip off of this face's 2-1 edge
|
|
NvEdgeInfo *edge21 = FindEdgeInfo(allEdgeInfos, nextFace->m_v1, nextFace->m_v2);
|
|
NvStripInfo *strip21 = new NvStripInfo(NvStripStartInfo(nextFace, edge21, false), stripId++, experimentId++);
|
|
experiments[experimentIndex++].push_back(strip21);
|
|
|
|
// build the strip off of this face's 2-0 edge
|
|
NvEdgeInfo *edge20 = FindEdgeInfo(allEdgeInfos, nextFace->m_v2, nextFace->m_v0);
|
|
NvStripInfo *strip20 = new NvStripInfo(NvStripStartInfo(nextFace, edge20, true), stripId++, experimentId++);
|
|
experiments[experimentIndex++].push_back(strip20);
|
|
|
|
// build the strip off of this face's 0-2 edge
|
|
NvEdgeInfo *edge02 = FindEdgeInfo(allEdgeInfos, nextFace->m_v2, nextFace->m_v0);
|
|
NvStripInfo *strip02 = new NvStripInfo(NvStripStartInfo(nextFace, edge02, false), stripId++, experimentId++);
|
|
experiments[experimentIndex++].push_back(strip02);
|
|
}
|
|
|
|
//
|
|
// PHASE 2: Iterate through that we setup in the last phase
|
|
// and really build each of the strips and strips that follow to see how
|
|
// far we get
|
|
//
|
|
int numExperiments = experimentIndex;
|
|
for (i = 0; i < numExperiments; i++)
|
|
{
|
|
|
|
// get the strip set
|
|
|
|
// build the first strip of the list
|
|
experiments[i][0]->Build(allEdgeInfos, allFaceInfos);
|
|
int experimentId = experiments[i][0]->m_experimentId;
|
|
|
|
NvStripInfo *stripIter = experiments[i][0];
|
|
NvStripStartInfo startInfo(NULL, NULL, false);
|
|
while (FindTraversal(allFaceInfos, allEdgeInfos, stripIter, startInfo))
|
|
{
|
|
|
|
// create the new strip info
|
|
stripIter = new NvStripInfo(startInfo, stripId++, experimentId);
|
|
|
|
// build the next strip
|
|
stripIter->Build(allEdgeInfos, allFaceInfos);
|
|
|
|
// add it to the list
|
|
experiments[i].push_back(stripIter);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Phase 3: Find the experiment that has the most promise
|
|
//
|
|
int bestIndex = 0;
|
|
double bestValue = 0;
|
|
for (i = 0; i < numExperiments; i++)
|
|
{
|
|
const float avgStripSizeWeight = 1.0f;
|
|
const float numTrisWeight = 1.0f;
|
|
float avgStripSize = AvgStripSize(experiments[i]);
|
|
float numStrips = (float) experiments[i].size();
|
|
float value = avgStripSize * avgStripSizeWeight + (avgStripSize * numStrips * numTrisWeight);
|
|
|
|
if (value > bestValue)
|
|
{
|
|
bestValue = value;
|
|
bestIndex = i;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Phase 4: commit the best experiment of the bunch
|
|
//
|
|
CommitStrips(allStrips, experiments[bestIndex]);
|
|
|
|
// and destroy all of the others
|
|
for (i = 0; i < numExperiments; i++)
|
|
{
|
|
if (i != bestIndex)
|
|
{
|
|
int numStrips = experiments[i].size();
|
|
for (int j = 0; j < numStrips; j++)
|
|
{
|
|
delete experiments[i][j];
|
|
}
|
|
}
|
|
}
|
|
|
|
// delete the array that we used for all experiments
|
|
delete [] experiments;
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// CountRemainingTris()
|
|
//
|
|
// This will count the number of triangles left in the
|
|
// strip list starting at iter and finishing up at end
|
|
//
|
|
int NvStripifier::CountRemainingTris(std::list<NvStripInfo*>::iterator iter,
|
|
std::list<NvStripInfo*>::iterator end)
|
|
{
|
|
int count = 0;
|
|
while (iter != end)
|
|
{
|
|
count += (*iter)->m_faces.size();
|
|
iter++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// NextIsCW()
|
|
//
|
|
// Returns true if the next face should be ordered in CW fashion
|
|
//
|
|
bool NvStripifier::NextIsCW(const int numIndices)
|
|
{
|
|
return ((numIndices % 2) == 0);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// IsCW()
|
|
//
|
|
// Returns true if the face is ordered in CW fashion
|
|
//
|
|
bool NvStripifier::IsCW(NvFaceInfo *faceInfo, int v0, int v1)
|
|
{
|
|
if (faceInfo->m_v0 == v0)
|
|
return (faceInfo->m_v1 == v1);
|
|
|
|
else if (faceInfo->m_v1 == v0)
|
|
return (faceInfo->m_v2 == v1);
|
|
|
|
else
|
|
return (faceInfo->m_v0 == v1);
|
|
|
|
// shouldn't get here
|
|
assert(0);
|
|
return false;
|
|
}
|
|
|
|
|
|
//used in CreateStrips
|
|
template<class T>
|
|
inline void SWAP(T& first, T& second)
|
|
{
|
|
T temp = first;
|
|
first = second;
|
|
second = temp;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// CreateStrips()
|
|
//
|
|
// Up until now, the strips had been strips at heart, but tri lists in reality.
|
|
// Now, remove redundant indices, and stitch together strips to form one, huge uber-strip
|
|
// using degenerate tris.
|
|
//
|
|
void NvStripifier::CreateStrips(
|
|
NvStripInfoVec& strips,
|
|
NvFaceInfoVec&,
|
|
WordVec& stripIndices)
|
|
{
|
|
// parameters:
|
|
// void NvStripifier::CreateStrips(
|
|
// NvStripInfoVec& strips,
|
|
// NvFaceInfoVec& leftoverFaces,
|
|
// WordVec& stripIndices)
|
|
// {
|
|
|
|
NvFaceInfo tLastFace(0, 0, 0);
|
|
int nStripCount = strips.size();
|
|
assert(nStripCount > 0);
|
|
|
|
for (int i = 0; i < nStripCount; i++)
|
|
{
|
|
NvStripInfo *strip = strips[i];
|
|
unsigned int nStripFaceCount = strip->m_faces.size();
|
|
// unsigned int stripIndicesSize = stripIndices.size();
|
|
assert(nStripFaceCount > 0);
|
|
|
|
// Handle the first face in the strip
|
|
{
|
|
NvFaceInfo tFirstFace(strip->m_faces[0]->m_v0, strip->m_faces[0]->m_v1, strip->m_faces[0]->m_v2);
|
|
|
|
// If there is a second face, reorder vertices such that the
|
|
// unique vertex is first
|
|
if (nStripFaceCount > 1)
|
|
{
|
|
int nUnique = NvStripifier::GetUniqueVertexInB(strip->m_faces[1], &tFirstFace);
|
|
if (nUnique == tFirstFace.m_v1)
|
|
{
|
|
SWAP(tFirstFace.m_v0, tFirstFace.m_v1);
|
|
}
|
|
else if (nUnique == tFirstFace.m_v2)
|
|
{
|
|
SWAP(tFirstFace.m_v0, tFirstFace.m_v2);
|
|
}
|
|
|
|
// If there is a third face, reorder vertices such that the
|
|
// shared vertex is last
|
|
if (nStripFaceCount > 2)
|
|
{
|
|
int nShared = NvStripifier::GetSharedVertex(strip->m_faces[2], &tFirstFace);
|
|
if (nShared == tFirstFace.m_v1)
|
|
{
|
|
SWAP(tFirstFace.m_v1, tFirstFace.m_v2);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i != 0)
|
|
{
|
|
// Double tap the first in the new strip
|
|
stripIndices.push_back(tFirstFace.m_v0);
|
|
|
|
// Check CW/CCW ordering
|
|
if (NextIsCW(stripIndices.size()) != IsCW(strip->m_faces[0], tFirstFace.m_v0, tFirstFace.m_v1))
|
|
{
|
|
stripIndices.push_back(tFirstFace.m_v0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!IsCW(strip->m_faces[0], tFirstFace.m_v0, tFirstFace.m_v1))
|
|
stripIndices.push_back(tFirstFace.m_v0);
|
|
}
|
|
|
|
stripIndices.push_back(tFirstFace.m_v0);
|
|
stripIndices.push_back(tFirstFace.m_v1);
|
|
stripIndices.push_back(tFirstFace.m_v2);
|
|
|
|
// Update last face info
|
|
tLastFace = tFirstFace;
|
|
}
|
|
|
|
for (unsigned int j = 1; j < nStripFaceCount; j++)
|
|
{
|
|
int nUnique = NvStripifier::GetUniqueVertexInB(&tLastFace, strip->m_faces[j]);
|
|
if (nUnique != -1)
|
|
{
|
|
stripIndices.push_back(nUnique);
|
|
|
|
// Update last face info
|
|
tLastFace.m_v0 = tLastFace.m_v1;
|
|
tLastFace.m_v1 = tLastFace.m_v2;
|
|
tLastFace.m_v2 = nUnique;
|
|
}
|
|
}
|
|
|
|
// Double tap between strips.
|
|
stripIndices.push_back(tLastFace.m_v2);
|
|
|
|
// Update last face info
|
|
tLastFace.m_v0 = tLastFace.m_v1;
|
|
tLastFace.m_v1 = tLastFace.m_v2;
|
|
tLastFace.m_v2 = tLastFace.m_v2;
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// OptimizeVertices()
|
|
//
|
|
// Function which optimizes the vertices in the mesh to minimize page misses
|
|
//
|
|
// Puts output verts into optimizedVerts vector
|
|
//
|
|
//
|
|
void NvStripifier::OptimizeVertices(NvStripInfoVec& strips,
|
|
NvFaceInfoVec& leftoverFaces,
|
|
WordVec& ,
|
|
MyVertexVec& vertices,
|
|
MyVertexVec& optimizedVerts)
|
|
{
|
|
// parameters.
|
|
// void NvStripifier::OptimizeVertices(NvStripInfoVec& strips,
|
|
// NvFaceInfoVec& leftoverFaces,
|
|
// WordVec& stripIndices,
|
|
// MyVertexVec& vertices,
|
|
// MyVertexVec& optimizedVerts)
|
|
|
|
//caches oldIndex --> newIndex conversion
|
|
int *indexCache;
|
|
indexCache = new int[vertices.size()];
|
|
|
|
memset(indexCache, -1, sizeof(int)*vertices.size());
|
|
|
|
//first do the strips
|
|
unsigned int i;
|
|
for(i = 0; i < strips.size(); i++)
|
|
{
|
|
for(unsigned int j = 0; j < strips[i]->m_faces.size(); j++)
|
|
{
|
|
int v0 = strips[i]->m_faces[j]->m_v0;
|
|
int v1 = strips[i]->m_faces[j]->m_v1;
|
|
int v2 = strips[i]->m_faces[j]->m_v2;
|
|
|
|
//v0
|
|
int index = indexCache[v0];
|
|
if(index == -1)
|
|
{
|
|
optimizedVerts.push_back(vertices[v0]);
|
|
strips[i]->m_faces[j]->m_v0 = optimizedVerts.size() - 1;
|
|
|
|
indexCache[v0] = strips[i]->m_faces[j]->m_v0;
|
|
}
|
|
else
|
|
{
|
|
strips[i]->m_faces[j]->m_v0 = index;
|
|
}
|
|
|
|
//v1
|
|
index = indexCache[v1];
|
|
if(index == -1)
|
|
{
|
|
optimizedVerts.push_back(vertices[v1]);
|
|
strips[i]->m_faces[j]->m_v1 = optimizedVerts.size() - 1;
|
|
|
|
indexCache[v1] = strips[i]->m_faces[j]->m_v1;
|
|
}
|
|
else
|
|
{
|
|
strips[i]->m_faces[j]->m_v1 = index;
|
|
}
|
|
|
|
//v2
|
|
index = indexCache[v2];
|
|
if(index == -1)
|
|
{
|
|
optimizedVerts.push_back(vertices[v2]);
|
|
strips[i]->m_faces[j]->m_v2 = optimizedVerts.size() - 1;
|
|
|
|
indexCache[v2] = strips[i]->m_faces[j]->m_v2;
|
|
}
|
|
else
|
|
{
|
|
strips[i]->m_faces[j]->m_v2 = index;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//now do the leftover list
|
|
for(i = 0; i < leftoverFaces.size(); i++)
|
|
{
|
|
int v0 = leftoverFaces[i]->m_v0;
|
|
int v1 = leftoverFaces[i]->m_v1;
|
|
int v2 = leftoverFaces[i]->m_v2;
|
|
|
|
//v0
|
|
int index = indexCache[v0];
|
|
if(index == -1)
|
|
{
|
|
optimizedVerts.push_back(vertices[v0]);
|
|
leftoverFaces[i]->m_v0 = optimizedVerts.size() - 1;
|
|
|
|
indexCache[v0] = leftoverFaces[i]->m_v0;
|
|
}
|
|
else
|
|
{
|
|
leftoverFaces[i]->m_v0 = index;
|
|
}
|
|
|
|
//v1
|
|
index = indexCache[v1];
|
|
if(index == -1)
|
|
{
|
|
optimizedVerts.push_back(vertices[v1]);
|
|
leftoverFaces[i]->m_v1 = optimizedVerts.size() - 1;
|
|
|
|
indexCache[v1] = leftoverFaces[i]->m_v1;
|
|
}
|
|
else
|
|
{
|
|
leftoverFaces[i]->m_v1 = index;
|
|
}
|
|
|
|
//v2
|
|
index = indexCache[v2];
|
|
if(index == -1)
|
|
{
|
|
optimizedVerts.push_back(vertices[v2]);
|
|
leftoverFaces[i]->m_v2 = optimizedVerts.size() - 1;
|
|
|
|
indexCache[v2] = leftoverFaces[i]->m_v2;
|
|
}
|
|
else
|
|
{
|
|
leftoverFaces[i]->m_v2 = index;
|
|
}
|
|
}
|
|
|
|
delete[] indexCache;
|
|
|
|
assert(optimizedVerts.size() == vertices.size());
|
|
}
|