Add the ChameleonMan PowerVR sample.

BUG=14598298

Change-Id: Id6dcaebe793cf3d3a10af571348f461023d2a9d2
Reviewed-on: https://swiftshader-review.googlesource.com/1090
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/tests/third_party/PowerVR/Tools/PVRTGeometry.cpp b/tests/third_party/PowerVR/Tools/PVRTGeometry.cpp
new file mode 100644
index 0000000..8c0cabe
--- /dev/null
+++ b/tests/third_party/PowerVR/Tools/PVRTGeometry.cpp
@@ -0,0 +1,1500 @@
+/******************************************************************************
+
+ @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)
+*****************************************************************************/
+