1014 lines
46 KiB
C++
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.
|
|
|
|
*/
|
|
|