|  | /****************************************************************************** | 
|  |  | 
|  | @File         PVRTBoneBatch.cpp | 
|  |  | 
|  | @Title        PVRTBoneBatch | 
|  |  | 
|  | @Version | 
|  |  | 
|  | @Copyright    Copyright (c) Imagination Technologies Limited. | 
|  |  | 
|  | @Platform     ANSI compatible | 
|  |  | 
|  | @Description  Utility functions which process vertices. | 
|  |  | 
|  | ******************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Includes | 
|  | ****************************************************************************/ | 
|  | #include "PVRTGlobal.h" | 
|  | #include "PVRTContext.h" | 
|  |  | 
|  | #include <vector> | 
|  | #include <list> | 
|  |  | 
|  | #include "PVRTMatrix.h" | 
|  | #include "PVRTVertex.h" | 
|  | #include "PVRTBoneBatch.h" | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Defines | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Macros | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Structures | 
|  | ****************************************************************************/ | 
|  | /*!*************************************************************************** | 
|  | @Class CBatch | 
|  | @Brief Class to contain and manage batch information. | 
|  | *****************************************************************************/ | 
|  | class CBatch | 
|  | { | 
|  | protected: | 
|  | int	m_nCapacity, 		// Maximum size of the batch | 
|  | m_nCnt, 				// Number of elements currently contained in the batch | 
|  | *m_pnPalette;			// Array of palette indices | 
|  |  | 
|  | public: | 
|  | /*!*************************************************************************** | 
|  | @Function		CBatch | 
|  | @Description	The default constructor | 
|  | *****************************************************************************/ | 
|  | CBatch() : 	m_nCapacity(0), | 
|  | m_nCnt(0), | 
|  | m_pnPalette(0) | 
|  | { | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		CBatch | 
|  | @Input			src				CBatch to copy | 
|  | @Description	Copy constructor | 
|  | *****************************************************************************/ | 
|  | CBatch(const CBatch &src) : m_pnPalette(0) | 
|  | { | 
|  | SetSize(src.m_nCapacity); | 
|  | *this = src; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		~CBatch | 
|  | @Description	Destructor | 
|  | *****************************************************************************/ | 
|  | ~CBatch() | 
|  | { | 
|  | FREE(m_pnPalette); | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		operator= | 
|  | @Description	Operator overload for the '=' operand | 
|  | *****************************************************************************/ | 
|  | CBatch& operator= (const CBatch &src) | 
|  | { | 
|  | _ASSERT(m_nCapacity == src.m_nCapacity); | 
|  | m_nCnt = src.m_nCnt; | 
|  | memcpy(m_pnPalette, src.m_pnPalette, m_nCnt * sizeof(*m_pnPalette)); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		SetSize | 
|  | @Input			nSize			The new size of the batch | 
|  | @Description	Delete all current information and resizes the batch | 
|  | to the value that has been passed in. | 
|  | *****************************************************************************/ | 
|  | void SetSize(const int nSize) | 
|  | { | 
|  | FREE(m_pnPalette); | 
|  |  | 
|  | m_nCapacity	= nSize; | 
|  | m_nCnt		= 0; | 
|  | m_pnPalette		= (int*)malloc(m_nCapacity * sizeof(*m_pnPalette)); | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		Clear | 
|  | @Description	Resets the count | 
|  | *****************************************************************************/ | 
|  | void Clear() | 
|  | { | 
|  | m_nCnt = 0; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		Clear | 
|  | @Input			n			The index of the new item | 
|  | Return			bool		Returns true if the item already exists or has been added. | 
|  | @Description	Adds a new item to the batch, providing it has not already | 
|  | been added to the batch and the count doesn't exceed the | 
|  | maximum number of bones the batch can hold. | 
|  | *****************************************************************************/ | 
|  | bool Add(const int n) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if(n < 0) | 
|  | return false; | 
|  |  | 
|  | // If we already have this item, do nothing | 
|  | for(i = 0; i < m_nCnt; ++i) | 
|  | { | 
|  | if(m_pnPalette[i] == n) | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Add the new item | 
|  | if(m_nCnt < m_nCapacity) | 
|  | { | 
|  | m_pnPalette[m_nCnt] = n; | 
|  | ++m_nCnt; | 
|  | return true; | 
|  | } | 
|  | else | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		Merge | 
|  | @Input			src				The batch to merge with | 
|  | @Description	Merges the input batch with the current batch. | 
|  | *****************************************************************************/ | 
|  | void Merge(const CBatch &src) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for(i = 0; i < src.m_nCnt; ++i) | 
|  | Add(src.m_pnPalette[i]); | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		TestMerge | 
|  | @Input			src				The batch to merge with | 
|  | @Return		int				The number of items that are not already | 
|  | present in the batch. -1 if the merge will | 
|  | exceed the capacity of the batch | 
|  | @Description	Tests how many of the items of the input batch are not | 
|  | already contained in the batch. This returns the number of | 
|  | items that would need to be added, or -1 if the number | 
|  | of additional items would exceed the capacity of the batch. | 
|  | *****************************************************************************/ | 
|  | int TestMerge(const CBatch &src) | 
|  | { | 
|  | int i, nCnt; | 
|  |  | 
|  | nCnt = 0; | 
|  | for(i = 0; i < src.m_nCnt; ++i) | 
|  | if(!Contains(src.m_pnPalette[i])) | 
|  | ++nCnt; | 
|  |  | 
|  | return m_nCnt+nCnt > m_nCapacity ? -1 : nCnt; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		Contains | 
|  | @Input			src				The batch to compare | 
|  | @Return		bool			Returns true if the batch and the input batch | 
|  | have at least one item in common | 
|  | @Description	Returns true if the batch's have at least one item in common | 
|  | *****************************************************************************/ | 
|  | bool Contains(const CBatch &batch) const | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for(i = 0; i < batch.m_nCnt; ++i) | 
|  | if(!Contains(batch.m_pnPalette[i])) | 
|  | return false; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		Contains | 
|  | @Input			n				The index of the new item | 
|  | @Return		bool			Returns true if the batch contains the item | 
|  | @Description	Returns true if the batch contains the item. | 
|  | *****************************************************************************/ | 
|  | bool Contains(const int n) const | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for(i = 0; i < m_nCnt; ++i) | 
|  | if(m_pnPalette[i] == n) | 
|  | return true; | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		Write | 
|  | @Output		pn				The array of items to overwrite | 
|  | @Output		pnCnt			The number of items in the array | 
|  | @Description	Writes the array of items and the number of items to the output | 
|  | parameters. | 
|  | *****************************************************************************/ | 
|  | void Write( | 
|  | int * const pn, | 
|  | int * const pnCnt) const | 
|  | { | 
|  | memcpy(pn, m_pnPalette, m_nCnt * sizeof(*pn)); | 
|  | *pnCnt = m_nCnt; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		GetVertexBoneIndices | 
|  | @Modified		pfI				Returned index | 
|  | @Input			pfW				Weight? | 
|  | @Input			n				Length of index array | 
|  | @Description	For each element of the input array, the index value is compared | 
|  | with the palette's index value. If the values are equal, the | 
|  | value of the current input array element is replaced with the | 
|  | palette index, otherwise the value is set to zero. | 
|  | *****************************************************************************/ | 
|  | void GetVertexBoneIndices( | 
|  | float		* const pfI, | 
|  | const float	* const pfW, | 
|  | const int	n) | 
|  | { | 
|  | int i, j; | 
|  |  | 
|  | for(i = 0; i < n; ++i) | 
|  | { | 
|  | if(pfW[i] != 0) | 
|  | { | 
|  | for(j = 0; j < m_nCnt; ++j) | 
|  | { | 
|  | if(pfI[i] != m_pnPalette[j]) | 
|  | continue; | 
|  |  | 
|  | pfI[i] = (float)j; | 
|  | break; | 
|  | } | 
|  |  | 
|  | // This batch *must* contain this vertex | 
|  | _ASSERT(j != m_nCnt); | 
|  | } | 
|  | else | 
|  | { | 
|  | pfI[i] = 0; | 
|  | } | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Class CGrowableArray | 
|  | @Brief Class that provides an array structure that can change its size dynamically. | 
|  | *****************************************************************************/ | 
|  | class CGrowableArray | 
|  | { | 
|  | protected: | 
|  | char	*m_p; | 
|  | int		m_nSize; | 
|  | int		m_nCnt; | 
|  |  | 
|  | public: | 
|  | /*!*************************************************************************** | 
|  | @Function		CGrowableArray | 
|  | @Input			nSize			The size of the data (in bytes) that the array will contain | 
|  | @Description	Initialises the size of the data the array will contain to the | 
|  | value that has been passed in and initialises the remaining | 
|  | data members with default values. | 
|  | *****************************************************************************/ | 
|  | CGrowableArray(const int nSize) | 
|  | { | 
|  | m_p		= NULL; | 
|  | m_nSize	= nSize; | 
|  | m_nCnt	= 0; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		~CGrowableArray | 
|  | @Description	The destructor | 
|  | *****************************************************************************/ | 
|  | ~CGrowableArray() | 
|  | { | 
|  | FREE(m_p); | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		Append | 
|  | @Input			pData			The data to append | 
|  | @Input			nCnt			The amount of data elements to append | 
|  | @Description	Resizes the array and appends the new data that has been passed in. | 
|  | *****************************************************************************/ | 
|  | void Append(const void * const pData, const int nCnt) | 
|  | { | 
|  | m_p = (char*)realloc(m_p, (m_nCnt + nCnt) * m_nSize); | 
|  | _ASSERT(m_p); | 
|  |  | 
|  | memcpy(&m_p[m_nCnt * m_nSize], pData, nCnt * m_nSize); | 
|  | m_nCnt += nCnt; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		last | 
|  | @Return		char*			The last element of the array | 
|  | @Description	Returns a pointer to the last element of the array. | 
|  | *****************************************************************************/ | 
|  | char *last() | 
|  | { | 
|  | return at(m_nCnt-1); | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		at | 
|  | @Input			nIdx			The index of the requested element | 
|  | @Return		char*			The element at the specified index of the array | 
|  | @Description	Returns a pointer to the data at the specified index of the array. | 
|  | *****************************************************************************/ | 
|  | char *at(const int nIdx) | 
|  | { | 
|  | return &m_p[nIdx * m_nSize]; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		size | 
|  | @Return		int				The number of elements contained in the array | 
|  | @Description	Returns the number of elements contained in the array. | 
|  | *****************************************************************************/ | 
|  | int size() const | 
|  | { | 
|  | return m_nCnt; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		Surrender | 
|  | @Output		pData			The pointer to surrender the data to | 
|  | @Description	Assigns the memory address of the data to the pointer that has | 
|  | been passed in. Sets the class's number of elements and | 
|  | data pointer back to their default values. | 
|  | *****************************************************************************/ | 
|  | int Surrender( | 
|  | char ** const pData) | 
|  | { | 
|  | int nCnt; | 
|  |  | 
|  | *pData = m_p; | 
|  | nCnt = m_nCnt; | 
|  |  | 
|  | m_p		= NULL; | 
|  | m_nCnt	= 0; | 
|  |  | 
|  | return nCnt; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Constants | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Local function definitions | 
|  | ****************************************************************************/ | 
|  | static bool FillBatch( | 
|  | CBatch					&batch, | 
|  | const unsigned int	* const pui32Idx,	// input AND output; index array for triangle list | 
|  | const char				* const pVtx,	// Input vertices | 
|  | const int				nStride,		// Size of a vertex (in bytes) | 
|  | const int				nOffsetWeight,	// Offset in bytes to the vertex bone-weights | 
|  | EPVRTDataType			eTypeWeight,	// Data type of the vertex bone-weights | 
|  | const int				nOffsetIdx,		// Offset in bytes to the vertex bone-indices | 
|  | EPVRTDataType			eTypeIdx,		// Data type of the vertex bone-indices | 
|  | const int				nVertexBones);	// Number of bones affecting each vertex | 
|  |  | 
|  | static bool BonesMatch( | 
|  | const float * const pfIdx0, | 
|  | const float * const pfIdx1); | 
|  |  | 
|  | /***************************************************************************** | 
|  | ** Functions | 
|  | *****************************************************************************/ | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		Create | 
|  | @Output		pnVtxNumOut		vertex count | 
|  | @Output		pVtxOut			Output vertices (program must free() this) | 
|  | @Modified		pui32Idx			index array for triangle list | 
|  | @Input			nVtxNum			vertex count | 
|  | @Input			pVtx			vertices | 
|  | @Input			nStride			Size of a vertex (in bytes) | 
|  | @Input			nOffsetWeight	Offset in bytes to the vertex bone-weights | 
|  | @Input			eTypeWeight		Data type of the vertex bone-weights | 
|  | @Input			nOffsetIdx		Offset in bytes to the vertex bone-indices | 
|  | @Input			eTypeIdx		Data type of the vertex bone-indices | 
|  | @Input			nTriNum			Number of triangles | 
|  | @Input			nBatchBoneMax	Number of bones a batch can reference | 
|  | @Input			nVertexBones	Number of bones affecting each vertex | 
|  | @Returns		PVR_SUCCESS if successful | 
|  | @Description	Fills the bone batch structure | 
|  | *****************************************************************************/ | 
|  | EPVRTError CPVRTBoneBatches::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) | 
|  | { | 
|  | int							i, j, k, nTriCnt; | 
|  | CBatch						batch; | 
|  | std::list<CBatch>			lBatch; | 
|  | std::list<CBatch>::iterator	iBatch, iBatch2; | 
|  | CBatch						**ppBatch; | 
|  | unsigned int				*pui32IdxNew; | 
|  | const char					*pV, *pV2; | 
|  | PVRTVECTOR4					vWeight, vIdx; | 
|  | PVRTVECTOR4					vWeight2, vIdx2; | 
|  | std::vector<int>			*pvDup; | 
|  | CGrowableArray				*pVtxBuf; | 
|  | unsigned int				ui32SrcIdx; | 
|  |  | 
|  | memset(this, 0, sizeof(*this)); | 
|  |  | 
|  | if(nVertexBones <= 0 || nVertexBones > 4) | 
|  | { | 
|  | _RPT0(_CRT_WARN, "CPVRTBoneBatching() will only handle 1..4 bones per vertex.\n"); | 
|  | return PVR_FAIL; | 
|  | } | 
|  |  | 
|  | memset(&vWeight, 0, sizeof(vWeight)); | 
|  | memset(&vWeight2, 0, sizeof(vWeight2)); | 
|  | memset(&vIdx, 0, sizeof(vIdx)); | 
|  | memset(&vIdx2, 0, sizeof(vIdx2)); | 
|  |  | 
|  | batch.SetSize(nBatchBoneMax); | 
|  |  | 
|  | // Allocate some working space | 
|  | ppBatch		= (CBatch**)malloc(nTriNum * sizeof(*ppBatch)); | 
|  | pui32IdxNew	= (unsigned int*)malloc(nTriNum * 3 * sizeof(*pui32IdxNew)); | 
|  | pvDup		= new std::vector<int>[nVtxNum]; | 
|  | pVtxBuf		= new CGrowableArray(nStride); | 
|  |  | 
|  | // Check what batches are necessary | 
|  | for(i = 0; i < nTriNum; ++i) | 
|  | { | 
|  | // Build the batch | 
|  | if(!FillBatch(batch, &pui32Idx[i * 3], pVtx, nStride, nOffsetWeight, eTypeWeight, nOffsetIdx, eTypeIdx, nVertexBones)) | 
|  | { | 
|  | free(pui32IdxNew); | 
|  | return PVR_FAIL; | 
|  | } | 
|  |  | 
|  | // Update the batch list | 
|  | for(iBatch = lBatch.begin(); iBatch != lBatch.end(); ++iBatch) | 
|  | { | 
|  | // Do nothing if an existing batch is a superset of this new batch | 
|  | if(iBatch->Contains(batch)) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | // If this new batch is a superset of an existing batch, replace the old with the new | 
|  | if(batch.Contains(*iBatch)) | 
|  | { | 
|  | *iBatch = batch; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // If no suitable batch exists, create a new one | 
|  | if(iBatch == lBatch.end()) | 
|  | { | 
|  | lBatch.push_back(batch); | 
|  | } | 
|  | } | 
|  |  | 
|  | //	Group batches into fewer batches. This simple greedy algorithm could be improved. | 
|  | int							nCurrent, nShortest; | 
|  | std::list<CBatch>::iterator	iShortest; | 
|  |  | 
|  | for(iBatch = lBatch.begin(); iBatch != lBatch.end(); ++iBatch) | 
|  | { | 
|  | for(;;) | 
|  | { | 
|  | nShortest	= nBatchBoneMax; | 
|  | iBatch2		= iBatch; | 
|  | ++iBatch2; | 
|  | for(; iBatch2 != lBatch.end(); ++iBatch2) | 
|  | { | 
|  | nCurrent = iBatch->TestMerge(*iBatch2); | 
|  |  | 
|  | if(nCurrent >= 0 && nCurrent < nShortest) | 
|  | { | 
|  | nShortest	= nCurrent; | 
|  | iShortest	= iBatch2; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(nShortest < nBatchBoneMax) | 
|  | { | 
|  | iBatch->Merge(*iShortest); | 
|  | lBatch.erase(iShortest); | 
|  | } | 
|  | else | 
|  | { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Place each triangle in a batch. | 
|  | for(i = 0; i < nTriNum; ++i) | 
|  | { | 
|  | if(!FillBatch(batch, &pui32Idx[i * 3], pVtx, nStride, nOffsetWeight, eTypeWeight, nOffsetIdx, eTypeIdx, nVertexBones)) | 
|  | { | 
|  | free(pui32IdxNew); | 
|  | return PVR_FAIL; | 
|  | } | 
|  |  | 
|  | for(iBatch = lBatch.begin(); iBatch != lBatch.end(); ++iBatch) | 
|  | { | 
|  | if(iBatch->Contains(batch)) | 
|  | { | 
|  | ppBatch[i] = &*iBatch; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | _ASSERT(iBatch != lBatch.end()); | 
|  | } | 
|  |  | 
|  | // Now that we know how many batches there are, we can allocate the output arrays | 
|  | CPVRTBoneBatches::nBatchBoneMax = nBatchBoneMax; | 
|  | pnBatches		= (int*) calloc(lBatch.size() * nBatchBoneMax, sizeof(*pnBatches)); | 
|  | pnBatchBoneCnt	= (int*) calloc(lBatch.size(), sizeof(*pnBatchBoneCnt)); | 
|  | pnBatchOffset	= (int*) calloc(lBatch.size(), sizeof(*pnBatchOffset)); | 
|  |  | 
|  | // Create the new triangle index list, the new vertex list, and the batch information. | 
|  | nTriCnt = 0; | 
|  | nBatchCnt = 0; | 
|  |  | 
|  | for(iBatch = lBatch.begin(); iBatch != lBatch.end(); ++iBatch) | 
|  | { | 
|  | // Write pnBatches, pnBatchBoneCnt and pnBatchOffset for this batch. | 
|  | iBatch->Write(&pnBatches[nBatchCnt * nBatchBoneMax], &pnBatchBoneCnt[nBatchCnt]); | 
|  | pnBatchOffset[nBatchCnt] = nTriCnt; | 
|  | ++nBatchCnt; | 
|  |  | 
|  | // Copy any triangle indices for this batch | 
|  | for(i = 0; i < nTriNum; ++i) | 
|  | { | 
|  | if(ppBatch[i] != &*iBatch) | 
|  | continue; | 
|  |  | 
|  | for(j = 0; j < 3; ++j) | 
|  | { | 
|  | ui32SrcIdx = pui32Idx[3 * i + j]; | 
|  |  | 
|  | // Get desired bone indices for this vertex/tri | 
|  | pV = &pVtx[ui32SrcIdx * nStride]; | 
|  |  | 
|  | PVRTVertexRead(&vWeight, &pV[nOffsetWeight], eTypeWeight, nVertexBones); | 
|  | PVRTVertexRead(&vIdx, &pV[nOffsetIdx], eTypeIdx, nVertexBones); | 
|  |  | 
|  | iBatch->GetVertexBoneIndices(&vIdx.x, &vWeight.x, nVertexBones); | 
|  | _ASSERT(vIdx.x == 0 || vIdx.x != vIdx.y); | 
|  |  | 
|  | // Check the list of copies of this vertex for one with suitable bone indices | 
|  | for(k = 0; k < (int)pvDup[ui32SrcIdx].size(); ++k) | 
|  | { | 
|  | pV2 = pVtxBuf->at(pvDup[ui32SrcIdx][k]); | 
|  |  | 
|  | PVRTVertexRead(&vWeight2, &pV2[nOffsetWeight], eTypeWeight, nVertexBones); | 
|  | PVRTVertexRead(&vIdx2, &pV2[nOffsetIdx], eTypeIdx, nVertexBones); | 
|  |  | 
|  | if(BonesMatch(&vIdx2.x, &vIdx.x)) | 
|  | { | 
|  | pui32IdxNew[3 * nTriCnt + j] = pvDup[ui32SrcIdx][k]; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(k != (int)pvDup[ui32SrcIdx].size()) | 
|  | continue; | 
|  |  | 
|  | //	Did not find a suitable duplicate of the vertex, so create one | 
|  | pVtxBuf->Append(pV, 1); | 
|  | pvDup[ui32SrcIdx].push_back(pVtxBuf->size() - 1); | 
|  |  | 
|  | PVRTVertexWrite(&pVtxBuf->last()[nOffsetIdx], eTypeIdx, nVertexBones, &vIdx); | 
|  |  | 
|  | pui32IdxNew[3 * nTriCnt + j] = pVtxBuf->size() - 1; | 
|  | } | 
|  | ++nTriCnt; | 
|  | } | 
|  | } | 
|  | _ASSERTE(nTriCnt == nTriNum); | 
|  | _ASSERTE(nBatchCnt == (int)lBatch.size()); | 
|  |  | 
|  | //	Copy indices to output | 
|  | memcpy(pui32Idx, pui32IdxNew, nTriNum * 3 * sizeof(*pui32IdxNew)); | 
|  |  | 
|  | //	Move vertices to output | 
|  | *pnVtxNumOut = pVtxBuf->Surrender(pVtxOut); | 
|  |  | 
|  | //	Free working memory | 
|  | delete [] pvDup; | 
|  | delete pVtxBuf; | 
|  | FREE(ppBatch); | 
|  | FREE(pui32IdxNew); | 
|  |  | 
|  | return PVR_SUCCESS; | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Local functions | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /*!*********************************************************************** | 
|  | @Function		FillBatch | 
|  | @Modified		batch 			The batch to fill | 
|  | @Input			pui32Idx		Input index array for triangle list | 
|  | @Input			pVtx			Input vertices | 
|  | @Input			nStride			Size of a vertex (in bytes) | 
|  | @Input			nOffsetWeight	Offset in bytes to the vertex bone-weights | 
|  | @Input			eTypeWeight		Data type of the vertex bone-weights | 
|  | @Input			nOffsetIdx		Offset in bytes to the vertex bone-indices | 
|  | @Input			eTypeIdx		Data type of the vertex bone-indices | 
|  | @Input			nVertexBones	Number of bones affecting each vertex | 
|  | @Returns		True if successful | 
|  | @Description	Creates a bone batch from a triangle. | 
|  | *************************************************************************/ | 
|  | static bool 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) | 
|  | { | 
|  | PVRTVECTOR4	vWeight, vIdx; | 
|  | const char	*pV; | 
|  | int			i; | 
|  | bool		bOk; | 
|  |  | 
|  | bOk = true; | 
|  | batch.Clear(); | 
|  | for(i = 0; i < 3; ++i) | 
|  | { | 
|  | pV = &pVtx[pui32Idx[i] * nStride]; | 
|  |  | 
|  | memset(&vWeight, 0, sizeof(vWeight)); | 
|  | PVRTVertexRead(&vWeight, &pV[nOffsetWeight], eTypeWeight, nVertexBones); | 
|  | PVRTVertexRead(&vIdx, &pV[nOffsetIdx], eTypeIdx, nVertexBones); | 
|  |  | 
|  | if(nVertexBones >= 1 && vWeight.x != 0)	bOk &= batch.Add((int)vIdx.x); | 
|  | if(nVertexBones >= 2 && vWeight.y != 0)	bOk &= batch.Add((int)vIdx.y); | 
|  | if(nVertexBones >= 3 && vWeight.z != 0)	bOk &= batch.Add((int)vIdx.z); | 
|  | if(nVertexBones >= 4 && vWeight.w != 0)	bOk &= batch.Add((int)vIdx.w); | 
|  | } | 
|  | return bOk; | 
|  | } | 
|  |  | 
|  | /*!*********************************************************************** | 
|  | @Function		BonesMatch | 
|  | @Input			pfIdx0 A float 4 array | 
|  | @Input			pfIdx1 A float 4 array | 
|  | @Returns		True if the two float4 arraus are identical | 
|  | @Description	Checks if the two float4 arrays are identical. | 
|  | *************************************************************************/ | 
|  | static bool BonesMatch( | 
|  | const float * const pfIdx0, | 
|  | const float * const pfIdx1) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for(i = 0; i < 4; ++i) | 
|  | { | 
|  | if(pfIdx0[i] != pfIdx1[i]) | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /***************************************************************************** | 
|  | End of file (PVRTBoneBatch.cpp) | 
|  | *****************************************************************************/ | 
|  |  |