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