| /****************************************************************************** |
| |
| @File PVRTGeometry.cpp |
| |
| @Title PVRTGeometry |
| |
| @Version |
| |
| @Copyright Copyright (c) Imagination Technologies Limited. |
| |
| @Platform Independant |
| |
| @Description Code to affect triangle mesh geometry. |
| |
| ******************************************************************************/ |
| |
| /***************************************************************************** |
| For each vertex with only one free triangle |
| Start collecting triangles from there |
| Add the triangle which gives the highest triangles/vertex number (extra tris usually come for free) |
| When full, test against current best |
| If markedly better tri/vtx, take new block |
| If close-enough to prev tri/vtx, take block which closes the highest number of edges (and opens fewest) |
| If not quite full, goto 1 to continue filling block |
| If no block has been found, start at any free triangle and use resulting block |
| Copy block to output, empty it and goto 1. |
| *****************************************************************************/ |
| |
| /**************************************************************************** |
| ** Build options |
| ****************************************************************************/ |
| #undef PVRTRISORT_ENABLE_VERIFY_RESULTS |
| |
| /**************************************************************************** |
| ** Includes |
| ****************************************************************************/ |
| #include <vector> |
| #include <math.h> |
| |
| #include "PVRTGeometry.h" |
| |
| #ifdef PVRTRISORT_ENABLE_VERIFY_RESULTS |
| #include "PvrVGPBlockTest.h" |
| #endif |
| |
| #include "PVRTGlobal.h" |
| #include "PVRTContext.h" |
| /**************************************************************************** |
| ** Structures |
| ****************************************************************************/ |
| |
| struct SVtx; |
| |
| /**************************************************************************** |
| @Function SEdg |
| @Description Information about an "edge" - the shared boundary between two triangles |
| ****************************************************************************/ |
| struct SEdg { |
| const SVtx *psVtx[2]; // Identify the edge by the two vertices it joins |
| int nTriNumFree; // Number of triangle using this edge |
| }; |
| |
| /**************************************************************************** |
| @Function STri |
| @Description Information about a triangle |
| ****************************************************************************/ |
| struct STri { |
| const PVRTGEOMETRY_IDX *pwIdx; // Vertex indices forming this triangle |
| SEdg *psEdg[3]; // Pointer to the three triangle edges |
| bool bUsed; |
| }; |
| |
| /**************************************************************************** |
| @Function SVtx |
| @Description Information about a vertex |
| ****************************************************************************/ |
| struct SVtx { |
| STri **psTri; // Allocated array of pointers to the triangles sharing this vertex |
| int nTriNumTot; // Length of the above array |
| int nTriNumFree; // Number of triangles unused in the above array |
| SVtx **ppMeshPos; // Position in VtxByMesh list |
| }; |
| |
| /**************************************************************************** |
| @Function SMesh |
| @Description Information about a mesh |
| ****************************************************************************/ |
| struct SMesh { |
| SVtx **ppVtx; |
| int nVtxNum; |
| }; |
| |
| /**************************************************************************** |
| @Function CObject |
| @Description Information about an object (i.e. collection of mesh's to form |
| a single entity) |
| ****************************************************************************/ |
| class CObject { |
| public: |
| STri *m_pTri; // Array of all the triangles in the mesh |
| SEdg *m_pEdg; // Array of all the edges in the mesh |
| SVtx *m_pVtx; // Array of all the vertices in a mesh |
| |
| int m_nTriNumFree; |
| |
| std::vector<SMesh> *m_pvMesh; |
| std::vector<SMesh> m_vMeshLg; |
| |
| protected: |
| int m_nVtxTot; // Total vertices in the object |
| int m_nEdgTot; // Total edges in the object |
| int m_nTriTot; // Total triangles in the object |
| |
| int m_nVtxLimit; // Maximum number of vertices a block can contain |
| int m_nTriLimit; // Maximum number of triangles a block can contain |
| |
| SVtx **m_ppVtxByMesh; |
| |
| public: |
| CObject( |
| const PVRTGEOMETRY_IDX * const pwIdx, |
| const int nVtxTot, |
| const int nTriTot, |
| const int nVtxLimit, |
| const int nTriLimit); |
| |
| ~CObject(); |
| |
| int GetVertexCount() const; |
| int GetTriangleCount() const; |
| void SplitMesh( |
| SMesh * const pMesh, |
| const int nVtxNum, |
| SVtx ** const ppVtx); |
| |
| void ResizeMesh( |
| const int nVtxNum, |
| SVtx ** const ppVtx); |
| |
| protected: |
| SEdg *BuildEdgeList( |
| const SVtx * const pVtx0, |
| const SVtx * const pVtx1); |
| |
| void CreateMeshList(); |
| }; |
| |
| /**************************************************************************** |
| @Function CBlockOption |
| @Description A possible group of polygons to use |
| ****************************************************************************/ |
| struct CBlockOption { |
| protected: |
| struct SEdgeDelta { |
| const SEdg *pEdg; |
| int nRefCnt; |
| }; |
| |
| public: |
| int nVtxNum; // Number of vertices in the block |
| int nEdgNum; // Number of edges in the block |
| int nTriNum; // Number of triangles in the block |
| |
| SVtx **psVtx; // Pointers to vertices |
| protected: |
| SEdgeDelta *psEdgeDelta; |
| STri **psTri; // Pointers to triangles |
| |
| int m_nVtxLimit; // Maximum number of vertices a block can contain |
| int m_nTriLimit; // Maximum number of triangles a block can contain |
| |
| public: |
| ~CBlockOption(); |
| |
| void Init( |
| const int nVtxLimit, |
| const int nTriLimit); |
| void Copy(const CBlockOption * const pSrc); |
| |
| void Clear(); |
| |
| void Output( |
| PVRTGEOMETRY_IDX * const pwOut, |
| int * const pnVtxCnt, |
| int * const pnTriCnt, |
| const CObject * const pOb) const; |
| |
| bool UsingVertex(const SVtx * const pVtx) const; |
| bool Contains(const STri * const pTri) const; |
| |
| bool IsEmpty() const; |
| bool IsFull() const; |
| |
| void AddVertex(SVtx * const pVtx); |
| void AddVertexCheckDup(SVtx * const pVtx); |
| |
| void AddTriangleCheckDup(STri * const pTri); |
| |
| void AddEdgeCheckDup(const SEdg * const pEdg); |
| |
| void AddTriangle(STri * const pTri); |
| |
| void AddOneTriangle( |
| STri * const pTri, |
| const CObject * const pOb); |
| |
| int GetClosedEdgeDelta() const; |
| bool IsBetterThan(const CBlockOption * const pCmp) const; |
| |
| void Add( |
| const CBlockOption * const pSrc, |
| const CObject * const pOb); |
| |
| void Add( |
| const SMesh * const pMesh); |
| }; |
| |
| /**************************************************************************** |
| @Function CBlock |
| @Description A model of a HW block (triangles and vertices) |
| ****************************************************************************/ |
| class CBlock { |
| protected: |
| CBlockOption m_sOpt, m_sOptBest; |
| |
| int m_nVtxLimit; // Maximum number of vertices a block can contain |
| int m_nTriLimit; // Maximum number of triangles a block can contain |
| |
| CBlockOption m_sJob0, m_sJob1; // Workspace: to find the best triangle to add |
| |
| public: |
| CBlock( |
| const int nBufferVtxLimit, |
| const int nBufferTriLimit); |
| |
| void Clear(); |
| |
| bool FillFrom( |
| SMesh * const pMesh, |
| SVtx * const pVtx, |
| CObject * const pOb); |
| |
| int Fill( |
| CObject * const pOb); |
| |
| void Output( |
| PVRTGEOMETRY_IDX * const pwOut, |
| int * const pnVtxCnt, |
| int * const pnTriCnt, |
| const CObject * const pOb) const; |
| |
| protected: |
| bool AddBestTrianglesAppraise( |
| CBlockOption * const pJob, |
| const CObject * const pOb, |
| const STri * const pTriAppraise); |
| |
| void AddBestTriangles(CObject * const pOb); |
| }; |
| |
| /**************************************************************************** |
| ** Local function prototypes |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| @Function CObject |
| @Input pwIdx Array of indices |
| @Input nVrxTot Total number of vertices |
| @Input nTriTot Total number of triangles |
| @Input nVtxLimit Max number of vertices a block can contain |
| @Input nTriLimit Max number of triangles a block can contain |
| @Description The class's constructor. |
| ****************************************************************************/ |
| CObject::CObject( |
| const PVRTGEOMETRY_IDX * const pwIdx, |
| const int nVtxTot, |
| const int nTriTot, |
| const int nVtxLimit, |
| const int nTriLimit) |
| { |
| int i; |
| SVtx *pVtx0, *pVtx1, *pVtx2; |
| |
| m_nVtxLimit = nVtxLimit; |
| m_nTriLimit = nTriLimit; |
| |
| m_pvMesh = new std::vector<SMesh>[nVtxLimit-2]; |
| _ASSERT(m_pvMesh); |
| |
| m_ppVtxByMesh = (SVtx**)calloc(nVtxTot, sizeof(*m_ppVtxByMesh)); |
| _ASSERT(m_ppVtxByMesh); |
| |
| m_nVtxTot = nVtxTot; |
| m_nEdgTot = 0; |
| m_nTriTot = nTriTot; |
| |
| m_nTriNumFree = m_nTriTot; |
| |
| m_pTri = (STri*)calloc(nTriTot, sizeof(*m_pTri)); |
| _ASSERT(m_pTri); |
| |
| m_pEdg = (SEdg*)calloc(nTriTot*3, sizeof(*m_pEdg)); // Allocate the maximum possible number of edges, though it should be far fewer than this |
| _ASSERT(m_pEdg); |
| |
| m_pVtx = (SVtx*)calloc(nVtxTot, sizeof(*m_pVtx)); |
| _ASSERT(m_pVtx); |
| |
| // Run through triangles... |
| for(i = 0; i < nTriTot; ++i) { |
| pVtx0 = &m_pVtx[pwIdx[3*i+0]]; |
| pVtx1 = &m_pVtx[pwIdx[3*i+1]]; |
| pVtx2 = &m_pVtx[pwIdx[3*i+2]]; |
| |
| // Mark each vertex for the number of times it's referenced |
| ++pVtx0->nTriNumFree; |
| ++pVtx1->nTriNumFree; |
| ++pVtx2->nTriNumFree; |
| |
| // Build the edge list |
| m_pTri[i].psEdg[0] = BuildEdgeList(pVtx0, pVtx1); |
| m_pTri[i].psEdg[1] = BuildEdgeList(pVtx1, pVtx2); |
| m_pTri[i].psEdg[2] = BuildEdgeList(pVtx2, pVtx0); |
| } |
| |
| // Run through vertices, creating enough space for pointers to each triangle using this vertex |
| for(i = 0; i < nVtxTot; ++i) |
| m_pVtx[i].psTri = (STri**)calloc(m_pVtx[i].nTriNumFree, sizeof(*m_pVtx[i].psTri)); |
| |
| // Run through triangles, marking each vertex used with a pointer to this tri |
| for(i = 0; i < nTriTot; ++i) { |
| pVtx0 = &m_pVtx[pwIdx[3*i+0]]; |
| pVtx1 = &m_pVtx[pwIdx[3*i+1]]; |
| pVtx2 = &m_pVtx[pwIdx[3*i+2]]; |
| |
| pVtx0->psTri[pVtx0->nTriNumTot++] = &m_pTri[i]; |
| pVtx1->psTri[pVtx1->nTriNumTot++] = &m_pTri[i]; |
| pVtx2->psTri[pVtx2->nTriNumTot++] = &m_pTri[i]; |
| |
| // Give each triangle a pointer to its indices |
| m_pTri[i].pwIdx = &pwIdx[3*i]; |
| } |
| |
| #ifdef _DEBUG |
| for(i = 0; i < nVtxTot; ++i) { |
| _ASSERTE(m_pVtx[i].nTriNumFree == m_pVtx[i].nTriNumTot); |
| } |
| #endif |
| |
| CreateMeshList(); |
| } |
| |
| /**************************************************************************** |
| @Function ~CObject |
| @Description Destructor |
| ****************************************************************************/ |
| CObject::~CObject() |
| { |
| _ASSERT(m_nTriNumFree == 0); |
| |
| while(m_nVtxTot) { |
| --m_nVtxTot; |
| FREE(m_pVtx[m_nVtxTot].psTri); |
| _ASSERTE(m_pVtx[m_nVtxTot].nTriNumFree == 0); |
| _ASSERTE(m_pVtx[m_nVtxTot].ppMeshPos); |
| } |
| |
| #ifdef _DEBUG |
| while(m_nEdgTot) { |
| --m_nEdgTot; |
| _ASSERTE(m_pEdg[m_nEdgTot].nTriNumFree == 0); |
| } |
| while(m_nTriTot) { |
| --m_nTriTot; |
| _ASSERTE(m_pTri[m_nTriTot].bUsed); |
| } |
| #endif |
| |
| FREE(m_pTri); |
| FREE(m_pEdg); |
| FREE(m_pVtx); |
| |
| delete [] m_pvMesh; |
| FREE(m_ppVtxByMesh); |
| } |
| |
| /**************************************************************************** |
| @Function GetVertexCount |
| @Return int |
| @Description Return the vertex count |
| ****************************************************************************/ |
| int CObject::GetVertexCount() const |
| { |
| return m_nVtxTot; |
| } |
| |
| /**************************************************************************** |
| @Function GetTriangleCount |
| @Return int |
| @Description Return the triangle count |
| ****************************************************************************/ |
| int CObject::GetTriangleCount() const |
| { |
| return m_nTriTot; |
| } |
| |
| /**************************************************************************** |
| @Function BuildEdgeList |
| @Input pVtx0 Edge 0 |
| @Input pVtx1 Edge 1 |
| @Return SEdg* |
| @Description If the vertices that have been passed in are already used by an edge, |
| the number of triangles sharing the edge is increased by one and a |
| pointer to the edge is returned. If the edge is not already in the |
| list, the edge is added to the list. |
| ****************************************************************************/ |
| SEdg *CObject::BuildEdgeList( |
| const SVtx * const pVtx0, |
| const SVtx * const pVtx1) |
| { |
| SEdg *pEdg; |
| const SVtx *pVtxL, *pVtxH; |
| int i; |
| |
| pVtxL = pVtx0 < pVtx1 ? pVtx0 : pVtx1; |
| pVtxH = pVtx0 > pVtx1 ? pVtx0 : pVtx1; |
| |
| // Do nothing if the edge already exists |
| i = m_nEdgTot; |
| while(i) { |
| --i; |
| |
| pEdg = &m_pEdg[i]; |
| if(pEdg->psVtx[0] == pVtxL && pEdg->psVtx[1] == pVtxH) |
| { |
| ++pEdg->nTriNumFree; |
| return pEdg; |
| } |
| } |
| |
| // Add the new edge |
| _ASSERT(m_nEdgTot < m_nTriTot*3); |
| pEdg = &m_pEdg[m_nEdgTot++]; |
| pEdg->psVtx[0] = pVtxL; |
| pEdg->psVtx[1] = pVtxH; |
| pEdg->nTriNumFree = 1; |
| |
| return pEdg; |
| } |
| |
| /**************************************************************************** |
| @Function CreateMeshList |
| @Description Creates the mesh list |
| ****************************************************************************/ |
| void CObject::CreateMeshList() |
| { |
| SVtx **ppR, **ppW, *pVtx; |
| STri *pTri; |
| int i, j, k; |
| SMesh sMesh; |
| int nMeshCnt; |
| |
| nMeshCnt = 0; |
| |
| ppR = m_ppVtxByMesh; |
| ppW = m_ppVtxByMesh; |
| |
| for(i = 0; i < m_nVtxTot; ++i) { |
| pVtx = &m_pVtx[i]; |
| |
| if(pVtx->ppMeshPos) { |
| _ASSERT(pVtx->ppMeshPos < ppW); |
| continue; |
| } |
| |
| ++nMeshCnt; |
| sMesh.ppVtx = ppW; |
| |
| *ppW = pVtx; |
| pVtx->ppMeshPos = ppW; |
| ++ppW; |
| |
| do { |
| // Add all the vertices of all the triangles of *ppR to the list - unless they're already in there |
| for(j = 0; j < (*ppR)->nTriNumTot; ++j) { |
| pTri = (*ppR)->psTri[j]; |
| |
| for(k = 0; k < 3; ++k) { |
| pVtx = &m_pVtx[pTri->pwIdx[k]]; |
| |
| if(pVtx->ppMeshPos) { |
| _ASSERT(pVtx->ppMeshPos < ppW); |
| continue; |
| } |
| |
| *ppW = pVtx; |
| pVtx->ppMeshPos = ppW; |
| ++ppW; |
| } |
| } |
| |
| ++ppR; |
| } while(ppR != ppW); |
| |
| sMesh.nVtxNum = (int)(ppR - sMesh.ppVtx); |
| // _RPT2(_CRT_WARN, "CreateMeshList() mesh %d %dvtx\n", nMeshCnt, sMesh.nVtxNum); |
| if(sMesh.nVtxNum >= 3) |
| { |
| if(sMesh.nVtxNum >= m_nVtxLimit) |
| m_vMeshLg.push_back(sMesh); |
| else |
| m_pvMesh[sMesh.nVtxNum-3].push_back(sMesh); |
| } |
| else |
| { |
| /* |
| Vertex is not used by any triangles; this may be because we're |
| optimising a subset of the mesh (e.g. for bone batching). |
| */ |
| _ASSERT(sMesh.nVtxNum == 1); |
| } |
| } |
| |
| _ASSERT(ppR == &m_ppVtxByMesh[m_nVtxTot]); |
| _ASSERT(ppW == &m_ppVtxByMesh[m_nVtxTot]); |
| // _RPT1(_CRT_WARN, "CreateMeshList() %d meshes\n", nMeshCnt); |
| |
| #ifdef _DEBUG |
| /* for(i = 0; i < m_nVtxLimit-2; ++i) |
| if(m_pvMesh[i].size()) |
| _RPT2(_CRT_WARN, "%d:%d ", i+3, m_pvMesh[i].size()); |
| _RPT1(_CRT_WARN, "lg:%d\n", m_vMeshLg.size());*/ |
| #endif |
| } |
| |
| /**************************************************************************** |
| @Function SplitMesh |
| @Input pMesh Pointer to mesh data |
| @Input nVtxNum Number of vertices in the mesh? |
| @Output ppVtx Array of vertices |
| @Description Note: Ask Aaron |
| ****************************************************************************/ |
| void CObject::SplitMesh( |
| SMesh * const pMesh, |
| const int nVtxNum, |
| SVtx ** const ppVtx) |
| { |
| SVtx *pTmp; |
| int i; |
| SMesh sNew; |
| |
| _ASSERT(nVtxNum); |
| |
| for(i = 0; i < nVtxNum; ++i) { |
| pTmp = pMesh->ppVtx[i]; // Keep a record of the old vtx that's already here |
| |
| pMesh->ppVtx[i] = ppVtx[i]; // Move the new vtx into place |
| *ppVtx[i]->ppMeshPos = pTmp; // Move the old vtx into place |
| |
| pTmp->ppMeshPos = ppVtx[i]->ppMeshPos; // Tell the old vtx where it is now |
| ppVtx[i]->ppMeshPos = &pMesh->ppVtx[i]; // Tell the new vtx where it is now |
| |
| _ASSERT(pMesh->ppVtx[i]->nTriNumFree); |
| } |
| |
| sNew.nVtxNum = nVtxNum; |
| sNew.ppVtx = pMesh->ppVtx; |
| m_pvMesh[nVtxNum-3].push_back(sNew); |
| |
| pMesh->ppVtx = &pMesh->ppVtx[nVtxNum]; |
| pMesh->nVtxNum -= nVtxNum; |
| if(pMesh->nVtxNum < m_nVtxLimit) { |
| ResizeMesh(pMesh->nVtxNum, pMesh->ppVtx); |
| m_vMeshLg.pop_back(); |
| #ifdef _DEBUG |
| /* } else { |
| for(i = 0; i < m_nVtxLimit-2; ++i) |
| if(m_pvMesh[i].size()) |
| _RPT2(_CRT_WARN, "%d:%d ", i+3, m_pvMesh[i].size()); |
| _RPT1(_CRT_WARN, "lg:%d\n", m_vMeshLg.size());*/ |
| #endif |
| } |
| } |
| |
| /**************************************************************************** |
| @Function ResizeMesh |
| @Input nVtxNum The size of the array of vertices being passed in |
| @Input ppVtx Array of vertices |
| @Description Note: Ask Aaron |
| ****************************************************************************/ |
| void CObject::ResizeMesh( |
| const int nVtxNum, |
| SVtx ** const ppVtx) |
| { |
| SVtx **ppR, **ppW; |
| SMesh sNew; |
| int i; |
| |
| ppR = ppVtx; |
| ppW = ppVtx; |
| |
| // Make a list of vertices that have unused triangles in their array of triangles |
| for(i = 0; i < nVtxNum; ++i) { |
| if((*ppR)->nTriNumFree) { |
| (*ppW) = (*ppR); |
| ++ppW; |
| } |
| ++ppR; |
| } |
| |
| sNew.nVtxNum = (int)(ppW - ppVtx); |
| _ASSERT(sNew.nVtxNum <= nVtxNum); |
| |
| // If any mesh still exists, add it to the relevant list |
| if(sNew.nVtxNum) { |
| _ASSERT(sNew.nVtxNum >= 3); |
| _ASSERT(sNew.nVtxNum < m_nVtxLimit); |
| |
| sNew.ppVtx = ppVtx; |
| m_pvMesh[sNew.nVtxNum-3].push_back(sNew); |
| } |
| |
| #ifdef _DEBUG |
| /* for(i = 0; i < m_nVtxLimit-2; ++i) |
| if(m_pvMesh[i].size()) |
| _RPT2(_CRT_WARN, "%d:%d ", i+3, m_pvMesh[i].size()); |
| _RPT1(_CRT_WARN, "lg:%d\n", m_vMeshLg.size());*/ |
| #endif |
| } |
| |
| /**************************************************************************** |
| @Function ~CBlockOption |
| @Description Default destructor |
| ****************************************************************************/ |
| CBlockOption::~CBlockOption() |
| { |
| FREE(psVtx); |
| FREE(psTri); |
| FREE(psEdgeDelta); |
| } |
| |
| /**************************************************************************** |
| @Function Init |
| @Input nVertexLimit The maximum number of vertices a block can contain |
| @Input nTriLimit The maximum number of triangles a block can contain |
| @Description Initialises the class |
| ****************************************************************************/ |
| void CBlockOption::Init( |
| const int nVtxLimit, |
| const int nTriLimit) |
| { |
| m_nVtxLimit = nVtxLimit; |
| m_nTriLimit = nTriLimit; |
| |
| psVtx = (SVtx**)malloc(nVtxLimit * sizeof(*psVtx)); |
| psTri = (STri**)malloc(nTriLimit * sizeof(*psTri)); |
| psEdgeDelta = (SEdgeDelta*)malloc(3 * nTriLimit * sizeof(*psEdgeDelta)); |
| } |
| |
| /**************************************************************************** |
| @Function Copy |
| @Input pSrc Pointer to the source data |
| @Description Overwrites the data in the current instance with the data from |
| the input CBlockOption. |
| ****************************************************************************/ |
| void CBlockOption::Copy(const CBlockOption * const pSrc) |
| { |
| nVtxNum = pSrc->nVtxNum; |
| nEdgNum = pSrc->nEdgNum; |
| nTriNum = pSrc->nTriNum; |
| |
| memcpy(psVtx, pSrc->psVtx, nVtxNum * sizeof(*psVtx)); |
| memcpy(psEdgeDelta, pSrc->psEdgeDelta, nEdgNum * sizeof(*psEdgeDelta)); |
| memcpy(psTri, pSrc->psTri, nTriNum * sizeof(*psTri)); |
| } |
| |
| /**************************************************************************** |
| @Function Clear |
| @Description Sets the value of the number of vertices, edges and triangles |
| to zero. |
| ****************************************************************************/ |
| void CBlockOption::Clear() |
| { |
| nVtxNum = 0; |
| nEdgNum = 0; |
| nTriNum = 0; |
| } |
| |
| /**************************************************************************** |
| @Function Output |
| @Output pwOut Index output |
| @Output pnVtxCnt Vertex count |
| @Output pnTriCnt Triangle count |
| @Modified pOb Pointer to an object |
| @Description Outputs key information about the instance of CBlockOption |
| ****************************************************************************/ |
| void CBlockOption::Output( |
| PVRTGEOMETRY_IDX * const pwOut, |
| int * const pnVtxCnt, |
| int * const pnTriCnt, |
| const CObject * const pOb) const |
| { |
| STri *pTri; |
| int i, j; |
| |
| for(i = 0; i < nTriNum; ++i) { |
| pTri = psTri[i]; |
| |
| _ASSERT(!pTri->bUsed); |
| |
| for(j = 0; j < 3; ++j) { |
| _ASSERT(pOb->m_pVtx[pTri->pwIdx[j]].nTriNumFree > 0); |
| _ASSERT(pTri->psEdg[j]->nTriNumFree > 0); |
| |
| --pOb->m_pVtx[pTri->pwIdx[j]].nTriNumFree; |
| --pTri->psEdg[j]->nTriNumFree; |
| |
| _ASSERT(pOb->m_pVtx[pTri->pwIdx[j]].nTriNumFree >= 0); |
| _ASSERT(pTri->psEdg[j]->nTriNumFree >= 0); |
| } |
| |
| pTri->bUsed = true; |
| |
| // Copy indices into output |
| memcpy(&pwOut[3*i], pTri->pwIdx, 3 * sizeof(*pTri->pwIdx)); |
| } |
| |
| *pnVtxCnt = nVtxNum; |
| *pnTriCnt = nTriNum; |
| } |
| |
| /**************************************************************************** |
| @Function UsingVertex |
| @Input pVtx Vertex to compare |
| @Return bool True on success |
| @Description Returns true if the supplied vertex is already being used |
| in the block option. |
| ****************************************************************************/ |
| bool CBlockOption::UsingVertex( |
| const SVtx * const pVtx) const |
| { |
| int i; |
| |
| i = nVtxNum; |
| while(i) { |
| --i; |
| |
| if(psVtx[i] == pVtx) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /**************************************************************************** |
| @Function Contains |
| @Input pVtx Triangle to compare |
| @Return bool True on success |
| @Description Returns true if the supplied triangle is already being used |
| in the block option. |
| ****************************************************************************/ |
| bool CBlockOption::Contains(const STri * const pTri) const |
| { |
| int i; |
| |
| i = nTriNum; |
| while(i) { |
| --i; |
| |
| if(psTri[i] == pTri) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /**************************************************************************** |
| @Function IsEmpty |
| @Return bool True if the block option is empty |
| @Description Returns true if the block option is empty. |
| ****************************************************************************/ |
| bool CBlockOption::IsEmpty() const |
| { |
| return !(nVtxNum + nEdgNum + nTriNum); |
| } |
| |
| /**************************************************************************** |
| @Function IsFull |
| @Return bool True if the block option is full |
| @Description Returns true if the block option is full. |
| ****************************************************************************/ |
| bool CBlockOption::IsFull() const |
| { |
| return (m_nVtxLimit - nVtxNum) < 3 || nTriNum == m_nTriLimit; |
| } |
| |
| /**************************************************************************** |
| @Function AddVertex |
| @Input pVtx Vertex to add |
| @Description Providing the current number of vertices is less than the |
| maximum, the input vertex is added to the end of the array. |
| ****************************************************************************/ |
| void CBlockOption::AddVertex(SVtx * const pVtx) |
| { |
| _ASSERT(nVtxNum < m_nVtxLimit); |
| psVtx[nVtxNum++] = pVtx; |
| } |
| |
| /**************************************************************************** |
| @Function AddVertexCheckDup |
| @Input pVtx Vertex to add |
| @Description Checks that the input vertex is not already contained in the |
| vertex array. If it is new, it is added to the array. |
| ****************************************************************************/ |
| void CBlockOption::AddVertexCheckDup(SVtx * const pVtx) |
| { |
| int i; |
| |
| for(i = 0; i < nVtxNum; ++i) |
| if(psVtx[i] == pVtx) |
| return; |
| |
| AddVertex(pVtx); |
| } |
| |
| /**************************************************************************** |
| @Function AddTriangleCheckDup |
| @Input pTri Triangle to add |
| @Description Checks that the input triangle is not already contained in the |
| triangle array. If it is new, it is added to the array. |
| ****************************************************************************/ |
| void CBlockOption::AddTriangleCheckDup(STri * const pTri) |
| { |
| int i; |
| |
| for(i = 0; i < nTriNum; ++i) |
| if(psTri[i] == pTri) |
| return; |
| |
| _ASSERT(nTriNum < m_nTriLimit); |
| psTri[nTriNum++] = pTri; |
| } |
| |
| /**************************************************************************** |
| @Function AddEdgeCheckDup |
| @Input pEdg Edge to add |
| @Description Checks that the input edge is not already contained in the |
| edge array. If it is new, it is added to the array. |
| ****************************************************************************/ |
| void CBlockOption::AddEdgeCheckDup(const SEdg * const pEdg) |
| { |
| int i; |
| |
| for(i = 0; i < nEdgNum; ++i) { |
| if(psEdgeDelta[i].pEdg == pEdg) { |
| ++psEdgeDelta[i].nRefCnt; |
| return; |
| } |
| } |
| |
| _ASSERT(nEdgNum < 3*m_nTriLimit); |
| psEdgeDelta[nEdgNum].pEdg = pEdg; |
| psEdgeDelta[nEdgNum].nRefCnt = 1; |
| ++nEdgNum; |
| } |
| |
| /**************************************************************************** |
| @Function AddTriangle |
| @Input pTri Triangle to add |
| @Description Providing the current number of triangles is less than the |
| maximum, the input triangle is added to the end of the array. |
| Once this has been done, the array of edges is updated. |
| ****************************************************************************/ |
| // TODO: if this is only used to add fresh triangles, all edges must be added |
| void CBlockOption::AddTriangle(STri * const pTri) |
| { |
| int i; |
| |
| _ASSERT(nTriNum < m_nTriLimit); |
| psTri[nTriNum++] = pTri; |
| |
| // Keep a count of edges and the number of tris which share them |
| for(i = 0; i < 3; ++i) |
| AddEdgeCheckDup(pTri->psEdg[i]); |
| } |
| |
| /**************************************************************************** |
| @Function AddOneTriangle |
| @Input pTri Triangle to add |
| @Input pOb Object to copy vertices from |
| @Description Calls the AddTriangle function. |
| Once this has been done, the array of vertices is updated. |
| ****************************************************************************/ |
| // TODO: if this is only called to add a fresh start triangle, all vertices must be added |
| void CBlockOption::AddOneTriangle( |
| STri * const pTri, |
| const CObject * const pOb) |
| { |
| int i; |
| |
| // Add the triangle to the block |
| AddTriangle(pTri); |
| |
| // Add the vertices to the block |
| for(i = 0; i < 3; ++i) |
| AddVertexCheckDup(&pOb->m_pVtx[pTri->pwIdx[i]]); |
| } |
| |
| /**************************************************************************** |
| @Function GetClosedEdgeDelta |
| @Return int The delta value of closed edges |
| @Description This method returns a value that represents the average state of |
| the edges. If the value is greater than zero, the majority of |
| edges are closed. If the value is less than zero, the majority |
| of edges are open. |
| ****************************************************************************/ |
| int CBlockOption::GetClosedEdgeDelta() const |
| { |
| int i, nDelta; |
| |
| nDelta = 0; |
| for(i = 0; i < nEdgNum; ++i) { |
| _ASSERT(psEdgeDelta[i].pEdg->nTriNumFree >= psEdgeDelta[i].nRefCnt); |
| |
| // Check how many tris will use the edge if these are taken away |
| switch(psEdgeDelta[i].pEdg->nTriNumFree - psEdgeDelta[i].nRefCnt) { |
| case 0: |
| // If the edge was open, and is now closed, that's good |
| if(psEdgeDelta[i].pEdg->nTriNumFree == 1) |
| ++nDelta; |
| break; |
| case 1: |
| // if the edge is now open, that's bad |
| --nDelta; |
| break; |
| } |
| } |
| |
| return nDelta; |
| } |
| |
| /**************************************************************************** |
| @Function IsBetterThan |
| @Input pCmp The block option to compare with |
| @Return bool True if the current block option is best |
| @Description Returns true if the current block option is better than the |
| block option that has been passed in. Otherwise, it returns false. |
| ****************************************************************************/ |
| bool CBlockOption::IsBetterThan(const CBlockOption * const pCmp) const |
| { |
| float fWorth0, fWorth1; |
| int nClosed0, nClosed1; |
| |
| // Check "worth" - TrisAdded/VtxAdded |
| fWorth0 = (float)nTriNum / (float)nVtxNum; |
| fWorth1 = (float)pCmp->nTriNum / (float)pCmp->nVtxNum; |
| |
| nClosed0 = GetClosedEdgeDelta(); |
| nClosed1 = pCmp->GetClosedEdgeDelta(); |
| |
| if(fabsf(fWorth0 - fWorth1) > 0.1f) { |
| return fWorth0 > fWorth1; |
| } else if(nClosed0 != nClosed1) { |
| return nClosed0 > nClosed1; |
| } else { |
| return nTriNum > pCmp->nTriNum; |
| } |
| } |
| |
| /**************************************************************************** |
| @Function Add |
| @Input pSrc The block option to add |
| @Input pOb Object to use vertices from |
| @Description Add's the input vertex and triangle data to the current block option |
| ****************************************************************************/ |
| void CBlockOption::Add( |
| const CBlockOption * const pSrc, |
| const CObject * const pOb) |
| { |
| PVRT_UNREFERENCED_PARAMETER(pOb); |
| |
| int i; |
| |
| // Add vertices from job to block |
| for(i = 0; i < pSrc->nVtxNum; ++i) |
| AddVertexCheckDup(pSrc->psVtx[i]); |
| |
| // Add triangles from job to block |
| for(i = 0; i < pSrc->nTriNum; ++i) |
| AddTriangle(pSrc->psTri[i]); |
| } |
| |
| /**************************************************************************** |
| @Function Add |
| @Input pMesh The mesh to add |
| @Description Add's the input mesh to the current block option |
| ****************************************************************************/ |
| void CBlockOption::Add( |
| const SMesh * const pMesh) |
| { |
| int i, j; |
| SVtx *pVtx; |
| |
| for(i = 0; i < pMesh->nVtxNum; ++i) { |
| pVtx = pMesh->ppVtx[i]; |
| |
| AddVertexCheckDup(pVtx); |
| |
| for(j = 0; j < pVtx->nTriNumTot; ++j) { |
| if(!pVtx->psTri[j]->bUsed) |
| AddTriangleCheckDup(pVtx->psTri[j]); |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| @Function CBlock |
| @Description Default constructor |
| ****************************************************************************/ |
| CBlock::CBlock( |
| const int nBufferVtxLimit, |
| const int nBufferTriLimit) |
| { |
| m_nVtxLimit = nBufferVtxLimit; |
| m_nTriLimit = nBufferTriLimit; |
| |
| m_sOpt.Init(m_nVtxLimit, m_nTriLimit); |
| m_sOptBest.Init(m_nVtxLimit, m_nTriLimit); |
| |
| // Intialise "job" blocks |
| m_sJob0.Init(3, m_nTriLimit); |
| m_sJob1.Init(3, m_nTriLimit); |
| } |
| |
| /**************************************************************************** |
| @Function Clear |
| @Description Clears the current and best block options |
| ****************************************************************************/ |
| void CBlock::Clear() |
| { |
| m_sOpt.Clear(); |
| m_sOptBest.Clear(); |
| } |
| |
| /**************************************************************************** |
| @Function Output |
| @Output pwOut Index output |
| @Output pnVtxCnt Vertex count |
| @Output pnTriCnt Triangle count |
| @Modified pOb Pointer to an object |
| @Description Outputs key information about the instance of CBlockOption |
| ****************************************************************************/ |
| void CBlock::Output( |
| PVRTGEOMETRY_IDX * const pwOut, |
| int * const pnVtxCnt, |
| int * const pnTriCnt, |
| const CObject * const pOb) const |
| { |
| m_sOptBest.Output(pwOut, pnVtxCnt, pnTriCnt, pOb); |
| } |
| |
| /**************************************************************************** |
| @Function AddBestTrianglesAppraise |
| @Modified pJob The block object to alter |
| @Input pOb The object |
| @Input pTriAppraise The triangle to appraise |
| @Return bool |
| @Description Uses the input object and triangle to create a new block option. |
| ****************************************************************************/ |
| bool CBlock::AddBestTrianglesAppraise( |
| CBlockOption * const pJob, |
| const CObject * const pOb, |
| const STri * const pTriAppraise) |
| { |
| SVtx *pVtx; |
| STri *pTri; |
| int i, j; |
| |
| pJob->Clear(); |
| |
| // Add vertices |
| for(i = 0; i < 3; ++i) { |
| pVtx = &pOb->m_pVtx[pTriAppraise->pwIdx[i]]; |
| if(!m_sOpt.UsingVertex(pVtx)) |
| pJob->AddVertex(pVtx); |
| } |
| |
| if(pJob->nVtxNum > (m_nVtxLimit-m_sOpt.nVtxNum)) |
| return false; |
| |
| // Add triangles referenced by each vertex |
| for(i = 0; i < 3; ++i) { |
| pVtx = &pOb->m_pVtx[pTriAppraise->pwIdx[i]]; |
| |
| _ASSERT(pVtx->nTriNumFree >= 1); |
| _ASSERT(pVtx->nTriNumFree <= pVtx->nTriNumTot); |
| |
| for(j = 0; j < pVtx->nTriNumTot; ++j) { |
| if(pJob->nTriNum >= (m_nTriLimit-m_sOpt.nTriNum)) |
| break; |
| |
| pTri = pVtx->psTri[j]; |
| |
| // Don't count the same triangle twice! |
| if(pTri->bUsed || m_sOpt.Contains(pTri) || pJob->Contains(pTri)) |
| continue; |
| |
| // If all the triangle's vertices are or will be in the block, then increase nTri |
| if( |
| ( |
| pTri->pwIdx[0] == pTriAppraise->pwIdx[0] || |
| pTri->pwIdx[0] == pTriAppraise->pwIdx[1] || |
| pTri->pwIdx[0] == pTriAppraise->pwIdx[2] || |
| m_sOpt.UsingVertex(&pOb->m_pVtx[pTri->pwIdx[0]]) |
| ) && ( |
| pTri->pwIdx[1] == pTriAppraise->pwIdx[0] || |
| pTri->pwIdx[1] == pTriAppraise->pwIdx[1] || |
| pTri->pwIdx[1] == pTriAppraise->pwIdx[2] || |
| m_sOpt.UsingVertex(&pOb->m_pVtx[pTri->pwIdx[1]]) |
| ) && ( |
| pTri->pwIdx[2] == pTriAppraise->pwIdx[0] || |
| pTri->pwIdx[2] == pTriAppraise->pwIdx[1] || |
| pTri->pwIdx[2] == pTriAppraise->pwIdx[2] || |
| m_sOpt.UsingVertex(&pOb->m_pVtx[pTri->pwIdx[2]]) |
| ) |
| ) |
| { |
| pJob->AddTriangle(pTri); |
| } |
| } |
| } |
| |
| _ASSERT(pJob->nTriNum); |
| _ASSERT(pJob->nTriNum <= (m_nTriLimit-m_sOpt.nTriNum)); |
| |
| return true; |
| } |
| |
| /**************************************************************************** |
| @Function AddBestTriangles |
| @Input pOb The object |
| @Description Finds the best triangles and adds them to the current block option (m_sOpt) |
| ****************************************************************************/ |
| void CBlock::AddBestTriangles(CObject * const pOb) |
| { |
| int i, j; |
| const SVtx *pVtx; |
| STri *pTri; |
| CBlockOption *pJob, *pJobBest; |
| |
| pJob = &m_sJob0; |
| |
| do { |
| pJobBest = 0; |
| |
| for(i = 0; i < m_sOpt.nVtxNum; ++i) { |
| pVtx = m_sOpt.psVtx[i]; |
| |
| if(!pVtx->nTriNumFree) |
| continue; |
| |
| for(j = 0; j < pVtx->nTriNumTot; ++j) { |
| pTri = pVtx->psTri[j]; |
| |
| if(pTri->bUsed || m_sOpt.Contains(pTri)) |
| continue; |
| |
| // Find out how many triangles and vertices this tri adds |
| if(!AddBestTrianglesAppraise(pJob, pOb, pTri)) |
| continue; |
| |
| if(!pJobBest || pJob->IsBetterThan(pJobBest)) { |
| pJobBest = pJob; |
| pJob = (pJob == &m_sJob0 ? &m_sJob1 : &m_sJob0); |
| } |
| } |
| } |
| |
| if(pJobBest) { |
| m_sOpt.Add(pJobBest, pOb); |
| } |
| } while(pJobBest && m_nTriLimit != m_sOpt.nTriNum); |
| } |
| |
| /**************************************************************************** |
| @Function FillFrom |
| @Input pMesh Mesh to fill with |
| @Input pVtx Vertex to fill with |
| @Input pOb Object to fill with |
| @Return bool Returns true if the current block option isn't full |
| @Description Returns TRUE if Fill() needs to be called again - i.e. blockOption is already filled |
| ****************************************************************************/ |
| bool CBlock::FillFrom( |
| SMesh * const pMesh, |
| SVtx * const pVtx, |
| CObject * const pOb) |
| { |
| // Let's try starting from this vertex then |
| _ASSERT(pVtx->nTriNumFree); |
| m_sOpt.Clear(); |
| m_sOpt.AddVertex(pVtx); |
| AddBestTriangles(pOb); |
| |
| if(m_sOpt.IsFull()) { |
| if(m_sOptBest.IsEmpty() || m_sOpt.IsBetterThan(&m_sOptBest)) |
| m_sOptBest.Copy(&m_sOpt); |
| return false; |
| } |
| else |
| { |
| _ASSERT(!m_sOpt.IsEmpty()); |
| pOb->SplitMesh(pMesh, m_sOpt.nVtxNum, m_sOpt.psVtx); // Split the sub-mesh into its own mesh |
| return true; |
| } |
| } |
| |
| /**************************************************************************** |
| @Function Fill |
| @Input pOb Object to fill with |
| @Return int -1 if the block if the best option is already full |
| @Description Note: Ask Aaron |
| ****************************************************************************/ |
| int CBlock::Fill( |
| CObject * const pOb) |
| { |
| SVtx *pVtx; |
| int i; |
| SMesh *pMesh; |
| |
| /* |
| Build blocks from the large meshes |
| */ |
| if(!pOb->m_vMeshLg.empty()) { |
| pMesh = &pOb->m_vMeshLg.back(); |
| |
| // _RPT1(_CRT_WARN, "Fill() using large with %d vtx\n", pMesh->nVtxNum); |
| |
| // Find the vertex with the fewest unused triangles |
| for(i = 0; i < pMesh->nVtxNum; ++i) { |
| pVtx = pMesh->ppVtx[i]; |
| |
| if(pVtx->nTriNumFree == 1) { |
| if(FillFrom(pMesh, pVtx, pOb)) |
| return Fill(pOb); |
| } |
| } |
| |
| if(m_sOptBest.IsEmpty()) { |
| // Just start from any old vertex |
| for(i = 0; i < pMesh->nVtxNum; ++i) { |
| pVtx = pMesh->ppVtx[i]; |
| |
| if(pVtx->nTriNumFree) { |
| if(FillFrom(pMesh, pVtx, pOb)) |
| return Fill(pOb); |
| break; |
| } |
| } |
| |
| if(m_sOptBest.IsEmpty()) { |
| pOb->m_vMeshLg.pop_back(); // Delete the mesh from the list |
| return Fill(pOb); |
| } |
| } |
| |
| if(m_sOptBest.IsFull()) |
| return -1; |
| } |
| |
| /* |
| Match together the small meshes into blocks |
| */ |
| _ASSERT(m_sOptBest.IsEmpty()); |
| i = m_nVtxLimit - m_sOptBest.nVtxNum - 3; |
| |
| // _RPT0(_CRT_WARN, "Fill() grouping small "); |
| |
| // Starting with the largest meshes, lump them into this block |
| while(i >= 0 && (m_nVtxLimit - m_sOptBest.nVtxNum) >= 3) { |
| if(pOb->m_pvMesh[i].empty()) { |
| --i; |
| continue; |
| } |
| |
| pMesh = &pOb->m_pvMesh[i].back(); |
| m_sOptBest.Add(pMesh); |
| // _RPT1(_CRT_WARN, "+%d", pMesh->nVtxNum); |
| pOb->m_pvMesh[i].pop_back(); |
| i = PVRT_MIN(i, m_nVtxLimit - m_sOptBest.nVtxNum - 3); |
| } |
| |
| // If there's any space left in this block (and clearly there are no blocks |
| // just the right size to fit) then take SOME of the largest block available. |
| if(!m_sOptBest.IsFull()) { |
| m_sOpt.Copy(&m_sOptBest); |
| |
| // Note: This loop purposely does not check m_pvMesh[0] - any block |
| // which is looking to grab more geometry would have already sucked |
| // up those meshes |
| for(i = (m_nVtxLimit-3); i; --i) { |
| if(!pOb->m_pvMesh[i].empty()) { |
| pMesh = &pOb->m_pvMesh[i].back(); |
| |
| _ASSERT(pMesh->ppVtx[0]->nTriNumFree); |
| _ASSERT(!m_sOpt.UsingVertex(pMesh->ppVtx[0])); |
| |
| m_sOpt.AddVertex(pMesh->ppVtx[0]); |
| // _RPT1(_CRT_WARN, "(+%d)\n", pMesh->nVtxNum); |
| AddBestTriangles(pOb); |
| |
| m_sOptBest.Copy(&m_sOpt); |
| _ASSERT(m_sOptBest.IsFull()); |
| return i; |
| } |
| } |
| } |
| // _RPT0(_CRT_WARN, "\n"); |
| return -1; |
| } |
| |
| /**************************************************************************** |
| ** Local functions |
| ****************************************************************************/ |
| /**************************************************************************** |
| @Function Fill |
| @Input pVtxData Vertex data |
| @Input pwIdx Index array |
| @Input nStride Stride |
| @Input nVertNum Number of vertices |
| @Input nIdxNum Number of indices |
| @Description Sorts the vertices. |
| ****************************************************************************/ |
| static void SortVertices( |
| void * const pVtxData, |
| PVRTGEOMETRY_IDX * const pwIdx, |
| const int nStride, |
| const int nVertNum, |
| const int nIdxNum) |
| { |
| void *pVtxNew; |
| int *pnVtxDest; |
| int i; |
| PVRTGEOMETRY_IDX wNext; |
| |
| pVtxNew = malloc(nVertNum * nStride); |
| _ASSERT(pVtxNew); |
| |
| pnVtxDest = (int*)malloc(nVertNum * sizeof(*pnVtxDest)); |
| _ASSERT(pnVtxDest); |
| |
| wNext = 0; |
| |
| // Default all indices to an invalid number |
| for(i = 0; i < nVertNum; ++i) |
| pnVtxDest[i] = -1; |
| |
| // Let's get on with it then. |
| for(i = 0; i < nIdxNum; ++i) { |
| if(pnVtxDest[pwIdx[i]] == -1) { |
| _ASSERT((int) wNext < nVertNum); |
| memcpy((char*)pVtxNew+(wNext*nStride), (char*)pVtxData+(pwIdx[i]*nStride), nStride); |
| pnVtxDest[pwIdx[i]] = wNext++; |
| } |
| |
| pwIdx[i] = pnVtxDest[pwIdx[i]]; |
| } |
| |
| /* |
| This assert will fail if sorting a sub-set of the triangles (e.g. if |
| the mesh is bone-batched). |
| |
| In that situation vertex sorting should be performed only once after |
| all the tri sorting is finished, not per tri-sort. |
| */ |
| _ASSERT((int) wNext == nVertNum); |
| memcpy(pVtxData, pVtxNew, nVertNum * nStride); |
| |
| FREE(pnVtxDest); |
| FREE(pVtxNew); |
| } |
| |
| /**************************************************************************** |
| ** Functions |
| ****************************************************************************/ |
| /*!*************************************************************************** |
| @Function PVRTGeometrySort |
| @Modified pVtxData Pointer to array of vertices |
| @Modified pwIdx Pointer to array of indices |
| @Input nStride Size of a vertex (in bytes) |
| @Input nVertNum Number of vertices. Length of pVtxData array |
| @Input nTriNum Number of triangles. Length of pwIdx array is 3* this |
| @Input nBufferVtxLimit Number of vertices that can be stored in a buffer |
| @Input nBufferTriLimit Number of triangles that can be stored in a buffer |
| @Input dwFlags PVRTGEOMETRY_SORT_* flags |
| @Description Triangle sorter |
| *****************************************************************************/ |
| void PVRTGeometrySort( |
| void * const pVtxData, |
| PVRTGEOMETRY_IDX * const pwIdx, |
| const int nStride, |
| const int nVertNum, |
| const int nTriNum, |
| const int nBufferVtxLimit, |
| const int nBufferTriLimit, |
| const unsigned int dwFlags) |
| { |
| CObject sOb(pwIdx, nVertNum, nTriNum, nBufferVtxLimit, nBufferTriLimit); |
| CBlock sBlock(nBufferVtxLimit, nBufferTriLimit); |
| PVRTGEOMETRY_IDX *pwIdxOut; |
| int nTriCnt, nVtxCnt; |
| int nOutTriCnt, nOutVtxCnt, nOutBlockCnt; |
| int nMeshToResize; |
| #ifdef PVRTRISORT_ENABLE_VERIFY_RESULTS |
| int i; |
| int pnBlockTriCnt[PVRVGPBLOCKTEST_MAX_BLOCKS]; |
| SVGPModel sVGPMdlBefore; |
| SVGPModel sVGPMdlAfter; |
| #endif |
| |
| if(dwFlags & PVRTGEOMETRY_SORT_VERTEXCACHE) { |
| #ifdef PVRTRISORT_ENABLE_VERIFY_RESULTS |
| VGP590Test(&sVGPMdlBefore, pwIdx, nTriNum); |
| _RPT4(_CRT_WARN, "OptimiseTriListPVR() Before: Tri: %d, Vtx: %d, vtx/tri=%f Blocks=%d\n", nTriNum, sVGPMdlBefore.nVtxCnt, (float)sVGPMdlBefore.nVtxCnt / (float)nTriNum, sVGPMdlBefore.nBlockCnt); |
| #endif |
| |
| pwIdxOut = (PVRTGEOMETRY_IDX*)malloc(nTriNum * 3 * sizeof(*pwIdxOut)); |
| _ASSERT(pwIdxOut); |
| |
| // Sort geometry into blocks |
| nOutTriCnt = 0; |
| nOutVtxCnt = 0; |
| nOutBlockCnt = 0; |
| do { |
| // Clear & fill the block |
| sBlock.Clear(); |
| nMeshToResize = sBlock.Fill(&sOb); |
| |
| // Copy indices into output |
| sBlock.Output(&pwIdxOut[3*nOutTriCnt], &nVtxCnt, &nTriCnt, &sOb); |
| sOb.m_nTriNumFree -= nTriCnt; |
| nOutTriCnt += nTriCnt; |
| |
| if(nMeshToResize >= 0) { |
| SMesh *pMesh; |
| _ASSERT(nMeshToResize <= (nBufferVtxLimit-3)); |
| pMesh = &sOb.m_pvMesh[nMeshToResize].back(); |
| sOb.ResizeMesh(pMesh->nVtxNum, pMesh->ppVtx); |
| sOb.m_pvMesh[nMeshToResize].pop_back(); |
| } |
| |
| _ASSERT(nVtxCnt <= nBufferVtxLimit); |
| _ASSERT(nTriCnt <= nBufferTriLimit); |
| |
| #ifdef PVRTRISORT_ENABLE_VERIFY_RESULTS |
| _ASSERT(nOutBlockCnt < PVRVGPBLOCKTEST_MAX_BLOCKS); |
| pnBlockTriCnt[nOutBlockCnt] = nTriCnt; |
| #endif |
| nOutVtxCnt += nVtxCnt; |
| nOutBlockCnt++; |
| |
| // _RPT4(_CRT_WARN, "%d/%d tris (+%d), %d blocks\n", nOutTriCnt, nTriNum, nTriCnt, nOutBlockCnt); |
| |
| _ASSERT(nTriCnt == nBufferTriLimit || (nBufferVtxLimit - nVtxCnt) < 3 || nOutTriCnt == nTriNum); |
| } while(nOutTriCnt < nTriNum); |
| |
| _ASSERT(nOutTriCnt == nTriNum); |
| // The following will fail if optimising a subset of the mesh (e.g. a bone batching) |
| //_ASSERT(nOutVtxCnt >= nVertNum); |
| |
| // Done! |
| memcpy(pwIdx, pwIdxOut, nTriNum * 3 * sizeof(*pwIdx)); |
| FREE(pwIdxOut); |
| |
| _RPT3(_CRT_WARN, "OptimiseTriListPVR() In: Tri: %d, Vtx: %d, vtx/tri=%f\n", nTriNum, nVertNum, (float)nVertNum / (float)nTriNum); |
| _RPT4(_CRT_WARN, "OptimiseTriListPVR() HW: Tri: %d, Vtx: %d, vtx/tri=%f Blocks=%d\n", nOutTriCnt, nOutVtxCnt, (float)nOutVtxCnt / (float)nOutTriCnt, nOutBlockCnt); |
| |
| #ifdef PVRTRISORT_ENABLE_VERIFY_RESULTS |
| VGP590Test(&sVGPMdlAfter, pwIdx, nTriNum); |
| _RPT4(_CRT_WARN, "OptimiseTriListPVR() After : Tri: %d, Vtx: %d, vtx/tri=%f Blocks=%d\n", nTriNum, sVGPMdlAfter.nVtxCnt, (float)sVGPMdlAfter.nVtxCnt / (float)nTriNum, sVGPMdlAfter.nBlockCnt); |
| _ASSERTE(sVGPMdlAfter.nVtxCnt <= sVGPMdlBefore.nVtxCnt); |
| _ASSERTE(sVGPMdlAfter.nBlockCnt <= sVGPMdlBefore.nBlockCnt); |
| |
| for(i = 0; i < nOutBlockCnt; ++i) { |
| _ASSERT(pnBlockTriCnt[i] == sVGPMdlAfter.pnBlockTriCnt[i]); |
| } |
| #endif |
| } |
| |
| if(!(dwFlags & PVRTGEOMETRY_SORT_IGNOREVERTS)) { |
| // Re-order the vertices so maybe they're accessed in a more linear |
| // manner. Should cut page-breaks on the initial memory read of |
| // vertices. Affects both the order of vertices, and the values |
| // of indices, but the triangle order is unchanged. |
| SortVertices(pVtxData, pwIdx, nStride, nVertNum, nTriNum*3); |
| } |
| } |
| |
| /***************************************************************************** |
| End of file (PVRTGeometry.cpp) |
| *****************************************************************************/ |
| |