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