diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /mdllib | |
| download | archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip | |
Diffstat (limited to 'mdllib')
| -rw-r--r-- | mdllib/mdllib.cpp | 70 | ||||
| -rw-r--r-- | mdllib/mdllib.vpc | 44 | ||||
| -rw-r--r-- | mdllib/mdllib_common.h | 67 | ||||
| -rw-r--r-- | mdllib/mdllib_stripinfo.cpp | 671 | ||||
| -rw-r--r-- | mdllib/mdllib_stripinfo.h | 127 | ||||
| -rw-r--r-- | mdllib/mdllib_stripmodel.cpp | 504 | ||||
| -rw-r--r-- | mdllib/mdllib_utils.cpp | 205 | ||||
| -rw-r--r-- | mdllib/mdllib_utils.h | 203 |
8 files changed, 1891 insertions, 0 deletions
diff --git a/mdllib/mdllib.cpp b/mdllib/mdllib.cpp new file mode 100644 index 0000000..332d2a3 --- /dev/null +++ b/mdllib/mdllib.cpp @@ -0,0 +1,70 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "mdllib_common.h" + +//----------------------------------------------------------------------------- +// Global instance +//----------------------------------------------------------------------------- +CMdlLib mdllib; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CMdlLib, IMdlLib, MDLLIB_INTERFACE_VERSION, mdllib ); + + + + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +CMdlLib::~CMdlLib() +{ +} + + +//----------------------------------------------------------------------------- +// Connect, disconnect +//----------------------------------------------------------------------------- +bool CMdlLib::Connect( CreateInterfaceFn factory ) +{ +// g_pFileSystem = (IFileSystem*)factory( FILESYSTEM_INTERFACE_VERSION, NULL ); +// return ( g_pFileSystem != NULL ); + return true; +} + +void CMdlLib::Disconnect() +{ +// g_pFileSystem = NULL; + return; +} + + +//----------------------------------------------------------------------------- +// Purpose: Startup +//----------------------------------------------------------------------------- +InitReturnVal_t CMdlLib::Init() +{ + return INIT_OK; +} + + +//----------------------------------------------------------------------------- +// Purpose: Cleanup +//----------------------------------------------------------------------------- +void CMdlLib::Shutdown() +{ + return; +} + +//----------------------------------------------------------------------------- +// Query interface +//----------------------------------------------------------------------------- +void *CMdlLib::QueryInterface( const char *pInterfaceName ) +{ + return Sys_GetFactoryThis()( pInterfaceName, NULL ); +} + + + + diff --git a/mdllib/mdllib.vpc b/mdllib/mdllib.vpc new file mode 100644 index 0000000..363ad9f --- /dev/null +++ b/mdllib/mdllib.vpc @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// MDLLIB.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR ".." +$Macro OUTBINDIR "$SRCDIR\..\game\bin" + +$Include "$SRCDIR\vpc_scripts\source_dll_base.vpc" + +$Configuration +{ + $Compiler + { + } + + $Linker + { + } +} + +$Project "mdllib" +{ + $Folder "Source Files" + { + $File "mdllib.cpp" + $File "mdllib_stripinfo.cpp" + $File "mdllib_stripmodel.cpp" + $File "mdllib_utils.cpp" + } + + $Folder "Header Files" + { + $File "$SRCDIR\public\mdllib\mdllib.h" + $File "mdllib_common.h" + $File "mdllib_stripinfo.h" + $File "mdllib_utils.h" + } + + $Folder "Link Libraries" + { + } +} diff --git a/mdllib/mdllib_common.h b/mdllib/mdllib_common.h new file mode 100644 index 0000000..24fcd6e --- /dev/null +++ b/mdllib/mdllib_common.h @@ -0,0 +1,67 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef MDLLIB_COMMON_H +#define MDLLIB_COMMON_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "mdllib/mdllib.h" + +#include "platform.h" +#pragma warning( disable : 4018 ) +#pragma warning( disable : 4389 ) + + + +//----------------------------------------------------------------------------- +// Purpose: Interface to accessing P4 commands +//----------------------------------------------------------------------------- +class CMdlLib : public CBaseAppSystem< IMdlLib > +{ +public: + // Destructor + virtual ~CMdlLib(); + + ////////////////////////////////////////////////////////////////////////// + // + // Methods of IAppSystem + // + ////////////////////////////////////////////////////////////////////////// +public: + virtual bool Connect( CreateInterfaceFn factory ); + virtual InitReturnVal_t Init(); + virtual void *QueryInterface( const char *pInterfaceName ); + virtual void Shutdown(); + virtual void Disconnect(); + + + ////////////////////////////////////////////////////////////////////////// + // + // Methods of IMdlLib + // + ////////////////////////////////////////////////////////////////////////// +public: + // + // 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 + // + virtual bool StripModelBuffers( CUtlBuffer &mdlBuffer, CUtlBuffer &vvdBuffer, CUtlBuffer &vtxBuffer, IMdlStripInfo **ppStripInfo ); + + // + // CreateNewStripInfo + // Creates an empty strip info so that it can be reused. + // + virtual bool CreateNewStripInfo( IMdlStripInfo **ppStripInfo ); +}; + +#endif // #ifndef MDLLIB_COMMON_H diff --git a/mdllib/mdllib_stripinfo.cpp b/mdllib/mdllib_stripinfo.cpp new file mode 100644 index 0000000..f99f889 --- /dev/null +++ b/mdllib/mdllib_stripinfo.cpp @@ -0,0 +1,671 @@ +//========= 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 "materialsystem/imaterial.h" +#include "materialsystem/hardwareverts.h" + +#include "smartptr.h" + + + +////////////////////////////////////////////////////////////////////////// +// +// CMdlStripInfo implementation +// +////////////////////////////////////////////////////////////////////////// + + +CMdlStripInfo::CMdlStripInfo() : + m_eMode( MODE_UNINITIALIZED ), + m_lChecksumOld( 0 ), + m_lChecksumNew( 0 ) +{ + NULL; +} + + +bool CMdlStripInfo::Serialize( CUtlBuffer &bufStorage ) const +{ + char chHeader[ 4 ] = { 'M', 'A', 'P', m_eMode }; + bufStorage.Put( chHeader, sizeof( chHeader ) ); + + switch ( m_eMode ) + { + default: + case MODE_UNINITIALIZED: + return true; + + case MODE_NO_CHANGE: + bufStorage.PutInt( m_lChecksumOld ); + bufStorage.PutInt( m_lChecksumNew ); + return true; + + case MODE_STRIP_LOD_1N: + bufStorage.PutInt( m_lChecksumOld ); + bufStorage.PutInt( m_lChecksumNew ); + + bufStorage.PutInt( m_vtxVerts.GetNumBits() ); + for ( uint32 const *pdwBase = m_vtxVerts.Base(), *pdwEnd = pdwBase + m_vtxVerts.GetNumDWords(); + pdwBase < pdwEnd; ++ pdwBase ) + bufStorage.PutUnsignedInt( *pdwBase ); + + bufStorage.PutInt( m_vtxIndices.Count() ); + for ( unsigned short const *pusBase = m_vtxIndices.Base(), *pusEnd = pusBase + m_vtxIndices.Count(); + pusBase < pusEnd; ++ pusBase ) + bufStorage.PutUnsignedShort( *pusBase ); + + bufStorage.PutInt( m_vtxMdlOffsets.Count() ); + for ( MdlRangeItem const *pmri = m_vtxMdlOffsets.Base(), *pmriEnd = pmri + m_vtxMdlOffsets.Count(); + pmri < pmriEnd; ++ pmri ) + bufStorage.PutInt( pmri->m_offOld ), + bufStorage.PutInt( pmri->m_offNew ), + bufStorage.PutInt( pmri->m_numOld ), + bufStorage.PutInt( pmri->m_numNew ); + + return true; + } +} + +bool CMdlStripInfo::UnSerialize( CUtlBuffer &bufData ) +{ + char chHeader[ 4 ]; + bufData.Get( chHeader, sizeof( chHeader ) ); + + if ( memcmp( chHeader, "MAP", 3 ) ) + return false; + + switch ( chHeader[3] ) + { + default: + return false; + + case MODE_UNINITIALIZED: + m_eMode = MODE_UNINITIALIZED; + m_lChecksumOld = 0; + m_lChecksumNew = 0; + return true; + + case MODE_NO_CHANGE: + m_eMode = MODE_NO_CHANGE; + m_lChecksumOld = bufData.GetInt(); + m_lChecksumNew = bufData.GetInt(); + return true; + + case MODE_STRIP_LOD_1N: + m_eMode = MODE_STRIP_LOD_1N; + m_lChecksumOld = bufData.GetInt(); + m_lChecksumNew = bufData.GetInt(); + + m_vtxVerts.Resize( bufData.GetInt(), true ); + for ( uint32 *pdwBase = m_vtxVerts.Base(), *pdwEnd = pdwBase + m_vtxVerts.GetNumDWords(); + pdwBase < pdwEnd; ++ pdwBase ) + *pdwBase = bufData.GetUnsignedInt(); + + m_vtxIndices.SetCount( bufData.GetInt() ); + for ( unsigned short *pusBase = m_vtxIndices.Base(), *pusEnd = pusBase + m_vtxIndices.Count(); + pusBase < pusEnd; ++ pusBase ) + *pusBase = bufData.GetUnsignedShort(); + + m_vtxMdlOffsets.SetCount( bufData.GetInt() ); + for ( MdlRangeItem *pmri = m_vtxMdlOffsets.Base(), *pmriEnd = pmri + m_vtxMdlOffsets.Count(); + pmri < pmriEnd; ++ pmri ) + pmri->m_offOld = bufData.GetInt(), + pmri->m_offNew = bufData.GetInt(), + pmri->m_numOld = bufData.GetInt(), + pmri->m_numNew = bufData.GetInt(); + + return true; + } +} + + +// Returns the checksums that the stripping info was generated for: +// plChecksumOriginal if non-NULL will hold the checksum of the original model submitted for stripping +// plChecksumStripped if non-NULL will hold the resulting checksum of the stripped model +bool CMdlStripInfo::GetCheckSum( long *plChecksumOriginal, long *plChecksumStripped ) const +{ + if ( m_eMode == MODE_UNINITIALIZED ) + return false; + + if ( plChecksumOriginal ) + *plChecksumOriginal = m_lChecksumOld; + + if ( plChecksumStripped ) + *plChecksumStripped = m_lChecksumNew; + + return true; +} + + +// +// StripHardwareVertsBuffer +// The main function that strips the vhv buffer +// vhvBuffer - vhv buffer, updated, size reduced +// +bool CMdlStripInfo::StripHardwareVertsBuffer( CUtlBuffer &vhvBuffer ) +{ + if ( m_eMode == MODE_UNINITIALIZED ) + return false; + + // + // Recover vhv header + // + DECLARE_PTR( HardwareVerts::FileHeader_t, vhvHdr, BYTE_OFF_PTR( vhvBuffer.Base(), vhvBuffer.TellGet() ) ); + int vhvLength = vhvBuffer.TellPut() - vhvBuffer.TellGet(); + + if ( vhvHdr->m_nChecksum != m_lChecksumOld ) + { + DLog( "mdllib", 1, "ERROR: [StripHardwareVertsBuffer] checksum mismatch!\n" ); + return false; + } + + vhvHdr->m_nChecksum = m_lChecksumNew; + + + // No remapping required + if ( m_eMode == MODE_NO_CHANGE ) + return true; + + + Assert( m_eMode == MODE_STRIP_LOD_1N ); + + // + // Now reconstruct the vhv structures to do the mapping + // + + CRemoveTracker vhvRemove; + size_t vhvVertOffset = ~size_t( 0 ), vhvEndMeshOffset = sizeof( HardwareVerts::FileHeader_t ); + int numMeshesRemoved = 0, numVertsRemoved = 0; + + ITERATE_CHILDREN( HardwareVerts::MeshHeader_t, vhvMesh, vhvHdr, pMesh, m_nMeshes ) + if ( vhvMesh->m_nOffset < vhvVertOffset ) + vhvVertOffset = vhvMesh->m_nOffset; + if ( BYTE_DIFF_PTR( vhvHdr, vhvMesh + 1 ) > vhvEndMeshOffset ) + vhvEndMeshOffset = BYTE_DIFF_PTR( vhvHdr, vhvMesh + 1 ); + if ( !vhvMesh->m_nLod ) + continue; + vhvRemove.RemoveBytes( BYTE_OFF_PTR( vhvHdr, vhvMesh->m_nOffset ), vhvMesh->m_nVertexes * vhvHdr->m_nVertexSize ); + vhvRemove.RemoveElements( vhvMesh ); + numVertsRemoved += vhvMesh->m_nVertexes; + ++ numMeshesRemoved; + ITERATE_END + vhvRemove.RemoveBytes( BYTE_OFF_PTR( vhvHdr, vhvEndMeshOffset ), vhvVertOffset - vhvEndMeshOffset ); // Padding + vhvRemove.RemoveBytes( BYTE_OFF_PTR( vhvHdr, vhvVertOffset + vhvHdr->m_nVertexes * vhvHdr->m_nVertexSize ), vhvLength - ( vhvVertOffset + vhvHdr->m_nVertexes * vhvHdr->m_nVertexSize ) ); + + vhvRemove.Finalize(); + DLog( "mdllib", 3, " Stripped %d vhv bytes.\n", vhvRemove.GetNumBytesRemoved() ); + + // Verts must be aligned from hdr, length must be aligned from hdr + size_t vhvNewVertOffset = vhvRemove.ComputeOffset( vhvHdr, vhvVertOffset ); + size_t vhvAlignedVertOffset = ALIGN_VALUE( vhvNewVertOffset, 4 ); + + ITERATE_CHILDREN( HardwareVerts::MeshHeader_t, vhvMesh, vhvHdr, pMesh, m_nMeshes ) + vhvMesh->m_nOffset = vhvRemove.ComputeOffset( vhvHdr, vhvMesh->m_nOffset ) + vhvAlignedVertOffset - vhvNewVertOffset; + ITERATE_END + vhvHdr->m_nMeshes -= numMeshesRemoved; + vhvHdr->m_nVertexes -= numVertsRemoved; + + // Remove the memory + vhvRemove.MemMove( vhvHdr, vhvLength ); // All padding has been removed + + size_t numBytesNewLength = vhvLength + vhvAlignedVertOffset - vhvNewVertOffset; + size_t numAlignedNewLength = ALIGN_VALUE( numBytesNewLength, 4 ); + + // Now reinsert the padding + CInsertionTracker vhvInsertPadding; + vhvInsertPadding.InsertBytes( BYTE_OFF_PTR( vhvHdr, vhvNewVertOffset ), vhvAlignedVertOffset - vhvNewVertOffset ); + vhvInsertPadding.InsertBytes( BYTE_OFF_PTR( vhvHdr, vhvLength ), numAlignedNewLength - numBytesNewLength ); + + vhvInsertPadding.Finalize(); + DLog( "mdllib", 3, " Inserted %d alignment bytes.\n", vhvInsertPadding.GetNumBytesInserted() ); + + vhvInsertPadding.MemMove( vhvHdr, vhvLength ); + + + // Update the buffer length + vhvBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, vhvBuffer.TellGet() + vhvLength - vhvBuffer.TellPut() ); + + DLog( "mdllib", 2, " Reduced vhv buffer by %d bytes.\n", vhvRemove.GetNumBytesRemoved() - vhvInsertPadding.GetNumBytesInserted() ); + + // Done + return true; +} + +// +// StripModelBuffer +// The main function that strips the mdl buffer +// mdlBuffer - mdl buffer, updated +// +bool CMdlStripInfo::StripModelBuffer( CUtlBuffer &mdlBuffer ) +{ + if ( m_eMode == MODE_UNINITIALIZED ) + return false; + + // + // Recover mdl header + // + DECLARE_PTR( studiohdr_t, mdlHdr, BYTE_OFF_PTR( mdlBuffer.Base(), mdlBuffer.TellGet() ) ); + + if ( mdlHdr->checksum != m_lChecksumOld ) + { + DLog( "mdllib", 1, "ERROR: [StripModelBuffer] checksum mismatch!\n" ); + return false; + } + + mdlHdr->checksum = m_lChecksumNew; + + + // No remapping required + if ( m_eMode == MODE_NO_CHANGE ) + return true; + + + Assert( m_eMode == MODE_STRIP_LOD_1N ); + + + // + // Do the model buffer stripping + // + + CUtlSortVector< unsigned short, CLessSimple< unsigned short > > &srcIndices = m_vtxIndices; + + ITERATE_CHILDREN( mstudiobodyparts_t, mdlBodyPart, mdlHdr, pBodypart, numbodyparts ) + ITERATE_CHILDREN( mstudiomodel_t, mdlModel, mdlBodyPart, pModel, nummodels ) + + DLog( "mdllib", 3, " Stripped %d vertexes (was: %d, now: %d).\n", mdlModel->numvertices - srcIndices.Count(), mdlModel->numvertices, srcIndices.Count() ); + + mdlModel->numvertices = srcIndices.Count(); + + ITERATE_CHILDREN( mstudiomesh_t, mdlMesh, mdlModel, pMesh, nummeshes ) + + mdlMesh->numvertices = srcIndices.FindLess( mdlMesh->vertexoffset + mdlMesh->numvertices ); + mdlMesh->vertexoffset = srcIndices.FindLess( mdlMesh->vertexoffset ) + 1; + mdlMesh->numvertices -= mdlMesh->vertexoffset - 1; + + // 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 ); + + return true; +} + + +// +// StripVertexDataBuffer +// The main function that strips the vvd buffer +// vvdBuffer - vvd buffer, updated, size reduced +// +bool CMdlStripInfo::StripVertexDataBuffer( CUtlBuffer &vvdBuffer ) +{ + if ( m_eMode == MODE_UNINITIALIZED ) + return false; + + // + // Recover vvd header + // + DECLARE_PTR( vertexFileHeader_t, vvdHdr, BYTE_OFF_PTR( vvdBuffer.Base(), vvdBuffer.TellGet() ) ); + int vvdLength = vvdBuffer.TellPut() - vvdBuffer.TellGet(); + + if ( vvdHdr->checksum != m_lChecksumOld ) + { + DLog( "mdllib", 1, "ERROR: [StripVertexDataBuffer] checksum mismatch!\n" ); + return false; + } + + vvdHdr->checksum = m_lChecksumNew; + + + // No remapping required + if ( m_eMode == MODE_NO_CHANGE ) + return true; + + + Assert( m_eMode == MODE_STRIP_LOD_1N ); + + + // + // Do the vertex data buffer stripping + // + + CUtlSortVector< unsigned short, CLessSimple< unsigned short > > &srcIndices = m_vtxIndices; + int mdlNumVerticesOld = vvdHdr->numLODVertexes[ 0 ]; + + 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 ); + } + + vvdBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, vvdBuffer.TellGet() + vvdLength - vvdBuffer.TellPut() ); + + DLog( "mdllib", 3, " Stripped %d vvd bytes.\n", vvdLengthOld - vvdLength ); + + return true; +} + +// +// StripOptimizedModelBuffer +// The main function that strips the vtx buffer +// vtxBuffer - vtx buffer, updated, size reduced +// +bool CMdlStripInfo::StripOptimizedModelBuffer( CUtlBuffer &vtxBuffer ) +{ + if ( m_eMode == MODE_UNINITIALIZED ) + return false; + + // + // Recover vtx header + // + DECLARE_PTR( OptimizedModel::FileHeader_t, vtxHdr, BYTE_OFF_PTR( vtxBuffer.Base(), vtxBuffer.TellGet() ) ); + int vtxLength = vtxBuffer.TellPut() - vtxBuffer.TellGet(); + + if ( vtxHdr->checkSum != m_lChecksumOld ) + { + DLog( "mdllib", 1, "ERROR: [StripOptimizedModelBuffer] checksum mismatch!\n" ); + return false; + } + + vtxHdr->checkSum = m_lChecksumNew; + + // No remapping required + if ( m_eMode == MODE_NO_CHANGE ) + return true; + + Assert( m_eMode == MODE_STRIP_LOD_1N ); + + + // + // Do the optimized model buffer stripping + // + + CUtlSortVector< unsigned short, CLessSimple< unsigned short > > &srcIndices = m_vtxIndices; + CUtlSortVector< CMdlStripInfo::MdlRangeItem, CLessSimple< CMdlStripInfo::MdlRangeItem > > &arrMdlOffsets = m_vtxMdlOffsets; + + 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( MdlRangeItem( 0, 0, vtxVertexElement - vtxVertexBuffer ) ); + Assert( iMesh >= 0 && iMesh < arrMdlOffsets.Count() ); + + MdlRangeItem &mri = arrMdlOffsets[ iMesh ]; + Assert( ( vtxVertexElement - vtxVertexBuffer >= mri.m_offNew ) && ( vtxVertexElement - vtxVertexBuffer < mri.m_offNew + mri.m_numNew ) ); + + Assert( m_vtxVerts.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 ); + + vtxBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, vtxBuffer.TellGet() + vtxLength - vtxBuffer.TellPut() ); + + return true; +} + + + + + +////////////////////////////////////////////////////////////////////////// +// +// Auxilliary methods +// +////////////////////////////////////////////////////////////////////////// + +void CMdlStripInfo::DeleteThis() +{ + delete this; +} + +void CMdlStripInfo::Reset() +{ + m_eMode = MODE_UNINITIALIZED; + m_lChecksumOld = 0; + m_lChecksumNew = 0; + + m_vtxVerts.Resize( 0 ); + m_vtxIndices.RemoveAll(); +} + diff --git a/mdllib/mdllib_stripinfo.h b/mdllib/mdllib_stripinfo.h new file mode 100644 index 0000000..e04d98c --- /dev/null +++ b/mdllib/mdllib_stripinfo.h @@ -0,0 +1,127 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef MDLLIB_STRIPINFO_H +#define MDLLIB_STRIPINFO_H +#ifdef _WIN32 +#pragma once +#endif + +#include "mdllib/mdllib.h" +#include "mdllib_utils.h" +#include "UtlSortVector.h" + + +// +// CMdlStripInfo +// Implementation of IMdlStripInfo interface +// +class CMdlStripInfo : public IMdlStripInfo +{ +public: + CMdlStripInfo(); + + // + // Serialization + // +public: + // Save the strip info to the buffer (appends to the end) + virtual bool Serialize( CUtlBuffer &bufStorage ) const; + + // Load the strip info from the buffer (reads from the current position as much as needed) + virtual bool UnSerialize( CUtlBuffer &bufData ); + + // + // Stripping info state + // +public: + // Returns the checksums that the stripping info was generated for: + // plChecksumOriginal if non-NULL will hold the checksum of the original model submitted for stripping + // plChecksumStripped if non-NULL will hold the resulting checksum of the stripped model + virtual bool GetCheckSum( long *plChecksumOriginal, long *plChecksumStripped ) const; + + // + // Stripping + // +public: + + // + // StripHardwareVertsBuffer + // The main function that strips the vhv buffer + // vhvBuffer - vhv buffer, updated, size reduced + // + virtual bool StripHardwareVertsBuffer( CUtlBuffer &vhvBuffer ); + + // + // StripModelBuffer + // The main function that strips the mdl buffer + // mdlBuffer - mdl buffer, updated + // + virtual bool StripModelBuffer( CUtlBuffer &mdlBuffer ); + + // + // StripVertexDataBuffer + // The main function that strips the vvd buffer + // vvdBuffer - vvd buffer, updated, size reduced + // + virtual bool StripVertexDataBuffer( CUtlBuffer &vvdBuffer ); + + // + // StripOptimizedModelBuffer + // The main function that strips the vtx buffer + // vtxBuffer - vtx buffer, updated, size reduced + // + virtual bool StripOptimizedModelBuffer( CUtlBuffer &vtxBuffer ); + + // + // Release the object with "delete this" + // +public: + virtual void DeleteThis(); + + + +public: + void Reset(); + + +public: + enum Mode + { + MODE_UNINITIALIZED = 0, + MODE_NO_CHANGE = 1, + MODE_STRIP_LOD_1N = 2, + }; + + // + // Internal data used for stripping + // +public: + int m_eMode; + long m_lChecksumOld, m_lChecksumNew; + CGrowableBitVec m_vtxVerts; + CUtlSortVector< unsigned short, CLessSimple< unsigned short > > m_vtxIndices; + + // + // Mesh ranges fixup + // +public: + struct MdlRangeItem + { + /* implicit */ MdlRangeItem( int offOld = 0, int numOld = 0, int offNew = 0, int numNew = 0 ) : + m_offOld( offOld ), m_offNew( offNew ), m_numOld( numOld ), m_numNew( numNew ) {} + + int m_offOld, m_offNew; + int m_numOld, m_numNew; + + bool operator < ( MdlRangeItem const &x ) const { return m_offNew < x.m_offNew; } + }; + CUtlSortVector< CMdlStripInfo::MdlRangeItem, CLessSimple< CMdlStripInfo::MdlRangeItem > > m_vtxMdlOffsets; + +}; + + +#endif // #ifndef MDLLIB_STRIPINFO_H diff --git a/mdllib/mdllib_stripmodel.cpp b/mdllib/mdllib_stripmodel.cpp new file mode 100644 index 0000000..e05d8cd --- /dev/null +++ b/mdllib/mdllib_stripmodel.cpp @@ -0,0 +1,504 @@ +//========= 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; +} + diff --git a/mdllib/mdllib_utils.cpp b/mdllib/mdllib_utils.cpp new file mode 100644 index 0000000..77ec4fa --- /dev/null +++ b/mdllib/mdllib_utils.cpp @@ -0,0 +1,205 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + + +#include "mdllib_utils.h" + + +////////////////////////////////////////////////////////////////////////// +// +// CInsertionTracker implementation +// +////////////////////////////////////////////////////////////////////////// + + +void CInsertionTracker::InsertBytes( void *pos, int length ) +{ + if ( length <= 0 ) + return; + + Assert( m_map.InvalidIndex() == m_map.Find( ( byte * ) pos ) ); + m_map.InsertOrReplace( ( byte * ) pos, length ); +} + +int CInsertionTracker::GetNumBytesInserted() const +{ + int iInserted = 0; + + for ( Map::IndexType_t idx = m_map.FirstInorder(); + idx != m_map.InvalidIndex(); idx = m_map.NextInorder( idx ) ) + { + int numBytes = m_map.Element( idx ); + iInserted += numBytes; + } + + return iInserted; +} + +void CInsertionTracker::Finalize() +{ + // Iterate the map and find all the adjacent removal data blocks + // TODO: +} + +void CInsertionTracker::MemMove( void *ptrBase, int &length ) const +{ + int numBytesInsertReq = GetNumBytesInserted(); + byte *pbBlockEnd = BYTE_OFF_PTR( ptrBase, length ); + length += numBytesInsertReq; + + for ( Map::IndexType_t idx = m_map.LastInorder(); + idx != m_map.InvalidIndex(); idx = m_map.PrevInorder( idx ) ) + { + byte *ptr = m_map.Key( idx ); + int numBytes = m_map.Element( idx ); + + // Move [ptr, pbBlockEnd) ->> + numBytesInsertReq + memmove( BYTE_OFF_PTR( ptr, numBytesInsertReq ), ptr, BYTE_DIFF_PTR( ptr, pbBlockEnd ) ); + + // Inserted data + memset( BYTE_OFF_PTR( ptr, numBytesInsertReq - numBytes ), 0, numBytes ); + + numBytesInsertReq -= numBytes; + pbBlockEnd = ptr; + } +} + +int CInsertionTracker::ComputeOffset( void *ptrBase, int off ) const +{ + void *ptrNewBase = ComputePointer( ptrBase ); + void *ptrNewData = ComputePointer( BYTE_OFF_PTR( ptrBase, off ) ); + return BYTE_DIFF_PTR( ptrNewBase, ptrNewData ); +} + +void * CInsertionTracker::ComputePointer( void *ptrNothingInserted ) const +{ + int iInserted = 0; + + // Iterate the map and find all the data that would be inserted before the given pointer + for ( Map::IndexType_t idx = m_map.FirstInorder(); + idx != m_map.InvalidIndex(); idx = m_map.NextInorder( idx ) ) + { + if ( m_map.Key( idx ) < ptrNothingInserted ) + iInserted += m_map.Element( idx ); + else + break; + } + + return BYTE_OFF_PTR( ptrNothingInserted, iInserted ); +} + + + +////////////////////////////////////////////////////////////////////////// +// +// CRemoveTracker implementation +// +////////////////////////////////////////////////////////////////////////// + + +void CRemoveTracker::RemoveBytes( void *pos, int length ) +{ + if ( length <= 0 ) + return; + + // -- hint + if ( m_map.Count() ) + { + if ( m_hint.ptr < pos ) + { + if ( BYTE_OFF_PTR( m_hint.ptr, m_hint.len ) == pos ) + { + m_hint.len += length; + m_map.Element( m_hint.idx ) = m_hint.len; + return; + } + } + else if ( m_hint.ptr > pos ) + { + if ( BYTE_OFF_PTR( pos, length ) == m_hint.ptr ) + { + m_hint.len += length; + m_hint.ptr = BYTE_OFF_PTR( m_hint.ptr, - length ); + m_map.Key( m_hint.idx ) = m_hint.ptr; + m_map.Element( m_hint.idx ) = m_hint.len; + return; + } + } + } + // -- end hint + + // Insert new + Assert( m_map.InvalidIndex() == m_map.Find( ( byte * ) pos ) ); + Map::IndexType_t idx = m_map.InsertOrReplace( ( byte * ) pos, length ); + + // New hint + m_hint.idx = idx; + m_hint.ptr = ( byte * ) pos; + m_hint.len = length; +} + +int CRemoveTracker::GetNumBytesRemoved() const +{ + int iRemoved = 0; + + for ( Map::IndexType_t idx = m_map.FirstInorder(); + idx != m_map.InvalidIndex(); idx = m_map.NextInorder( idx ) ) + { + int numBytes = m_map.Element( idx ); + iRemoved += numBytes; + } + + return iRemoved; +} + +void CRemoveTracker::Finalize() +{ + // Iterate the map and find all the adjacent removal data blocks + // TODO: +} + +void CRemoveTracker::MemMove( void *ptrBase, int &length ) const +{ + int iRemoved = 0; + + for ( Map::IndexType_t idx = m_map.FirstInorder(); + idx != m_map.InvalidIndex(); idx = m_map.NextInorder( idx ) ) + { + byte *ptr = m_map.Key( idx ); + byte *ptrDest = BYTE_OFF_PTR( ptr, - iRemoved ); + int numBytes = m_map.Element( idx ); + memmove( ptrDest, BYTE_OFF_PTR( ptrDest, numBytes ), BYTE_DIFF_PTR( BYTE_OFF_PTR( ptr, numBytes ), BYTE_OFF_PTR( ptrBase, length ) ) ); + iRemoved += numBytes; + } + + length -= iRemoved; +} + +int CRemoveTracker::ComputeOffset( void *ptrBase, int off ) const +{ + void *ptrNewBase = ComputePointer( ptrBase ); + void *ptrNewData = ComputePointer( BYTE_OFF_PTR( ptrBase, off ) ); + return BYTE_DIFF_PTR( ptrNewBase, ptrNewData ); +} + +void * CRemoveTracker::ComputePointer( void *ptrNothingRemoved ) const +{ + int iRemoved = 0; + + // Iterate the map and find all the data that would be removed before the given pointer + for ( Map::IndexType_t idx = m_map.FirstInorder(); + idx != m_map.InvalidIndex(); idx = m_map.NextInorder( idx ) ) + { + if ( m_map.Key( idx ) < ptrNothingRemoved ) + iRemoved += m_map.Element( idx ); + else + break; + } + + return BYTE_OFF_PTR( ptrNothingRemoved, - iRemoved ); +} + + diff --git a/mdllib/mdllib_utils.h b/mdllib/mdllib_utils.h new file mode 100644 index 0000000..cf14629 --- /dev/null +++ b/mdllib/mdllib_utils.h @@ -0,0 +1,203 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#ifndef MDLLIB_UTILS_H +#define MDLLIB_UTILS_H + +#ifdef _WIN32 +#pragma once +#endif + + +#include "utlmap.h" +#include "utlvector.h" +#include "bitvec.h" + + +////////////////////////////////////////////////////////////////////////// +// +// Helper macros +// +////////////////////////////////////////////////////////////////////////// + +// Declare a pointer and automatically do the cast of initial value to the pointer type +#define DECLARE_PTR( type, name, initval ) type *name = ( type * ) ( initval ) + +// Compute a pointer that is offset given number of bytes from the base pointer +#define BYTE_OFF_PTR( initval, offval ) ( ( ( byte * ) ( initval ) ) + ( offval ) ) + +// Compute difference in bytes between two pointers +#define BYTE_DIFF_PTR( begin, end ) ( ( ( byte * ) ( end ) ) - ( ( byte * ) ( begin ) ) ) + + +// "for {" to iterate children of a studio container +#define ITERATE_CHILDREN( type, name, parent, accessor, count ) \ + for ( int name##_idx = 0; name##_idx < (parent)->count; ++ name##_idx ) { \ + type *name = (parent)->accessor( name##_idx ); + +// "for {" to jointly iterate children of 2 studio containers of same size +#define ITERATE_CHILDREN2( type, type2, name, name2, parent, parent2, accessor, accessor2, count ) \ + for ( int name##_idx = 0; name##_idx < (parent)->count; ++ name##_idx ) { \ + type *name = (parent)->accessor( name##_idx ); \ + type2 *name2 = (parent2)->accessor2( name##_idx ); + +// "}" to mark the end of iteration block +#define ITERATE_END } + +// Get the child of a container by index +#define CHILD_AT( parent, accessor, idx ) ( (parent)->accessor( idx ) ) + + +// +// CLessSimple< T > +// Comparison policy to use "t1 < t2" comparison rule. +// +template < typename T > +class CLessSimple +{ +public: + bool Less( const T& src1, const T& src2, void *pCtx ) + { + pCtx; + return ( src1 < src2 ); + } +}; + +// +// CInsertionTracker +// Class that is tracking insertions that are scheduled to happen at given points. +// Use policy: +// InsertBytes / InsertElements [*] -- schedule insertions +// Finalize -- finalize insertion information +// ComputePointer / ComputeOffset [*] -- compute new pointers/offsets that will happen after insertions +// MemMove -- perform memory moves to apply insertions +// +class CInsertionTracker +{ +public: + CInsertionTracker() : m_map( DefLessFunc( byte * ) ) {} + + // Schedules a piece of memory for insertion +public: + void InsertBytes( void *pos, int length ); + + template< typename T > + void InsertElements( T *ptr, int count = 1 ) { InsertBytes( ( byte * ) ptr, count * sizeof( T ) ); } + + int GetNumBytesInserted() const; + + // Finalizes the insertion information +public: + void Finalize(); + + // Computes where the pointer would point after memory insertion occurs +public: + void * ComputePointer( void *ptrNothingInserted ) const; + int ComputeOffset( void *ptrBase, int off ) const; + + // Perform memory moves, the buffer should be large enough to accommodate inserted bytes +public: + void MemMove( void *ptrBase, int &length ) const; + +protected: + typedef CUtlMap< byte *, int, unsigned int > Map; + Map m_map; // pos -> length +}; + + +// +// CRemoveTracker +// Class that is tracking removals that are scheduled to happen at given points. +// Use policy: +// RemoveBytes / RemoveElements [*] -- schedule removals +// Finalize -- finalize removal information +// ComputePointer / ComputeOffset [*] -- compute new pointers/offsets that will happen after removals +// MemMove -- perform memory moves to apply removals +// +class CRemoveTracker +{ +public: + CRemoveTracker() : m_map( DefLessFunc( byte * ) ) {} + + // Schedules a piece of memory for removal +public: + void RemoveBytes( void *pos, int length ); + + template< typename T > + void RemoveElements( T *ptr, int count = 1 ) { RemoveBytes( ( byte * ) ptr, count * sizeof( T ) ); } + + int GetNumBytesRemoved() const; + + // Finalizes the removal information +public: + void Finalize(); + + // Computes where the pointer would point after memory removal occurs +public: + void * ComputePointer( void *ptrNothingRemoved ) const; + int ComputeOffset( void *ptrBase, int off ) const; + +public: + void MemMove( void *ptrBase, int &length ) const; + +protected: + typedef CUtlMap< byte *, int, unsigned int > Map; + Map m_map; // pos -> length + + struct Item + { + Map::IndexType_t idx; + byte *ptr; + int len; + }; + Item m_hint; +}; + + +// +// CGrowableBitVec +// Serves bit accumulation. +// Provides "GrowSetBit" method to automatically grow to the required size +// and set the given bit. +// Provides safe "IsBitSet" that would return false for missing bits. +// +class CGrowableBitVec : public CVarBitVec +{ +public: + void GrowSetBit( int iBit ) + { + if ( iBit >= GetNumBits() ) + Resize( iBit + 1, false ); + Set( iBit ); + } + + bool IsBitSet( int bitNum ) const + { + return ( bitNum < GetNumBits() ) && CVarBitVec::IsBitSet( bitNum ); + } +}; + + +// +// CGrowableVector +// Provides zero-initialization for new elements. +// +template < typename T > +class CGrowableVector : public CUtlVector < T > +{ +public: + T & operator[] ( int idx ) + { + while ( idx >= Count() ) + AddToTail( T() ); + return CUtlVector < T >::operator []( idx ); + } +}; + + + + +#endif // #ifndef MDLLIB_UTILS_H |