/******************************************************************************
|
|
@File PVRTTriStrip.cpp
|
|
@Title PVRTTriStrip
|
|
@Version @Version
|
|
@Copyright Copyright (c) Imagination Technologies Limited.
|
|
@Platform Independent
|
|
@Description Strips a triangle list.
|
|
******************************************************************************/
|
|
/****************************************************************************
|
** Includes
|
****************************************************************************/
|
#include <stdlib.h>
|
|
#include "PVRTGlobal.h"
|
#include "PVRTContext.h"
|
#include "PVRTTriStrip.h"
|
|
/****************************************************************************
|
** Defines
|
****************************************************************************/
|
#define RND_TRIS_ORDER
|
|
/****************************************************************************
|
** Structures
|
****************************************************************************/
|
|
/****************************************************************************
|
** Class: CTri
|
****************************************************************************/
|
class CTri;
|
|
/*!***************************************************************************
|
@Class CTriState
|
@Description Stores a pointer to the triangles either side of itself,
|
as well as it's winding.
|
*****************************************************************************/
|
class CTriState
|
{
|
public:
|
CTri *pRev, *pFwd;
|
bool bWindFwd;
|
|
CTriState()
|
{
|
bWindFwd = true; // Initial value irrelevent
|
pRev = NULL;
|
pFwd = NULL;
|
}
|
};
|
/*!***************************************************************************
|
@Class CTri
|
@Description Object used to store information about the triangle, such as
|
the vertex indices it is made from, which triangles are
|
adjacent to it, etc.
|
*****************************************************************************/
|
class CTri
|
{
|
public:
|
CTriState sNew, sOld;
|
|
CTri *pAdj[3];
|
bool bInStrip;
|
|
const unsigned int *pIdx; // three indices for the tri
|
bool bOutput;
|
|
public:
|
CTri();
|
int FindEdge(const unsigned int pw0, const unsigned int pw1) const;
|
void Cement();
|
void Undo();
|
int EdgeFromAdjTri(const CTri &tri) const; // Find the index of the adjacent tri
|
};
|
|
/*!***************************************************************************
|
@Class CStrip
|
@Description Object used to store the triangles that a given strip is
|
composed from.
|
*****************************************************************************/
|
class CStrip
|
{
|
protected:
|
unsigned int m_nTriCnt;
|
CTri *m_pTri;
|
unsigned int m_nStrips;
|
|
CTri **m_psStrip; // Working space for finding strips
|
|
public:
|
CStrip(
|
const unsigned int * const pui32TriList,
|
const unsigned int nTriCnt);
|
~CStrip();
|
|
protected:
|
bool StripGrow(
|
CTri &triFrom,
|
const unsigned int nEdgeFrom,
|
const int nMaxChange);
|
|
public:
|
void StripFromEdges();
|
void StripImprove();
|
|
void Output(
|
unsigned int **ppui32Strips,
|
unsigned int **ppnStripLen,
|
unsigned int *pnStripCnt);
|
};
|
|
/****************************************************************************
|
** Constants
|
****************************************************************************/
|
|
/****************************************************************************
|
** Code: Class: CTri
|
****************************************************************************/
|
CTri::CTri()
|
{
|
pAdj[0] = NULL;
|
pAdj[1] = NULL;
|
pAdj[2] = NULL;
|
bInStrip = false;
|
bOutput = false;
|
}
|
|
/*!***************************************************************************
|
@Function FindEdge
|
@Input pw0 The first index
|
@Input pw1 The second index
|
@Return The index of the edge
|
@Description Finds the index of the edge that the current object shares
|
with the two vertex index values that have been passed in
|
(or returns -1 if they dont share an edge).
|
*****************************************************************************/
|
int CTri::FindEdge(const unsigned int pw0, const unsigned int pw1) const
|
{
|
if((pIdx[0] == pw0 && pIdx[1] == pw1))
|
return 0;
|
if((pIdx[1] == pw0 && pIdx[2] == pw1))
|
return 1;
|
if((pIdx[2] == pw0 && pIdx[0] == pw1))
|
return 2;
|
return -1;
|
}
|
/*!***************************************************************************
|
@Function Cement
|
@Description Assigns the new state as the old state.
|
*****************************************************************************/
|
void CTri::Cement()
|
{
|
sOld = sNew;
|
}
|
/*!***************************************************************************
|
@Function Undo
|
@Description Reverts the new state to the old state.
|
*****************************************************************************/
|
void CTri::Undo()
|
{
|
sNew = sOld;
|
}
|
/*!***************************************************************************
|
@Function EdgeFromAdjTri
|
@Input tri The triangle to compare
|
@Return int Index of adjacent triangle (-1 if not adjacent)
|
@Description If the input triangle is adjacent to the current triangle,
|
it's index is returned.
|
*****************************************************************************/
|
int CTri::EdgeFromAdjTri(const CTri &tri) const
|
{
|
for(int i = 0; i < 3; ++i)
|
{
|
if(pAdj[i] == &tri)
|
{
|
return i;
|
}
|
}
|
_ASSERT(false);
|
return -1;
|
}
|
|
/****************************************************************************
|
** Local code
|
****************************************************************************/
|
/*!***************************************************************************
|
@Function OrphanTri
|
@Input tri The triangle test
|
@Return int Returns 1 if change was made
|
@Description If the input triangle is not wound forward and is not the last
|
triangle in the strip, the connection with the next triangle
|
in the strip is removed.
|
*****************************************************************************/
|
static int OrphanTri(
|
CTri * const pTri)
|
{
|
_ASSERT(!pTri->bInStrip);
|
if(pTri->sNew.bWindFwd || !pTri->sNew.pFwd)
|
return 0;
|
|
pTri->sNew.pFwd->sNew.pRev = NULL;
|
pTri->sNew.pFwd = NULL;
|
return 1;
|
}
|
/*!***************************************************************************
|
@Function TakeTri
|
@Input pTri The triangle to take
|
@Input pRevNew The triangle that is before pTri in the new strip
|
@Return int Returns 1 if a new strip has been created
|
@Description Removes the triangle from it's current strip
|
and places it in a new one (following pRevNew in the new strip).
|
*****************************************************************************/
|
static int TakeTri(
|
CTri * const pTri,
|
CTri * const pRevNew,
|
const bool bFwd)
|
{
|
int nRet;
|
|
_ASSERT(!pTri->bInStrip);
|
|
if(pTri->sNew.pFwd && pTri->sNew.pRev)
|
{
|
_ASSERT(pTri->sNew.pFwd->sNew.pRev == pTri);
|
pTri->sNew.pFwd->sNew.pRev = NULL;
|
_ASSERT(pTri->sNew.pRev->sNew.pFwd == pTri);
|
pTri->sNew.pRev->sNew.pFwd = NULL;
|
|
// If in the middle of a Strip, this will generate a new Strip
|
nRet = 1;
|
|
// The second tri in the strip may need to be orphaned, or it will have wrong winding order
|
nRet += OrphanTri(pTri->sNew.pFwd);
|
}
|
else if(pTri->sNew.pFwd)
|
{
|
_ASSERT(pTri->sNew.pFwd->sNew.pRev == pTri);
|
pTri->sNew.pFwd->sNew.pRev = NULL;
|
|
// If at the beginning of a Strip, no change
|
nRet = 0;
|
|
// The second tri in the strip may need to be orphaned, or it will have wrong winding order
|
nRet += OrphanTri(pTri->sNew.pFwd);
|
}
|
else if(pTri->sNew.pRev)
|
{
|
_ASSERT(pTri->sNew.pRev->sNew.pFwd == pTri);
|
pTri->sNew.pRev->sNew.pFwd = NULL;
|
|
// If at the end of a Strip, no change
|
nRet = 0;
|
}
|
else
|
{
|
// Otherwise it's a lonesome triangle; one Strip removed!
|
nRet = -1;
|
}
|
|
pTri->sNew.pFwd = NULL;
|
pTri->sNew.pRev = pRevNew;
|
pTri->bInStrip = true;
|
pTri->sNew.bWindFwd = bFwd;
|
|
if(pRevNew)
|
{
|
_ASSERT(!pRevNew->sNew.pFwd);
|
pRevNew->sNew.pFwd = pTri;
|
}
|
|
return nRet;
|
}
|
/*!***************************************************************************
|
@Function TryLinkEdge
|
@Input src The source triangle
|
@Input cmp The triangle to compare with
|
@Input nSrcEdge The edge of souce triangle to compare
|
@Input idx0 Vertex index 0 of the compare triangle
|
@Input idx1 Vertex index 1 of the compare triangle
|
@Description If the triangle to compare currently has no adjacent
|
triangle along the specified edge, link the source triangle
|
(along it's specified edge) with the compare triangle.
|
*****************************************************************************/
|
static bool TryLinkEdge(
|
CTri &src,
|
CTri &cmp,
|
const int nSrcEdge,
|
const unsigned int idx0,
|
const unsigned int idx1)
|
{
|
int nCmpEdge;
|
|
nCmpEdge = cmp.FindEdge(idx0, idx1);
|
if(nCmpEdge != -1 && !cmp.pAdj[nCmpEdge])
|
{
|
cmp.pAdj[nCmpEdge] = &src;
|
src.pAdj[nSrcEdge] = &cmp;
|
return true;
|
}
|
return false;
|
}
|
|
/****************************************************************************
|
** Code: Class: CStrip
|
****************************************************************************/
|
CStrip::CStrip(
|
const unsigned int * const pui32TriList,
|
const unsigned int nTriCnt)
|
{
|
unsigned int i, j;
|
bool b0, b1, b2;
|
|
m_nTriCnt = nTriCnt;
|
|
/*
|
Generate adjacency info
|
*/
|
m_pTri = new CTri[nTriCnt];
|
for(i = 0; i < nTriCnt; ++i)
|
{
|
// Set pointer to indices
|
m_pTri[i].pIdx = &pui32TriList[3 * i];
|
|
b0 = false;
|
b1 = false;
|
b2 = false;
|
for(j = 0; j < i && !(b0 & b1 & b2); ++j)
|
{
|
if(!b0)
|
b0 = TryLinkEdge(m_pTri[i], m_pTri[j], 0, m_pTri[i].pIdx[1], m_pTri[i].pIdx[0]);
|
|
if(!b1)
|
b1 = TryLinkEdge(m_pTri[i], m_pTri[j], 1, m_pTri[i].pIdx[2], m_pTri[i].pIdx[1]);
|
|
if(!b2)
|
b2 = TryLinkEdge(m_pTri[i], m_pTri[j], 2, m_pTri[i].pIdx[0], m_pTri[i].pIdx[2]);
|
}
|
}
|
|
// Initially, every triangle is a strip.
|
m_nStrips = m_nTriCnt;
|
|
// Allocate working space for the strippers
|
m_psStrip = new CTri*[m_nTriCnt];
|
}
|
|
CStrip::~CStrip()
|
{
|
delete [] m_pTri;
|
delete [] m_psStrip;
|
}
|
/*!***************************************************************************
|
@Function StripGrow
|
@Input triFrom The triangle to begin from
|
@Input nEdgeFrom The edge of the triangle to begin from
|
@Input maxChange The maximum number of changes to be made
|
@Description Takes triFrom as a starting point of triangles to add to
|
the list and adds triangles sequentially by finding the next
|
triangle that is adjacent to the current triangle.
|
This is repeated until the maximum number of changes
|
have been made.
|
*****************************************************************************/
|
bool CStrip::StripGrow(
|
CTri &triFrom,
|
const unsigned int nEdgeFrom,
|
const int nMaxChange)
|
{
|
unsigned int i;
|
bool bFwd;
|
int nDiff, nDiffTot, nEdge;
|
CTri *pTri, *pTriPrev, *pTmp;
|
unsigned int nStripLen;
|
|
// Start strip from this tri
|
pTri = &triFrom;
|
pTriPrev = NULL;
|
|
nDiffTot = 0;
|
nStripLen = 0;
|
|
// Start strip from this edge
|
nEdge = nEdgeFrom;
|
bFwd = true;
|
|
// Extend the strip until we run out, or we find an improvement
|
nDiff = 1;
|
while(nDiff > nMaxChange)
|
{
|
// Add pTri to the strip
|
_ASSERT(pTri);
|
nDiff += TakeTri(pTri, pTriPrev, bFwd);
|
_ASSERT(nStripLen < m_nTriCnt);
|
m_psStrip[nStripLen++] = pTri;
|
|
// Jump to next tri
|
pTriPrev = pTri;
|
pTri = pTri->pAdj[nEdge];
|
if(!pTri)
|
break; // No more tris, gotta stop
|
|
if(pTri->bInStrip)
|
break; // No more tris, gotta stop
|
|
// Find which edge we came over
|
nEdge = pTri->EdgeFromAdjTri(*pTriPrev);
|
|
// Find the edge to leave over
|
if(bFwd)
|
{
|
if(--nEdge < 0)
|
nEdge = 2;
|
}
|
else
|
{
|
if(++nEdge > 2)
|
nEdge = 0;
|
}
|
|
// Swap the winding order for the next tri
|
bFwd = !bFwd;
|
}
|
_ASSERT(!pTriPrev->sNew.pFwd);
|
|
/*
|
Accept or reject this strip.
|
|
Accepting changes which don't change the number of strips
|
adds variety, which can help better strips to develop.
|
*/
|
if(nDiff <= nMaxChange)
|
{
|
nDiffTot += nDiff;
|
|
// Great, take the Strip
|
for(i = 0; i < nStripLen; ++i)
|
{
|
pTri = m_psStrip[i];
|
_ASSERT(pTri->bInStrip);
|
|
// Cement affected tris
|
pTmp = pTri->sOld.pFwd;
|
if(pTmp && !pTmp->bInStrip)
|
{
|
if(pTmp->sOld.pFwd && !pTmp->sOld.pFwd->bInStrip)
|
pTmp->sOld.pFwd->Cement();
|
pTmp->Cement();
|
}
|
|
pTmp = pTri->sOld.pRev;
|
if(pTmp && !pTmp->bInStrip)
|
{
|
pTmp->Cement();
|
}
|
|
// Cement this tris
|
pTri->bInStrip = false;
|
pTri->Cement();
|
}
|
}
|
else
|
{
|
// Shame, undo the strip
|
for(i = 0; i < nStripLen; ++i)
|
{
|
pTri = m_psStrip[i];
|
_ASSERT(pTri->bInStrip);
|
|
// Undo affected tris
|
pTmp = pTri->sOld.pFwd;
|
if(pTmp && !pTmp->bInStrip)
|
{
|
if(pTmp->sOld.pFwd && !pTmp->sOld.pFwd->bInStrip)
|
pTmp->sOld.pFwd->Undo();
|
pTmp->Undo();
|
}
|
|
pTmp = pTri->sOld.pRev;
|
if(pTmp && !pTmp->bInStrip)
|
{
|
pTmp->Undo();
|
}
|
|
// Undo this tris
|
pTri->bInStrip = false;
|
pTri->Undo();
|
}
|
}
|
|
#ifdef _DEBUG
|
for(int nDbg = 0; nDbg < (int)m_nTriCnt; ++nDbg)
|
{
|
_ASSERT(m_pTri[nDbg].bInStrip == false);
|
_ASSERT(m_pTri[nDbg].bOutput == false);
|
_ASSERT(m_pTri[nDbg].sOld.pRev == m_pTri[nDbg].sNew.pRev);
|
_ASSERT(m_pTri[nDbg].sOld.pFwd == m_pTri[nDbg].sNew.pFwd);
|
|
if(m_pTri[nDbg].sNew.pRev)
|
{
|
_ASSERT(m_pTri[nDbg].sNew.pRev->sNew.pFwd == &m_pTri[nDbg]);
|
}
|
|
if(m_pTri[nDbg].sNew.pFwd)
|
{
|
_ASSERT(m_pTri[nDbg].sNew.pFwd->sNew.pRev == &m_pTri[nDbg]);
|
}
|
}
|
#endif
|
|
if(nDiffTot)
|
{
|
m_nStrips += nDiffTot;
|
return true;
|
}
|
return false;
|
}
|
|
/*!***************************************************************************
|
@Function StripFromEdges
|
@Description Creates a strip from the object's edge information.
|
*****************************************************************************/
|
void CStrip::StripFromEdges()
|
{
|
unsigned int i, j, nTest;
|
CTri *pTri, *pTriPrev;
|
int nEdge = 0;
|
|
/*
|
Attempt to create grid-oriented strips.
|
*/
|
for(i = 0; i < m_nTriCnt; ++i)
|
{
|
pTri = &m_pTri[i];
|
|
// Count the number of empty edges
|
nTest = 0;
|
for(j = 0; j < 3; ++j)
|
{
|
if(!pTri->pAdj[j])
|
{
|
++nTest;
|
}
|
else
|
{
|
nEdge = j;
|
}
|
}
|
|
if(nTest != 2)
|
continue;
|
|
for(;;)
|
{
|
// A tri with two empty edges is a corner (there are other corners too, but this works so...)
|
while(StripGrow(*pTri, nEdge, -1)) {};
|
|
pTriPrev = pTri;
|
pTri = pTri->pAdj[nEdge];
|
if(!pTri)
|
break;
|
|
// Find the edge we came over
|
nEdge = pTri->EdgeFromAdjTri(*pTriPrev);
|
|
// Step around to the next edge
|
if(++nEdge > 2)
|
nEdge = 0;
|
|
pTriPrev = pTri;
|
pTri = pTri->pAdj[nEdge];
|
if(!pTri)
|
break;
|
|
// Find the edge we came over
|
nEdge = pTri->EdgeFromAdjTri(*pTriPrev);
|
|
// Step around to the next edge
|
if(--nEdge < 0)
|
nEdge = 2;
|
|
#if 0
|
// If we're not tracking the edge, give up
|
nTest = nEdge - 1;
|
if(nTest < 0)
|
nTest = 2;
|
if(pTri->pAdj[nTest])
|
break;
|
else
|
continue;
|
#endif
|
}
|
}
|
}
|
|
#ifdef RND_TRIS_ORDER
|
struct pair
|
{
|
unsigned int i, o;
|
};
|
|
static int compare(const void *arg1, const void *arg2)
|
{
|
return ((pair*)arg1)->i - ((pair*)arg2)->i;
|
}
|
#endif
|
/*!***************************************************************************
|
@Function StripImprove
|
@Description Optimises the strip
|
*****************************************************************************/
|
void CStrip::StripImprove()
|
{
|
unsigned int i, j;
|
bool bChanged;
|
int nRepCnt, nChecks;
|
int nMaxChange;
|
#ifdef RND_TRIS_ORDER
|
pair *pnOrder;
|
|
/*
|
Create a random order to process the tris
|
*/
|
pnOrder = new pair[m_nTriCnt];
|
#endif
|
|
nRepCnt = 0;
|
nChecks = 2;
|
nMaxChange = 0;
|
|
/*
|
Reduce strip count by growing each of the three strips each tri can start.
|
*/
|
while(nChecks)
|
{
|
--nChecks;
|
|
bChanged = false;
|
|
#ifdef RND_TRIS_ORDER
|
/*
|
Create a random order to process the tris
|
*/
|
for(i = 0; i < m_nTriCnt; ++i)
|
{
|
pnOrder[i].i = rand() * rand();
|
pnOrder[i].o = i;
|
}
|
qsort(pnOrder, m_nTriCnt, sizeof(*pnOrder), compare);
|
#endif
|
|
/*
|
Process the tris
|
*/
|
for(i = 0; i < m_nTriCnt; ++i)
|
{
|
for(j = 0; j < 3; ++j)
|
{
|
#ifdef RND_TRIS_ORDER
|
bChanged |= StripGrow(m_pTri[pnOrder[i].o], j, nMaxChange);
|
#else
|
bChanged |= StripGrow(m_pTri[i], j, nMaxChange);
|
#endif
|
}
|
}
|
++nRepCnt;
|
|
// Check the results once or twice
|
if(bChanged)
|
nChecks = 2;
|
|
nMaxChange = (nMaxChange == 0 ? -1 : 0);
|
}
|
|
#ifdef RND_TRIS_ORDER
|
delete [] pnOrder;
|
#endif
|
_RPT1(_CRT_WARN, "Reps: %d\n", nRepCnt);
|
}
|
/*!***************************************************************************
|
@Function Output
|
@Output ppui32Strips
|
@Output ppnStripLen The length of the strip
|
@Output pnStripCnt
|
@Description Outputs key information about the strip to the output
|
parameters.
|
*****************************************************************************/
|
void CStrip::Output(
|
unsigned int **ppui32Strips,
|
unsigned int **ppnStripLen,
|
unsigned int *pnStripCnt)
|
{
|
unsigned int *pui32Strips;
|
unsigned int *pnStripLen;
|
unsigned int i, j, nIdx, nStrip;
|
CTri *pTri;
|
|
/*
|
Output Strips
|
*/
|
pnStripLen = (unsigned int*)malloc(m_nStrips * sizeof(*pnStripLen));
|
pui32Strips = (unsigned int*)malloc((m_nTriCnt + m_nStrips * 2) * sizeof(*pui32Strips));
|
nStrip = 0;
|
nIdx = 0;
|
for(i = 0; i < m_nTriCnt; ++i)
|
{
|
pTri = &m_pTri[i];
|
|
if(pTri->sNew.pRev)
|
continue;
|
_ASSERT(!pTri->sNew.pFwd || pTri->sNew.bWindFwd);
|
_ASSERT(pTri->bOutput == false);
|
|
if(!pTri->sNew.pFwd)
|
{
|
pui32Strips[nIdx++] = pTri->pIdx[0];
|
pui32Strips[nIdx++] = pTri->pIdx[1];
|
pui32Strips[nIdx++] = pTri->pIdx[2];
|
pnStripLen[nStrip] = 1;
|
pTri->bOutput = true;
|
}
|
else
|
{
|
if(pTri->sNew.pFwd == pTri->pAdj[0])
|
{
|
pui32Strips[nIdx++] = pTri->pIdx[2];
|
pui32Strips[nIdx++] = pTri->pIdx[0];
|
}
|
else if(pTri->sNew.pFwd == pTri->pAdj[1])
|
{
|
pui32Strips[nIdx++] = pTri->pIdx[0];
|
pui32Strips[nIdx++] = pTri->pIdx[1];
|
}
|
else
|
{
|
_ASSERT(pTri->sNew.pFwd == pTri->pAdj[2]);
|
pui32Strips[nIdx++] = pTri->pIdx[1];
|
pui32Strips[nIdx++] = pTri->pIdx[2];
|
}
|
|
pnStripLen[nStrip] = 0;
|
do
|
{
|
_ASSERT(pTri->bOutput == false);
|
|
// Increment tris-in-this-strip counter
|
++pnStripLen[nStrip];
|
|
// Output the new vertex index
|
for(j = 0; j < 3; ++j)
|
{
|
if(
|
(pui32Strips[nIdx-2] != pTri->pIdx[j]) &&
|
(pui32Strips[nIdx-1] != pTri->pIdx[j]))
|
{
|
break;
|
}
|
}
|
_ASSERT(j != 3);
|
pui32Strips[nIdx++] = pTri->pIdx[j];
|
|
// Double-check that the previous three indices are the indices of this tris (in some order)
|
_ASSERT(
|
((pui32Strips[nIdx-3] == pTri->pIdx[0]) && (pui32Strips[nIdx-2] == pTri->pIdx[1]) && (pui32Strips[nIdx-1] == pTri->pIdx[2])) ||
|
((pui32Strips[nIdx-3] == pTri->pIdx[1]) && (pui32Strips[nIdx-2] == pTri->pIdx[2]) && (pui32Strips[nIdx-1] == pTri->pIdx[0])) ||
|
((pui32Strips[nIdx-3] == pTri->pIdx[2]) && (pui32Strips[nIdx-2] == pTri->pIdx[0]) && (pui32Strips[nIdx-1] == pTri->pIdx[1])) ||
|
((pui32Strips[nIdx-3] == pTri->pIdx[2]) && (pui32Strips[nIdx-2] == pTri->pIdx[1]) && (pui32Strips[nIdx-1] == pTri->pIdx[0])) ||
|
((pui32Strips[nIdx-3] == pTri->pIdx[1]) && (pui32Strips[nIdx-2] == pTri->pIdx[0]) && (pui32Strips[nIdx-1] == pTri->pIdx[2])) ||
|
((pui32Strips[nIdx-3] == pTri->pIdx[0]) && (pui32Strips[nIdx-2] == pTri->pIdx[2]) && (pui32Strips[nIdx-1] == pTri->pIdx[1])));
|
|
// Check that the latest three indices are not degenerate
|
_ASSERT(pui32Strips[nIdx-1] != pui32Strips[nIdx-2]);
|
_ASSERT(pui32Strips[nIdx-1] != pui32Strips[nIdx-3]);
|
_ASSERT(pui32Strips[nIdx-2] != pui32Strips[nIdx-3]);
|
|
pTri->bOutput = true;
|
|
// Check that the next triangle is adjacent to this triangle
|
_ASSERT(
|
(pTri->sNew.pFwd == pTri->pAdj[0]) ||
|
(pTri->sNew.pFwd == pTri->pAdj[1]) ||
|
(pTri->sNew.pFwd == pTri->pAdj[2]) ||
|
(!pTri->sNew.pFwd));
|
// Check that this triangle is adjacent to the next triangle
|
_ASSERT(
|
(!pTri->sNew.pFwd) ||
|
(pTri == pTri->sNew.pFwd->pAdj[0]) ||
|
(pTri == pTri->sNew.pFwd->pAdj[1]) ||
|
(pTri == pTri->sNew.pFwd->pAdj[2]));
|
|
pTri = pTri->sNew.pFwd;
|
} while(pTri);
|
}
|
|
++nStrip;
|
}
|
_ASSERT(nIdx == m_nTriCnt + m_nStrips * 2);
|
_ASSERT(nStrip == m_nStrips);
|
|
// Check all triangles have been output
|
for(i = 0; i < m_nTriCnt; ++i)
|
{
|
_ASSERT(m_pTri[i].bOutput == true);
|
}
|
|
// Check all triangles are present
|
j = 0;
|
for(i = 0; i < m_nStrips; ++i)
|
{
|
j += pnStripLen[i];
|
}
|
_ASSERT(j == m_nTriCnt);
|
|
// Output data
|
*pnStripCnt = m_nStrips;
|
*ppui32Strips = pui32Strips;
|
*ppnStripLen = pnStripLen;
|
}
|
|
/****************************************************************************
|
** Code
|
****************************************************************************/
|
|
/*!***************************************************************************
|
@Function PVRTTriStrip
|
@Output ppui32Strips
|
@Output ppnStripLen
|
@Output pnStripCnt
|
@Input pui32TriList
|
@Input nTriCnt
|
@Description Reads a triangle list and generates an optimised triangle strip.
|
*****************************************************************************/
|
void PVRTTriStrip(
|
unsigned int **ppui32Strips,
|
unsigned int **ppnStripLen,
|
unsigned int *pnStripCnt,
|
const unsigned int * const pui32TriList,
|
const unsigned int nTriCnt)
|
{
|
unsigned int *pui32Strips;
|
unsigned int *pnStripLen;
|
unsigned int nStripCnt;
|
|
/*
|
If the order in which triangles are tested as strip roots is
|
randomised, then several attempts can be made. Use the best result.
|
*/
|
for(int i = 0; i <
|
#ifdef RND_TRIS_ORDER
|
5
|
#else
|
1
|
#endif
|
; ++i)
|
{
|
CStrip stripper(pui32TriList, nTriCnt);
|
|
#ifdef RND_TRIS_ORDER
|
srand(i);
|
#endif
|
|
stripper.StripFromEdges();
|
stripper.StripImprove();
|
stripper.Output(&pui32Strips, &pnStripLen, &nStripCnt);
|
|
if(!i || nStripCnt < *pnStripCnt)
|
{
|
if(i)
|
{
|
FREE(*ppui32Strips);
|
FREE(*ppnStripLen);
|
}
|
|
*ppui32Strips = pui32Strips;
|
*ppnStripLen = pnStripLen;
|
*pnStripCnt = nStripCnt;
|
}
|
else
|
{
|
FREE(pui32Strips);
|
FREE(pnStripLen);
|
}
|
}
|
}
|
|
/*!***************************************************************************
|
@Function PVRTTriStripList
|
@Modified pui32TriList
|
@Input nTriCnt
|
@Description Reads a triangle list and generates an optimised triangle strip.
|
Result is converted back to a triangle list.
|
*****************************************************************************/
|
void PVRTTriStripList(unsigned int * const pui32TriList, const unsigned int nTriCnt)
|
{
|
unsigned int *pui32Strips;
|
unsigned int *pnStripLength;
|
unsigned int nNumStrips;
|
unsigned int *pui32TriPtr, *pui32StripPtr;
|
|
/*
|
Strip the geometry
|
*/
|
PVRTTriStrip(&pui32Strips, &pnStripLength, &nNumStrips, pui32TriList, nTriCnt);
|
|
/*
|
Convert back to a triangle list
|
*/
|
pui32StripPtr = pui32Strips;
|
pui32TriPtr = pui32TriList;
|
for(unsigned int i = 0; i < nNumStrips; ++i)
|
{
|
*pui32TriPtr++ = *pui32StripPtr++;
|
*pui32TriPtr++ = *pui32StripPtr++;
|
*pui32TriPtr++ = *pui32StripPtr++;
|
|
for(unsigned int j = 1; j < pnStripLength[i]; ++j)
|
{
|
// Use two indices from previous triangle, flipping tri order alternately.
|
if(j & 0x01)
|
{
|
*pui32TriPtr++ = pui32StripPtr[-1];
|
*pui32TriPtr++ = pui32StripPtr[-2];
|
}
|
else
|
{
|
*pui32TriPtr++ = pui32StripPtr[-2];
|
*pui32TriPtr++ = pui32StripPtr[-1];
|
}
|
|
*pui32TriPtr++ = *pui32StripPtr++;
|
}
|
}
|
|
free(pui32Strips);
|
free(pnStripLength);
|
}
|
|
/*****************************************************************************
|
End of file (PVRTTriStrip.cpp)
|
*****************************************************************************/
|