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