csgo-2018-source/mdllib/mdllib_preparemodelps3.cpp
2021-07-24 21:11:47 -07:00

1014 lines
46 KiB
C++

//====== 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.
*/