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