/*!*********************************************************************** @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 Name : FillBatch * Inputs : * Returns : * Description : Creates a bone batch from a triangle. ****************************************************************************/ static bool FillBatch( CBatch &batch, const unsigned short * const pwIdx, // input 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 { PVRTVECTOR4 vWeight, vIdx; const char *pV; int i; bool bOk; bOk = true; batch.Clear(); for(i = 0; i < 3; ++i) { pV = &pVtx[pwIdx[i] * nStride]; memset(&vWeight, 0, sizeof(vWeight)); PVRTDataTypeRead(&vWeight, &pV[nOffsetWeight], eTypeWeight, nVertexBones); PVRTDataTypeRead(&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 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; }