You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
649 lines
25 KiB
649 lines
25 KiB
//====== 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"
|
|
|
|
DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_ModelLib, "ModelLib", 0, LS_ERROR );
|
|
|
|
bool CMdlLib::CreateNewStripInfo( IMdlStripInfo **ppStripInfo )
|
|
{
|
|
if ( !ppStripInfo )
|
|
return false;
|
|
|
|
if ( *ppStripInfo )
|
|
{
|
|
CMdlStripInfo *pMdlStripInfo = ( CMdlStripInfo * ) ( *ppStripInfo );
|
|
pMdlStripInfo->Reset();
|
|
return true;
|
|
}
|
|
|
|
*ppStripInfo = new CMdlStripInfo;
|
|
return ( NULL != *ppStripInfo );
|
|
}
|
|
|
|
//
|
|
// 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::StripModelBuffers( 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: [StripModelBuffers] checksum mismatch!\n" );
|
|
return false;
|
|
}
|
|
|
|
// Modify the checksums
|
|
mdlHdr->checksum ^= ( mdlHdr->checksum * 123457 );
|
|
vvdHdr->checksum ^= ( vvdHdr->checksum * 123457 );
|
|
vtxHdr->checkSum ^= ( vtxHdr->checkSum * 123457 );
|
|
|
|
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;
|
|
|
|
//
|
|
// Early outs
|
|
//
|
|
|
|
if ( !( mdlHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP ) )
|
|
{
|
|
Log_Msg( LOG_ModelLib, "No special stripping - the model is not a static prop.\n" );
|
|
pMsi->m_eMode = CMdlStripInfo::MODE_NO_CHANGE;
|
|
return true;
|
|
}
|
|
|
|
if ( vvdHdr->numLODs <= 1 )
|
|
{
|
|
Log_Msg( LOG_ModelLib, "No special stripping - the model has only %d lod(s).\n", vvdHdr->numLODs );
|
|
pMsi->m_eMode = CMdlStripInfo::MODE_NO_CHANGE;
|
|
return true;
|
|
}
|
|
|
|
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_NO_CHANGE;
|
|
return true;
|
|
}
|
|
|
|
// Otherwise do stripping
|
|
pMsi->m_eMode = CMdlStripInfo::MODE_STRIP_LOD_1N;
|
|
|
|
|
|
//
|
|
// ===================
|
|
// =================== Build out table of LOD0 vertexes
|
|
// ===================
|
|
//
|
|
|
|
CGrowableBitVec &mapVtxIndex = pMsi->m_vtxVerts;
|
|
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 )
|
|
|
|
OptimizedModel::ModelLODHeader_t *vtxLod = CHILD_AT( vtxModel, pLOD, 0 );
|
|
ITERATE_CHILDREN2( OptimizedModel::MeshHeader_t, mstudiomesh_t, vtxMesh, mdlMesh, vtxLod, mdlModel, pMesh, pMesh, numMeshes )
|
|
ITERATE_CHILDREN( OptimizedModel::StripGroupHeader_t, vtxStripGroup, vtxMesh, pStripGroup, numStripGroups )
|
|
ITERATE_CHILDREN( OptimizedModel::StripHeader_t, vtxStrip, vtxStripGroup, pStrip, numStrips )
|
|
|
|
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 );
|
|
|
|
unsigned short usIdx = vtxVertex->origMeshVertID + mdlMesh->vertexoffset;
|
|
mapVtxIndex.GrowSetBit( usIdx );
|
|
}
|
|
|
|
ITERATE_END
|
|
ITERATE_END
|
|
ITERATE_END
|
|
|
|
ITERATE_END
|
|
ITERATE_END
|
|
|
|
//
|
|
// Now having the table of which vertexes to keep we will construct a remapping table
|
|
//
|
|
CUtlSortVector< unsigned short, CLessSimple< unsigned short > > &srcIndices = pMsi->m_vtxIndices;
|
|
srcIndices.EnsureCapacity( mapVtxIndex.GetNumBits() );
|
|
for ( int iBit = -1; ( iBit = mapVtxIndex.FindNextSetBit( iBit + 1 ) ) >= 0; )
|
|
srcIndices.InsertNoSort( ( unsigned short ) ( unsigned int ) iBit );
|
|
srcIndices.RedoSort(); // - doesn't do anything, just validates the vector
|
|
|
|
// Now we have the following questions answered:
|
|
// - for every index we know if it belongs to lod0 "mapVtxIndex.IsBitSet( oldVertIdx )"
|
|
// - for every new vertex we know its old index "srcIndices[ newVertIdx ]"
|
|
// - for every old vertex if it's in lod0 we know its new index "srcIndices.Find( oldVertIdx )"
|
|
|
|
|
|
//
|
|
// ===================
|
|
// =================== Process MDL file
|
|
// ===================
|
|
//
|
|
|
|
//
|
|
// Update vertex counts
|
|
//
|
|
int mdlNumVerticesOld = 0;
|
|
CUtlSortVector< CMdlStripInfo::MdlRangeItem, CLessSimple< CMdlStripInfo::MdlRangeItem > > &arrMdlOffsets = pMsi->m_vtxMdlOffsets;
|
|
ITERATE_CHILDREN( mstudiobodyparts_t, mdlBodyPart, mdlHdr, pBodypart, numbodyparts )
|
|
ITERATE_CHILDREN( mstudiomodel_t, mdlModel, mdlBodyPart, pModel, nummodels )
|
|
|
|
Log_Msg( LOG_ModelLib, " Stripped %d lod(s).\n", vvdHdr->numLODs - 1 );
|
|
Log_Msg( LOG_ModelLib, " Stripped %d vertexes (was: %d, now: %d).\n", mdlModel->numvertices - srcIndices.Count(), mdlModel->numvertices, srcIndices.Count() );
|
|
|
|
mdlNumVerticesOld = mdlModel->numvertices;
|
|
mdlModel->numvertices = srcIndices.Count();
|
|
|
|
mdlModel->vertexdata.pVertexData = BYTE_OFF_PTR( vvdHdr, vvdHdr->vertexDataStart );
|
|
mdlModel->vertexdata.pTangentData = BYTE_OFF_PTR( vvdHdr, vvdHdr->tangentDataStart );
|
|
|
|
ITERATE_CHILDREN( mstudiomesh_t, mdlMesh, mdlModel, pMesh, nummeshes )
|
|
|
|
CMdlStripInfo::MdlRangeItem mdlRangeItem( mdlMesh->vertexoffset, mdlMesh->numvertices );
|
|
|
|
mdlMesh->vertexdata.modelvertexdata = &mdlModel->vertexdata;
|
|
mdlMesh->numvertices = srcIndices.FindLess( mdlMesh->vertexoffset + mdlMesh->numvertices );
|
|
mdlMesh->vertexoffset = srcIndices.FindLess( mdlMesh->vertexoffset ) + 1;
|
|
mdlMesh->numvertices -= mdlMesh->vertexoffset - 1;
|
|
|
|
mdlRangeItem.m_offNew = mdlMesh->vertexoffset;
|
|
mdlRangeItem.m_numNew = mdlMesh->numvertices;
|
|
arrMdlOffsets.Insert( mdlRangeItem );
|
|
|
|
// Truncate the number of vertexes
|
|
for ( int k = 0; k < ARRAYSIZE( mdlMesh->vertexdata.numLODVertexes ); ++ k )
|
|
mdlMesh->vertexdata.numLODVertexes[ k ] = mdlMesh->numvertices;
|
|
|
|
ITERATE_END
|
|
ITERATE_END
|
|
ITERATE_END
|
|
|
|
//
|
|
// Update bones not to mention anything below LOD0
|
|
//
|
|
ITERATE_CHILDREN( const mstudiobone_t, mdlBone, mdlHdr, pBone, numbones )
|
|
((mstudiobone_t *)mdlBone)->flags &= ( BONE_USED_BY_VERTEX_LOD0 | ~BONE_USED_BY_VERTEX_MASK );
|
|
ITERATE_END
|
|
|
|
Log_Msg( LOG_ModelLib, " Updated %d bone(s).\n", mdlHdr->numbones );
|
|
|
|
|
|
//
|
|
// ===================
|
|
// =================== Process VVD file
|
|
// ===================
|
|
//
|
|
|
|
vvdHdr->numLODs = 1;
|
|
for ( int k = 0; k < ARRAYSIZE( vvdHdr->numLODVertexes ); ++ k )
|
|
vvdHdr->numLODVertexes[ k ] = srcIndices.Count();
|
|
|
|
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 ), mdlNumVerticesOld * sizeof( *vvdVertexSrc ) ) : 0;
|
|
vvdTangentSrc ? memcpy( vvdTangentSrc, BYTE_OFF_PTR( memTempVVD.Get(), vvdHdr->tangentDataStart ), mdlNumVerticesOld * sizeof( *vvdTangentSrc ) ) : 0;
|
|
}
|
|
|
|
vvdHdr->vertexDataStart -= ALIGN_VALUE( sizeof( vertexFileFixup_t ) * vvdHdr->numFixups, 16 );
|
|
vvdHdr->numFixups = 0;
|
|
DECLARE_PTR( mstudiovertex_t, vvdVertexNew, BYTE_OFF_PTR( vvdHdr, vvdHdr->vertexDataStart ) );
|
|
for ( int k = 0; k < srcIndices.Count(); ++ k )
|
|
vvdVertexNew[ k ] = vvdVertexSrc[ srcIndices[ k ] ];
|
|
|
|
size_t newVertexDataSize = srcIndices.Count() * sizeof( mstudiovertex_t );
|
|
int vvdLengthOld = vvdLength;
|
|
vvdLength = vvdHdr->vertexDataStart + newVertexDataSize;
|
|
|
|
if ( vvdTangentSrc )
|
|
{
|
|
// Move the tangents
|
|
vvdHdr->tangentDataStart = vvdLength;
|
|
DECLARE_PTR( Vector4D, vvdTangentNew, BYTE_OFF_PTR( vvdHdr, vvdHdr->tangentDataStart ) );
|
|
|
|
for ( int k = 0; k < srcIndices.Count(); ++ k )
|
|
vvdTangentNew[ k ] = vvdTangentSrc[ srcIndices[ k ] ];
|
|
|
|
vvdLength += srcIndices.Count() * sizeof( Vector4D );
|
|
}
|
|
Log_Msg( LOG_ModelLib, " Stripped %d vvd bytes.\n", vvdLengthOld - vvdLength );
|
|
|
|
|
|
//
|
|
// ===================
|
|
// =================== Process VTX file
|
|
// ===================
|
|
//
|
|
|
|
size_t vtxOffIndexBuffer = ~size_t(0), vtxOffIndexBufferEnd = 0;
|
|
size_t vtxOffVertexBuffer = ~size_t(0), vtxOffVertexBufferEnd = 0;
|
|
CMemoryMovingTracker vtxRemove( CMemoryMovingTracker::MEMORY_REMOVE );
|
|
CUtlVector< size_t > vtxOffIndex;
|
|
CUtlVector< size_t > vtxOffVertex;
|
|
|
|
vtxRemove.RegisterElements( CHILD_AT( vtxHdr, pMaterialReplacementList, 1 ), vtxHdr->numLODs - 1 );
|
|
ITERATE_CHILDREN( OptimizedModel::MaterialReplacementListHeader_t, vtxMatList, vtxHdr, pMaterialReplacementList, numLODs )
|
|
if ( !vtxMatList_idx ) continue;
|
|
vtxRemove.RegisterElements( CHILD_AT( vtxMatList, pMaterialReplacement, 0 ), vtxMatList->numReplacements );
|
|
ITERATE_CHILDREN( OptimizedModel::MaterialReplacementHeader_t, vtxMat, vtxMatList, pMaterialReplacement, numReplacements )
|
|
char const *szName = vtxMat->pMaterialReplacementName();
|
|
vtxRemove.RegisterElements( szName, szName ? strlen( szName ) + 1 : 0 );
|
|
ITERATE_END
|
|
ITERATE_END
|
|
|
|
ITERATE_CHILDREN( OptimizedModel::BodyPartHeader_t, vtxBodyPart, vtxHdr, pBodyPart, numBodyParts )
|
|
ITERATE_CHILDREN( OptimizedModel::ModelHeader_t, vtxModel, vtxBodyPart, pModel, numModels )
|
|
|
|
vtxRemove.RegisterElements( CHILD_AT( vtxModel, pLOD, 1 ), vtxModel->numLODs - 1 );
|
|
ITERATE_CHILDREN( OptimizedModel::ModelLODHeader_t, vtxLod, vtxModel, pLOD, numLODs )
|
|
if ( !vtxLod_idx ) // Process only lod1-N
|
|
continue;
|
|
|
|
vtxRemove.RegisterElements( CHILD_AT( vtxLod, pMesh, 0 ), vtxLod->numMeshes );
|
|
ITERATE_CHILDREN( OptimizedModel::MeshHeader_t, vtxMesh, vtxLod, pMesh, numMeshes )
|
|
vtxRemove.RegisterElements( CHILD_AT( vtxMesh, pStripGroup, 0 ), vtxMesh->numStripGroups );
|
|
ITERATE_CHILDREN( OptimizedModel::StripGroupHeader_t, vtxStripGroup, vtxMesh, pStripGroup, numStripGroups )
|
|
vtxRemove.RegisterElements( CHILD_AT( vtxStripGroup, pStrip, 0 ), vtxStripGroup->numStrips );
|
|
ITERATE_CHILDREN( OptimizedModel::StripHeader_t, vtxStrip, vtxStripGroup, pStrip, numStrips )
|
|
vtxRemove.RegisterElements( CHILD_AT( vtxStrip, pBoneStateChange, 0 ), vtxStrip->numBoneStateChanges );
|
|
ITERATE_END
|
|
ITERATE_END
|
|
ITERATE_END
|
|
|
|
ITERATE_END
|
|
|
|
// Use all lods to determine the ranges of vertex and index buffers.
|
|
// We rely on the fact that vertex and index buffers are laid out as one solid memory block for all lods.
|
|
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;
|
|
|
|
if ( !vtxLod_idx )
|
|
{
|
|
vtxOffIndex.AddToTail( offIndex );
|
|
vtxOffIndex.AddToTail( offIndexEnd );
|
|
vtxOffVertex.AddToTail( offVertex );
|
|
vtxOffVertex.AddToTail( offVertexEnd );
|
|
}
|
|
|
|
ITERATE_END
|
|
ITERATE_END
|
|
ITERATE_END
|
|
|
|
ITERATE_END
|
|
ITERATE_END
|
|
|
|
// Fixup the vertex buffer
|
|
DECLARE_PTR( OptimizedModel::Vertex_t, vtxVertexBuffer, BYTE_OFF_PTR( vtxHdr, vtxOffVertexBuffer ) );
|
|
DECLARE_PTR( OptimizedModel::Vertex_t, vtxVertexBufferEnd, BYTE_OFF_PTR( vtxHdr, vtxOffVertexBufferEnd ) );
|
|
CUtlVector< int > vtxIndexDeltas;
|
|
vtxIndexDeltas.EnsureCapacity( vtxVertexBufferEnd - vtxVertexBuffer );
|
|
int vtxNumVertexRemoved = 0;
|
|
for ( OptimizedModel::Vertex_t *vtxVertexElement = vtxVertexBuffer; vtxVertexElement < vtxVertexBufferEnd; ++ vtxVertexElement )
|
|
{
|
|
size_t const off = BYTE_DIFF_PTR( vtxHdr, vtxVertexElement );
|
|
bool bUsed = false;
|
|
for ( int k = 0; k < vtxOffVertex.Count(); k += 2 )
|
|
{
|
|
if ( off >= vtxOffVertex[ k ] && off < vtxOffVertex[ k + 1 ] )
|
|
{
|
|
bUsed = true;
|
|
break;
|
|
}
|
|
}
|
|
if ( !bUsed )
|
|
{
|
|
// Index is not in use
|
|
vtxRemove.RegisterElements( vtxVertexElement );
|
|
vtxIndexDeltas.AddToTail( 0 );
|
|
vtxNumVertexRemoved ++;
|
|
}
|
|
else
|
|
{ // Index is in use and must be remapped
|
|
// Find the mesh where this index belongs
|
|
int iMesh = arrMdlOffsets.FindLessOrEqual( CMdlStripInfo::MdlRangeItem( 0, 0, vtxVertexElement - vtxVertexBuffer ) );
|
|
Assert( iMesh >= 0 && iMesh < arrMdlOffsets.Count() );
|
|
|
|
CMdlStripInfo::MdlRangeItem &mri = arrMdlOffsets[ iMesh ];
|
|
Assert( ( vtxVertexElement - vtxVertexBuffer >= mri.m_offNew ) && ( vtxVertexElement - vtxVertexBuffer < mri.m_offNew + mri.m_numNew ) );
|
|
|
|
Assert( mapVtxIndex.IsBitSet( vtxVertexElement->origMeshVertID + mri.m_offOld ) );
|
|
vtxVertexElement->origMeshVertID = srcIndices.Find( vtxVertexElement->origMeshVertID + mri.m_offOld ) - mri.m_offNew;
|
|
Assert( vtxVertexElement->origMeshVertID < mri.m_numNew );
|
|
vtxIndexDeltas.AddToTail( vtxNumVertexRemoved );
|
|
}
|
|
}
|
|
|
|
// Fixup the index buffer
|
|
DECLARE_PTR( unsigned short, vtxIndexBuffer, BYTE_OFF_PTR( vtxHdr, vtxOffIndexBuffer ) );
|
|
DECLARE_PTR( unsigned short, vtxIndexBufferEnd, BYTE_OFF_PTR( vtxHdr, vtxOffIndexBufferEnd ) );
|
|
for ( unsigned short *vtxIndexElement = vtxIndexBuffer; vtxIndexElement < vtxIndexBufferEnd; ++ vtxIndexElement )
|
|
{
|
|
size_t const off = BYTE_DIFF_PTR( vtxHdr, vtxIndexElement );
|
|
bool bUsed = false;
|
|
for ( int k = 0; k < vtxOffIndex.Count(); k += 2 )
|
|
{
|
|
if ( off >= vtxOffIndex[ k ] && off < vtxOffIndex[ k + 1 ] )
|
|
{
|
|
bUsed = true;
|
|
break;
|
|
}
|
|
}
|
|
if ( !bUsed )
|
|
{
|
|
// Index is not in use
|
|
vtxRemove.RegisterElements( vtxIndexElement );
|
|
}
|
|
else
|
|
{
|
|
// Index is in use and must be remapped
|
|
*vtxIndexElement -= vtxIndexDeltas[ *vtxIndexElement ];
|
|
}
|
|
}
|
|
|
|
// By now should have scheduled all removal information
|
|
vtxRemove.Finalize();
|
|
Log_Msg( LOG_ModelLib, " Stripped %d vtx bytes.\n", vtxRemove.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 = vtxRemove.ComputeOffset( vtxMat, vtxMat->replacementMaterialNameOffset );
|
|
ITERATE_END
|
|
vtxMatList->replacementOffset = vtxRemove.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 =
|
|
vtxRemove.ComputeOffset( vtxStripGroup, vtxStripGroup->indexOffset + vtxStrip->indexOffset ) -
|
|
vtxRemove.ComputeOffset( vtxStripGroup, vtxStripGroup->indexOffset );
|
|
vtxStrip->vertOffset =
|
|
vtxRemove.ComputeOffset( vtxStripGroup, vtxStripGroup->vertOffset + vtxStrip->vertOffset ) -
|
|
vtxRemove.ComputeOffset( vtxStripGroup, vtxStripGroup->vertOffset );
|
|
vtxStrip->boneStateChangeOffset = vtxRemove.ComputeOffset( vtxStrip, vtxStrip->boneStateChangeOffset );
|
|
ITERATE_END
|
|
|
|
vtxStripGroup->vertOffset = vtxRemove.ComputeOffset( vtxStripGroup, vtxStripGroup->vertOffset );
|
|
vtxStripGroup->indexOffset = vtxRemove.ComputeOffset( vtxStripGroup, vtxStripGroup->indexOffset );
|
|
vtxStripGroup->stripOffset = vtxRemove.ComputeOffset( vtxStripGroup, vtxStripGroup->stripOffset );
|
|
ITERATE_END
|
|
vtxMesh->stripGroupHeaderOffset = vtxRemove.ComputeOffset( vtxMesh, vtxMesh->stripGroupHeaderOffset );
|
|
ITERATE_END
|
|
vtxLod->meshOffset = vtxRemove.ComputeOffset( vtxLod, vtxLod->meshOffset );
|
|
ITERATE_END
|
|
vtxModel->lodOffset = vtxRemove.ComputeOffset( vtxModel, vtxModel->lodOffset );
|
|
vtxModel->numLODs = 1;
|
|
ITERATE_END
|
|
vtxBodyPart->modelOffset = vtxRemove.ComputeOffset( vtxBodyPart, vtxBodyPart->modelOffset );
|
|
ITERATE_END
|
|
vtxHdr->materialReplacementListOffset = vtxRemove.ComputeOffset( vtxHdr, vtxHdr->materialReplacementListOffset );
|
|
vtxHdr->bodyPartOffset = vtxRemove.ComputeOffset( vtxHdr, vtxHdr->bodyPartOffset );
|
|
vtxHdr->numLODs = 1;
|
|
|
|
// Perform final memory move
|
|
vtxRemove.MemMove( vtxHdr, vtxLength );
|
|
|
|
|
|
//
|
|
// ===================
|
|
// =================== Truncate buffer sizes
|
|
// ===================
|
|
//
|
|
|
|
vvdBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, vvdBuffer.TellGet() + vvdLength - vvdBuffer.TellPut() );
|
|
vtxBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, vtxBuffer.TellGet() + vtxLength - vtxBuffer.TellPut() );
|
|
|
|
|
|
Log_Msg( LOG_ModelLib, " Reduced model buffers by %d bytes.\n", vtxRemove.GetNumBytesRegistered() + ( vvdLengthOld - vvdLength ) );
|
|
|
|
// Done
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// ParseMdlMesh
|
|
// The main function that parses the mesh buffers
|
|
// mdlBuffer - mdl buffer
|
|
// vvdBuffer - vvd buffer
|
|
// vtxBuffer - vtx buffer
|
|
// mesh - on return will be filled with the mesh info
|
|
//
|
|
bool CMdlLib::ParseMdlMesh( CUtlBuffer &mdlBuffer, CUtlBuffer &vvdBuffer, CUtlBuffer &vtxBuffer, MdlLib::MdlMesh &mesh )
|
|
{
|
|
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();
|
|
|
|
DECLARE_PTR( studiohdr_t, mdlHdr, mdl );
|
|
DECLARE_PTR( vertexFileHeader_t, vvdHdr, vvd );
|
|
DECLARE_PTR( OptimizedModel::FileHeader_t, vtxHdr, vtx );
|
|
|
|
// Don't do anything if the checksums don't match
|
|
if ( ( mdlHdr->checksum != vvdHdr->checksum ) ||
|
|
( mdlHdr->checksum != vtxHdr->checkSum ) )
|
|
{
|
|
Log_Msg( LOG_ModelLib, "ERROR: [ParseMdlMesh] checksum mismatch!\n" );
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Early outs
|
|
//
|
|
|
|
if ( vvdHdr->numLODs != 1 )
|
|
{
|
|
Log_Msg( LOG_ModelLib, "ERROR: [ParseMdlMesh] the model has %d lod(s).\n", vvdHdr->numLODs );
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// ===================
|
|
// =================== Process MDL file
|
|
// ===================
|
|
//
|
|
|
|
mesh.checksum = mdlHdr->checksum;
|
|
|
|
int mdlNumVertices = 0;
|
|
|
|
ITERATE_CHILDREN( mstudiobodyparts_t, mdlBodyPart, mdlHdr, pBodypart, numbodyparts )
|
|
ITERATE_CHILDREN( mstudiomodel_t, mdlModel, mdlBodyPart, pModel, nummodels )
|
|
|
|
mdlNumVertices = mdlModel->numvertices;
|
|
|
|
ITERATE_END
|
|
ITERATE_END
|
|
|
|
//
|
|
// ===================
|
|
// =================== Build out table of indices
|
|
// ===================
|
|
//
|
|
|
|
mesh.ib.RemoveAll();
|
|
|
|
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 )
|
|
|
|
OptimizedModel::ModelLODHeader_t *vtxLod = CHILD_AT( vtxModel, pLOD, 0 );
|
|
ITERATE_CHILDREN2( OptimizedModel::MeshHeader_t, mstudiomesh_t, vtxMesh, mdlMesh, vtxLod, mdlModel, pMesh, pMesh, numMeshes )
|
|
ITERATE_CHILDREN( OptimizedModel::StripGroupHeader_t, vtxStripGroup, vtxMesh, pStripGroup, numStripGroups )
|
|
ITERATE_CHILDREN( OptimizedModel::StripHeader_t, vtxStrip, vtxStripGroup, pStrip, numStrips )
|
|
|
|
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 );
|
|
|
|
unsigned short usIdx = vtxVertex->origMeshVertID + mdlMesh->vertexoffset;
|
|
mesh.ib.AddToTail( usIdx );
|
|
}
|
|
|
|
ITERATE_END
|
|
ITERATE_END
|
|
ITERATE_END
|
|
|
|
ITERATE_END
|
|
ITERATE_END
|
|
|
|
//
|
|
// ===================
|
|
// =================== Build out table of vertices
|
|
// ===================
|
|
//
|
|
|
|
mesh.vb.RemoveAll();
|
|
|
|
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 ), mdlNumVertices * sizeof( *vvdVertexSrc ) ) : 0;
|
|
vvdTangentSrc ? memcpy( vvdTangentSrc, BYTE_OFF_PTR( memTempVVD.Get(), vvdHdr->tangentDataStart ), mdlNumVertices * sizeof( *vvdTangentSrc ) ) : 0;
|
|
}
|
|
|
|
for ( mstudiovertex_t *pSrc = vvdVertexSrc, *pEnd = pSrc + mdlNumVertices;
|
|
pSrc < pEnd; ++ pSrc )
|
|
{
|
|
MdlLib::MdlVertex vert = {0};
|
|
|
|
vert.position[0] = pSrc->m_vecPosition.x;
|
|
vert.position[1] = pSrc->m_vecPosition.y;
|
|
vert.position[2] = pSrc->m_vecPosition.z;
|
|
|
|
vert.normal[0] = pSrc->m_vecNormal.x;
|
|
vert.normal[1] = pSrc->m_vecNormal.y;
|
|
vert.normal[2] = pSrc->m_vecNormal.z;
|
|
|
|
vert.texcoord[0] = pSrc->m_vecTexCoord.x;
|
|
vert.texcoord[1] = pSrc->m_vecTexCoord.y;
|
|
|
|
mesh.vb.AddToTail( vert );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|