From 9772c4dcf45c9fc23a58b9093d67968fcc02fb54 Mon Sep 17 00:00:00 2001 From: RaphaelIT7 Date: Fri, 22 Nov 2024 09:33:39 +0100 Subject: [PATCH] [client] Fix SetModelScale having no clientside collisions Origins: 1 - https://github.com/RaphaelIT7/obsolete-source-engine/commit/dd24daecdca79b33d685259273b9785f531670ef 2 - https://github.com/RaphaelIT7/obsolete-source-engine/commit/4e6f0c1c4dd9a6aebc149a9ea7debcf02abede34 3 - https://github.com/RaphaelIT7/obsolete-source-engine/commit/f8faf4eb688c750c3cd5b78ddee0198c41a34686 4 - https://github.com/RaphaelIT7/obsolete-source-engine/commit/49f346e8264260dcb4704cc90718cb0c5526549f --- game/client/c_baseanimating.cpp | 36 ++++++++- game/client/c_baseanimating.h | 4 + game/client/c_baseentity.h | 3 + game/client/cdll_util.cpp | 128 ++++++++++++++++++++++++++++++++ game/client/cdll_util.h | 10 +++ 5 files changed, 180 insertions(+), 1 deletion(-) diff --git a/game/client/c_baseanimating.cpp b/game/client/c_baseanimating.cpp index 3f87f07bb..1c3c44f48 100644 --- a/game/client/c_baseanimating.cpp +++ b/game/client/c_baseanimating.cpp @@ -789,6 +789,12 @@ C_BaseAnimating::~C_BaseAnimating() m_pAttachedTo->RemoveBoneAttachment( this ); m_pAttachedTo = NULL; } + + if ( m_pScaledCollidable ) + { + UTIL_RemoveScaledPhysCollide( m_pScaledCollidable ); + m_pScaledCollidable = NULL; + } } bool C_BaseAnimating::UsesPowerOfTwoFrameBufferTexture( void ) @@ -5157,9 +5163,18 @@ void C_BaseAnimating::Simulate() } } - bool C_BaseAnimating::TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ) { + if ( GetModelScale() != 1.0f ) + { + if ( m_pScaledCollidable != NULL ) + { + physcollision->TraceBox( ray, m_pScaledCollidable, GetAbsOrigin(), GetAbsAngles(), &tr ); + + return tr.DidHit(); + } + } + if ( ray.m_IsRay && IsSolidFlagSet( FSOLID_CUSTOMRAYTEST )) { if (!TestHitboxes( ray, fContentsMask, tr )) @@ -6706,3 +6721,22 @@ void C_BaseAnimating::MoveBoneAttachments( C_BaseAnimating* attachTarget ) } } } + +//----------------------------------------------------------------------------- +// Purpose: Update Collisions. (Same way it's done serverside. We only update it when Activate is called) +//----------------------------------------------------------------------------- +void C_BaseAnimating::Activate() +{ + if ( GetModelScale() != 1.0f ) + { + if ( m_pScaledCollidable ) + { + UTIL_RemoveScaledPhysCollide( m_pScaledCollidable ); + m_pScaledCollidable = NULL; + } + + m_pScaledCollidable = UTIL_GetScaledPhysCollide( GetModelIndex(), GetModelScale() ); + } + + BaseClass::Activate(); +} \ No newline at end of file diff --git a/game/client/c_baseanimating.h b/game/client/c_baseanimating.h index d47fadf22..9f4c9a3bf 100644 --- a/game/client/c_baseanimating.h +++ b/game/client/c_baseanimating.h @@ -449,6 +449,7 @@ class C_BaseAnimating : public C_BaseEntity, private IModelLoadCallback virtual bool IsViewModel() const; void UpdateOnRemove( void ) override; + virtual void Activate( void ); protected: // View models scale their attachment positions to account for FOV. To get the unmodified @@ -640,6 +641,9 @@ class C_BaseAnimating : public C_BaseEntity, private IModelLoadCallback mutable MDLHandle_t m_hStudioHdr; CThreadFastMutex m_StudioHdrInitLock; bool m_bHasAttachedParticles; + +private: + CPhysCollide *m_pScaledCollidable; }; enum diff --git a/game/client/c_baseentity.h b/game/client/c_baseentity.h index 454d24b98..c82a87895 100644 --- a/game/client/c_baseentity.h +++ b/game/client/c_baseentity.h @@ -1710,6 +1710,9 @@ class C_BaseEntity : public IClientEntity private: bool m_bOldShouldDraw; + +public: + virtual void OnModelChange( int oldModelIndex, int newModelIndex ) {}; }; EXTERN_RECV_TABLE(DT_BaseEntity); diff --git a/game/client/cdll_util.cpp b/game/client/cdll_util.cpp index ca902f996..e6c725a51 100644 --- a/game/client/cdll_util.cpp +++ b/game/client/cdll_util.cpp @@ -28,6 +28,10 @@ #include "view.h" #include "ixboxsystem.h" #include "inputsystem/iinputsystem.h" +#include +#include +#include +#include // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -1315,3 +1319,127 @@ bool UTIL_HasLoadedAnyMap() return g_pFullFileSystem->FileExists( szFilename, "MOD" ); } + +struct CPhysEntry +{ + int references = 1; + int modelIndex = 0; + float scale = 1.0f; +}; + +std::unordered_map g_pScaledReferences; +std::unordered_map*> g_pScaledCollidables; +CPhysCollide *UTIL_GetScaledPhysCollide( int modelIndex, float scale ) // Based off UTIL_CreateScaledPhysObject +{ + VPROF( "UTIL_GetScaledPhysCollide", VPROF_BUDGETGROUP_PHYSICS ); + + if (scale == 1.0f) + return NULL; + + std::unordered_map* scaledCollidables = nullptr; + auto iModel = g_pScaledCollidables.find( modelIndex ); + if ( iModel != g_pScaledCollidables.end() ) + { + std::unordered_map* collidables = iModel->second; + auto iCollidable = collidables->find( scale ); + if ( iCollidable != collidables->end() ) + { + auto it = g_pScaledReferences.find( iCollidable->second ); + if ( it != g_pScaledReferences.end() ) + { + ++it->second.references; + } else { + DevWarning( "UTIL_GetScaledPhysCollide: Failed to find reference counter!\n" ); + } + + return iCollidable->second; + } else { + scaledCollidables = collidables; + } + } else { + scaledCollidables = new std::unordered_map; + g_pScaledCollidables[modelIndex] = scaledCollidables; + } + + ICollisionQuery *pQuery = physcollision->CreateQueryModel( modelinfo->GetVCollide( modelIndex )->solids[0] ); + if ( pQuery == NULL ) + { + Warning( "UTIL_GetScaledPhysCollide: Failed to created scaled CPhysCollide for model %s!\n", modelinfo->GetModelName( modelinfo->GetModel( modelIndex ) ) ); + return NULL; + } + + const int nNumConvex = pQuery->ConvexCount(); + CPhysPolysoup *pPolySoups = physcollision->PolysoupCreate(); + + for ( int i = 0; i < nNumConvex; ++i ) + { + int nNumTris = pQuery->TriangleCount( i ); + int nNumVerts = nNumTris * 3; + + Vector *pVerts = (Vector *) stackalloc( sizeof(Vector) * nNumVerts ); + for ( int j = 0; j < nNumTris; ++j ) + { + int p = j*3; + pQuery->GetTriangleVerts( i, j, pVerts+p ); + *(pVerts+p) *= scale; + *(pVerts+p+1) *= scale; + *(pVerts+p+2) *= scale; + } + + for ( int j = 0; j < nNumVerts; j += 3 ) + { + physcollision->PolysoupAddTriangle( pPolySoups, pVerts[j], pVerts[j + 1], pVerts[j + 2], 0 ); + } + } + + physcollision->DestroyQueryModel( pQuery ); + + CPhysCollide* physCollide = physcollision->ConvertPolysoupToCollide( pPolySoups, true ); + physcollision->PolysoupDestroy( pPolySoups ); + if ( physCollide == NULL ) + { + Warning( "UTIL_GetScaledPhysCollide: Failed to created scaled CPhysCollide for model %s %f!\n", modelinfo->GetModelName( modelinfo->GetModel( modelIndex ) ), scale ); + return NULL; + } + + (*scaledCollidables)[scale] = physCollide; + + CPhysEntry entry; + entry.modelIndex = modelIndex; + entry.scale = scale; + g_pScaledReferences[physCollide] = entry; + + return physCollide; +} + +void UTIL_RemoveScaledPhysCollide( CPhysCollide *physCollide ) +{ + VPROF( "UTIL_RemoveScaledPhysCollide", VPROF_BUDGETGROUP_PHYSICS ); + + auto it = g_pScaledReferences.find( physCollide ); + if ( it != g_pScaledReferences.end() ) + { + --it->second.references; + if (it->second.references > 0) + return; + } else { + DevWarning( "UTIL_GetScaledPhysCollide: Failed to find reference counter!\n" ); + } + + auto iModel = g_pScaledCollidables.find( it->second.modelIndex ); + if ( iModel == g_pScaledCollidables.end() ) + return; + + std::unordered_map scaledCollibales = *iModel->second; + auto iEntry = scaledCollibales.find( it->second.scale ); + if ( iEntry == scaledCollibales.end() ) + return; + + scaledCollibales.erase( iEntry->first ); + physcollision->DestroyCollide( physCollide ); + + if ( scaledCollibales.size() == 0 ) + g_pScaledCollidables.erase( iModel ); + + DevMsg( "UTIL_RemoveScaledPhysCollide: Freed model %s\n", modelinfo->GetModelName(modelinfo->GetModel(it->second.modelIndex)) ); +} \ No newline at end of file diff --git a/game/client/cdll_util.h b/game/client/cdll_util.h index 6860ab3d0..0e4d2fdd7 100644 --- a/game/client/cdll_util.h +++ b/game/client/cdll_util.h @@ -32,6 +32,8 @@ class IClientEntity; class CHudTexture; class CGameTrace; class C_BaseEntity; +class C_BaseAnimating; +class CPhysCollide; struct Ray_t; struct client_textmessage_t; @@ -184,4 +186,12 @@ int UTIL_GetMapKeyCount( const char *pszCustomKey ); // Returns true if the user has loaded any maps, false otherwise. bool UTIL_HasLoadedAnyMap(); +// SetModelScale being clientside broken fix +// Returns the given CPhysCollide for the given model index and scale. If not found it will create it. +CPhysCollide* UTIL_GetScaledPhysCollide( int modelIndex, float scale ); + +// Frees the given CPhysCollide if the internal reference count reaches 0. +// NOTE: Only supports CPhysCollide created by UTIL_GetScaledPhysCollide! +void UTIL_RemoveScaledPhysCollide( CPhysCollide *physCollide ); + #endif // !UTIL_H