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