• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /******************************************************************************
2 
3  @File         PVRTGeometry.cpp
4 
5  @Title        PVRTGeometry
6 
7  @Version
8 
9  @Copyright    Copyright (c) Imagination Technologies Limited.
10 
11  @Platform     Independant
12 
13  @Description  Code to affect triangle mesh geometry.
14 
15 ******************************************************************************/
16 
17 /*****************************************************************************
18   For each vertex with only one free triangle
19     Start collecting triangles from there
20 	  Add the triangle which gives the highest triangles/vertex number (extra tris usually come for free)
21 	  When full, test against current best
22 	    If markedly better tri/vtx, take new block
23 		If close-enough to prev tri/vtx, take block which closes the highest number of edges (and opens fewest)
24 	  If not quite full, goto 1 to continue filling block
25   If no block has been found, start at any free triangle and use resulting block
26   Copy block to output, empty it and goto 1.
27 *****************************************************************************/
28 
29 /****************************************************************************
30 ** Build options
31 ****************************************************************************/
32 #undef PVRTRISORT_ENABLE_VERIFY_RESULTS
33 
34 /****************************************************************************
35 ** Includes
36 ****************************************************************************/
37 #include <vector>
38 #include <math.h>
39 
40 #include "PVRTGeometry.h"
41 
42 #ifdef PVRTRISORT_ENABLE_VERIFY_RESULTS
43 #include "PvrVGPBlockTest.h"
44 #endif
45 
46 #include "PVRTGlobal.h"
47 #include "PVRTContext.h"
48 /****************************************************************************
49 ** Structures
50 ****************************************************************************/
51 
52 struct SVtx;
53 
54 /****************************************************************************
55 @Function 		SEdg
56 @Description	Information about an "edge" - the shared boundary between two triangles
57 ****************************************************************************/
58 struct SEdg {
59 	const SVtx	*psVtx[2];		// Identify the edge by the two vertices it joins
60 	int			nTriNumFree;	// Number of triangle using this edge
61 };
62 
63 /****************************************************************************
64 @Function 		STri
65 @Description	Information about a triangle
66 ****************************************************************************/
67 struct STri {
68 	const PVRTGEOMETRY_IDX	*pwIdx;			// Vertex indices forming this triangle
69 	SEdg					*psEdg[3];		// Pointer to the three triangle edges
70 	bool					bUsed;
71 };
72 
73 /****************************************************************************
74 @Function 	SVtx
75 @Description	Information about a vertex
76 ****************************************************************************/
77 struct SVtx {
78 	STri	**psTri;		// Allocated array of pointers to the triangles sharing this vertex
79 	int		nTriNumTot;		// Length of the above array
80 	int		nTriNumFree;	// Number of triangles unused in the above array
81 	SVtx	**ppMeshPos;	// Position in VtxByMesh list
82 };
83 
84 /****************************************************************************
85 @Function 		SMesh
86 @Description	Information about a mesh
87 ****************************************************************************/
88 struct SMesh {
89 	SVtx	**ppVtx;
90 	int		nVtxNum;
91 };
92 
93 /****************************************************************************
94 @Function 		CObject
95 @Description	Information about an object (i.e. collection of mesh's to form
96 				a single entity)
97 ****************************************************************************/
98 class CObject {
99 public:
100 	STri	*m_pTri;		// Array of all the triangles in the mesh
101 	SEdg	*m_pEdg;		// Array of all the edges in the mesh
102 	SVtx	*m_pVtx;		// Array of all the vertices in a mesh
103 
104 	int		m_nTriNumFree;
105 
106 	std::vector<SMesh> *m_pvMesh;
107 	std::vector<SMesh> m_vMeshLg;
108 
109 protected:
110 	int		m_nVtxTot;		// Total vertices in the object
111 	int		m_nEdgTot;		// Total edges in the object
112 	int		m_nTriTot;		// Total triangles in the object
113 
114 	int		m_nVtxLimit;	// Maximum number of vertices a block can contain
115 	int		m_nTriLimit;	// Maximum number of triangles a block can contain
116 
117 	SVtx	**m_ppVtxByMesh;
118 
119 public:
120 	CObject(
121 		const PVRTGEOMETRY_IDX	* const pwIdx,
122 		const int				nVtxTot,
123 		const int				nTriTot,
124 		const int				nVtxLimit,
125 		const int				nTriLimit);
126 
127 	~CObject();
128 
129 	int GetVertexCount() const;
130 	int GetTriangleCount() const;
131 		void SplitMesh(
132 		SMesh		* const pMesh,
133 		const int	nVtxNum,
134 		SVtx		** const ppVtx);
135 
136 	void ResizeMesh(
137 		const int	nVtxNum,
138 		SVtx		** const ppVtx);
139 
140 protected:
141 	SEdg *BuildEdgeList(
142 		const SVtx * const pVtx0,
143 		const SVtx * const pVtx1);
144 
145 	void CreateMeshList();
146 };
147 
148 /****************************************************************************
149 @Function 		CBlockOption
150 @Description	A possible group of polygons to use
151 ****************************************************************************/
152 struct CBlockOption {
153 protected:
154 	struct SEdgeDelta {
155 		const SEdg	*pEdg;
156 		int			nRefCnt;
157 	};
158 
159 public:
160 	int			nVtxNum;			// Number of vertices in the block
161 	int			nEdgNum;			// Number of edges in the block
162 	int			nTriNum;			// Number of triangles in the block
163 
164 	SVtx		**psVtx;			// Pointers to vertices
165 protected:
166 	SEdgeDelta	*psEdgeDelta;
167 	STri		**psTri;			// Pointers to triangles
168 
169 	int			m_nVtxLimit;		// Maximum number of vertices a block can contain
170 	int			m_nTriLimit;		// Maximum number of triangles a block can contain
171 
172 public:
173 	~CBlockOption();
174 
175 	void Init(
176 		const int	nVtxLimit,
177 		const int	nTriLimit);
178 	void Copy(const CBlockOption * const pSrc);
179 
180 	void Clear();
181 
182 	void Output(
183 		PVRTGEOMETRY_IDX	* const pwOut,
184 		int					* const pnVtxCnt,
185 		int					* const pnTriCnt,
186 		const CObject		* const pOb) const;
187 
188 	bool UsingVertex(const SVtx * const pVtx) const;
189 	bool Contains(const STri * const pTri) const;
190 
191 	bool IsEmpty() const;
192 	bool IsFull() const;
193 
194 	void AddVertex(SVtx * const pVtx);
195 	void AddVertexCheckDup(SVtx * const pVtx);
196 
197 	void AddTriangleCheckDup(STri * const pTri);
198 
199 	void AddEdgeCheckDup(const SEdg * const pEdg);
200 
201 	void AddTriangle(STri * const pTri);
202 
203 	void AddOneTriangle(
204 		STri		* const pTri,
205 		const CObject	* const pOb);
206 
207 	int GetClosedEdgeDelta() const;
208 	bool IsBetterThan(const CBlockOption * const pCmp) const;
209 
210 	void Add(
211 		const CBlockOption	* const pSrc,
212 		const CObject		* const pOb);
213 
214 	void Add(
215 		const SMesh		* const pMesh);
216 };
217 
218 /****************************************************************************
219 @Function 		CBlock
220 @Description	A model of a HW block (triangles and vertices)
221 ****************************************************************************/
222 class CBlock {
223 protected:
224 	CBlockOption	m_sOpt, m_sOptBest;
225 
226 	int				m_nVtxLimit;		// Maximum number of vertices a block can contain
227 	int				m_nTriLimit;		// Maximum number of triangles a block can contain
228 
229 	CBlockOption	m_sJob0, m_sJob1;	// Workspace: to find the best triangle to add
230 
231 public:
232 	CBlock(
233 		const int	nBufferVtxLimit,
234 		const int	nBufferTriLimit);
235 
236 	void Clear();
237 
238 	bool FillFrom(
239 		SMesh	* const pMesh,
240 		SVtx	* const pVtx,
241 		CObject	* const pOb);
242 
243 	int Fill(
244 		CObject	* const pOb);
245 
246 	void Output(
247 		PVRTGEOMETRY_IDX	* const pwOut,
248 		int					* const pnVtxCnt,
249 		int					* const pnTriCnt,
250 		const CObject		* const pOb) const;
251 
252 protected:
253 	bool AddBestTrianglesAppraise(
254 		CBlockOption	* const pJob,
255 		const CObject		* const pOb,
256 		const STri		* const pTriAppraise);
257 
258 	void AddBestTriangles(CObject * const pOb);
259 };
260 
261 /****************************************************************************
262 ** Local function prototypes
263 ****************************************************************************/
264 
265 /****************************************************************************
266 @Function		CObject
267 @Input			pwIdx			Array of indices
268 @Input			nVrxTot			Total number of vertices
269 @Input			nTriTot			Total number of triangles
270 @Input			nVtxLimit		Max number of vertices a block can contain
271 @Input			nTriLimit		Max number of triangles a block can contain
272 @Description	The class's constructor.
273 ****************************************************************************/
CObject(const PVRTGEOMETRY_IDX * const pwIdx,const int nVtxTot,const int nTriTot,const int nVtxLimit,const int nTriLimit)274 CObject::CObject(
275 	const PVRTGEOMETRY_IDX	* const pwIdx,
276 	const int				nVtxTot,
277 	const int				nTriTot,
278 	const int				nVtxLimit,
279 	const int				nTriLimit)
280 {
281 	int		i;
282 	SVtx	*pVtx0, *pVtx1, *pVtx2;
283 
284 	m_nVtxLimit		= nVtxLimit;
285 	m_nTriLimit		= nTriLimit;
286 
287 	m_pvMesh = new std::vector<SMesh>[nVtxLimit-2];
288 	_ASSERT(m_pvMesh);
289 
290 	m_ppVtxByMesh = (SVtx**)calloc(nVtxTot, sizeof(*m_ppVtxByMesh));
291 	_ASSERT(m_ppVtxByMesh);
292 
293 	m_nVtxTot = nVtxTot;
294 	m_nEdgTot = 0;
295 	m_nTriTot = nTriTot;
296 
297 	m_nTriNumFree = m_nTriTot;
298 
299 	m_pTri = (STri*)calloc(nTriTot, sizeof(*m_pTri));
300 	_ASSERT(m_pTri);
301 
302 	m_pEdg = (SEdg*)calloc(nTriTot*3, sizeof(*m_pEdg));	// Allocate the maximum possible number of edges, though it should be far fewer than this
303 	_ASSERT(m_pEdg);
304 
305 	m_pVtx = (SVtx*)calloc(nVtxTot, sizeof(*m_pVtx));
306 	_ASSERT(m_pVtx);
307 
308 	// Run through triangles...
309 	for(i = 0; i < nTriTot; ++i) {
310 		pVtx0 = &m_pVtx[pwIdx[3*i+0]];
311 		pVtx1 = &m_pVtx[pwIdx[3*i+1]];
312 		pVtx2 = &m_pVtx[pwIdx[3*i+2]];
313 
314 		// Mark each vertex for the number of times it's referenced
315 		++pVtx0->nTriNumFree;
316 		++pVtx1->nTriNumFree;
317 		++pVtx2->nTriNumFree;
318 
319 		// Build the edge list
320 		m_pTri[i].psEdg[0] = BuildEdgeList(pVtx0, pVtx1);
321 		m_pTri[i].psEdg[1] = BuildEdgeList(pVtx1, pVtx2);
322 		m_pTri[i].psEdg[2] = BuildEdgeList(pVtx2, pVtx0);
323 	}
324 
325 	// Run through vertices, creating enough space for pointers to each triangle using this vertex
326 	for(i = 0; i < nVtxTot; ++i)
327 		m_pVtx[i].psTri = (STri**)calloc(m_pVtx[i].nTriNumFree, sizeof(*m_pVtx[i].psTri));
328 
329 	// Run through triangles, marking each vertex used with a pointer to this tri
330 	for(i = 0; i < nTriTot; ++i) {
331 		pVtx0 = &m_pVtx[pwIdx[3*i+0]];
332 		pVtx1 = &m_pVtx[pwIdx[3*i+1]];
333 		pVtx2 = &m_pVtx[pwIdx[3*i+2]];
334 
335 		pVtx0->psTri[pVtx0->nTriNumTot++] = &m_pTri[i];
336 		pVtx1->psTri[pVtx1->nTriNumTot++] = &m_pTri[i];
337 		pVtx2->psTri[pVtx2->nTriNumTot++] = &m_pTri[i];
338 
339 		// Give each triangle a pointer to its indices
340 		m_pTri[i].pwIdx = &pwIdx[3*i];
341 	}
342 
343 #ifdef _DEBUG
344 	for(i = 0; i < nVtxTot; ++i) {
345 		_ASSERTE(m_pVtx[i].nTriNumFree == m_pVtx[i].nTriNumTot);
346 	}
347 #endif
348 
349 	CreateMeshList();
350 }
351 
352 /****************************************************************************
353 @Function 		~CObject
354 @Description	Destructor
355 ****************************************************************************/
~CObject()356 CObject::~CObject()
357 {
358 	_ASSERT(m_nTriNumFree == 0);
359 
360 	while(m_nVtxTot) {
361 		--m_nVtxTot;
362 		FREE(m_pVtx[m_nVtxTot].psTri);
363 		_ASSERTE(m_pVtx[m_nVtxTot].nTriNumFree == 0);
364 		_ASSERTE(m_pVtx[m_nVtxTot].ppMeshPos);
365 	}
366 
367 #ifdef _DEBUG
368 	while(m_nEdgTot) {
369 		--m_nEdgTot;
370 		_ASSERTE(m_pEdg[m_nEdgTot].nTriNumFree == 0);
371 	}
372 	while(m_nTriTot) {
373 		--m_nTriTot;
374 		_ASSERTE(m_pTri[m_nTriTot].bUsed);
375 	}
376 #endif
377 
378 	FREE(m_pTri);
379 	FREE(m_pEdg);
380 	FREE(m_pVtx);
381 
382 	delete [] m_pvMesh;
383 	FREE(m_ppVtxByMesh);
384 }
385 
386 /****************************************************************************
387 @Function 		GetVertexCount
388 @Return			int
389 @Description	Return the vertex count
390 ****************************************************************************/
GetVertexCount() const391 int CObject::GetVertexCount() const
392 {
393 	return m_nVtxTot;
394 }
395 
396 /****************************************************************************
397 @Function 		GetTriangleCount
398 @Return			int
399 @Description	Return the triangle count
400 ****************************************************************************/
GetTriangleCount() const401 int CObject::GetTriangleCount() const
402 {
403 	return m_nTriTot;
404 }
405 
406 /****************************************************************************
407 @Function 		BuildEdgeList
408 @Input			pVtx0			Edge 0
409 @Input			pVtx1			Edge 1
410 @Return			SEdg*
411 @Description	If the vertices that have been passed in are already used by an edge,
412 				the number of triangles sharing the edge is increased by one and a
413 				pointer to the edge is returned. If the edge is not already in the
414 				list, the edge is added to the list.
415 ****************************************************************************/
BuildEdgeList(const SVtx * const pVtx0,const SVtx * const pVtx1)416 SEdg *CObject::BuildEdgeList(
417 	const SVtx * const pVtx0,
418 	const SVtx * const pVtx1)
419 {
420 	SEdg		*pEdg;
421 	const SVtx	*pVtxL, *pVtxH;
422 	int			i;
423 
424 	pVtxL = pVtx0 < pVtx1 ? pVtx0 : pVtx1;
425 	pVtxH = pVtx0 > pVtx1 ? pVtx0 : pVtx1;
426 
427 	// Do nothing if the edge already exists
428 	i = m_nEdgTot;
429 	while(i) {
430 		--i;
431 
432 		pEdg = &m_pEdg[i];
433 		if(pEdg->psVtx[0] == pVtxL && pEdg->psVtx[1] == pVtxH)
434 		{
435 			++pEdg->nTriNumFree;
436 			return pEdg;
437 		}
438 	}
439 
440 	// Add the new edge
441 	_ASSERT(m_nEdgTot < m_nTriTot*3);
442 	pEdg				= &m_pEdg[m_nEdgTot++];
443 	pEdg->psVtx[0]		= pVtxL;
444 	pEdg->psVtx[1]		= pVtxH;
445 	pEdg->nTriNumFree	= 1;
446 
447 	return pEdg;
448 }
449 
450 /****************************************************************************
451 @Function 		CreateMeshList
452 @Description	Creates the mesh list
453 ****************************************************************************/
CreateMeshList()454 void CObject::CreateMeshList()
455 {
456 	SVtx	**ppR, **ppW, *pVtx;
457 	STri	*pTri;
458 	int		i, j, k;
459 	SMesh	sMesh;
460 	int		nMeshCnt;
461 
462 	nMeshCnt = 0;
463 
464 	ppR = m_ppVtxByMesh;
465 	ppW = m_ppVtxByMesh;
466 
467 	for(i = 0; i < m_nVtxTot; ++i) {
468 		pVtx = &m_pVtx[i];
469 
470 		if(pVtx->ppMeshPos) {
471 			_ASSERT(pVtx->ppMeshPos < ppW);
472 			continue;
473 		}
474 
475 		++nMeshCnt;
476 		sMesh.ppVtx = ppW;
477 
478 		*ppW			= pVtx;
479 		pVtx->ppMeshPos	= ppW;
480 		++ppW;
481 
482 		do {
483 			// Add all the vertices of all the triangles of *ppR to the list - unless they're already in there
484 			for(j = 0; j < (*ppR)->nTriNumTot; ++j) {
485 				pTri = (*ppR)->psTri[j];
486 
487 				for(k = 0; k < 3; ++k) {
488 					pVtx = &m_pVtx[pTri->pwIdx[k]];
489 
490 					if(pVtx->ppMeshPos) {
491 						_ASSERT(pVtx->ppMeshPos < ppW);
492 						continue;
493 					}
494 
495 					*ppW			= pVtx;
496 					pVtx->ppMeshPos	= ppW;
497 					++ppW;
498 				}
499 			}
500 
501 			++ppR;
502 		} while(ppR != ppW);
503 
504 		sMesh.nVtxNum = (int)(ppR - sMesh.ppVtx);
505 //		_RPT2(_CRT_WARN, "CreateMeshList() mesh %d %dvtx\n", nMeshCnt, sMesh.nVtxNum);
506 		if(sMesh.nVtxNum >= 3)
507 		{
508 			if(sMesh.nVtxNum >= m_nVtxLimit)
509 				m_vMeshLg.push_back(sMesh);
510 			else
511 				m_pvMesh[sMesh.nVtxNum-3].push_back(sMesh);
512 		}
513 		else
514 		{
515 			/*
516 				Vertex is not used by any triangles; this may be because we're
517 				optimising a subset of the mesh (e.g. for bone batching).
518 			*/
519 			_ASSERT(sMesh.nVtxNum == 1);
520 		}
521 	}
522 
523 	_ASSERT(ppR == &m_ppVtxByMesh[m_nVtxTot]);
524 	_ASSERT(ppW == &m_ppVtxByMesh[m_nVtxTot]);
525 //	_RPT1(_CRT_WARN, "CreateMeshList() %d meshes\n", nMeshCnt);
526 
527 #ifdef _DEBUG
528 /*	for(i = 0; i < m_nVtxLimit-2; ++i)
529 		if(m_pvMesh[i].size())
530 			_RPT2(_CRT_WARN, "%d:%d ", i+3, m_pvMesh[i].size());
531 	_RPT1(_CRT_WARN, "lg:%d\n", m_vMeshLg.size());*/
532 #endif
533 }
534 
535 /****************************************************************************
536 @Function 		SplitMesh
537 @Input			pMesh			Pointer to mesh data
538 @Input			nVtxNum			Number of vertices in the mesh?
539 @Output			ppVtx			Array of vertices
540 @Description	Note: Ask Aaron
541 ****************************************************************************/
SplitMesh(SMesh * const pMesh,const int nVtxNum,SVtx ** const ppVtx)542 void CObject::SplitMesh(
543 	SMesh		* const pMesh,
544 	const int	nVtxNum,
545 	SVtx		** const ppVtx)
546 {
547 	SVtx	*pTmp;
548 	int		i;
549 	SMesh	sNew;
550 
551 	_ASSERT(nVtxNum);
552 
553 	for(i = 0; i < nVtxNum; ++i) {
554 		pTmp					= pMesh->ppVtx[i];		// Keep a record of the old vtx that's already here
555 
556 		pMesh->ppVtx[i]			= ppVtx[i];				// Move the new vtx into place
557 		*ppVtx[i]->ppMeshPos	= pTmp;					// Move the old vtx into place
558 
559 		pTmp->ppMeshPos			= ppVtx[i]->ppMeshPos;	// Tell the old vtx where it is now
560 		ppVtx[i]->ppMeshPos		= &pMesh->ppVtx[i];		// Tell the new vtx where it is now
561 
562 		_ASSERT(pMesh->ppVtx[i]->nTriNumFree);
563 	}
564 
565 	sNew.nVtxNum	= nVtxNum;
566 	sNew.ppVtx		= pMesh->ppVtx;
567 	m_pvMesh[nVtxNum-3].push_back(sNew);
568 
569 	pMesh->ppVtx	= &pMesh->ppVtx[nVtxNum];
570 	pMesh->nVtxNum	-= nVtxNum;
571 	if(pMesh->nVtxNum < m_nVtxLimit) {
572 		ResizeMesh(pMesh->nVtxNum, pMesh->ppVtx);
573 		m_vMeshLg.pop_back();
574 #ifdef _DEBUG
575 /*	} else {
576 		for(i = 0; i < m_nVtxLimit-2; ++i)
577 			if(m_pvMesh[i].size())
578 				_RPT2(_CRT_WARN, "%d:%d ", i+3, m_pvMesh[i].size());
579 		_RPT1(_CRT_WARN, "lg:%d\n", m_vMeshLg.size());*/
580 #endif
581 	}
582 }
583 
584 /****************************************************************************
585 @Function 		ResizeMesh
586 @Input			nVtxNum			The size of the array of vertices being passed in
587 @Input			ppVtx			Array of vertices
588 @Description	Note: Ask Aaron
589 ****************************************************************************/
ResizeMesh(const int nVtxNum,SVtx ** const ppVtx)590 void CObject::ResizeMesh(
591 	const int	nVtxNum,
592 	SVtx		** const ppVtx)
593 {
594 	SVtx	**ppR, **ppW;
595 	SMesh	sNew;
596 	int		i;
597 
598 	ppR = ppVtx;
599 	ppW = ppVtx;
600 
601 	// Make a list of vertices that have unused triangles in their array of triangles
602 	for(i = 0; i < nVtxNum; ++i) {
603 		if((*ppR)->nTriNumFree) {
604 			(*ppW) = (*ppR);
605 			++ppW;
606 		}
607 		++ppR;
608 	}
609 
610 	sNew.nVtxNum = (int)(ppW - ppVtx);
611 	_ASSERT(sNew.nVtxNum <= nVtxNum);
612 
613 	// If any mesh still exists, add it to the relevant list
614 	if(sNew.nVtxNum) {
615 		_ASSERT(sNew.nVtxNum >= 3);
616 		_ASSERT(sNew.nVtxNum < m_nVtxLimit);
617 
618 		sNew.ppVtx = ppVtx;
619 		m_pvMesh[sNew.nVtxNum-3].push_back(sNew);
620 	}
621 
622 #ifdef _DEBUG
623 /*	for(i = 0; i < m_nVtxLimit-2; ++i)
624 		if(m_pvMesh[i].size())
625 			_RPT2(_CRT_WARN, "%d:%d ", i+3, m_pvMesh[i].size());
626 	_RPT1(_CRT_WARN, "lg:%d\n", m_vMeshLg.size());*/
627 #endif
628 }
629 
630 /****************************************************************************
631 @Function 		~CBlockOption
632 @Description	Default destructor
633 ****************************************************************************/
~CBlockOption()634 CBlockOption::~CBlockOption()
635 {
636 	FREE(psVtx);
637 	FREE(psTri);
638 	FREE(psEdgeDelta);
639 }
640 
641 /****************************************************************************
642 @Function 		Init
643 @Input			nVertexLimit		The maximum number of vertices a block can contain
644 @Input			nTriLimit			The maximum number of triangles a block can contain
645 @Description	Initialises the class
646 ****************************************************************************/
Init(const int nVtxLimit,const int nTriLimit)647 void CBlockOption::Init(
648 	const int	nVtxLimit,
649 	const int	nTriLimit)
650 {
651 	m_nVtxLimit = nVtxLimit;
652 	m_nTriLimit = nTriLimit;
653 
654 	psVtx		= (SVtx**)malloc(nVtxLimit * sizeof(*psVtx));
655 	psTri		= (STri**)malloc(nTriLimit * sizeof(*psTri));
656 	psEdgeDelta	= (SEdgeDelta*)malloc(3 * nTriLimit * sizeof(*psEdgeDelta));
657 }
658 
659 /****************************************************************************
660 @Function 		Copy
661 @Input			pSrc				Pointer to the source data
662 @Description	Overwrites the data in the current instance with the data from
663 				the input CBlockOption.
664 ****************************************************************************/
Copy(const CBlockOption * const pSrc)665 void CBlockOption::Copy(const CBlockOption * const pSrc)
666 {
667 	nVtxNum = pSrc->nVtxNum;
668 	nEdgNum = pSrc->nEdgNum;
669 	nTriNum = pSrc->nTriNum;
670 
671 	memcpy(psVtx, pSrc->psVtx, nVtxNum * sizeof(*psVtx));
672 	memcpy(psEdgeDelta, pSrc->psEdgeDelta, nEdgNum * sizeof(*psEdgeDelta));
673 	memcpy(psTri, pSrc->psTri, nTriNum * sizeof(*psTri));
674 }
675 
676 /****************************************************************************
677 @Function 		Clear
678 @Description	Sets the value of the number of vertices, edges and triangles
679 				to zero.
680 ****************************************************************************/
Clear()681 void CBlockOption::Clear()
682 {
683 	nVtxNum = 0;
684 	nEdgNum = 0;
685 	nTriNum = 0;
686 }
687 
688 /****************************************************************************
689 @Function 		Output
690 @Output			pwOut			Index output
691 @Output			pnVtxCnt		Vertex count
692 @Output			pnTriCnt		Triangle count
693 @Modified		pOb				Pointer to an object
694 @Description	Outputs key information about the instance of CBlockOption
695 ****************************************************************************/
Output(PVRTGEOMETRY_IDX * const pwOut,int * const pnVtxCnt,int * const pnTriCnt,const CObject * const pOb) const696 void CBlockOption::Output(
697 	PVRTGEOMETRY_IDX	* const pwOut,
698 	int					* const pnVtxCnt,
699 	int					* const pnTriCnt,
700 	const CObject		* const pOb) const
701 {
702 	STri	*pTri;
703 	int		i, j;
704 
705 	for(i = 0; i < nTriNum; ++i) {
706 		pTri = psTri[i];
707 
708 		_ASSERT(!pTri->bUsed);
709 
710 		for(j = 0; j < 3; ++j) {
711 			_ASSERT(pOb->m_pVtx[pTri->pwIdx[j]].nTriNumFree > 0);
712 			_ASSERT(pTri->psEdg[j]->nTriNumFree > 0);
713 
714 			--pOb->m_pVtx[pTri->pwIdx[j]].nTriNumFree;
715 			--pTri->psEdg[j]->nTriNumFree;
716 
717 			_ASSERT(pOb->m_pVtx[pTri->pwIdx[j]].nTriNumFree >= 0);
718 			_ASSERT(pTri->psEdg[j]->nTriNumFree >= 0);
719 		}
720 
721 		pTri->bUsed = true;
722 
723 		// Copy indices into output
724 		memcpy(&pwOut[3*i], pTri->pwIdx, 3 * sizeof(*pTri->pwIdx));
725 	}
726 
727 	*pnVtxCnt = nVtxNum;
728 	*pnTriCnt = nTriNum;
729 }
730 
731 /****************************************************************************
732 @Function 		UsingVertex
733 @Input			pVtx			Vertex to compare
734 @Return			bool			True on success
735 @Description	Returns true if the supplied vertex is already being used
736 				in the block option.
737 ****************************************************************************/
UsingVertex(const SVtx * const pVtx) const738 bool CBlockOption::UsingVertex(
739 	const SVtx		* const pVtx) const
740 {
741 	int i;
742 
743 	i = nVtxNum;
744 	while(i) {
745 		--i;
746 
747 		if(psVtx[i] == pVtx)
748 			return true;
749 	}
750 
751 	return false;
752 }
753 
754 /****************************************************************************
755 @Function 		Contains
756 @Input			pVtx			Triangle to compare
757 @Return			bool			True on success
758 @Description	Returns true if the supplied triangle is already being used
759 				in the block option.
760 ****************************************************************************/
Contains(const STri * const pTri) const761 bool CBlockOption::Contains(const STri * const pTri) const
762 {
763 	int i;
764 
765 	i = nTriNum;
766 	while(i) {
767 		--i;
768 
769 		if(psTri[i] == pTri)
770 			return true;
771 	}
772 
773 	return false;
774 }
775 
776 /****************************************************************************
777 @Function 		IsEmpty
778 @Return			bool			True if the block option is empty
779 @Description	Returns true if the block option is empty.
780 ****************************************************************************/
IsEmpty() const781 bool CBlockOption::IsEmpty() const
782 {
783 	return !(nVtxNum + nEdgNum + nTriNum);
784 }
785 
786 /****************************************************************************
787 @Function 		IsFull
788 @Return			bool			True if the block option is full
789 @Description	Returns true if the block option is full.
790 ****************************************************************************/
IsFull() const791 bool CBlockOption::IsFull() const
792 {
793 	return  (m_nVtxLimit - nVtxNum) < 3 || nTriNum == m_nTriLimit;
794 }
795 
796 /****************************************************************************
797 @Function 		AddVertex
798 @Input			pVtx			Vertex to add
799 @Description	Providing the current number of vertices is less than the
800 				maximum, the input vertex is added to the end of the array.
801 ****************************************************************************/
AddVertex(SVtx * const pVtx)802 void CBlockOption::AddVertex(SVtx * const pVtx)
803 {
804 	_ASSERT(nVtxNum < m_nVtxLimit);
805 	psVtx[nVtxNum++] = pVtx;
806 }
807 
808 /****************************************************************************
809 @Function 		AddVertexCheckDup
810 @Input			pVtx			Vertex to add
811 @Description	Checks that the input vertex is not already contained in the
812 				vertex array. If it is new, it is added to the array.
813 ****************************************************************************/
AddVertexCheckDup(SVtx * const pVtx)814 void CBlockOption::AddVertexCheckDup(SVtx * const pVtx)
815 {
816 	int i;
817 
818 	for(i = 0; i < nVtxNum; ++i)
819 		if(psVtx[i] == pVtx)
820 			return;
821 
822 	AddVertex(pVtx);
823 }
824 
825 /****************************************************************************
826 @Function 		AddTriangleCheckDup
827 @Input			pTri			Triangle to add
828 @Description	Checks that the input triangle is not already contained in the
829 				triangle array. If it is new, it is added to the array.
830 ****************************************************************************/
AddTriangleCheckDup(STri * const pTri)831 void CBlockOption::AddTriangleCheckDup(STri * const pTri)
832 {
833 	int i;
834 
835 	for(i = 0; i < nTriNum; ++i)
836 		if(psTri[i] == pTri)
837 			return;
838 
839 	_ASSERT(nTriNum < m_nTriLimit);
840 	psTri[nTriNum++] = pTri;
841 }
842 
843 /****************************************************************************
844 @Function 		AddEdgeCheckDup
845 @Input			pEdg			Edge to add
846 @Description	Checks that the input edge is not already contained in the
847 				edge array. If it is new, it is added to the array.
848 ****************************************************************************/
AddEdgeCheckDup(const SEdg * const pEdg)849 void CBlockOption::AddEdgeCheckDup(const SEdg * const pEdg)
850 {
851 	int i;
852 
853 	for(i = 0; i < nEdgNum; ++i) {
854 		if(psEdgeDelta[i].pEdg == pEdg) {
855 			++psEdgeDelta[i].nRefCnt;
856 			return;
857 		}
858 	}
859 
860 	_ASSERT(nEdgNum < 3*m_nTriLimit);
861 	psEdgeDelta[nEdgNum].pEdg		= pEdg;
862 	psEdgeDelta[nEdgNum].nRefCnt	= 1;
863 	++nEdgNum;
864 }
865 
866 /****************************************************************************
867 @Function 		AddTriangle
868 @Input			pTri			Triangle to add
869 @Description	Providing the current number of triangles is less than the
870 				maximum, the input triangle is added to the end of the array.
871 				Once this has been done, the array of edges is updated.
872 ****************************************************************************/
873 // TODO: if this is only used to add fresh triangles, all edges must be added
AddTriangle(STri * const pTri)874 void CBlockOption::AddTriangle(STri * const pTri)
875 {
876 	int i;
877 
878 	_ASSERT(nTriNum < m_nTriLimit);
879 	psTri[nTriNum++] = pTri;
880 
881 	// Keep a count of edges and the number of tris which share them
882 	for(i = 0; i < 3; ++i)
883 		AddEdgeCheckDup(pTri->psEdg[i]);
884 }
885 
886 /****************************************************************************
887 @Function 		AddOneTriangle
888 @Input			pTri			Triangle to add
889 @Input			pOb				Object to copy vertices from
890 @Description	Calls the AddTriangle function.
891 				Once this has been done, the array of vertices is updated.
892 ****************************************************************************/
893 // TODO: if this is only called to add a fresh start triangle, all vertices must be added
AddOneTriangle(STri * const pTri,const CObject * const pOb)894 void CBlockOption::AddOneTriangle(
895 	STri			* const pTri,
896 	const CObject	* const pOb)
897 {
898 	int		i;
899 
900 	// Add the triangle to the block
901 	AddTriangle(pTri);
902 
903 	// Add the vertices to the block
904 	for(i = 0; i < 3; ++i)
905 		AddVertexCheckDup(&pOb->m_pVtx[pTri->pwIdx[i]]);
906 }
907 
908 /****************************************************************************
909 @Function 		GetClosedEdgeDelta
910 @Return			int					The delta value of closed edges
911 @Description	This method returns a value that represents the average state of
912 				the edges. If the value is greater than zero, the majority of
913 				edges are closed. If the value is less than zero, the majority
914 				of edges are open.
915 ****************************************************************************/
GetClosedEdgeDelta() const916 int CBlockOption::GetClosedEdgeDelta() const
917 {
918 	int i, nDelta;
919 
920 	nDelta = 0;
921 	for(i = 0; i < nEdgNum; ++i) {
922 		_ASSERT(psEdgeDelta[i].pEdg->nTriNumFree >= psEdgeDelta[i].nRefCnt);
923 
924 		// Check how many tris will use the edge if these are taken away
925 		switch(psEdgeDelta[i].pEdg->nTriNumFree - psEdgeDelta[i].nRefCnt) {
926 			case 0:
927 				// If the edge was open, and is now closed, that's good
928 				if(psEdgeDelta[i].pEdg->nTriNumFree == 1)
929 					++nDelta;
930 				break;
931 			case 1:
932 				// if the edge is now open, that's bad
933 				--nDelta;
934 				break;
935 		}
936 	}
937 
938 	return nDelta;
939 }
940 
941 /****************************************************************************
942 @Function 		IsBetterThan
943 @Input			pCmp				The block option to compare with
944 @Return			bool				True if the current block option is best
945 @Description	Returns true if the current block option is better than the
946 				block option that has been passed in. Otherwise, it returns false.
947 ****************************************************************************/
IsBetterThan(const CBlockOption * const pCmp) const948 bool CBlockOption::IsBetterThan(const CBlockOption * const pCmp) const
949 {
950 	float	fWorth0, fWorth1;
951 	int		nClosed0, nClosed1;
952 
953 	// Check "worth" - TrisAdded/VtxAdded
954 	fWorth0 = (float)nTriNum / (float)nVtxNum;
955 	fWorth1 = (float)pCmp->nTriNum / (float)pCmp->nVtxNum;
956 
957 	nClosed0 = GetClosedEdgeDelta();
958 	nClosed1 = pCmp->GetClosedEdgeDelta();
959 
960 	if(fabsf(fWorth0 - fWorth1) > 0.1f) {
961 		return fWorth0 > fWorth1;
962 	} else if(nClosed0 != nClosed1) {
963 		return nClosed0 > nClosed1;
964 	} else {
965 		return nTriNum > pCmp->nTriNum;
966 	}
967 }
968 
969 /****************************************************************************
970 @Function 		Add
971 @Input			pSrc			The block option to add
972 @Input			pOb				Object to use vertices from
973 @Description	Add's the input vertex and triangle data to the current block option
974 ****************************************************************************/
Add(const CBlockOption * const pSrc,const CObject * const pOb)975 void CBlockOption::Add(
976 	const CBlockOption	* const pSrc,
977 	const CObject		* const pOb)
978 {
979 	PVRT_UNREFERENCED_PARAMETER(pOb);
980 
981 	int i;
982 
983 	// Add vertices from job to block
984 	for(i = 0; i < pSrc->nVtxNum; ++i)
985 		AddVertexCheckDup(pSrc->psVtx[i]);
986 
987 	// Add triangles from job to block
988 	for(i = 0; i < pSrc->nTriNum; ++i)
989 		AddTriangle(pSrc->psTri[i]);
990 }
991 
992 /****************************************************************************
993 @Function 		Add
994 @Input			pMesh			The mesh to add
995 @Description	Add's the input mesh to the current block option
996 ****************************************************************************/
Add(const SMesh * const pMesh)997 void CBlockOption::Add(
998 	const SMesh		* const pMesh)
999 {
1000 	int i, j;
1001 	SVtx	*pVtx;
1002 
1003 	for(i = 0; i < pMesh->nVtxNum; ++i) {
1004 		pVtx = pMesh->ppVtx[i];
1005 
1006 		AddVertexCheckDup(pVtx);
1007 
1008 		for(j = 0; j < pVtx->nTriNumTot; ++j) {
1009 			if(!pVtx->psTri[j]->bUsed)
1010 				AddTriangleCheckDup(pVtx->psTri[j]);
1011 		}
1012 	}
1013 }
1014 
1015 /****************************************************************************
1016 @Function 		CBlock
1017 @Description	Default constructor
1018 ****************************************************************************/
CBlock(const int nBufferVtxLimit,const int nBufferTriLimit)1019 CBlock::CBlock(
1020 	const int	nBufferVtxLimit,
1021 	const int	nBufferTriLimit)
1022 {
1023 	m_nVtxLimit = nBufferVtxLimit;
1024 	m_nTriLimit = nBufferTriLimit;
1025 
1026 	m_sOpt.Init(m_nVtxLimit, m_nTriLimit);
1027 	m_sOptBest.Init(m_nVtxLimit, m_nTriLimit);
1028 
1029 	// Intialise "job" blocks
1030 	m_sJob0.Init(3, m_nTriLimit);
1031 	m_sJob1.Init(3, m_nTriLimit);
1032 }
1033 
1034 /****************************************************************************
1035 @Function 		Clear
1036 @Description	Clears the current and best block options
1037 ****************************************************************************/
Clear()1038 void CBlock::Clear()
1039 {
1040 	m_sOpt.Clear();
1041 	m_sOptBest.Clear();
1042 }
1043 
1044 /****************************************************************************
1045 @Function 		Output
1046 @Output			pwOut			Index output
1047 @Output			pnVtxCnt		Vertex count
1048 @Output			pnTriCnt		Triangle count
1049 @Modified		pOb				Pointer to an object
1050 @Description	Outputs key information about the instance of CBlockOption
1051 ****************************************************************************/
Output(PVRTGEOMETRY_IDX * const pwOut,int * const pnVtxCnt,int * const pnTriCnt,const CObject * const pOb) const1052 void CBlock::Output(
1053 	PVRTGEOMETRY_IDX	* const pwOut,
1054 	int					* const pnVtxCnt,
1055 	int					* const pnTriCnt,
1056 	const CObject		* const pOb) const
1057 {
1058 	m_sOptBest.Output(pwOut, pnVtxCnt, pnTriCnt, pOb);
1059 }
1060 
1061 /****************************************************************************
1062 @Function 		AddBestTrianglesAppraise
1063 @Modified		pJob			The block object to alter
1064 @Input			pOb				The object
1065 @Input			pTriAppraise	The triangle to appraise
1066 @Return			bool
1067 @Description	Uses the input object and triangle to create a new block option.
1068 ****************************************************************************/
AddBestTrianglesAppraise(CBlockOption * const pJob,const CObject * const pOb,const STri * const pTriAppraise)1069 bool CBlock::AddBestTrianglesAppraise(
1070 	CBlockOption	* const pJob,
1071 	const CObject	* const pOb,
1072 	const STri		* const pTriAppraise)
1073 {
1074 	SVtx	*pVtx;
1075 	STri	*pTri;
1076 	int		i, j;
1077 
1078 	pJob->Clear();
1079 
1080 	// Add vertices
1081 	for(i = 0; i < 3; ++i) {
1082 		pVtx = &pOb->m_pVtx[pTriAppraise->pwIdx[i]];
1083 		if(!m_sOpt.UsingVertex(pVtx))
1084 			pJob->AddVertex(pVtx);
1085 	}
1086 
1087 	if(pJob->nVtxNum > (m_nVtxLimit-m_sOpt.nVtxNum))
1088 		return false;
1089 
1090 	// Add triangles referenced by each vertex
1091 	for(i = 0; i < 3; ++i) {
1092 		pVtx = &pOb->m_pVtx[pTriAppraise->pwIdx[i]];
1093 
1094 		_ASSERT(pVtx->nTriNumFree >= 1);
1095 		_ASSERT(pVtx->nTriNumFree <= pVtx->nTriNumTot);
1096 
1097 		for(j = 0; j < pVtx->nTriNumTot; ++j) {
1098 			if(pJob->nTriNum >= (m_nTriLimit-m_sOpt.nTriNum))
1099 				break;
1100 
1101 			pTri = pVtx->psTri[j];
1102 
1103 			// Don't count the same triangle twice!
1104 			if(pTri->bUsed || m_sOpt.Contains(pTri) || pJob->Contains(pTri))
1105 				continue;
1106 
1107 			// If all the triangle's vertices are or will be in the block, then increase nTri
1108 			if(
1109 					(
1110 						pTri->pwIdx[0] == pTriAppraise->pwIdx[0] ||
1111 						pTri->pwIdx[0] == pTriAppraise->pwIdx[1] ||
1112 						pTri->pwIdx[0] == pTriAppraise->pwIdx[2] ||
1113 						m_sOpt.UsingVertex(&pOb->m_pVtx[pTri->pwIdx[0]])
1114 					) && (
1115 						pTri->pwIdx[1] == pTriAppraise->pwIdx[0] ||
1116 						pTri->pwIdx[1] == pTriAppraise->pwIdx[1] ||
1117 						pTri->pwIdx[1] == pTriAppraise->pwIdx[2] ||
1118 						m_sOpt.UsingVertex(&pOb->m_pVtx[pTri->pwIdx[1]])
1119 					) && (
1120 						pTri->pwIdx[2] == pTriAppraise->pwIdx[0] ||
1121 						pTri->pwIdx[2] == pTriAppraise->pwIdx[1] ||
1122 						pTri->pwIdx[2] == pTriAppraise->pwIdx[2] ||
1123 						m_sOpt.UsingVertex(&pOb->m_pVtx[pTri->pwIdx[2]])
1124 					)
1125 				)
1126 			{
1127 				pJob->AddTriangle(pTri);
1128 			}
1129 		}
1130 	}
1131 
1132 	_ASSERT(pJob->nTriNum);
1133 	_ASSERT(pJob->nTriNum <= (m_nTriLimit-m_sOpt.nTriNum));
1134 
1135 	return true;
1136 }
1137 
1138 /****************************************************************************
1139 @Function 		AddBestTriangles
1140 @Input			pOb				The object
1141 @Description	Finds the best triangles and adds them to the current block option (m_sOpt)
1142 ****************************************************************************/
AddBestTriangles(CObject * const pOb)1143 void CBlock::AddBestTriangles(CObject * const pOb)
1144 {
1145 	int				i, j;
1146 	const SVtx		*pVtx;
1147 	STri			*pTri;
1148 	CBlockOption	*pJob, *pJobBest;
1149 
1150 	pJob = &m_sJob0;
1151 
1152 	do {
1153 		pJobBest = 0;
1154 
1155 		for(i = 0; i < m_sOpt.nVtxNum; ++i) {
1156 			pVtx = m_sOpt.psVtx[i];
1157 
1158 			if(!pVtx->nTriNumFree)
1159 				continue;
1160 
1161 			for(j = 0; j < pVtx->nTriNumTot; ++j) {
1162 				pTri = pVtx->psTri[j];
1163 
1164 				if(pTri->bUsed || m_sOpt.Contains(pTri))
1165 					continue;
1166 
1167 				// Find out how many triangles and vertices this tri adds
1168 				if(!AddBestTrianglesAppraise(pJob, pOb, pTri))
1169 					continue;
1170 
1171 				if(!pJobBest || pJob->IsBetterThan(pJobBest)) {
1172 					pJobBest	= pJob;
1173 					pJob		= (pJob == &m_sJob0 ? &m_sJob1 : &m_sJob0);
1174 				}
1175 			}
1176 		}
1177 
1178 		if(pJobBest) {
1179 			m_sOpt.Add(pJobBest, pOb);
1180 		}
1181 	} while(pJobBest && m_nTriLimit != m_sOpt.nTriNum);
1182 }
1183 
1184 /****************************************************************************
1185 @Function 		FillFrom
1186 @Input			pMesh			Mesh to fill with
1187 @Input			pVtx			Vertex to fill with
1188 @Input			pOb				Object to fill with
1189 @Return			bool			Returns true if the current block option isn't full
1190 @Description	Returns TRUE if Fill() needs to be called again - i.e. blockOption is already filled
1191 ****************************************************************************/
FillFrom(SMesh * const pMesh,SVtx * const pVtx,CObject * const pOb)1192 bool CBlock::FillFrom(
1193 	SMesh		* const pMesh,
1194 	SVtx		* const pVtx,
1195 	CObject		* const pOb)
1196 {
1197 	// Let's try starting from this vertex then
1198 	_ASSERT(pVtx->nTriNumFree);
1199 	m_sOpt.Clear();
1200 	m_sOpt.AddVertex(pVtx);
1201 	AddBestTriangles(pOb);
1202 
1203 	if(m_sOpt.IsFull()) {
1204 		if(m_sOptBest.IsEmpty() || m_sOpt.IsBetterThan(&m_sOptBest))
1205 			m_sOptBest.Copy(&m_sOpt);
1206 		return false;
1207 	}
1208 	else
1209 	{
1210 		_ASSERT(!m_sOpt.IsEmpty());
1211 		pOb->SplitMesh(pMesh, m_sOpt.nVtxNum, m_sOpt.psVtx);		// Split the sub-mesh into its own mesh
1212 		return true;
1213 	}
1214 }
1215 
1216 /****************************************************************************
1217 @Function 		Fill
1218 @Input			pOb				Object to fill with
1219 @Return			int				-1 if the block if the best option is already full
1220 @Description	Note: Ask Aaron
1221 ****************************************************************************/
Fill(CObject * const pOb)1222 int CBlock::Fill(
1223 	CObject	* const pOb)
1224 {
1225 	SVtx	*pVtx;
1226 	int		i;
1227 	SMesh	*pMesh;
1228 
1229 	/*
1230 		Build blocks from the large meshes
1231 	*/
1232 	if(!pOb->m_vMeshLg.empty()) {
1233 		pMesh = &pOb->m_vMeshLg.back();
1234 
1235 //		_RPT1(_CRT_WARN, "Fill() using large with %d vtx\n", pMesh->nVtxNum);
1236 
1237 		// Find the vertex with the fewest unused triangles
1238 		for(i = 0; i < pMesh->nVtxNum; ++i) {
1239 			pVtx = pMesh->ppVtx[i];
1240 
1241 			if(pVtx->nTriNumFree == 1) {
1242 				if(FillFrom(pMesh, pVtx, pOb))
1243 					return Fill(pOb);
1244 			}
1245 		}
1246 
1247 		if(m_sOptBest.IsEmpty()) {
1248 			// Just start from any old vertex
1249 			for(i = 0; i < pMesh->nVtxNum; ++i) {
1250 				pVtx = pMesh->ppVtx[i];
1251 
1252 				if(pVtx->nTriNumFree) {
1253 					if(FillFrom(pMesh, pVtx, pOb))
1254 						return Fill(pOb);
1255 					break;
1256 				}
1257 			}
1258 
1259 			if(m_sOptBest.IsEmpty()) {
1260 				pOb->m_vMeshLg.pop_back();					// Delete the mesh from the list
1261 				return Fill(pOb);
1262 			}
1263 		}
1264 
1265 		if(m_sOptBest.IsFull())
1266 			return -1;
1267 	}
1268 
1269 	/*
1270 		Match together the small meshes into blocks
1271 	*/
1272 	_ASSERT(m_sOptBest.IsEmpty());
1273 	i = m_nVtxLimit - m_sOptBest.nVtxNum - 3;
1274 
1275 //	_RPT0(_CRT_WARN, "Fill() grouping small ");
1276 
1277 	// Starting with the largest meshes, lump them into this block
1278 	while(i >= 0 && (m_nVtxLimit - m_sOptBest.nVtxNum) >= 3) {
1279 		if(pOb->m_pvMesh[i].empty()) {
1280 			--i;
1281 			continue;
1282 		}
1283 
1284 		pMesh = &pOb->m_pvMesh[i].back();
1285 		m_sOptBest.Add(pMesh);
1286 //		_RPT1(_CRT_WARN, "+%d", pMesh->nVtxNum);
1287 		pOb->m_pvMesh[i].pop_back();
1288 		i = PVRT_MIN(i, m_nVtxLimit - m_sOptBest.nVtxNum - 3);
1289 	}
1290 
1291 	// If there's any space left in this block (and clearly there are no blocks
1292 	// just the right size to fit) then take SOME of the largest block available.
1293 	if(!m_sOptBest.IsFull()) {
1294 		m_sOpt.Copy(&m_sOptBest);
1295 
1296 		// Note: This loop purposely does not check m_pvMesh[0] - any block
1297 		// which is looking to grab more geometry would have already sucked
1298 		// up those meshes
1299 		for(i = (m_nVtxLimit-3); i; --i) {
1300 			if(!pOb->m_pvMesh[i].empty()) {
1301 				pMesh = &pOb->m_pvMesh[i].back();
1302 
1303 				_ASSERT(pMesh->ppVtx[0]->nTriNumFree);
1304 				_ASSERT(!m_sOpt.UsingVertex(pMesh->ppVtx[0]));
1305 
1306 				m_sOpt.AddVertex(pMesh->ppVtx[0]);
1307 //				_RPT1(_CRT_WARN, "(+%d)\n", pMesh->nVtxNum);
1308 				AddBestTriangles(pOb);
1309 
1310 				m_sOptBest.Copy(&m_sOpt);
1311 				_ASSERT(m_sOptBest.IsFull());
1312 				return i;
1313 			}
1314 		}
1315 	}
1316 //	_RPT0(_CRT_WARN, "\n");
1317 	return -1;
1318 }
1319 
1320 /****************************************************************************
1321 ** Local functions
1322 ****************************************************************************/
1323 /****************************************************************************
1324 @Function 		Fill
1325 @Input			pVtxData		Vertex data
1326 @Input			pwIdx			Index array
1327 @Input			nStride			Stride
1328 @Input			nVertNum		Number of vertices
1329 @Input			nIdxNum			Number of indices
1330 @Description	Sorts the vertices.
1331 ****************************************************************************/
SortVertices(void * const pVtxData,PVRTGEOMETRY_IDX * const pwIdx,const int nStride,const int nVertNum,const int nIdxNum)1332 static void SortVertices(
1333 	void				* const pVtxData,
1334 	PVRTGEOMETRY_IDX	* const pwIdx,
1335 	const int			nStride,
1336 	const int			nVertNum,
1337 	const int			nIdxNum)
1338 {
1339 	void				*pVtxNew;
1340 	int					*pnVtxDest;
1341 	int					i;
1342 	PVRTGEOMETRY_IDX	wNext;
1343 
1344 	pVtxNew		= malloc(nVertNum * nStride);
1345 	_ASSERT(pVtxNew);
1346 
1347 	pnVtxDest	= (int*)malloc(nVertNum * sizeof(*pnVtxDest));
1348 	_ASSERT(pnVtxDest);
1349 
1350 	wNext = 0;
1351 
1352 	// Default all indices to an invalid number
1353 	for(i = 0; i < nVertNum; ++i)
1354 		pnVtxDest[i] = -1;
1355 
1356 	// Let's get on with it then.
1357 	for(i = 0; i < nIdxNum; ++i) {
1358 		if(pnVtxDest[pwIdx[i]] == -1) {
1359 			_ASSERT((int) wNext < nVertNum);
1360 			memcpy((char*)pVtxNew+(wNext*nStride), (char*)pVtxData+(pwIdx[i]*nStride), nStride);
1361 			pnVtxDest[pwIdx[i]] = wNext++;
1362 		}
1363 
1364 		pwIdx[i] = pnVtxDest[pwIdx[i]];
1365 	}
1366 
1367 	/*
1368 		This assert will fail if sorting a sub-set of the triangles (e.g. if
1369 		the mesh is bone-batched).
1370 
1371 		In that situation vertex sorting should be performed only once after
1372 		all the tri sorting is finished, not per tri-sort.
1373 	*/
1374 	_ASSERT((int) wNext == nVertNum);
1375 	memcpy(pVtxData, pVtxNew, nVertNum * nStride);
1376 
1377 	FREE(pnVtxDest);
1378 	FREE(pVtxNew);
1379 }
1380 
1381 /****************************************************************************
1382 ** Functions
1383 ****************************************************************************/
1384 /*!***************************************************************************
1385  @Function		PVRTGeometrySort
1386  @Modified		pVtxData		Pointer to array of vertices
1387  @Modified		pwIdx			Pointer to array of indices
1388  @Input			nStride			Size of a vertex (in bytes)
1389  @Input			nVertNum		Number of vertices. Length of pVtxData array
1390  @Input			nTriNum			Number of triangles. Length of pwIdx array is 3* this
1391  @Input			nBufferVtxLimit	Number of vertices that can be stored in a buffer
1392  @Input			nBufferTriLimit	Number of triangles that can be stored in a buffer
1393  @Input			dwFlags			PVRTGEOMETRY_SORT_* flags
1394  @Description	Triangle sorter
1395 *****************************************************************************/
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)1396 void PVRTGeometrySort(
1397 	void				* const pVtxData,
1398 	PVRTGEOMETRY_IDX	* const pwIdx,
1399 	const int			nStride,
1400 	const int			nVertNum,
1401 	const int			nTriNum,
1402 	const int			nBufferVtxLimit,
1403 	const int			nBufferTriLimit,
1404 	const unsigned int	dwFlags)
1405 {
1406 	CObject				sOb(pwIdx, nVertNum, nTriNum, nBufferVtxLimit, nBufferTriLimit);
1407 	CBlock				sBlock(nBufferVtxLimit, nBufferTriLimit);
1408 	PVRTGEOMETRY_IDX	*pwIdxOut;
1409 	int					nTriCnt, nVtxCnt;
1410 	int					nOutTriCnt, nOutVtxCnt, nOutBlockCnt;
1411 	int					nMeshToResize;
1412 #ifdef PVRTRISORT_ENABLE_VERIFY_RESULTS
1413 	int					i;
1414 	int					pnBlockTriCnt[PVRVGPBLOCKTEST_MAX_BLOCKS];
1415 	SVGPModel			sVGPMdlBefore;
1416 	SVGPModel			sVGPMdlAfter;
1417 #endif
1418 
1419 	if(dwFlags & PVRTGEOMETRY_SORT_VERTEXCACHE) {
1420 #ifdef PVRTRISORT_ENABLE_VERIFY_RESULTS
1421 		VGP590Test(&sVGPMdlBefore, pwIdx, nTriNum);
1422 		_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);
1423 #endif
1424 
1425 		pwIdxOut	= (PVRTGEOMETRY_IDX*)malloc(nTriNum * 3 * sizeof(*pwIdxOut));
1426 		_ASSERT(pwIdxOut);
1427 
1428 		// Sort geometry into blocks
1429 		nOutTriCnt		= 0;
1430 		nOutVtxCnt		= 0;
1431 		nOutBlockCnt	= 0;
1432 		do {
1433 			// Clear & fill the block
1434 			sBlock.Clear();
1435 			nMeshToResize = sBlock.Fill(&sOb);
1436 
1437 			// Copy indices into output
1438 			sBlock.Output(&pwIdxOut[3*nOutTriCnt], &nVtxCnt, &nTriCnt, &sOb);
1439 			sOb.m_nTriNumFree	-= nTriCnt;
1440 			nOutTriCnt			+= nTriCnt;
1441 
1442 			if(nMeshToResize >= 0) {
1443 				SMesh	*pMesh;
1444 				_ASSERT(nMeshToResize <= (nBufferVtxLimit-3));
1445 				pMesh = &sOb.m_pvMesh[nMeshToResize].back();
1446 				sOb.ResizeMesh(pMesh->nVtxNum, pMesh->ppVtx);
1447 				sOb.m_pvMesh[nMeshToResize].pop_back();
1448 			}
1449 
1450 			_ASSERT(nVtxCnt <= nBufferVtxLimit);
1451 			_ASSERT(nTriCnt <= nBufferTriLimit);
1452 
1453 #ifdef PVRTRISORT_ENABLE_VERIFY_RESULTS
1454 			_ASSERT(nOutBlockCnt < PVRVGPBLOCKTEST_MAX_BLOCKS);
1455 			pnBlockTriCnt[nOutBlockCnt] = nTriCnt;
1456 #endif
1457 			nOutVtxCnt += nVtxCnt;
1458 			nOutBlockCnt++;
1459 
1460 //			_RPT4(_CRT_WARN, "%d/%d tris (+%d), %d blocks\n", nOutTriCnt, nTriNum, nTriCnt, nOutBlockCnt);
1461 
1462 			_ASSERT(nTriCnt == nBufferTriLimit || (nBufferVtxLimit - nVtxCnt) < 3 || nOutTriCnt == nTriNum);
1463 		} while(nOutTriCnt < nTriNum);
1464 
1465 		_ASSERT(nOutTriCnt == nTriNum);
1466 		// The following will fail if optimising a subset of the mesh (e.g. a bone batching)
1467 		//_ASSERT(nOutVtxCnt >= nVertNum);
1468 
1469 		// Done!
1470 		memcpy(pwIdx, pwIdxOut, nTriNum * 3 * sizeof(*pwIdx));
1471 		FREE(pwIdxOut);
1472 
1473 		_RPT3(_CRT_WARN, "OptimiseTriListPVR() In: Tri: %d, Vtx: %d, vtx/tri=%f\n", nTriNum, nVertNum, (float)nVertNum / (float)nTriNum);
1474 		_RPT4(_CRT_WARN, "OptimiseTriListPVR() HW: Tri: %d, Vtx: %d, vtx/tri=%f Blocks=%d\n", nOutTriCnt, nOutVtxCnt, (float)nOutVtxCnt / (float)nOutTriCnt, nOutBlockCnt);
1475 
1476 #ifdef PVRTRISORT_ENABLE_VERIFY_RESULTS
1477 		VGP590Test(&sVGPMdlAfter, pwIdx, nTriNum);
1478 		_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);
1479 		_ASSERTE(sVGPMdlAfter.nVtxCnt <= sVGPMdlBefore.nVtxCnt);
1480 		_ASSERTE(sVGPMdlAfter.nBlockCnt <= sVGPMdlBefore.nBlockCnt);
1481 
1482 		for(i = 0; i < nOutBlockCnt; ++i) {
1483 			_ASSERT(pnBlockTriCnt[i] == sVGPMdlAfter.pnBlockTriCnt[i]);
1484 		}
1485 #endif
1486 	}
1487 
1488 	if(!(dwFlags & PVRTGEOMETRY_SORT_IGNOREVERTS)) {
1489 		// Re-order the vertices so maybe they're accessed in a more linear
1490 		// manner. Should cut page-breaks on the initial memory read of
1491 		// vertices. Affects both the order of vertices, and the values
1492 		// of indices, but the triangle order is unchanged.
1493 		SortVertices(pVtxData, pwIdx, nStride, nVertNum, nTriNum*3);
1494 	}
1495 }
1496 
1497 /*****************************************************************************
1498  End of file (PVRTGeometry.cpp)
1499 *****************************************************************************/
1500 
1501