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