C++程序  |  946行  |  23.19 KB

/******************************************************************************

 @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)
*****************************************************************************/