//========= Copyright 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" 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 ) ) { DLog( "mdllib", 1, "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 ) ) { DLog( "mdllib", 2, "No special stripping - the model is not a static prop.\n" ); pMsi->m_eMode = CMdlStripInfo::MODE_NO_CHANGE; return true; } if ( vvdHdr->numLODs <= 1 ) { DLog( "mdllib", 2, "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 ) { DLog( "mdllib", 2, "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 ) if ( !( vtxStrip->flags & OptimizedModel::STRIP_IS_TRILIST ) ) continue; 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 ) DLog( "mdllib", 3, " Stripped %d lod(s).\n", vvdHdr->numLODs - 1 ), DLog( "mdllib", 3, " 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( mstudiobone_t, mdlBone, mdlHdr, pBone, numbones ) mdlBone->flags &= ( BONE_USED_BY_VERTEX_LOD0 | ~BONE_USED_BY_VERTEX_MASK ); ITERATE_END DLog( "mdllib", 3, " 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 ); } DLog( "mdllib", 3, " 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; CRemoveTracker vtxRemove; CUtlVector< size_t > vtxOffIndex; CUtlVector< size_t > vtxOffVertex; vtxRemove.RemoveElements( CHILD_AT( vtxHdr, pMaterialReplacementList, 1 ), vtxHdr->numLODs - 1 ); ITERATE_CHILDREN( OptimizedModel::MaterialReplacementListHeader_t, vtxMatList, vtxHdr, pMaterialReplacementList, numLODs ) if ( !vtxMatList_idx ) continue; vtxRemove.RemoveElements( CHILD_AT( vtxMatList, pMaterialReplacement, 0 ), vtxMatList->numReplacements ); ITERATE_CHILDREN( OptimizedModel::MaterialReplacementHeader_t, vtxMat, vtxMatList, pMaterialReplacement, numReplacements ) char const *szName = vtxMat->pMaterialReplacementName(); vtxRemove.RemoveElements( 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.RemoveElements( 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.RemoveElements( CHILD_AT( vtxLod, pMesh, 0 ), vtxLod->numMeshes ); ITERATE_CHILDREN( OptimizedModel::MeshHeader_t, vtxMesh, vtxLod, pMesh, numMeshes ) vtxRemove.RemoveElements( CHILD_AT( vtxMesh, pStripGroup, 0 ), vtxMesh->numStripGroups ); ITERATE_CHILDREN( OptimizedModel::StripGroupHeader_t, vtxStripGroup, vtxMesh, pStripGroup, numStripGroups ) vtxRemove.RemoveElements( CHILD_AT( vtxStripGroup, pStrip, 0 ), vtxStripGroup->numStrips ); ITERATE_CHILDREN( OptimizedModel::StripHeader_t, vtxStrip, vtxStripGroup, pStrip, numStrips ) vtxRemove.RemoveElements( 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.RemoveElements( 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.RemoveElements( vtxIndexElement ); } else { // Index is in use and must be remapped *vtxIndexElement -= vtxIndexDeltas[ *vtxIndexElement ]; } } // By now should have scheduled all removal information vtxRemove.Finalize(); DLog( "mdllib", 3, " Stripped %d vtx bytes.\n", vtxRemove.GetNumBytesRemoved() ); // // 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() ); DLog( "mdllib", 2, " Reduced model buffers by %d bytes.\n", vtxRemove.GetNumBytesRemoved() + ( vvdLengthOld - vvdLength ) ); // Done return true; }