• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /******************************************************************************
2 
3  @File         PVRTBoneBatch.cpp
4 
5  @Title        PVRTBoneBatch
6 
7  @Version
8 
9  @Copyright    Copyright (c) Imagination Technologies Limited.
10 
11  @Platform     ANSI compatible
12 
13  @Description  Utility functions which process vertices.
14 
15 ******************************************************************************/
16 
17 /****************************************************************************
18 ** Includes
19 ****************************************************************************/
20 #include "PVRTGlobal.h"
21 #include "PVRTContext.h"
22 
23 #include <vector>
24 #include <list>
25 
26 #include "PVRTMatrix.h"
27 #include "PVRTVertex.h"
28 #include "PVRTBoneBatch.h"
29 
30 /****************************************************************************
31 ** Defines
32 ****************************************************************************/
33 
34 /****************************************************************************
35 ** Macros
36 ****************************************************************************/
37 
38 /****************************************************************************
39 ** Structures
40 ****************************************************************************/
41 /*!***************************************************************************
42 @Class CBatch
43 @Brief Class to contain and manage batch information.
44 *****************************************************************************/
45 class CBatch
46 {
47 protected:
48 	int	m_nCapacity, 		// Maximum size of the batch
49 	m_nCnt, 				// Number of elements currently contained in the batch
50 	*m_pnPalette;			// Array of palette indices
51 
52 public:
53 /*!***************************************************************************
54  @Function		CBatch
55  @Description	The default constructor
56 *****************************************************************************/
CBatch()57 	CBatch() : 	m_nCapacity(0),
58 				m_nCnt(0),
59 				m_pnPalette(0)
60 	{
61 	}
62 
63 /*!***************************************************************************
64  @Function		CBatch
65  @Input			src				CBatch to copy
66  @Description	Copy constructor
67 *****************************************************************************/
CBatch(const CBatch & src)68 	CBatch(const CBatch &src) : m_pnPalette(0)
69 	{
70 		SetSize(src.m_nCapacity);
71 		*this = src;
72 	}
73 
74 /*!***************************************************************************
75  @Function		~CBatch
76  @Description	Destructor
77 *****************************************************************************/
~CBatch()78 	~CBatch()
79 	{
80 		FREE(m_pnPalette);
81 	}
82 
83 /*!***************************************************************************
84  @Function		operator=
85  @Description	Operator overload for the '=' operand
86 *****************************************************************************/
operator =(const CBatch & src)87 	CBatch& operator= (const CBatch &src)
88 	{
89 		_ASSERT(m_nCapacity == src.m_nCapacity);
90 		m_nCnt = src.m_nCnt;
91 		memcpy(m_pnPalette, src.m_pnPalette, m_nCnt * sizeof(*m_pnPalette));
92 		return *this;
93 	}
94 
95 /*!***************************************************************************
96  @Function		SetSize
97  @Input			nSize			The new size of the batch
98  @Description	Delete all current information and resizes the batch
99 				to the value that has been passed in.
100 *****************************************************************************/
SetSize(const int nSize)101 	void SetSize(const int nSize)
102 	{
103 		FREE(m_pnPalette);
104 
105 		m_nCapacity	= nSize;
106 		m_nCnt		= 0;
107 		m_pnPalette		= (int*)malloc(m_nCapacity * sizeof(*m_pnPalette));
108 	}
109 
110 /*!***************************************************************************
111  @Function		Clear
112  @Description	Resets the count
113 *****************************************************************************/
Clear()114 	void Clear()
115 	{
116 		m_nCnt = 0;
117 	}
118 
119 /*!***************************************************************************
120  @Function		Clear
121  @Input			n			The index of the new item
122  Return			bool		Returns true if the item already exists or has been added.
123  @Description	Adds a new item to the batch, providing it has not already
124 				been added to the batch and the count doesn't exceed the
125 				maximum number of bones the batch can hold.
126 *****************************************************************************/
Add(const int n)127 	bool Add(const int n)
128 	{
129 		int i;
130 
131 		if(n < 0)
132 			return false;
133 
134 		// If we already have this item, do nothing
135 		for(i = 0; i < m_nCnt; ++i)
136 		{
137 			if(m_pnPalette[i] == n)
138 				return true;
139 		}
140 
141 		// Add the new item
142 		if(m_nCnt < m_nCapacity)
143 		{
144 			m_pnPalette[m_nCnt] = n;
145 			++m_nCnt;
146 			return true;
147 		}
148 		else
149 		{
150 			return false;
151 		}
152 	}
153 
154 /*!***************************************************************************
155  @Function		Merge
156  @Input			src				The batch to merge with
157  @Description	Merges the input batch with the current batch.
158 *****************************************************************************/
Merge(const CBatch & src)159 	void Merge(const CBatch &src)
160 	{
161 		int i;
162 
163 		for(i = 0; i < src.m_nCnt; ++i)
164 			Add(src.m_pnPalette[i]);
165 	}
166 
167 /*!***************************************************************************
168  @Function		TestMerge
169  @Input			src				The batch to merge with
170  @Return		int				The number of items that are not already
171 								present in the batch. -1 if the merge will
172 								exceed the capacity of the batch
173  @Description	Tests how many of the items of the input batch are not
174 				already contained in the batch. This returns the number of
175 				items that would need to be added, or -1 if the number
176 				of additional items would exceed the capacity of the batch.
177 *****************************************************************************/
TestMerge(const CBatch & src)178 	int TestMerge(const CBatch &src)
179 	{
180 		int i, nCnt;
181 
182 		nCnt = 0;
183 		for(i = 0; i < src.m_nCnt; ++i)
184 			if(!Contains(src.m_pnPalette[i]))
185 				++nCnt;
186 
187 		return m_nCnt+nCnt > m_nCapacity ? -1 : nCnt;
188 	}
189 
190 /*!***************************************************************************
191  @Function		Contains
192  @Input			src				The batch to compare
193  @Return		bool			Returns true if the batch and the input batch
194 								have at least one item in common
195  @Description	Returns true if the batch's have at least one item in common
196 *****************************************************************************/
Contains(const CBatch & batch) const197 	bool Contains(const CBatch &batch) const
198 	{
199 		int i;
200 
201 		for(i = 0; i < batch.m_nCnt; ++i)
202 			if(!Contains(batch.m_pnPalette[i]))
203 				return false;
204 
205 		return true;
206 	}
207 
208 /*!***************************************************************************
209  @Function		Contains
210  @Input			n				The index of the new item
211  @Return		bool			Returns true if the batch contains the item
212  @Description	Returns true if the batch contains the item.
213 *****************************************************************************/
Contains(const int n) const214 	bool Contains(const int n) const
215 	{
216 		int i;
217 
218 		for(i = 0; i < m_nCnt; ++i)
219 			if(m_pnPalette[i] == n)
220 				return true;
221 
222 		return false;
223 	}
224 
225 /*!***************************************************************************
226  @Function		Write
227  @Output		pn				The array of items to overwrite
228  @Output		pnCnt			The number of items in the array
229  @Description	Writes the array of items and the number of items to the output
230 				parameters.
231 *****************************************************************************/
Write(int * const pn,int * const pnCnt) const232 	void Write(
233 		int * const pn,
234 		int * const pnCnt) const
235 	{
236 		memcpy(pn, m_pnPalette, m_nCnt * sizeof(*pn));
237 		*pnCnt = m_nCnt;
238 	}
239 
240 /*!***************************************************************************
241  @Function		GetVertexBoneIndices
242  @Modified		pfI				Returned index
243  @Input			pfW				Weight?
244  @Input			n				Length of index array
245  @Description	For each element of the input array, the index value is compared
246 				with the palette's index value. If the values are equal, the
247 				value of the current input array element is replaced with the
248 				palette index, otherwise the value is set to zero.
249 *****************************************************************************/
GetVertexBoneIndices(float * const pfI,const float * const pfW,const int n)250 	void GetVertexBoneIndices(
251 		float		* const pfI,
252 		const float	* const pfW,
253 		const int	n)
254 	{
255 		int i, j;
256 
257 		for(i = 0; i < n; ++i)
258 		{
259 			if(pfW[i] != 0)
260 			{
261 				for(j = 0; j < m_nCnt; ++j)
262 				{
263 					if(pfI[i] != m_pnPalette[j])
264 						continue;
265 
266 					pfI[i] = (float)j;
267 					break;
268 				}
269 
270 				// This batch *must* contain this vertex
271 				_ASSERT(j != m_nCnt);
272 			}
273 			else
274 			{
275 				pfI[i] = 0;
276 			}
277 		}
278 	}
279 };
280 
281 /*!***************************************************************************
282 @Class CGrowableArray
283 @Brief Class that provides an array structure that can change its size dynamically.
284 *****************************************************************************/
285 class CGrowableArray
286 {
287 protected:
288 	char	*m_p;
289 	int		m_nSize;
290 	int		m_nCnt;
291 
292 public:
293 /*!***************************************************************************
294  @Function		CGrowableArray
295  @Input			nSize			The size of the data (in bytes) that the array will contain
296  @Description	Initialises the size of the data the array will contain to the
297 				value that has been passed in and initialises the remaining
298 				data members with default values.
299 *****************************************************************************/
CGrowableArray(const int nSize)300 	CGrowableArray(const int nSize)
301 	{
302 		m_p		= NULL;
303 		m_nSize	= nSize;
304 		m_nCnt	= 0;
305 	}
306 
307 /*!***************************************************************************
308  @Function		~CGrowableArray
309  @Description	The destructor
310 *****************************************************************************/
~CGrowableArray()311 	~CGrowableArray()
312 	{
313 		FREE(m_p);
314 	}
315 
316 /*!***************************************************************************
317  @Function		Append
318  @Input			pData			The data to append
319  @Input			nCnt			The amount of data elements to append
320  @Description	Resizes the array and appends the new data that has been passed in.
321 *****************************************************************************/
Append(const void * const pData,const int nCnt)322 	void Append(const void * const pData, const int nCnt)
323 	{
324 		m_p = (char*)realloc(m_p, (m_nCnt + nCnt) * m_nSize);
325 		_ASSERT(m_p);
326 
327 		memcpy(&m_p[m_nCnt * m_nSize], pData, nCnt * m_nSize);
328 		m_nCnt += nCnt;
329 	}
330 
331 /*!***************************************************************************
332  @Function		last
333  @Return		char*			The last element of the array
334  @Description	Returns a pointer to the last element of the array.
335 *****************************************************************************/
last()336 	char *last()
337 	{
338 		return at(m_nCnt-1);
339 	}
340 
341 /*!***************************************************************************
342  @Function		at
343  @Input			nIdx			The index of the requested element
344  @Return		char*			The element at the specified index of the array
345  @Description	Returns a pointer to the data at the specified index of the array.
346 *****************************************************************************/
at(const int nIdx)347 	char *at(const int nIdx)
348 	{
349 		return &m_p[nIdx * m_nSize];
350 	}
351 
352 /*!***************************************************************************
353  @Function		size
354  @Return		int				The number of elements contained in the array
355  @Description	Returns the number of elements contained in the array.
356 *****************************************************************************/
size() const357 	int size() const
358 	{
359 		return m_nCnt;
360 	}
361 
362 /*!***************************************************************************
363  @Function		Surrender
364  @Output		pData			The pointer to surrender the data to
365  @Description	Assigns the memory address of the data to the pointer that has
366 				been passed in. Sets the class's number of elements and
367 				data pointer back to their default values.
368 *****************************************************************************/
Surrender(char ** const pData)369 	int Surrender(
370 		char ** const pData)
371 	{
372 		int nCnt;
373 
374 		*pData = m_p;
375 		nCnt = m_nCnt;
376 
377 		m_p		= NULL;
378 		m_nCnt	= 0;
379 
380 		return nCnt;
381 	}
382 };
383 
384 /****************************************************************************
385 ** Constants
386 ****************************************************************************/
387 
388 /****************************************************************************
389 ** Local function definitions
390 ****************************************************************************/
391 static bool FillBatch(
392 	CBatch					&batch,
393 	const unsigned int	* const pui32Idx,	// input AND output; index array for triangle list
394 	const char				* const pVtx,	// Input vertices
395 	const int				nStride,		// Size of a vertex (in bytes)
396 	const int				nOffsetWeight,	// Offset in bytes to the vertex bone-weights
397 	EPVRTDataType			eTypeWeight,	// Data type of the vertex bone-weights
398 	const int				nOffsetIdx,		// Offset in bytes to the vertex bone-indices
399 	EPVRTDataType			eTypeIdx,		// Data type of the vertex bone-indices
400 	const int				nVertexBones);	// Number of bones affecting each vertex
401 
402 static bool BonesMatch(
403 	const float * const pfIdx0,
404 	const float * const pfIdx1);
405 
406 /*****************************************************************************
407 ** Functions
408 *****************************************************************************/
409 
410 /*!***************************************************************************
411  @Function		Create
412  @Output		pnVtxNumOut		vertex count
413  @Output		pVtxOut			Output vertices (program must free() this)
414  @Modified		pui32Idx			index array for triangle list
415  @Input			nVtxNum			vertex count
416  @Input			pVtx			vertices
417  @Input			nStride			Size of a vertex (in bytes)
418  @Input			nOffsetWeight	Offset in bytes to the vertex bone-weights
419  @Input			eTypeWeight		Data type of the vertex bone-weights
420  @Input			nOffsetIdx		Offset in bytes to the vertex bone-indices
421  @Input			eTypeIdx		Data type of the vertex bone-indices
422  @Input			nTriNum			Number of triangles
423  @Input			nBatchBoneMax	Number of bones a batch can reference
424  @Input			nVertexBones	Number of bones affecting each vertex
425  @Returns		PVR_SUCCESS if successful
426  @Description	Fills the bone batch structure
427 *****************************************************************************/
Create(int * const pnVtxNumOut,char ** const pVtxOut,unsigned int * const pui32Idx,const int nVtxNum,const char * const pVtx,const int nStride,const int nOffsetWeight,const EPVRTDataType eTypeWeight,const int nOffsetIdx,const EPVRTDataType eTypeIdx,const int nTriNum,const int nBatchBoneMax,const int nVertexBones)428 EPVRTError CPVRTBoneBatches::Create(
429 	int					* const pnVtxNumOut,
430 	char				** const pVtxOut,
431 	unsigned int		* const pui32Idx,
432 	const int			nVtxNum,
433 	const char			* const pVtx,
434 	const int			nStride,
435 	const int			nOffsetWeight,
436 	const EPVRTDataType	eTypeWeight,
437 	const int			nOffsetIdx,
438 	const EPVRTDataType	eTypeIdx,
439 	const int			nTriNum,
440 	const int			nBatchBoneMax,
441 	const int			nVertexBones)
442 {
443 	int							i, j, k, nTriCnt;
444 	CBatch						batch;
445 	std::list<CBatch>			lBatch;
446 	std::list<CBatch>::iterator	iBatch, iBatch2;
447 	CBatch						**ppBatch;
448 	unsigned int				*pui32IdxNew;
449 	const char					*pV, *pV2;
450 	PVRTVECTOR4					vWeight, vIdx;
451 	PVRTVECTOR4					vWeight2, vIdx2;
452 	std::vector<int>			*pvDup;
453 	CGrowableArray				*pVtxBuf;
454 	unsigned int				ui32SrcIdx;
455 
456 	memset(this, 0, sizeof(*this));
457 
458 	if(nVertexBones <= 0 || nVertexBones > 4)
459 	{
460 		_RPT0(_CRT_WARN, "CPVRTBoneBatching() will only handle 1..4 bones per vertex.\n");
461 		return PVR_FAIL;
462 	}
463 
464 	memset(&vWeight, 0, sizeof(vWeight));
465 	memset(&vWeight2, 0, sizeof(vWeight2));
466 	memset(&vIdx, 0, sizeof(vIdx));
467 	memset(&vIdx2, 0, sizeof(vIdx2));
468 
469 	batch.SetSize(nBatchBoneMax);
470 
471 	// Allocate some working space
472 	ppBatch		= (CBatch**)malloc(nTriNum * sizeof(*ppBatch));
473 	pui32IdxNew	= (unsigned int*)malloc(nTriNum * 3 * sizeof(*pui32IdxNew));
474 	pvDup		= new std::vector<int>[nVtxNum];
475 	pVtxBuf		= new CGrowableArray(nStride);
476 
477 	// Check what batches are necessary
478 	for(i = 0; i < nTriNum; ++i)
479 	{
480 		// Build the batch
481 		if(!FillBatch(batch, &pui32Idx[i * 3], pVtx, nStride, nOffsetWeight, eTypeWeight, nOffsetIdx, eTypeIdx, nVertexBones))
482 		{
483 			free(pui32IdxNew);
484 			return PVR_FAIL;
485 		}
486 
487 		// Update the batch list
488 		for(iBatch = lBatch.begin(); iBatch != lBatch.end(); ++iBatch)
489 		{
490 			// Do nothing if an existing batch is a superset of this new batch
491 			if(iBatch->Contains(batch))
492 			{
493 				break;
494 			}
495 
496 			// If this new batch is a superset of an existing batch, replace the old with the new
497 			if(batch.Contains(*iBatch))
498 			{
499 				*iBatch = batch;
500 				break;
501 			}
502 		}
503 
504 		// If no suitable batch exists, create a new one
505 		if(iBatch == lBatch.end())
506 		{
507 			lBatch.push_back(batch);
508 		}
509 	}
510 
511 	//	Group batches into fewer batches. This simple greedy algorithm could be improved.
512 		int							nCurrent, nShortest;
513 		std::list<CBatch>::iterator	iShortest;
514 
515 		for(iBatch = lBatch.begin(); iBatch != lBatch.end(); ++iBatch)
516 		{
517 			for(;;)
518 			{
519 				nShortest	= nBatchBoneMax;
520 				iBatch2		= iBatch;
521 				++iBatch2;
522 				for(; iBatch2 != lBatch.end(); ++iBatch2)
523 				{
524 					nCurrent = iBatch->TestMerge(*iBatch2);
525 
526 					if(nCurrent >= 0 && nCurrent < nShortest)
527 					{
528 						nShortest	= nCurrent;
529 						iShortest	= iBatch2;
530 					}
531 				}
532 
533 				if(nShortest < nBatchBoneMax)
534 				{
535 					iBatch->Merge(*iShortest);
536 					lBatch.erase(iShortest);
537 				}
538 				else
539 				{
540 					break;
541 				}
542 			}
543 		}
544 
545 	// Place each triangle in a batch.
546 	for(i = 0; i < nTriNum; ++i)
547 	{
548 		if(!FillBatch(batch, &pui32Idx[i * 3], pVtx, nStride, nOffsetWeight, eTypeWeight, nOffsetIdx, eTypeIdx, nVertexBones))
549 		{
550 			free(pui32IdxNew);
551 			return PVR_FAIL;
552 		}
553 
554 		for(iBatch = lBatch.begin(); iBatch != lBatch.end(); ++iBatch)
555 		{
556 			if(iBatch->Contains(batch))
557 			{
558 				ppBatch[i] = &*iBatch;
559 				break;
560 			}
561 		}
562 
563 		_ASSERT(iBatch != lBatch.end());
564 	}
565 
566 	// Now that we know how many batches there are, we can allocate the output arrays
567 	CPVRTBoneBatches::nBatchBoneMax = nBatchBoneMax;
568 	pnBatches		= (int*) calloc(lBatch.size() * nBatchBoneMax, sizeof(*pnBatches));
569 	pnBatchBoneCnt	= (int*) calloc(lBatch.size(), sizeof(*pnBatchBoneCnt));
570 	pnBatchOffset	= (int*) calloc(lBatch.size(), sizeof(*pnBatchOffset));
571 
572 	// Create the new triangle index list, the new vertex list, and the batch information.
573 	nTriCnt = 0;
574 	nBatchCnt = 0;
575 
576 	for(iBatch = lBatch.begin(); iBatch != lBatch.end(); ++iBatch)
577 	{
578 		// Write pnBatches, pnBatchBoneCnt and pnBatchOffset for this batch.
579 		iBatch->Write(&pnBatches[nBatchCnt * nBatchBoneMax], &pnBatchBoneCnt[nBatchCnt]);
580 		pnBatchOffset[nBatchCnt] = nTriCnt;
581 		++nBatchCnt;
582 
583 		// Copy any triangle indices for this batch
584 		for(i = 0; i < nTriNum; ++i)
585 		{
586 			if(ppBatch[i] != &*iBatch)
587 				continue;
588 
589 			for(j = 0; j < 3; ++j)
590 			{
591 				ui32SrcIdx = pui32Idx[3 * i + j];
592 
593 				// Get desired bone indices for this vertex/tri
594 				pV = &pVtx[ui32SrcIdx * nStride];
595 
596 				PVRTVertexRead(&vWeight, &pV[nOffsetWeight], eTypeWeight, nVertexBones);
597 				PVRTVertexRead(&vIdx, &pV[nOffsetIdx], eTypeIdx, nVertexBones);
598 
599 				iBatch->GetVertexBoneIndices(&vIdx.x, &vWeight.x, nVertexBones);
600 				_ASSERT(vIdx.x == 0 || vIdx.x != vIdx.y);
601 
602 				// Check the list of copies of this vertex for one with suitable bone indices
603 				for(k = 0; k < (int)pvDup[ui32SrcIdx].size(); ++k)
604 				{
605 					pV2 = pVtxBuf->at(pvDup[ui32SrcIdx][k]);
606 
607 					PVRTVertexRead(&vWeight2, &pV2[nOffsetWeight], eTypeWeight, nVertexBones);
608 					PVRTVertexRead(&vIdx2, &pV2[nOffsetIdx], eTypeIdx, nVertexBones);
609 
610 					if(BonesMatch(&vIdx2.x, &vIdx.x))
611 					{
612 						pui32IdxNew[3 * nTriCnt + j] = pvDup[ui32SrcIdx][k];
613 						break;
614 					}
615 				}
616 
617 				if(k != (int)pvDup[ui32SrcIdx].size())
618 					continue;
619 
620 				//	Did not find a suitable duplicate of the vertex, so create one
621 				pVtxBuf->Append(pV, 1);
622 				pvDup[ui32SrcIdx].push_back(pVtxBuf->size() - 1);
623 
624 				PVRTVertexWrite(&pVtxBuf->last()[nOffsetIdx], eTypeIdx, nVertexBones, &vIdx);
625 
626 				pui32IdxNew[3 * nTriCnt + j] = pVtxBuf->size() - 1;
627 			}
628 			++nTriCnt;
629 		}
630 	}
631 	_ASSERTE(nTriCnt == nTriNum);
632 	_ASSERTE(nBatchCnt == (int)lBatch.size());
633 
634 	//	Copy indices to output
635 	memcpy(pui32Idx, pui32IdxNew, nTriNum * 3 * sizeof(*pui32IdxNew));
636 
637 	//	Move vertices to output
638 	*pnVtxNumOut = pVtxBuf->Surrender(pVtxOut);
639 
640 	//	Free working memory
641 	delete [] pvDup;
642 	delete pVtxBuf;
643 	FREE(ppBatch);
644 	FREE(pui32IdxNew);
645 
646 	return PVR_SUCCESS;
647 }
648 
649 /****************************************************************************
650 ** Local functions
651 ****************************************************************************/
652 
653 /*!***********************************************************************
654  @Function		FillBatch
655  @Modified		batch 			The batch to fill
656  @Input			pui32Idx		Input index array for triangle list
657  @Input			pVtx			Input vertices
658  @Input			nStride			Size of a vertex (in bytes)
659  @Input			nOffsetWeight	Offset in bytes to the vertex bone-weights
660  @Input			eTypeWeight		Data type of the vertex bone-weights
661  @Input			nOffsetIdx		Offset in bytes to the vertex bone-indices
662  @Input			eTypeIdx		Data type of the vertex bone-indices
663  @Input			nVertexBones	Number of bones affecting each vertex
664  @Returns		True if successful
665  @Description	Creates a bone batch from a triangle.
666 *************************************************************************/
FillBatch(CBatch & batch,const unsigned int * const pui32Idx,const char * const pVtx,const int nStride,const int nOffsetWeight,EPVRTDataType eTypeWeight,const int nOffsetIdx,EPVRTDataType eTypeIdx,const int nVertexBones)667 static bool FillBatch(
668 	CBatch					&batch,
669 	const unsigned int	* const pui32Idx,
670 	const char				* const pVtx,
671 	const int				nStride,
672 	const int				nOffsetWeight,
673 	EPVRTDataType			eTypeWeight,
674 	const int				nOffsetIdx,
675 	EPVRTDataType			eTypeIdx,
676 	const int				nVertexBones)
677 {
678 	PVRTVECTOR4	vWeight, vIdx;
679 	const char	*pV;
680 	int			i;
681 	bool		bOk;
682 
683 	bOk = true;
684 	batch.Clear();
685 	for(i = 0; i < 3; ++i)
686 	{
687 		pV = &pVtx[pui32Idx[i] * nStride];
688 
689 		memset(&vWeight, 0, sizeof(vWeight));
690 		PVRTVertexRead(&vWeight, &pV[nOffsetWeight], eTypeWeight, nVertexBones);
691 		PVRTVertexRead(&vIdx, &pV[nOffsetIdx], eTypeIdx, nVertexBones);
692 
693 		if(nVertexBones >= 1 && vWeight.x != 0)	bOk &= batch.Add((int)vIdx.x);
694 		if(nVertexBones >= 2 && vWeight.y != 0)	bOk &= batch.Add((int)vIdx.y);
695 		if(nVertexBones >= 3 && vWeight.z != 0)	bOk &= batch.Add((int)vIdx.z);
696 		if(nVertexBones >= 4 && vWeight.w != 0)	bOk &= batch.Add((int)vIdx.w);
697 	}
698 	return bOk;
699 }
700 
701 /*!***********************************************************************
702  @Function		BonesMatch
703  @Input			pfIdx0 A float 4 array
704  @Input			pfIdx1 A float 4 array
705  @Returns		True if the two float4 arraus are identical
706  @Description	Checks if the two float4 arrays are identical.
707 *************************************************************************/
BonesMatch(const float * const pfIdx0,const float * const pfIdx1)708 static bool BonesMatch(
709 	const float * const pfIdx0,
710 	const float * const pfIdx1)
711 {
712 	int i;
713 
714 	for(i = 0; i < 4; ++i)
715 	{
716 		if(pfIdx0[i] != pfIdx1[i])
717 			return false;
718 	}
719 
720 	return true;
721 }
722 
723 /*****************************************************************************
724  End of file (PVRTBoneBatch.cpp)
725 *****************************************************************************/
726 
727