|
|
//====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#include "mdllib_common.h"
#include "mdllib_stripinfo.h"
#include "mdllib_utils.h"
#include "studio.h"
#include "optimize.h"
#include "smartptr.h"
#include "edge/libedgegeomtool/libedgegeomtool.h"
#include "vjobs/ibmarkup_shared.h"
DECLARE_LOGGING_CHANNEL( LOG_ModelLib );
#define DmaTagsListSize2(var) ( (var)[0] + (var)[1] )
#define DmaTagsListSize3(var) ( DmaTagsListSize2(var) + (var)[2] )
static void Helper_SwapInsideIndexBuffer_forPs3( void *pvMemory, uint32 uiSize ) { uint16 *puiMemory = reinterpret_cast< uint16 * >( pvMemory ); uiSize /= 2;
for ( uint32 j = 0; j < uiSize/2; ++ j ) { uint16 uiTemp = puiMemory[j]; puiMemory[j] = puiMemory[ uiSize - j - 1 ]; puiMemory[ uiSize - j - 1 ] = uiTemp; } }
static void Helper_SwapOptimizedIndexBufferMarkup_forPs3( OptimizedModel::OptimizedIndexBufferMarkupPs3_t *pMarkup ) { for ( int j = 0; j < pMarkup->m_numPartitions; ++ j ) { OptimizedModel::OptimizedIndexBufferMarkupPs3_t::Partition_t &partition = pMarkup->m_partitions[j]; Helper_SwapInsideIndexBuffer_forPs3( &partition.m_numIndicesToSkipInIBs, sizeof( partition.m_numIndicesToSkipInIBs ) ); Helper_SwapInsideIndexBuffer_forPs3( &partition.m_numVerticesToSkipInVBs, sizeof( partition.m_numVerticesToSkipInVBs ) ); Helper_SwapInsideIndexBuffer_forPs3( &partition.m_nIoBufferSize, sizeof( partition.m_nIoBufferSize ) ); Helper_SwapInsideIndexBuffer_forPs3( &partition.m_numIndices, sizeof( partition.m_numIndices ) ); Helper_SwapInsideIndexBuffer_forPs3( &partition.m_numVertices, sizeof( partition.m_numVertices ) ); Helper_SwapInsideIndexBuffer_forPs3( &partition.m_nEdgeDmaInputIdx, sizeof( partition.m_nEdgeDmaInputIdx ) ); Helper_SwapInsideIndexBuffer_forPs3( &partition.m_nEdgeDmaInputVtx, sizeof( partition.m_nEdgeDmaInputVtx ) ); Helper_SwapInsideIndexBuffer_forPs3( &partition.m_nEdgeDmaInputEnd, sizeof( partition.m_nEdgeDmaInputEnd ) ); }
Helper_SwapInsideIndexBuffer_forPs3( &pMarkup->m_uiHeaderCookie, sizeof( pMarkup->m_uiHeaderCookie ) ); Helper_SwapInsideIndexBuffer_forPs3( &pMarkup->m_uiVersionFlags, sizeof( pMarkup->m_uiVersionFlags ) ); Helper_SwapInsideIndexBuffer_forPs3( &pMarkup->m_numBytesMarkup, sizeof( pMarkup->m_numBytesMarkup ) ); Helper_SwapInsideIndexBuffer_forPs3( &pMarkup->m_numPartitions, sizeof( pMarkup->m_numPartitions ) ); Helper_SwapInsideIndexBuffer_forPs3( &pMarkup->m_numIndicesTotal, sizeof( pMarkup->m_numIndicesTotal ) ); Helper_SwapInsideIndexBuffer_forPs3( &pMarkup->m_numVerticesTotal, sizeof( pMarkup->m_numVerticesTotal ) ); Helper_SwapInsideIndexBuffer_forPs3( &pMarkup->m_nEdgeDmaInputOffsetPerStripGroup, sizeof( pMarkup->m_nEdgeDmaInputOffsetPerStripGroup ) ); Helper_SwapInsideIndexBuffer_forPs3( &pMarkup->m_nEdgeDmaInputSizePerStripGroup, sizeof( pMarkup->m_nEdgeDmaInputSizePerStripGroup ) ); }
struct ModelBatchKey_t { ModelBatchKey_t() { V_memset( this, 0, sizeof( *this ) ); } ModelBatchKey_t( int bp, int mdl, int lod, int mesh, int sgroup, int strip) { V_memset( this, 0, sizeof( *this ) ); iBodyPart = bp; iModel = mdl; iLod = lod; iMesh = mesh; iStripGroup = sgroup; iStrip = strip; } ModelBatchKey_t( ModelBatchKey_t const & x ) { V_memcpy( this, &x, sizeof( *this ) ); } ModelBatchKey_t& operator=( ModelBatchKey_t const &x ) { if ( &x != this ) V_memcpy( this, &x, sizeof( *this ) ); return *this; } int iBodyPart; int iModel; int iLod; int iMesh; int iStripGroup; int iStrip; bool operator <( ModelBatchKey_t const &x ) const { return V_memcmp( this, &x, sizeof( *this ) ) < 0; } };
//
// StripModelBuffers
// The main function that strips the model buffers
// mdlBuffer - mdl buffer, updated, no size change
// vvdBuffer - vvd buffer, updated, size reduced
// vtxBuffer - vtx buffer, updated, size reduced
// ppStripInfo - if nonzero on return will be filled with the stripping info
//
bool CMdlLib::PrepareModelForPs3( CUtlBuffer &mdlBuffer, CUtlBuffer &vvdBuffer, CUtlBuffer &vtxBuffer, IMdlStripInfo **ppStripInfo ) { DECLARE_PTR( byte, mdl, BYTE_OFF_PTR( mdlBuffer.Base(), mdlBuffer.TellGet() ) ); DECLARE_PTR( byte, vvd, BYTE_OFF_PTR( vvdBuffer.Base(), vvdBuffer.TellGet() ) ); DECLARE_PTR( byte, vtx, BYTE_OFF_PTR( vtxBuffer.Base(), vtxBuffer.TellGet() ) );
int vvdLength = vvdBuffer.TellPut() - vvdBuffer.TellGet(); int vtxLength = vtxBuffer.TellPut() - vtxBuffer.TellGet();
//
// ===================
// =================== Modify the checksum and check if further processing is needed
// ===================
//
DECLARE_PTR( studiohdr_t, mdlHdr, mdl ); DECLARE_PTR( vertexFileHeader_t, vvdHdr, vvd ); DECLARE_PTR( OptimizedModel::FileHeader_t, vtxHdr, vtx ); long checksumOld = mdlHdr->checksum;
// Don't do anything if the checksums don't match
if ( ( mdlHdr->checksum != vvdHdr->checksum ) || ( mdlHdr->checksum != vtxHdr->checkSum ) ) { Log_Msg( LOG_ModelLib, "ERROR: [PrepareModelForPs3] checksum mismatch!\n" ); return false; }
// Modify the checksums
mdlHdr->checksum ^= ( mdlHdr->checksum * 123333 ); vvdHdr->checksum ^= ( vvdHdr->checksum * 123333 ); vtxHdr->checkSum ^= ( vtxHdr->checkSum * 123333 );
long checksumNew = mdlHdr->checksum;
// Allocate the model stripping info
CMdlStripInfo msi; CMdlStripInfo *pMsi;
if ( ppStripInfo ) { if ( *ppStripInfo ) { pMsi = ( CMdlStripInfo * ) ( *ppStripInfo ); pMsi->Reset(); } else { *ppStripInfo = pMsi = new CMdlStripInfo; } } else { pMsi = &msi; }
// Set the basic stripping info settings
pMsi->m_lChecksumOld = checksumOld; pMsi->m_lChecksumNew = checksumNew; mdlHdr->flags &= ~STUDIOHDR_FLAGS_PS3_EDGE_FORMAT; if ( !vvdHdr->numFixups ) vvdHdr->fixupTableStart = 0;
//
// Early outs
//
if ( mdlHdr->numbones != 1 ) { Log_Msg( LOG_ModelLib, "No special stripping - the model has %d bone(s).\n", mdlHdr->numbones ); pMsi->m_eMode = CMdlStripInfo::MODE_PS3_FORMAT_BASIC; return true; }
if ( CommandLine()->FindParm( "-mdllib_ps3_noedge" ) ) { pMsi->m_eMode = CMdlStripInfo::MODE_PS3_FORMAT_BASIC; return true; }
bool const bEmitIndices = !!CommandLine()->FindParm( "-mdllib_ps3_edgeidxbuf" ); bool const bUncompressedIndices = !!CommandLine()->FindParm( "-mdllib_ps3_edgeidxbufUncompressed" ); bool const bUncompressedVertices = true || !!CommandLine()->FindParm( "-mdllib_ps3_edgevtxbufUncompressed" ); struct EdgeGeomFreeMemoryTracker_t : public CUtlVector< void * > { ~EdgeGeomFreeMemoryTracker_t() { for ( int k = 0; k < Count(); ++ k ) if ( void *p = Element( k ) ) edgeGeomFree( p ); } } ps3edgeGeomFreeMemoryTracker;
// Otherwise do stripping
pMsi->m_eMode = CMdlStripInfo::MODE_PS3_PARTITIONS; mdlHdr->flags |= STUDIOHDR_FLAGS_PS3_EDGE_FORMAT;
CByteswap ps3byteswap; ps3byteswap.SetTargetBigEndian( true ); // PS3 target is big-endian
//
// ===================
// =================== Build out table of LODx vertexes
// ===================
//
CUtlVector< CMdlStripInfo::Ps3studioBatch_t * > &ps3studioBatches = pMsi->m_ps3studioBatches; typedef CUtlMap< ModelBatchKey_t, int > ModelBatch2idx; ModelBatch2idx mapMBI( DefLessFunc( ModelBatchKey_t ) ); uint32 numVhvOriginalModelVertices = 0; {
DECLARE_PTR( mstudiovertex_t, vvdVertexSrc, BYTE_OFF_PTR( vvdHdr, vvdHdr->vertexDataStart ) );
ITERATE_CHILDREN2( OptimizedModel::BodyPartHeader_t, mstudiobodyparts_t, vtxBodyPart, mdlBodyPart, vtxHdr, mdlHdr, pBodyPart, pBodypart, numBodyParts ) ITERATE_CHILDREN2( OptimizedModel::ModelHeader_t, mstudiomodel_t, vtxModel, mdlModel, vtxBodyPart, mdlBodyPart, pModel, pModel, numModels )
if ( ( mdlModel->vertexindex%sizeof( mstudiovertex_t ) ) || ( mdlModel->tangentsindex%sizeof( Vector4D ) ) || ( (mdlModel->vertexindex/sizeof( mstudiovertex_t )) != (mdlModel->tangentsindex/sizeof( Vector4D )) ) ) { Error( "PrepareModelForPs3: invalid model setup [mdlModel_idx=%d]!\n", vtxModel_idx ); } uint32 uiModelIndexOffset = (mdlModel->vertexindex/sizeof( mstudiovertex_t )); ITERATE_CHILDREN( OptimizedModel::ModelLODHeader_t, vtxLod, vtxModel, pLOD, numLODs ) ITERATE_CHILDREN2( OptimizedModel::MeshHeader_t, mstudiomesh_t, vtxMesh, mdlMesh, vtxLod, mdlModel, pMesh, pMesh, numMeshes ) ITERATE_CHILDREN( OptimizedModel::StripGroupHeader_t, vtxStripGroup, vtxMesh, pStripGroup, numStripGroups ) pMsi->m_ps3studioStripGroupHeaderBatchOffset.AddToTail( ps3studioBatches.Count() ); ITERATE_CHILDREN( OptimizedModel::StripHeader_t, vtxStrip, vtxStripGroup, pStrip, numStrips )
//
// Compute triangle indices list for edge
//
if( ( vtxStrip->numIndices % 3 ) != 0 ) { Error( "PrepareModelForPs3: invalid number of indices/3! [%d]\n", vtxStrip->numIndices ); } CUtlVector< uint32 > arrEdgeInputIndices; CUtlVector< uint32 > arrStripLocalEdgeInputIndex; arrEdgeInputIndices.SetCount( vtxStrip->numIndices ); arrStripLocalEdgeInputIndex.EnsureCapacity( vtxStrip->numIndices ); for ( int i = 0; i < vtxStrip->numIndices; ++ i ) { unsigned short *vtxIdx = CHILD_AT( vtxStripGroup, pIndex, vtxStrip->indexOffset + i ); OptimizedModel::Vertex_t *vtxVertex = CHILD_AT( vtxStripGroup, pVertex, *vtxIdx ); uint32 uiInputIdx = vtxVertex->origMeshVertID + mdlMesh->vertexoffset; arrEdgeInputIndices[i] = uiInputIdx; if ( arrStripLocalEdgeInputIndex.Count() <= uiInputIdx ) arrStripLocalEdgeInputIndex.SetCountNonDestructively( uiInputIdx + 1 ); arrStripLocalEdgeInputIndex[uiInputIdx] = *vtxIdx; }
//
// Compute triangle centroids for the batch
//
float *pEdgeTriangleCentroids = NULL; float *flModelSourceVertices = reinterpret_cast< float * >( vvdVertexSrc ) + uiModelIndexOffset; uint16 uiModelSourceVerticesPositionIdx = reinterpret_cast< float * >( &vvdVertexSrc->m_vecPosition.x ) - reinterpret_cast< float * >( vvdVertexSrc ); edgeGeomComputeTriangleCentroids( flModelSourceVertices, sizeof( mstudiovertex_t ) / sizeof( float ), uiModelSourceVerticesPositionIdx, arrEdgeInputIndices.Base(), vtxStrip->numIndices / 3, &pEdgeTriangleCentroids ); //
// Let EDGE tool framework perform partitioning of our studio batch
//
EdgeGeomPartitionerInput egpi; egpi.m_numTriangles = vtxStrip->numIndices / 3; egpi.m_triangleList = arrEdgeInputIndices.Base();
egpi.m_numInputAttributes = 1; egpi.m_numOutputAttributes = 0; egpi.m_inputVertexStride[0] = 12; egpi.m_inputVertexStride[1] = 0; egpi.m_outputVertexStride = 0; egpi.m_skinningFlavor = kSkinNone; egpi.m_indexListFlavor = kIndexesU16TriangleListCW; egpi.m_skinningMatrixFormat = kMatrix4x4RowMajor; egpi.m_cacheOptimizerCallback = CommandLine()->FindParm( "-mdllib_ps3_edgenokcache" ) ? 0 : edgeGeomKCacheOptimizerHillclimber; EdgeGeomKCacheOptimizerHillclimberUserData edgeCacheOptimizerUserData = { 400, // iterations (100 iterations give about 1% improvement)
kIndexesU16TriangleListCW, // indexes type
1, 0 // RSX input and output attributes count
}; egpi.m_cacheOptimizerUserData = &edgeCacheOptimizerUserData; egpi.m_customDataSizeCallback = NULL; egpi.m_triangleCentroids = pEdgeTriangleCentroids; egpi.m_skinningMatrixIndexesPerVertex = NULL; // no skinning
egpi.m_deltaStreamVertexStride = 0; // no blendshapes
egpi.m_blendedVertexIndexes = NULL; // no skinning
egpi.m_numBlendedVertexes = 0; // no blendshapes
egpi.m_customCommandBufferHoleSizeCallback = 0; // no custom data
// Partition our geometry
EdgeGeomPartitionerOutput egpo; edgeGeomPartitioner( egpi, &egpo );
//
// Represent partitioned geometry as mdllib batch description
//
CMdlStripInfo::Ps3studioBatch_t *ps3studioBatch = new CMdlStripInfo::Ps3studioBatch_t; ps3studioBatch->m_uiModelIndexOffset = uiModelIndexOffset; ps3studioBatch->m_uiVhvIndexOffset = numVhvOriginalModelVertices; int iBatchIdx = ps3studioBatches.AddToTail( ps3studioBatch ); ModelBatchKey_t mbk( vtxBodyPart_idx, vtxModel_idx, vtxLod_idx, vtxMesh_idx, vtxStripGroup_idx, vtxStrip_idx ); Assert( mapMBI.Find( mbk ) == mapMBI.InvalidIndex() ); mapMBI.Insert( mbk, iBatchIdx ); EdgeGeomPartitionerOutput egpoConsumable = egpo; while ( egpoConsumable.m_numPartitions -- > 0 ) { CMdlStripInfo::Ps3studioPartition_t *ps3partition = new CMdlStripInfo::Ps3studioPartition_t; ps3studioBatch->m_arrPartitions.AddToTail( ps3partition ); ps3partition->m_nIoBufferSize = * ( egpoConsumable.m_ioBufferSizePerPartition ++ ); ps3partition->m_arrLocalIndices.EnsureCapacity( *egpoConsumable.m_numTrianglesPerPartition ); // Compress index information
edgeGeomMakeIndexBuffer( egpoConsumable.m_triangleListOut, *egpoConsumable.m_numTrianglesPerPartition, kIndexesCompressedTriangleListCW, &ps3partition->m_pEdgeCompressedIdx, ps3partition->m_uiEdgeCompressedIdxDmaTagSize ); ps3edgeGeomFreeMemoryTracker.AddToTail( ps3partition->m_pEdgeCompressedIdx );
// Compress vertex information
EdgeGeomAttributeId edgeGeomAttributeIdSpuVB[1] = { EDGE_GEOM_ATTRIBUTE_ID_POSITION }; uint16 edgeGeomAttributeIndexSpuVB[1] = { uiModelSourceVerticesPositionIdx }; EdgeGeomSpuVertexFormat edgeGeomSpuVertexFmt; V_memset( &edgeGeomSpuVertexFmt, 0, sizeof( edgeGeomSpuVertexFmt ) ); edgeGeomSpuVertexFmt.m_numAttributes = 1; edgeGeomSpuVertexFmt.m_vertexStride = 3 * 16 / 8; // compressing to 16-bit
{ EdgeGeomSpuVertexAttributeDefinition &attr = edgeGeomSpuVertexFmt.m_attributeDefinition[0]; attr.m_type = kSpuAttr_FixedPoint; attr.m_count = 3; attr.m_attributeId = EDGE_GEOM_ATTRIBUTE_ID_POSITION; attr.m_byteOffset = 0; attr.m_fixedPointBitDepthInteger[0] = 0; attr.m_fixedPointBitDepthInteger[1] = 0; attr.m_fixedPointBitDepthInteger[2] = 0; attr.m_fixedPointBitDepthFractional[0] = 16; attr.m_fixedPointBitDepthFractional[1] = 16; attr.m_fixedPointBitDepthFractional[2] = 16; } edgeGeomMakeSpuVertexBuffer( flModelSourceVertices, sizeof( mstudiovertex_t ) / sizeof( float ), edgeGeomAttributeIndexSpuVB, edgeGeomAttributeIdSpuVB, 1, egpoConsumable.m_originalVertexIndexesPerPartition, *egpoConsumable.m_numUniqueVertexesPerPartition, edgeGeomSpuVertexFmt, &ps3partition->m_pEdgeCompressedVtx, ps3partition->m_uiEdgeCompressedVtxDmaTagSize, &ps3partition->m_pEdgeCompressedVtxFixedOffsets, &ps3partition->m_uiEdgeCompressedVtxFixedOffsetsSize ); ps3edgeGeomFreeMemoryTracker.AddToTail( ps3partition->m_pEdgeCompressedVtx ); ps3edgeGeomFreeMemoryTracker.AddToTail( ps3partition->m_pEdgeCompressedVtxFixedOffsets );
// Copy index buffer to partition data
while ( ( *egpoConsumable.m_numTrianglesPerPartition ) -- > 0 ) { for ( int kTriangleIndex = 0; kTriangleIndex < 3; ++ kTriangleIndex ) { uint32 uiEgpoIndex = * ( egpoConsumable.m_triangleListOut ++ ); if ( uiEgpoIndex > 0xFFFE ) { Error( "PrepareModelForPs3: index in partition exceeding 0xFFFE! [%u]\n", uiEgpoIndex ); } ps3partition->m_arrLocalIndices.AddToTail( uiEgpoIndex ); } } egpoConsumable.m_numTrianglesPerPartition ++;
ps3partition->m_arrVertOriginalIndices.EnsureCapacity( *egpoConsumable.m_numUniqueVertexesPerPartition ); while ( ( *egpoConsumable.m_numUniqueVertexesPerPartition ) -- > 0 ) { uint32 uiEgpoIndex = * ( egpoConsumable.m_originalVertexIndexesPerPartition ++ ); ps3partition->m_arrVertOriginalIndices.AddToTail( uiEgpoIndex ); ps3partition->m_arrStripLocalOriginalIndices.AddToTail( arrStripLocalEdgeInputIndex[uiEgpoIndex] ); } egpoConsumable.m_numUniqueVertexesPerPartition ++; }
//
// Free EDGE memory
//
edgeGeomFree( pEdgeTriangleCentroids ); edgeGeomFree( egpo.m_numTrianglesPerPartition ); edgeGeomFree( egpo.m_triangleListOut ); edgeGeomFree( egpo.m_originalVertexIndexesPerPartition ); edgeGeomFree( egpo.m_numUniqueVertexesPerPartition ); edgeGeomFree( egpo.m_ioBufferSizePerPartition );
ITERATE_END numVhvOriginalModelVertices += vtxStripGroup->numVerts;
ITERATE_END ITERATE_END ITERATE_END ITERATE_END ITERATE_END
}
//
// Now that we have partitions we need to extend index layouts of every strip
// to account for special markup data describing the partitions layout.
//
uint32 numPartitions = 0, numVertices = 0, numEdgeDmaInputBytesTotal = 0; enum { kToolEdgeDmaAlignIdx = 16, kToolEdgeDmaAlignVtx = 16, kToolEdgeDmaAlignXfr = 16, kToolEdgeDmaAlignGlobal = (1<<8), }; for ( int iBatch = 0; iBatch < ps3studioBatches.Count(); ++ iBatch ) { CMdlStripInfo::Ps3studioBatch_t &batch = *ps3studioBatches[iBatch]; numPartitions += batch.m_arrPartitions.Count(); for ( int iPartition = 0; iPartition < batch.m_arrPartitions.Count(); ++ iPartition ) { CMdlStripInfo::Ps3studioPartition_t &partition = *batch.m_arrPartitions[iPartition]; numVertices += partition.m_arrVertOriginalIndices.Count();
// Must be IDX-aligned
numEdgeDmaInputBytesTotal = AlignValue( numEdgeDmaInputBytesTotal, kToolEdgeDmaAlignIdx ); // Edge will need all indices
uint32 numBytesIndexStream = bUncompressedIndices ? ( sizeof( uint16 ) * partition.m_arrLocalIndices.Count() ) : DmaTagsListSize2( partition.m_uiEdgeCompressedIdxDmaTagSize ); numEdgeDmaInputBytesTotal += numBytesIndexStream;
if ( !bUncompressedVertices && partition.m_pEdgeCompressedVtxFixedOffsets && partition.m_uiEdgeCompressedVtxFixedOffsetsSize ) { // Must be VTX-aligned
numEdgeDmaInputBytesTotal = AlignValue( numEdgeDmaInputBytesTotal, kToolEdgeDmaAlignVtx ); numEdgeDmaInputBytesTotal += partition.m_uiEdgeCompressedVtxFixedOffsetsSize; }
// Must be VTX-aligned
numEdgeDmaInputBytesTotal = AlignValue( numEdgeDmaInputBytesTotal, kToolEdgeDmaAlignVtx ); // Edge will need all positions
uint32 numBytesVertexStream = bUncompressedVertices ? 3 * sizeof( float ) * partition.m_arrVertOriginalIndices.Count() : DmaTagsListSize3( partition.m_uiEdgeCompressedVtxDmaTagSize ); numEdgeDmaInputBytesTotal += numBytesVertexStream;
// Must be XFR-aligned
numEdgeDmaInputBytesTotal = AlignValue( numEdgeDmaInputBytesTotal, kToolEdgeDmaAlignXfr ); } } // Must be 256-byte aligned
numEdgeDmaInputBytesTotal = AlignValue( numEdgeDmaInputBytesTotal, kToolEdgeDmaAlignGlobal );
//
// ===================
// =================== Process MDL file
// ===================
//
{
int numCumulativeMeshVertices = 0;
ITERATE_CHILDREN2( OptimizedModel::BodyPartHeader_t, mstudiobodyparts_t, vtxBodyPart, mdlBodyPart, vtxHdr, mdlHdr, pBodyPart, pBodypart, numBodyParts ) ITERATE_CHILDREN2( OptimizedModel::ModelHeader_t, mstudiomodel_t, vtxModel, mdlModel, vtxBodyPart, mdlBodyPart, pModel, pModel, numModels )
// need to update numvertices
int update_studiomodel_numvertices = 0; mdlModel->vertexindex = numCumulativeMeshVertices * sizeof( mstudiovertex_t ); mdlModel->tangentsindex = numCumulativeMeshVertices * sizeof( Vector4D );
// Process each mesh separately (NOTE: loop reversed: MESH, then LOD)
ITERATE_CHILDREN( mstudiomesh_t, mdlMesh, mdlModel, pMesh, nummeshes )
// need to update numvertices
int update_studiomesh_numvertices = 0; V_memset( mdlMesh->vertexdata.numLODVertexes, 0, sizeof( mdlMesh->vertexdata.numLODVertexes ) );
ITERATE_CHILDREN( OptimizedModel::ModelLODHeader_t, vtxLod, vtxModel, pLOD, numLODs ) if ( vtxLod->numMeshes <= mdlMesh_idx ) continue; OptimizedModel::MeshHeader_t *vtxMesh = vtxLod->pMesh( mdlMesh_idx );
ITERATE_CHILDREN( OptimizedModel::StripGroupHeader_t, vtxStripGroup, vtxMesh, pStripGroup, numStripGroups ) ITERATE_CHILDREN( OptimizedModel::StripHeader_t, vtxStrip, vtxStripGroup, pStrip, numStrips ) vtxStrip;
CMdlStripInfo::Ps3studioBatch_t &batch = *ps3studioBatches[ mapMBI[ mapMBI.Find( ModelBatchKey_t( vtxBodyPart_idx, vtxModel_idx, vtxLod_idx, mdlMesh_idx, vtxStripGroup_idx, vtxStrip_idx ) ) ] ]; int numBatchVertices = 0; for( int iPartition = 0; iPartition < batch.m_arrPartitions.Count(); ++ iPartition ) { numBatchVertices += batch.m_arrPartitions[iPartition]->m_arrVertOriginalIndices.Count(); }
update_studiomesh_numvertices += numBatchVertices; mdlMesh->vertexdata.numLODVertexes[ vtxLod_idx ] += numBatchVertices;
ITERATE_END ITERATE_END
ITERATE_END
mdlMesh->numvertices = update_studiomesh_numvertices; numCumulativeMeshVertices += update_studiomesh_numvertices; mdlMesh->vertexoffset = update_studiomodel_numvertices; update_studiomodel_numvertices += update_studiomesh_numvertices;
// Adjust LOD vertex counts
for ( int iRootLodVertexes = MAX_NUM_LODS; iRootLodVertexes -- > 1; ) { mdlMesh->vertexdata.numLODVertexes[iRootLodVertexes-1] += mdlMesh->vertexdata.numLODVertexes[iRootLodVertexes]; } for ( int iRootLodVertexes = 0; iRootLodVertexes < MAX_NUM_LODS; ++ iRootLodVertexes ) { if ( !mdlMesh->vertexdata.numLODVertexes[iRootLodVertexes] && iRootLodVertexes ) mdlMesh->vertexdata.numLODVertexes[iRootLodVertexes] = mdlMesh->vertexdata.numLODVertexes[iRootLodVertexes-1]; }
ITERATE_END mdlModel->numvertices = update_studiomodel_numvertices;
ITERATE_END ITERATE_END
}
//
// ===================
// =================== Process VVD file
// ===================
//
DECLARE_PTR( mstudiovertex_t, vvdVertexSrc, BYTE_OFF_PTR( vvdHdr, vvdHdr->vertexDataStart ) ); DECLARE_PTR( Vector4D, vvdTangentSrc, vvdHdr->tangentDataStart ? BYTE_OFF_PTR( vvdHdr, vvdHdr->tangentDataStart ) : NULL ); // Apply the fixups first of all
if ( vvdHdr->numFixups ) { CArrayAutoPtr< byte > memTempVVD( new byte[ vvdLength ] ); DECLARE_PTR( mstudiovertex_t, vvdVertexNew, BYTE_OFF_PTR( memTempVVD.Get(), vvdHdr->vertexDataStart ) ); DECLARE_PTR( Vector4D, vvdTangentNew, BYTE_OFF_PTR( memTempVVD.Get(), vvdHdr->tangentDataStart ) ); DECLARE_PTR( vertexFileFixup_t, vvdFixup, BYTE_OFF_PTR( vvdHdr, vvdHdr->fixupTableStart ) ); for ( int k = 0; k < vvdHdr->numFixups; ++ k ) { memcpy( vvdVertexNew, vvdVertexSrc + vvdFixup[ k ].sourceVertexID, vvdFixup[ k ].numVertexes * sizeof( *vvdVertexNew ) ); vvdVertexNew += vvdFixup[ k ].numVertexes; if ( vvdTangentSrc ) { memcpy( vvdTangentNew, vvdTangentSrc + vvdFixup[ k ].sourceVertexID, vvdFixup[ k ].numVertexes * sizeof( *vvdTangentNew ) ); vvdTangentNew += vvdFixup[ k ].numVertexes; } }
// Move back the memory after fixups were applied
vvdVertexSrc ? memcpy( vvdVertexSrc, BYTE_OFF_PTR( memTempVVD.Get(), vvdHdr->vertexDataStart ), vvdHdr->numLODVertexes[0] * sizeof( *vvdVertexSrc ) ) : 0; vvdTangentSrc ? memcpy( vvdTangentSrc, BYTE_OFF_PTR( memTempVVD.Get(), vvdHdr->tangentDataStart ), vvdHdr->numLODVertexes[0] * sizeof( *vvdTangentSrc ) ) : 0; } vvdHdr->vertexDataStart -= ALIGN_VALUE( sizeof( vertexFileFixup_t ) * vvdHdr->numFixups, 16 ); vvdHdr->numFixups = 0;
if ( vvdHdr->id != MODEL_VERTEX_FILE_ID ) Error( "PrepareModelForPs3: unsupported VVD structure!\n" );
//
// Now loop through batches and reorder vertices
//
{
uint32 numNewVvdBufferBytesBase = vvdHdr->vertexDataStart + numVertices * sizeof( *vvdVertexSrc ) + ( vvdTangentSrc ? ( numVertices * sizeof( *vvdTangentSrc ) ) : 0 ); numNewVvdBufferBytesBase = AlignValue( numNewVvdBufferBytesBase, 1 << 8 ); uint32 numNewVvdBufferBytes = numNewVvdBufferBytesBase + numEdgeDmaInputBytesTotal; if ( ( numNewVvdBufferBytesBase >> 8 ) & ~0xFFFF ) Error( "PrepareModelForPs3: VVD buffer DMA offset too large!\n" ); if ( ( numEdgeDmaInputBytesTotal >> 8 ) & ~0x7FFF ) Error( "PrepareModelForPs3: VVD buffer DMA size too large!\n" ); if ( numEdgeDmaInputBytesTotal & 0xFF ) Error( "PrepareModelForPs3: VVD buffer DMA size incorrect!\n" ); vvdHdr->fixupTableStart = ( numNewVvdBufferBytesBase >> 8 ) | ( numEdgeDmaInputBytesTotal << 8 ) | 0x80000000; CArrayAutoPtr< byte > memTempVVD( new byte[ numNewVvdBufferBytes ] ); for ( int k = 0; k < MAX_NUM_LODS; ++ k ) vvdHdr->numLODVertexes[k] = numVertices; // Root LOD unsupported
V_memcpy( memTempVVD.Get(), vvdHdr, vvdHdr->vertexDataStart ); DECLARE_PTR( mstudiovertex_t, vvdVertexNew, BYTE_OFF_PTR( memTempVVD.Get(), vvdHdr->vertexDataStart ) ); DECLARE_PTR( Vector4D, vvdTangentNew, BYTE_OFF_PTR( memTempVVD.Get(), vvdHdr->vertexDataStart + numVertices * sizeof( *vvdVertexSrc ) ) ); DECLARE_PTR( uint8, vvdEdgeDmaInputNew, BYTE_OFF_PTR( memTempVVD.Get(), numNewVvdBufferBytesBase ) );
ITERATE_CHILDREN2( OptimizedModel::BodyPartHeader_t, mstudiobodyparts_t, vtxBodyPart, mdlBodyPart, vtxHdr, mdlHdr, pBodyPart, pBodypart, numBodyParts ) ITERATE_CHILDREN2( OptimizedModel::ModelHeader_t, mstudiomodel_t, vtxModel, mdlModel, vtxBodyPart, mdlBodyPart, pModel, pModel, numModels ) // Process each mesh separately (NOTE: loop reversed: MESH, then LOD)
ITERATE_CHILDREN( mstudiomesh_t, mdlMesh, mdlModel, pMesh, nummeshes ) mdlMesh; ITERATE_CHILDREN( OptimizedModel::ModelLODHeader_t, vtxLod, vtxModel, pLOD, numLODs ) if ( vtxLod->numMeshes <= mdlMesh_idx ) continue; OptimizedModel::MeshHeader_t *vtxMesh = vtxLod->pMesh( mdlMesh_idx ); ITERATE_CHILDREN( OptimizedModel::StripGroupHeader_t, vtxStripGroup, vtxMesh, pStripGroup, numStripGroups ) ITERATE_CHILDREN( OptimizedModel::StripHeader_t, vtxStrip, vtxStripGroup, pStrip, numStrips ) vtxStrip;
CMdlStripInfo::Ps3studioBatch_t &batch = *ps3studioBatches[ mapMBI[ mapMBI.Find( ModelBatchKey_t( vtxBodyPart_idx, vtxModel_idx, vtxLod_idx, mdlMesh_idx, vtxStripGroup_idx, vtxStrip_idx ) ) ] ]; for( int iPartition = 0; iPartition < batch.m_arrPartitions.Count(); ++ iPartition ) { CMdlStripInfo::Ps3studioPartition_t &partition = *batch.m_arrPartitions[iPartition]; // Must be IDX-aligned
DECLARE_UPDATE_PTR( uint8, vvdEdgeDmaInputNew, BYTE_OFF_PTR( memTempVVD.Get(), AlignValue( BYTE_DIFF_PTR( memTempVVD.Get(), vvdEdgeDmaInputNew ), kToolEdgeDmaAlignIdx ) ) ); partition.m_nEdgeDmaInputIdx = BYTE_DIFF_PTR( memTempVVD.Get(), vvdEdgeDmaInputNew ) - numNewVvdBufferBytesBase; // EDGE DMA INDEX DATA
if ( bUncompressedIndices ) { ps3byteswap.SwapBufferToTargetEndian<uint16>( (uint16*) vvdEdgeDmaInputNew, partition.m_arrLocalIndices.Base(), partition.m_arrLocalIndices.Count() ); vvdEdgeDmaInputNew += partition.m_arrLocalIndices.Count() * sizeof( uint16 ); } else { V_memcpy( vvdEdgeDmaInputNew, partition.m_pEdgeCompressedIdx, DmaTagsListSize2( partition.m_uiEdgeCompressedIdxDmaTagSize ) ); vvdEdgeDmaInputNew += DmaTagsListSize2( partition.m_uiEdgeCompressedIdxDmaTagSize ); }
// Must be VTX-aligned
DECLARE_UPDATE_PTR( uint8, vvdEdgeDmaInputNew, BYTE_OFF_PTR( memTempVVD.Get(), AlignValue( BYTE_DIFF_PTR( memTempVVD.Get(), vvdEdgeDmaInputNew ), kToolEdgeDmaAlignVtx ) ) ); partition.m_nEdgeDmaInputVtx = BYTE_DIFF_PTR( memTempVVD.Get(), vvdEdgeDmaInputNew ) - numNewVvdBufferBytesBase;
for ( int iOrigVert = 0; iOrigVert < partition.m_arrVertOriginalIndices.Count(); ++ iOrigVert ) { uint32 uiOrigVertIdx = partition.m_arrVertOriginalIndices[iOrigVert]; uiOrigVertIdx += batch.m_uiModelIndexOffset; memcpy( vvdVertexNew, vvdVertexSrc + uiOrigVertIdx, sizeof( *vvdVertexNew ) ); ++ vvdVertexNew; if ( vvdTangentSrc ) { memcpy( vvdTangentNew, vvdTangentSrc + uiOrigVertIdx, sizeof( *vvdTangentNew ) ); ++ vvdTangentNew; }
// EDGE DMA VERTEX DATA
if ( bUncompressedVertices ) { ps3byteswap.SwapBufferToTargetEndian<float>( (float*) vvdEdgeDmaInputNew, (float*) &vvdVertexSrc[uiOrigVertIdx].m_vecPosition.x, 3 ); vvdEdgeDmaInputNew += 3 * sizeof( float ); } }
// EDGE DMA VERTEX DATA
if ( !bUncompressedVertices ) { if ( partition.m_pEdgeCompressedVtxFixedOffsets && partition.m_uiEdgeCompressedVtxFixedOffsetsSize ) { V_memcpy( vvdEdgeDmaInputNew, partition.m_pEdgeCompressedVtxFixedOffsets, partition.m_uiEdgeCompressedVtxFixedOffsetsSize ); vvdEdgeDmaInputNew += partition.m_uiEdgeCompressedVtxFixedOffsetsSize; DECLARE_UPDATE_PTR( uint8, vvdEdgeDmaInputNew, BYTE_OFF_PTR( memTempVVD.Get(), AlignValue( BYTE_DIFF_PTR( memTempVVD.Get(), vvdEdgeDmaInputNew ), kToolEdgeDmaAlignVtx ) ) ); } V_memcpy( vvdEdgeDmaInputNew, partition.m_pEdgeCompressedVtx, DmaTagsListSize3( partition.m_uiEdgeCompressedVtxDmaTagSize ) ); vvdEdgeDmaInputNew += DmaTagsListSize3( partition.m_uiEdgeCompressedVtxDmaTagSize ); }
// Must be XFR-aligned
DECLARE_UPDATE_PTR( uint8, vvdEdgeDmaInputNew, BYTE_OFF_PTR( memTempVVD.Get(), AlignValue( BYTE_DIFF_PTR( memTempVVD.Get(), vvdEdgeDmaInputNew ), kToolEdgeDmaAlignXfr ) ) ); partition.m_nEdgeDmaInputEnd = BYTE_DIFF_PTR( memTempVVD.Get(), vvdEdgeDmaInputNew ) - numNewVvdBufferBytesBase; Assert( vvdEdgeDmaInputNew <= BYTE_OFF_PTR( memTempVVD.Get(), numNewVvdBufferBytes ) ); } ITERATE_END ITERATE_END ITERATE_END ITERATE_END ITERATE_END ITERATE_END
//
// Copy back the newly created VVD buffer
//
vvdBuffer.EnsureCapacity( numNewVvdBufferBytes + vvdBuffer.TellGet() ); V_memcpy( BYTE_OFF_PTR( vvdBuffer.Base(), vvdBuffer.TellGet() ), memTempVVD.Get(), numNewVvdBufferBytes ); vvdBuffer.SeekPut( vvdBuffer.SEEK_HEAD, vvdBuffer.TellGet() + numNewVvdBufferBytes );
Log_Msg( LOG_ModelLib, " Increased VVD size by %d bytes.\n", numNewVvdBufferBytes - vvdLength ); // Since we could have caused a buffer reallocation, update cached pointers and values
DECLARE_UPDATE_PTR( byte, vvd, BYTE_OFF_PTR( vvdBuffer.Base(), vvdBuffer.TellGet() ) ); DECLARE_UPDATE_PTR( vertexFileHeader_t, vvdHdr, vvd ); DECLARE_UPDATE_PTR( mstudiovertex_t, vvdVertexSrc, BYTE_OFF_PTR( vvdHdr, vvdHdr->vertexDataStart ) ); if ( vvdTangentSrc ) { vvdHdr->tangentDataStart = vvdHdr->vertexDataStart + numVertices * sizeof( *vvdVertexSrc ); DECLARE_UPDATE_PTR( Vector4D, vvdTangentSrc, BYTE_OFF_PTR( vvdHdr, vvdHdr->tangentDataStart ) ); } vvdLength = numNewVvdBufferBytes;
} //
// ===================
// =================== Process VTX file
// ===================
//
size_t vtxOffIndexBuffer = ~size_t(0), vtxOffIndexBufferEnd = 0; size_t vtxOffVertexBuffer = ~size_t(0), vtxOffVertexBufferEnd = 0; CMemoryMovingTracker vtxMemMove( CMemoryMovingTracker::MEMORY_MODIFY );
{
ITERATE_CHILDREN( OptimizedModel::BodyPartHeader_t, vtxBodyPart, vtxHdr, pBodyPart, numBodyParts ) ITERATE_CHILDREN( OptimizedModel::ModelHeader_t, vtxModel, vtxBodyPart, pModel, numModels ) ITERATE_CHILDREN( OptimizedModel::ModelLODHeader_t, vtxLod, vtxModel, pLOD, numLODs ) ITERATE_CHILDREN( OptimizedModel::MeshHeader_t, vtxMesh, vtxLod, pMesh, numMeshes ) ITERATE_CHILDREN( OptimizedModel::StripGroupHeader_t, vtxStripGroup, vtxMesh, pStripGroup, numStripGroups )
size_t offIndex = BYTE_DIFF_PTR( vtxHdr, CHILD_AT( vtxStripGroup, pIndex, 0 ) ); size_t offIndexEnd = BYTE_DIFF_PTR( vtxHdr, CHILD_AT( vtxStripGroup, pIndex, vtxStripGroup->numIndices ) ); size_t offVertex = BYTE_DIFF_PTR( vtxHdr, CHILD_AT( vtxStripGroup, pVertex, 0 ) ); size_t offVertexEnd = BYTE_DIFF_PTR( vtxHdr, CHILD_AT( vtxStripGroup, pVertex, vtxStripGroup->numVerts ) );
if ( offIndex < vtxOffIndexBuffer ) vtxOffIndexBuffer = offIndex; if ( offIndexEnd > vtxOffIndexBufferEnd ) vtxOffIndexBufferEnd = offIndexEnd; if ( offVertex < vtxOffVertexBuffer ) vtxOffVertexBuffer = offVertex; if ( offVertexEnd > vtxOffVertexBufferEnd ) vtxOffVertexBufferEnd = offVertexEnd;
uint32 uiNumVertsInStripGroupUpdate = 0; uint32 uiNumIndicesInStripGroupUpdate = 0;
ITERATE_CHILDREN( OptimizedModel::StripHeader_t, vtxStrip, vtxStripGroup, pStrip, numStrips ) CMdlStripInfo::Ps3studioBatch_t &batch = *ps3studioBatches[ mapMBI[ mapMBI.Find( ModelBatchKey_t( vtxBodyPart_idx, vtxModel_idx, vtxLod_idx, vtxMesh_idx, vtxStripGroup_idx, vtxStrip_idx ) ) ] ]; uint16 *vtxIdx = CHILD_AT( vtxStripGroup, pIndex, vtxStrip->indexOffset ); uint32 uiNumExtraBytes = sizeof( OptimizedModel::OptimizedIndexBufferMarkupPs3_t ) + batch.m_arrPartitions.Count() * sizeof( OptimizedModel::OptimizedIndexBufferMarkupPs3_t::Partition_t ); uiNumExtraBytes = ( ( uiNumExtraBytes + 5 ) / 6 ) * 6; // make it even number of 3 indices
vtxStrip->numVerts = 0; // Since we are not adjusting the verts here properly, zero them out
uint32 uiNumIndicesInPartitions = 0; for ( int k = 0; k < batch.m_arrPartitions.Count(); ++ k ) { uiNumIndicesInPartitions += batch.m_arrPartitions[k]->m_arrLocalIndices.Count(); uiNumVertsInStripGroupUpdate += batch.m_arrPartitions[k]->m_arrVertOriginalIndices.Count(); }
uiNumIndicesInStripGroupUpdate += uiNumExtraBytes / sizeof( uint16 ) + ( bEmitIndices ? uiNumIndicesInPartitions : 0 ); vtxMemMove.RegisterBytes( vtxIdx, uiNumExtraBytes - ( bEmitIndices ? 0 : ( uiNumIndicesInPartitions * sizeof( uint16 ) ) ) );
ITERATE_END
vtxMemMove.RegisterBytes( vtxStripGroup->pVertex(0), ( uiNumVertsInStripGroupUpdate - vtxStripGroup->numVerts ) * sizeof( *vtxStripGroup->pVertex(0) ) ); vtxStripGroup->numIndices = uiNumIndicesInStripGroupUpdate; vtxStripGroup->numVerts = uiNumVertsInStripGroupUpdate;
ITERATE_END ITERATE_END ITERATE_END ITERATE_END ITERATE_END
}
//
// Now enlarge the VTX buffer
//
vtxBuffer.EnsureCapacity( vtxBuffer.TellGet() + vtxLength + vtxMemMove.GetNumBytesRegistered() ); vtxBuffer.SeekPut( vtxBuffer.SEEK_HEAD, vtxBuffer.TellGet() + vtxLength + vtxMemMove.GetNumBytesRegistered() );
DECLARE_UPDATE_PTR( byte, vtx, BYTE_OFF_PTR( vtxBuffer.Base(), vtxBuffer.TellGet() ) ); vtxMemMove.RegisterBaseDelta( vtxHdr, vtx ); DECLARE_UPDATE_PTR( OptimizedModel::FileHeader_t, vtxHdr, vtx ); vtxMemMove.Finalize();
// By now should have scheduled all memmove information
Log_Msg( LOG_ModelLib, " Increased VTX size by %d bytes.\n", vtxMemMove.GetNumBytesRegistered() );
//
// Fixup all the offsets
//
ITERATE_CHILDREN( OptimizedModel::MaterialReplacementListHeader_t, vtxMatList, vtxHdr, pMaterialReplacementList, numLODs ) ITERATE_CHILDREN( OptimizedModel::MaterialReplacementHeader_t, vtxMat, vtxMatList, pMaterialReplacement, numReplacements ) vtxMat->replacementMaterialNameOffset = vtxMemMove.ComputeOffset( vtxMat, vtxMat->replacementMaterialNameOffset ); ITERATE_END vtxMatList->replacementOffset = vtxMemMove.ComputeOffset( vtxMatList, vtxMatList->replacementOffset ); ITERATE_END ITERATE_CHILDREN( OptimizedModel::BodyPartHeader_t, vtxBodyPart, vtxHdr, pBodyPart, numBodyParts ) ITERATE_CHILDREN( OptimizedModel::ModelHeader_t, vtxModel, vtxBodyPart, pModel, numModels ) ITERATE_CHILDREN( OptimizedModel::ModelLODHeader_t, vtxLod, vtxModel, pLOD, numLODs ) ITERATE_CHILDREN( OptimizedModel::MeshHeader_t, vtxMesh, vtxLod, pMesh, numMeshes ) ITERATE_CHILDREN( OptimizedModel::StripGroupHeader_t, vtxStripGroup, vtxMesh, pStripGroup, numStripGroups ) ITERATE_CHILDREN( OptimizedModel::StripHeader_t, vtxStrip, vtxStripGroup, pStrip, numStrips ) vtxStrip->indexOffset = vtxMemMove.ComputeOffset( vtxStripGroup, vtxStripGroup->indexOffset + vtxStrip->indexOffset ) - vtxMemMove.ComputeOffset( vtxStripGroup, vtxStripGroup->indexOffset ); vtxStrip->vertOffset = vtxMemMove.ComputeOffset( vtxStripGroup, vtxStripGroup->vertOffset + vtxStrip->vertOffset ) - vtxMemMove.ComputeOffset( vtxStripGroup, vtxStripGroup->vertOffset ); vtxStrip->boneStateChangeOffset = vtxMemMove.ComputeOffset( vtxStrip, vtxStrip->boneStateChangeOffset ); ITERATE_END
vtxStripGroup->vertOffset = vtxMemMove.ComputeOffset( vtxStripGroup, vtxStripGroup->vertOffset ); vtxStripGroup->indexOffset = vtxMemMove.ComputeOffset( vtxStripGroup, vtxStripGroup->indexOffset ); vtxStripGroup->stripOffset = vtxMemMove.ComputeOffset( vtxStripGroup, vtxStripGroup->stripOffset ); vtxStripGroup->topologyOffset = vtxMemMove.ComputeOffset( vtxStripGroup, vtxStripGroup->topologyOffset ); ITERATE_END vtxMesh->stripGroupHeaderOffset = vtxMemMove.ComputeOffset( vtxMesh, vtxMesh->stripGroupHeaderOffset ); ITERATE_END vtxLod->meshOffset = vtxMemMove.ComputeOffset( vtxLod, vtxLod->meshOffset ); ITERATE_END vtxModel->lodOffset = vtxMemMove.ComputeOffset( vtxModel, vtxModel->lodOffset ); ITERATE_END vtxBodyPart->modelOffset = vtxMemMove.ComputeOffset( vtxBodyPart, vtxBodyPart->modelOffset ); ITERATE_END vtxHdr->materialReplacementListOffset = vtxMemMove.ComputeOffset( vtxHdr, vtxHdr->materialReplacementListOffset ); vtxHdr->bodyPartOffset = vtxMemMove.ComputeOffset( vtxHdr, vtxHdr->bodyPartOffset );
// Perform final memory move
vtxMemMove.MemMove( vtxHdr, vtxLength ); vtxBuffer.SeekPut( vtxBuffer.SEEK_HEAD, vtxBuffer.TellGet() + vtxLength );
//
// Fill the newly insterted gaps with real data
//
{
OptimizedModel::Vertex_t optVertex; for ( int j = 0; j < MAX_NUM_BONES_PER_VERT; ++ j ) optVertex.boneWeightIndex[j] = j, optVertex.boneID[j] = 0; optVertex.numBones = 1; // we are processing only 1-bone models
uint32 uiEdgeDmaInputOffsetPerStripGroup = 0;
ITERATE_CHILDREN2( OptimizedModel::BodyPartHeader_t, mstudiobodyparts_t, vtxBodyPart, mdlBodyPart, vtxHdr, mdlHdr, pBodyPart, pBodypart, numBodyParts ) ITERATE_CHILDREN2( OptimizedModel::ModelHeader_t, mstudiomodel_t, vtxModel, mdlModel, vtxBodyPart, mdlBodyPart, pModel, pModel, numModels ) uint32 uiRunningOriginalVertexId = 0; // Process each mesh separately (NOTE: loop reversed: MESH, then LOD)
ITERATE_CHILDREN( mstudiomesh_t, mdlMesh, mdlModel, pMesh, nummeshes ) ITERATE_CHILDREN( OptimizedModel::ModelLODHeader_t, vtxLod, vtxModel, pLOD, numLODs ) if ( vtxLod->numMeshes <= mdlMesh_idx ) continue; OptimizedModel::MeshHeader_t *vtxMesh = vtxLod->pMesh( mdlMesh_idx ); ITERATE_CHILDREN( OptimizedModel::StripGroupHeader_t, vtxStripGroup, vtxMesh, pStripGroup, numStripGroups )
uint32 uiStipGroupFilledVerts = 0; uint32 uiRunningEdgeDmaInputEnd = uiEdgeDmaInputOffsetPerStripGroup; CUtlVector< OptimizedModel::OptimizedIndexBufferMarkupPs3_t * > arrGroupMarkups;
ITERATE_CHILDREN( OptimizedModel::StripHeader_t, vtxStrip, vtxStripGroup, pStrip, numStrips ) CMdlStripInfo::Ps3studioBatch_t &batch = *ps3studioBatches[ mapMBI[ mapMBI.Find( ModelBatchKey_t( vtxBodyPart_idx, vtxModel_idx, vtxLod_idx, mdlMesh_idx, vtxStripGroup_idx, vtxStrip_idx ) ) ] ]; uint16 *vtxIdx = CHILD_AT( vtxStripGroup, pIndex, vtxStrip->indexOffset ); uint32 uiNumExtraBytes = sizeof( OptimizedModel::OptimizedIndexBufferMarkupPs3_t ) + batch.m_arrPartitions.Count() * sizeof( OptimizedModel::OptimizedIndexBufferMarkupPs3_t::Partition_t ); uiNumExtraBytes = ( ( uiNumExtraBytes + 5 ) / 6 ) * 6; // make it even number of 3 indices
// Write the "markup" header
DECLARE_PTR( OptimizedModel::OptimizedIndexBufferMarkupPs3_t, pMarkup, vtxIdx ); arrGroupMarkups.AddToTail( pMarkup ); pMarkup->m_uiHeaderCookie = pMarkup->kHeaderCookie; pMarkup->m_uiVersionFlags = pMarkup->kVersion1; pMarkup->m_numBytesMarkup = uiNumExtraBytes; pMarkup->m_numPartitions = batch.m_arrPartitions.Count(); pMarkup->m_numIndicesTotal = 0; pMarkup->m_numVerticesTotal = 0; pMarkup->m_nEdgeDmaInputOffsetPerStripGroup = uiEdgeDmaInputOffsetPerStripGroup; // updated later
pMarkup->m_nEdgeDmaInputSizePerStripGroup = 0; // filled later
uint32 uiSkipIdx = uiNumExtraBytes / sizeof( uint16 ), uiSkipVerts = 0; for ( int k = 0; k < batch.m_arrPartitions.Count(); ++ k ) { CMdlStripInfo::Ps3studioPartition_t &partition = *batch.m_arrPartitions[k]; pMarkup->m_partitions[k].m_numIndicesToSkipInIBs = uiSkipIdx; pMarkup->m_partitions[k].m_numVerticesToSkipInVBs = uiSkipVerts; pMarkup->m_partitions[k].m_nIoBufferSize = partition.m_nIoBufferSize; pMarkup->m_partitions[k].m_numIndices = partition.m_arrLocalIndices.Count(); pMarkup->m_partitions[k].m_numVertices = partition.m_arrVertOriginalIndices.Count(); if ( uiRunningEdgeDmaInputEnd == uiEdgeDmaInputOffsetPerStripGroup ) { pMarkup->m_nEdgeDmaInputOffsetPerStripGroup = uiEdgeDmaInputOffsetPerStripGroup = partition.m_nEdgeDmaInputIdx; } pMarkup->m_partitions[k].m_nEdgeDmaInputIdx = partition.m_nEdgeDmaInputIdx - uiEdgeDmaInputOffsetPerStripGroup; pMarkup->m_partitions[k].m_nEdgeDmaInputVtx = partition.m_nEdgeDmaInputVtx - uiEdgeDmaInputOffsetPerStripGroup; pMarkup->m_partitions[k].m_nEdgeDmaInputEnd = partition.m_nEdgeDmaInputEnd - uiEdgeDmaInputOffsetPerStripGroup; uiRunningEdgeDmaInputEnd = partition.m_nEdgeDmaInputEnd;
pMarkup->m_numIndicesTotal += pMarkup->m_partitions[k].m_numIndices; pMarkup->m_numVerticesTotal += pMarkup->m_partitions[k].m_numVertices;
if ( bEmitIndices ) { //
// Write raw partition indices
//
for ( int j = 0; j < partition.m_arrLocalIndices.Count(); ++ j ) { vtxIdx[ uiSkipIdx + j ] = partition.m_arrLocalIndices[j]; } uiSkipIdx += partition.m_arrLocalIndices.Count(); }
//
// Advance written verts
//
uiSkipVerts += partition.m_arrVertOriginalIndices.Count(); } vtxStrip->numIndices = uiSkipIdx;
// Fill out our Vertex_t structures
bool bWarningSpewed = false; if ( uiSkipVerts > vtxStripGroup->numVerts ) { Warning( "WARNING: %s: VTX strip group overflow %d/%d\n", mdlHdr->pszName(), uiSkipVerts, vtxStripGroup->numVerts ); Assert( !"VTX strip group overflow!" ); bWarningSpewed = true; } for ( int j = 0; j < uiSkipVerts; ++ j ) { optVertex.origMeshVertID = uiRunningOriginalVertexId - mdlMesh->vertexoffset; if ( !bWarningSpewed && ( ( uiRunningOriginalVertexId < mdlMesh->vertexoffset ) || ( uint32( optVertex.origMeshVertID ) != uint32( uiRunningOriginalVertexId - mdlMesh->vertexoffset ) ) || ( mdlMesh->numvertices <= optVertex.origMeshVertID ) ) ) { Warning( "WARNING: %s: VTX vertex indirection overflow id=%d, off=%d, idx=%d/%d\n", mdlHdr->pszName(), uiRunningOriginalVertexId, mdlMesh->vertexoffset, uint32( optVertex.origMeshVertID ), mdlMesh->numvertices ); Assert( !"VTX vertex indirection overflow!" ); bWarningSpewed = true; } uiRunningOriginalVertexId ++; V_memcpy( vtxStripGroup->pVertex( uiStipGroupFilledVerts + j ), &optVertex, sizeof( optVertex ) ); } uiStipGroupFilledVerts += uiSkipVerts;
ITERATE_END for ( int jjj = 0; jjj < arrGroupMarkups.Count(); ++ jjj ) { arrGroupMarkups[jjj]->m_nEdgeDmaInputSizePerStripGroup = uiRunningEdgeDmaInputEnd - uiEdgeDmaInputOffsetPerStripGroup; // Need pre-swapping inside index buffer
Helper_SwapOptimizedIndexBufferMarkup_forPs3( arrGroupMarkups[jjj] ); } uiEdgeDmaInputOffsetPerStripGroup = uiRunningEdgeDmaInputEnd;
ITERATE_END ITERATE_END ITERATE_END ITERATE_END ITERATE_END
}
//
// ===================
// =================== Validate that we have valid information for VHV processing
// ===================
//
{
// Keep track of which verts got touched
CGrowableBitVec arrTouchedOriginalVerts; uint32 uiDebugOriginalVertsPresent = numVhvOriginalModelVertices; for ( uint32 iMesh = 0, iBatch = 0; iMesh < pMsi->m_ps3studioStripGroupHeaderBatchOffset.Count(); ++ iMesh ) { uint32 numVerts = 0; uint32 iBatchEnd = ( iMesh < pMsi->m_ps3studioStripGroupHeaderBatchOffset.Count() - 1 ) ? pMsi->m_ps3studioStripGroupHeaderBatchOffset[iMesh+1] : pMsi->m_ps3studioBatches.Count(); iBatchEnd = MIN( iBatchEnd, pMsi->m_ps3studioBatches.Count() ); for ( ; iBatch < iBatchEnd; ++ iBatch ) { CMdlStripInfo::Ps3studioBatch_t &batch = *pMsi->m_ps3studioBatches[iBatch]; for ( uint32 iPartition = 0; iPartition < batch.m_arrPartitions.Count(); ++ iPartition ) { CMdlStripInfo::Ps3studioPartition_t &partition = *batch.m_arrPartitions[iPartition]; numVerts += partition.m_arrVertOriginalIndices.Count(); for ( uint32 iVertIndex = 0; iVertIndex < partition.m_arrVertOriginalIndices.Count(); ++ iVertIndex ) { // uint32 uiOrigVertIndex = partition.m_arrVertOriginalIndices[iVertIndex];
uint32 uiOrigVertIndex = partition.m_arrStripLocalOriginalIndices[iVertIndex]; uiOrigVertIndex += batch.m_uiVhvIndexOffset; arrTouchedOriginalVerts.GrowSetBit( uiOrigVertIndex ); } } } } { uint32 uiDebugTouchedOriginalVerts = arrTouchedOriginalVerts.GetNumBits(); if ( uiDebugTouchedOriginalVerts == uiDebugOriginalVertsPresent ) { for ( uint32 iDebugOrigVert = 0; iDebugOrigVert < uiDebugOriginalVertsPresent; ++ iDebugOrigVert ) { if ( !arrTouchedOriginalVerts.IsBitSet( iDebugOrigVert ) ) { Warning( "WARNING: %s: VHV source vertex %d/%d skipped\n", mdlHdr->pszName(), iDebugOrigVert, uiDebugOriginalVertsPresent ); Assert( !"VHV source vertex skipped!" ); break; } } } else { Warning( "WARNING: %s: VHV expected vertex count mismatch %d!=%d\n", mdlHdr->pszName(), uiDebugTouchedOriginalVerts, uiDebugOriginalVertsPresent ); Assert( !"VHV expected vertex count mismatch!" ); } }
}
// Done
return true; }
/*
If the changelist comment includes the following line:
*** force rebuild ***
then it will trigger a full content rebuild which is very useful when changing output formats.
*/
|