|
|
//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Clones a physics object (usually with a matrix transform applied)
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "physicsshadowclone.h"
#include "portal_util_shared.h"
#include "vphysics/object_hash.h"
#include "trains.h"
#include "props.h"
#include "model_types.h"
#include "portal/weapon_physcannon.h" //grab controllers
#include "PortalSimulation.h"
#define MAX_SHADOW_CLONE_COUNT 200
static int g_iShadowCloneCount = 0; ConVar sv_debug_physicsshadowclones("sv_debug_physicsshadowclones", "0", FCVAR_REPLICATED ); ConVar sv_use_shadow_clones( "sv_use_shadow_clones", "1", FCVAR_REPLICATED | FCVAR_CHEAT ); //should we create shadow clones?
LINK_ENTITY_TO_CLASS( physicsshadowclone, CPhysicsShadowClone );
static CUtlVector<CPhysicsShadowClone *> s_ActiveShadowClones; CUtlVector<CPhysicsShadowClone *> const &CPhysicsShadowClone::g_ShadowCloneList = s_ActiveShadowClones; static bool s_IsShadowClone[MAX_EDICTS] = { false };
static CPhysicsShadowCloneLL *s_EntityClones[MAX_EDICTS] = { NULL }; struct ShadowCloneLLEntryManager { CPhysicsShadowCloneLL m_ShadowCloneLLEntries[MAX_SHADOW_CLONE_COUNT]; CPhysicsShadowCloneLL *m_pFreeShadowCloneLLEntries[MAX_SHADOW_CLONE_COUNT]; int m_iUsedEntryIndex;
ShadowCloneLLEntryManager( void ) { m_iUsedEntryIndex = 0; for( int i = 0; i != MAX_SHADOW_CLONE_COUNT; ++i ) { m_pFreeShadowCloneLLEntries[i] = &m_ShadowCloneLLEntries[i]; } }
inline CPhysicsShadowCloneLL *Alloc( void ) { return m_pFreeShadowCloneLLEntries[m_iUsedEntryIndex++]; }
inline void Free( CPhysicsShadowCloneLL *pFree ) { m_pFreeShadowCloneLLEntries[--m_iUsedEntryIndex] = pFree; } }; static ShadowCloneLLEntryManager s_SCLLManager;
CPhysicsShadowClone::CPhysicsShadowClone( void ) { m_matrixShadowTransform.Identity(); m_matrixShadowTransform_Inverse.Identity(); m_bShadowTransformIsIdentity = true; s_ActiveShadowClones.AddToTail( this ); }
CPhysicsShadowClone::~CPhysicsShadowClone( void ) { VPhysicsDestroyObject(); VPhysicsSetObject( NULL ); m_hClonedEntity = NULL; s_ActiveShadowClones.FindAndRemove( this ); //also removed in UpdateOnRemove()
Assert( s_IsShadowClone[entindex()] == true ); s_IsShadowClone[entindex()] = false; }
void CPhysicsShadowClone::UpdateOnRemove( void ) { CBaseEntity *pSource = m_hClonedEntity; if( pSource ) { CPhysicsShadowCloneLL *pCloneListHead = s_EntityClones[pSource->entindex()]; Assert( pCloneListHead != NULL );
CPhysicsShadowCloneLL *pFind = pCloneListHead; CPhysicsShadowCloneLL *pLast = pFind; while( pFind->pClone != this ) { pLast = pFind; Assert( pFind->pNext != NULL ); pFind = pFind->pNext; }
if( pFind == pCloneListHead ) { s_EntityClones[pSource->entindex()] = pFind->pNext; } else { pLast->pNext = pFind->pNext; } s_SCLLManager.Free( pFind ); } #ifdef _DEBUG
else { //verify that it didn't weasel into a list somewhere and get left behind
for( int i = 0; i != MAX_SHADOW_CLONE_COUNT; ++i ) { CPhysicsShadowCloneLL *pCloneSearch = s_EntityClones[i]; while( pCloneSearch ) { Assert( pCloneSearch->pClone != this ); pCloneSearch = pCloneSearch->pNext; } } } #endif
VPhysicsDestroyObject(); VPhysicsSetObject( NULL ); m_hClonedEntity = NULL; s_ActiveShadowClones.FindAndRemove( this ); //also removed in Destructor
BaseClass::UpdateOnRemove(); }
void CPhysicsShadowClone::Spawn( void ) { AddFlag( FL_DONTTOUCH ); AddEffects( EF_NODRAW | EF_NOSHADOW | EF_NORECEIVESHADOW );
FullSync( false ); m_bInAssumedSyncState = false; BaseClass::Spawn();
s_IsShadowClone[entindex()] = true; }
void CPhysicsShadowClone::FullSync( bool bAllowAssumedSync ) { Assert( IsMarkedForDeletion() == false );
CBaseEntity *pClonedEntity = m_hClonedEntity.Get();
if( pClonedEntity == NULL ) { AssertMsg( VPhysicsGetObject() != NULL, "Been linkless for more than this update, something should have killed this clone." ); SetMoveType( MOVETYPE_NONE ); SetSolid( SOLID_NONE ); SetSolidFlags( 0 ); SetCollisionGroup( COLLISION_GROUP_NONE ); VPhysicsDestroyObject(); return; }
SetGroundEntity( NULL );
bool bIsSynced = bAllowAssumedSync; bool bBigChanges = true; //assume there are, and be proven wrong
if( bAllowAssumedSync ) { IPhysicsObject *pSourceObjects[1024]; int iObjectCount = pClonedEntity->VPhysicsGetObjectList( pSourceObjects, 1024 );
//scan for really big differences that would definitely require a full sync
bBigChanges = ( iObjectCount != m_CloneLinks.Count() ); if( !bBigChanges ) { for( int i = 0; i != iObjectCount; ++i ) { IPhysicsObject *pSourcePhysics = pSourceObjects[i]; IPhysicsObject *pClonedPhysics = m_CloneLinks[i].pClone;
if( (pSourcePhysics != m_CloneLinks[i].pSource) || (pSourcePhysics->IsCollisionEnabled() != pClonedPhysics->IsCollisionEnabled()) ) { bBigChanges = true; bIsSynced = false; break; }
Vector ptSourcePosition, ptClonePosition; pSourcePhysics->GetPosition( &ptSourcePosition, NULL ); if( !m_bShadowTransformIsIdentity ) ptSourcePosition = m_matrixShadowTransform * ptSourcePosition;
pClonedPhysics->GetPosition( &ptClonePosition, NULL );
if( (ptClonePosition - ptSourcePosition).LengthSqr() > 2500.0f ) { bBigChanges = true; bIsSynced = false; break; } //Vector vSourceVelocity, vCloneVelocity;
if( !pSourcePhysics->IsAsleep() ) //only allow full syncrosity if the source entity is entirely asleep
bIsSynced = false;
if( m_bInAssumedSyncState && !pClonedPhysics->IsAsleep() ) bIsSynced = false; } } else { bIsSynced = false; }
bIsSynced = false;
if( bIsSynced ) { //good enough to skip a full update
if( !m_bInAssumedSyncState ) { //do one last sync
PartialSync( true );
//if we don't do this, objects just fall out of the world (it happens, I swear)
for( int i = m_CloneLinks.Count(); --i >= 0; ) { if( (m_CloneLinks[i].pSource->GetShadowController() == NULL) && m_CloneLinks[i].pClone->IsMotionEnabled() ) { //m_CloneLinks[i].pClone->SetVelocityInstantaneous( &vec3_origin, &vec3_origin );
//m_CloneLinks[i].pClone->SetVelocity( &vec3_origin, &vec3_origin );
m_CloneLinks[i].pClone->EnableGravity( false ); m_CloneLinks[i].pClone->EnableMotion( false ); m_CloneLinks[i].pClone->Sleep(); } }
m_bInAssumedSyncState = true; } if( sv_debug_physicsshadowclones.GetBool() ) DrawDebugOverlayForShadowClone( this );
return; } } m_bInAssumedSyncState = false;
//past this point, we're committed to a broad update
if( bBigChanges ) { MoveType_t sourceMoveType = pClonedEntity->GetMoveType();
IPhysicsObject *pPhysObject = pClonedEntity->VPhysicsGetObject(); if( (sourceMoveType == MOVETYPE_CUSTOM) || (sourceMoveType == MOVETYPE_STEP) || (sourceMoveType == MOVETYPE_WALK) || (pPhysObject && ( (pPhysObject->GetGameFlags() & FVPHYSICS_PLAYER_HELD) || (pPhysObject->GetShadowController() != NULL) ) ) ) { //#ifdef _DEBUG
SetMoveType( MOVETYPE_NONE ); //to kill an assert
//#endif
//PUSH should be used sparingly, you can't stand on a MOVETYPE_PUSH object :/
SetMoveType( MOVETYPE_VPHYSICS, pClonedEntity->GetMoveCollide() ); //either an unclonable movetype, or a shadow/held object
} /*else if(sourceMoveType == MOVETYPE_STEP)
{ //SetMoveType( MOVETYPE_NONE ); //to kill an assert
SetMoveType( MOVETYPE_VPHYSICS, pClonedEntity->GetMoveCollide() ); }*/ else { //if( m_bShadowTransformIsIdentity )
SetMoveType( sourceMoveType, pClonedEntity->GetMoveCollide() ); //else
//{
// SetMoveType( MOVETYPE_NONE ); //to kill an assert
// SetMoveType( MOVETYPE_PUSH, pClonedEntity->GetMoveCollide() );
//}
}
SolidType_t sourceSolidType = pClonedEntity->GetSolid(); if( sourceSolidType == SOLID_BBOX ) SetSolid( SOLID_VPHYSICS ); else SetSolid( sourceSolidType ); //SetSolid( SOLID_VPHYSICS );
SetElasticity( pClonedEntity->GetElasticity() ); SetFriction( pClonedEntity->GetFriction() );
int iSolidFlags = pClonedEntity->GetSolidFlags() | FSOLID_CUSTOMRAYTEST; if( m_bShadowTransformIsIdentity ) iSolidFlags |= FSOLID_CUSTOMBOXTEST; //need this at least for the player or they get stuck in themselves
else iSolidFlags &= ~FSOLID_FORCE_WORLD_ALIGNED; /*if( pClonedEntity->IsPlayer() )
{ iSolidFlags |= FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST; }*/
SetSolidFlags( iSolidFlags );
SetEffects( pClonedEntity->GetEffects() | (EF_NODRAW | EF_NOSHADOW | EF_NORECEIVESHADOW) );
SetCollisionGroup( pClonedEntity->GetCollisionGroup() );
SetModelIndex( pClonedEntity->GetModelIndex() ); SetModelName( pClonedEntity->GetModelName() );
if( modelinfo->GetModelType( pClonedEntity->GetModel() ) == mod_studio ) SetModel( STRING( pClonedEntity->GetModelName() ) );
CCollisionProperty *pClonedCollisionProp = pClonedEntity->CollisionProp(); SetSize( pClonedCollisionProp->OBBMins(), pClonedCollisionProp->OBBMaxs() ); }
FullSyncClonedPhysicsObjects( bBigChanges ); SyncEntity( true );
if( bBigChanges ) CollisionRulesChanged();
if( sv_debug_physicsshadowclones.GetBool() ) DrawDebugOverlayForShadowClone( this ); }
// this enables a fast/cheap version of teleporting that is less accurate WRT physics objects' contacts after small teleportations
ConVar fast_teleport_enable("fast_teleport_enable", "1");
void CPhysicsShadowClone::SyncEntity( bool bPullChanges ) { m_bShouldUpSync = false;
CBaseEntity *pSource, *pDest; VMatrix *pTransform; if( bPullChanges ) { pSource = m_hClonedEntity.Get(); pDest = this; pTransform = &m_matrixShadowTransform;
if( pSource == NULL ) return; } else { pSource = this; pDest = m_hClonedEntity.Get(); pTransform = &m_matrixShadowTransform_Inverse;
if( pDest == NULL ) return; }
Vector ptOrigin, vVelocity; QAngle qAngles;
ptOrigin = pSource->GetAbsOrigin(); qAngles = pSource->IsPlayer() ? vec3_angle : pSource->GetAbsAngles(); vVelocity = pSource->GetAbsVelocity();
if( !m_bShadowTransformIsIdentity ) { ptOrigin = (*pTransform) * ptOrigin; qAngles = TransformAnglesToWorldSpace( qAngles, pTransform->As3x4() ); vVelocity = pTransform->ApplyRotation( vVelocity ); } //else
//{
// pDest->SetGroundEntity( pSource->GetGroundEntity() );
//}
QAngle qDiff; RotationDelta( pDest->GetAbsAngles(), qAngles, &qDiff );
if( (ptOrigin - pDest->GetAbsOrigin()).LengthSqr() > 0.0001f || qDiff.LengthSqr() > 0.0001f ) { pDest->Teleport( &ptOrigin, &qAngles, NULL, !fast_teleport_enable.GetBool() ); } if( vVelocity != pDest->GetAbsVelocity() ) { //pDest->AddEffects( EF_NOINTERP );
pDest->SetAbsVelocity( vec3_origin ); //the two step process helps, I don't know why, but it does
pDest->ApplyAbsVelocityImpulse( vVelocity ); } }
static void FullSyncPhysicsObject( IPhysicsObject *pSource, IPhysicsObject *pDest, const VMatrix *pTransform, bool bTeleport ) { CGrabController *pGrabController = NULL;
if( !pSource->IsAsleep() ) pDest->Wake();
float fSavedMass = 0.0f, fSavedRotationalDamping; //setting mass to 0.0f purely to kill a warning that I can't seem to kill with pragmas
if( pSource->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) { //CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
//Assert( pPlayer );
CBaseEntity *pLookingForEntity = (CBaseEntity *)pSource->GetGameData();
CBasePlayer *pHoldingPlayer = GetPlayerHoldingEntity( pLookingForEntity ); if( pHoldingPlayer ) { pGrabController = GetGrabControllerForPlayer( pHoldingPlayer );
if ( !pGrabController ) pGrabController = GetGrabControllerForPhysCannon( pHoldingPlayer->GetActiveWeapon() ); }
AssertMsg( pGrabController, "Physics object is held, but we can't find the holding controller." ); GetSavedParamsForCarriedPhysObject( pGrabController, pSource, &fSavedMass, &fSavedRotationalDamping ); }
//Boiler plate
{ pDest->SetGameIndex( pSource->GetGameIndex() ); //what's it do?
pDest->SetCallbackFlags( pSource->GetCallbackFlags() ); //wise?
pDest->SetGameFlags( pSource->GetGameFlags() | FVPHYSICS_NO_SELF_COLLISIONS | FVPHYSICS_IS_SHADOWCLONE ); pDest->SetMaterialIndex( pSource->GetMaterialIndex() ); pDest->SetContents( pSource->GetContents() );
pDest->SyncWith( pSource ); }
//Damping
{ float fSpeedDamp, fRotDamp; if( pGrabController ) { pSource->GetDamping( &fSpeedDamp, NULL ); pDest->SetDamping( &fSpeedDamp, &fSavedRotationalDamping ); } else { pSource->GetDamping( &fSpeedDamp, &fRotDamp ); pDest->SetDamping( &fSpeedDamp, &fRotDamp ); } }
//stuff that we really care about
{ if( pGrabController ) { pDest->SetMass( fSavedMass ); } else { float flMass = pSource->GetMass(); if ( pDest->GetMass() != flMass ) { pDest->SetMass( flMass ); } }
Vector vInertia; Vector vVelocity, vAngularVelocity;
pSource->GetVelocity( &vVelocity, &vAngularVelocity ); vInertia = pSource->GetInertia();
if( pTransform ) { vVelocity = pTransform->ApplyRotation( vVelocity ); vAngularVelocity = pTransform->ApplyRotation( vAngularVelocity ); }
//avoid oversetting variables (I think that even setting them to the same value they already are disrupts the delicate physics balance)
if( vInertia != pDest->GetInertia() ) pDest->SetInertia( vInertia );
//pDest->SetVelocityInstantaneous( &vec3_origin, &vec3_origin );
//pDest->Sleep();
Vector vDestVelocity, vDestAngularVelocity; pDest->GetVelocity( &vDestVelocity, &vDestAngularVelocity );
if( (vVelocity != vDestVelocity) || (vAngularVelocity != vDestAngularVelocity) ) pDest->SetVelocityInstantaneous( &vVelocity, &vAngularVelocity );
IPhysicsShadowController *pSourceController = pSource->GetShadowController(); if( pSourceController == NULL ) { if( pDest->GetShadowController() != NULL ) { //we don't need a shadow controller anymore
pDest->RemoveShadowController(); } if ( bTeleport ) { Vector ptOrigin; QAngle qAngles;
pSource->GetPosition( &ptOrigin, &qAngles );
Vector ptDestOrigin; QAngle qDestAngles; pDest->GetPosition( &ptDestOrigin, &qDestAngles ); if( pTransform ) { ptOrigin = (*pTransform) * ptOrigin; qAngles = TransformAnglesToWorldSpace( qAngles, pTransform->As3x4() ); }
if( (ptOrigin != ptDestOrigin) || (qAngles != qDestAngles) ) { pDest->SetPosition( ptOrigin, qAngles, bTeleport ); } } } else { IPhysicsShadowController *pDestController = pDest->GetShadowController(); if( pDestController == NULL ) { //we need a shadow controller
float fMaxSpeed, fMaxAngularSpeed; pSourceController->GetMaxSpeed( &fMaxSpeed, &fMaxAngularSpeed );
pDest->SetShadow( fMaxSpeed, fMaxAngularSpeed, pSourceController->AllowsTranslation(), pSourceController->AllowsRotation() ); pDestController = pDest->GetShadowController(); pDestController->SetTeleportDistance( pSourceController->GetTeleportDistance() ); pDestController->SetPhysicallyControlled( pSourceController->IsPhysicallyControlled() ); }
//sync shadow controllers
float fTimeOffset; Vector ptTargetPosition; QAngle qTargetAngles; fTimeOffset = pSourceController->GetTargetPosition( &ptTargetPosition, &qTargetAngles );
if( pTransform ) { ptTargetPosition = (*pTransform) * ptTargetPosition; qTargetAngles = TransformAnglesToWorldSpace( qTargetAngles, pTransform->As3x4() ); }
pDestController->Update( ptTargetPosition, qTargetAngles, fTimeOffset ); }
}
//pDest->RecheckContactPoints();
}
static void PartialSyncPhysicsObject( IPhysicsObject *pSource, IPhysicsObject *pDest, const VMatrix *pTransform ) { Vector ptOrigin, vVelocity, vAngularVelocity, vInertia; QAngle qAngles;
pSource->GetPosition( &ptOrigin, &qAngles ); pSource->GetVelocity( &vVelocity, &vAngularVelocity ); vInertia = pSource->GetInertia();
if( pTransform ) { #if 0
//pDest->SetPositionMatrix( matTransform.As3x4(), true ); //works like we think?
#else
ptOrigin = (*pTransform) * ptOrigin; qAngles = TransformAnglesToWorldSpace( qAngles, pTransform->As3x4() ); vVelocity = pTransform->ApplyRotation( vVelocity ); vAngularVelocity = pTransform->ApplyRotation( vAngularVelocity ); #endif
}
//avoid oversetting variables (I think that even setting them to the same value they already are disrupts the delicate physics balance)
if( vInertia != pDest->GetInertia() ) pDest->SetInertia( vInertia );
Vector ptDestOrigin, vDestVelocity, vDestAngularVelocity; QAngle qDestAngles; pDest->GetPosition( &ptDestOrigin, &qDestAngles ); pDest->GetVelocity( &vDestVelocity, &vDestAngularVelocity );
if( (ptOrigin != ptDestOrigin) || (qAngles != qDestAngles) ) pDest->SetPosition( ptOrigin, qAngles, false );
if( (vVelocity != vDestVelocity) || (vAngularVelocity != vDestAngularVelocity) ) pDest->SetVelocity( &vVelocity, &vAngularVelocity );
pDest->EnableCollisions( pSource->IsCollisionEnabled() ); }
IPhysicsObject *ClonePhysObject( IPhysicsObject *pSource, IPhysicsEnvironment *pDestEnvironment, void *pCloneGameData ) { unsigned int size = physenv->GetObjectSerializeSize(pSource); byte *pBuffer = (byte *)stackalloc(size); memset( pBuffer, 0, size );
physenv->SerializeObjectToBuffer( pSource, pBuffer, size ); //this should work across physics environments because the serializer doesn't write anything about itself to the template
return pDestEnvironment->UnserializeObjectFromBuffer( pCloneGameData, pBuffer, size, false ); //unserializer has to be in the target environment
}
void CPhysicsShadowClone::FullSyncClonedPhysicsObjects( bool bTeleport ) { CBaseEntity *pClonedEntity = m_hClonedEntity.Get(); if( pClonedEntity == NULL ) { VPhysicsDestroyObject(); return; }
VMatrix *pTransform; if( m_bShadowTransformIsIdentity ) pTransform = NULL; else pTransform = &m_matrixShadowTransform;
IPhysicsObject *(pSourceObjects[1024]); int iObjectCount = pClonedEntity->VPhysicsGetObjectList( pSourceObjects, 1024 );
//easy out if nothing has changed
if( iObjectCount == m_CloneLinks.Count() ) { int i; for( i = 0; i != iObjectCount; ++i ) { if( pSourceObjects[i] == NULL ) break;
if( pSourceObjects[i] != m_CloneLinks[i].pSource ) break; }
if( i == iObjectCount ) //no changes
{ for( i = 0; i != iObjectCount; ++i ) FullSyncPhysicsObject( m_CloneLinks[i].pSource, m_CloneLinks[i].pClone, pTransform, bTeleport );
return; } }
//copy the existing list of clone links to a temp array, we're going to be starting from scratch and copying links as we need them
PhysicsObjectCloneLink_t *pExistingLinks = NULL; int iExistingLinkCount = m_CloneLinks.Count(); if( iExistingLinkCount != 0 ) { pExistingLinks = (PhysicsObjectCloneLink_t *)stackalloc( sizeof(PhysicsObjectCloneLink_t) * m_CloneLinks.Count() ); memcpy( pExistingLinks, m_CloneLinks.Base(), sizeof(PhysicsObjectCloneLink_t) * m_CloneLinks.Count() ); } m_CloneLinks.RemoveAll();
//now, go over the object list we just got from the source entity, and either copy or create links as necessary
int i; for( i = 0; i != iObjectCount; ++i ) { IPhysicsObject *pSource = pSourceObjects[i];
if( pSource == NULL ) //this really shouldn't happen, but it does >_<
continue;
PhysicsObjectCloneLink_t cloneLink;
int j; for( j = 0; j != iExistingLinkCount; ++j ) { if( pExistingLinks[j].pSource == pSource ) break; }
if( j != iExistingLinkCount ) { //copyable link found
cloneLink = pExistingLinks[j]; memset( &pExistingLinks[j], 0, sizeof( PhysicsObjectCloneLink_t ) ); //zero out this slot so we don't destroy it in cleanup
} else { //no link found to copy, create a new one
cloneLink.pSource = pSource;
//apparently some collision code gets called on creation before we've set extra game flags, so we're going to cheat a bit and temporarily set our extra flags on the source
unsigned int iOldGameFlags = pSource->GetGameFlags(); pSource->SetGameFlags( iOldGameFlags | FVPHYSICS_IS_SHADOWCLONE );
cloneLink.pClone = ClonePhysObject( pSource, m_pOwnerPhysEnvironment, this ); assert( cloneLink.pClone ); //there should be absolutely no case where we can't clone a valid existing physics object
pSource->SetGameFlags( iOldGameFlags ); }
FullSyncPhysicsObject( cloneLink.pSource, cloneLink.pClone, pTransform, bTeleport );
//cloneLink.pClone->Wake();
m_CloneLinks.AddToTail( cloneLink ); }
//now go over the existing links, if any of them haven't been nullified, they need to be deleted
for( i = 0; i != iExistingLinkCount; ++i ) { if( pExistingLinks[i].pClone ) m_pOwnerPhysEnvironment->DestroyObject( pExistingLinks[i].pClone ); //also destroys shadow controller
}
VPhysicsSetObject( NULL );
IPhysicsObject *pSource = m_hClonedEntity->VPhysicsGetObject();
for( i = m_CloneLinks.Count(); --i >= 0; ) { if( m_CloneLinks[i].pSource == pSource ) { //m_CloneLinks[i].pClone->Wake();
VPhysicsSetObject( m_CloneLinks[i].pClone ); break; } }
if( (i < 0) && (m_CloneLinks.Count() != 0) ) { VPhysicsSetObject( m_CloneLinks[0].pClone ); }
stackfree( pExistingLinks );
//CollisionRulesChanged();
}
void CPhysicsShadowClone::PartialSync( bool bPullChanges ) { VMatrix *pTransform; if( bPullChanges ) { if( m_bShadowTransformIsIdentity ) pTransform = NULL; else pTransform = &m_matrixShadowTransform;
for( int i = m_CloneLinks.Count(); --i >= 0; ) PartialSyncPhysicsObject( m_CloneLinks[i].pSource, m_CloneLinks[i].pClone, pTransform ); } else { if( m_bShadowTransformIsIdentity ) pTransform = NULL; else pTransform = &m_matrixShadowTransform_Inverse;
for( int i = m_CloneLinks.Count(); --i >= 0; ) PartialSyncPhysicsObject( m_CloneLinks[i].pClone, m_CloneLinks[i].pSource, pTransform ); }
SyncEntity( bPullChanges ); }
int CPhysicsShadowClone::VPhysicsGetObjectList( IPhysicsObject **pList, int listMax ) { int iCountStop = m_CloneLinks.Count(); if( iCountStop > listMax ) iCountStop = listMax;
for( int i = 0; i != iCountStop; ++i, ++pList ) *pList = m_CloneLinks[i].pClone;
return iCountStop; }
void CPhysicsShadowClone::VPhysicsDestroyObject( void ) { SetMoveType( MOVETYPE_NONE ); SetSolid( SOLID_NONE ); SetSolidFlags( 0 ); SetCollisionGroup( COLLISION_GROUP_NONE );
CollisionRulesChanged();
VPhysicsSetObject( NULL ); for( int i = m_CloneLinks.Count(); --i >= 0; ) { Assert( m_CloneLinks[i].pClone != NULL ); m_pOwnerPhysEnvironment->DestroyObject( m_CloneLinks[i].pClone ); } m_CloneLinks.RemoveAll();
BaseClass::VPhysicsDestroyObject(); }
bool CPhysicsShadowClone::ShouldCollide( int collisionGroup, int contentsMask ) const { CBaseEntity *pClonedEntity = m_hClonedEntity.Get();
if( pClonedEntity ) return pClonedEntity->ShouldCollide( collisionGroup, contentsMask ); else return false; }
bool CPhysicsShadowClone::TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& trace ) { return false;
/*CBaseEntity *pSourceEntity = m_hClonedEntity.Get();
if( pSourceEntity == NULL ) return false;
enginetrace->ClipRayToEntity( ray, fContentsMask, pSourceEntity, &trace ); return trace.DidHit();*/ }
int CPhysicsShadowClone::ObjectCaps( void ) { return ((BaseClass::ObjectCaps() | FCAP_DONT_SAVE) & ~(FCAP_FORCE_TRANSITION | FCAP_ACROSS_TRANSITION | FCAP_MUST_SPAWN | FCAP_SAVE_NON_NETWORKABLE)); }
void CPhysicsShadowClone::SetCloneTransformationMatrix( const matrix3x4_t &sourceMatrix ) { m_matrixShadowTransform.Init( sourceMatrix ); m_bShadowTransformIsIdentity = m_matrixShadowTransform.IsIdentity();
if( m_matrixShadowTransform.InverseGeneral( m_matrixShadowTransform_Inverse ) == false ) { m_matrixShadowTransform.InverseTR( m_matrixShadowTransform_Inverse ); //probably not the right matrix, but we're out of options
}
FullSync(); //PartialSync( true );
}
void CPhysicsShadowClone::SetClonedEntity( EHANDLE hEntToClone ) { VPhysicsDestroyObject(); m_hClonedEntity = hEntToClone;
//FullSyncClonedPhysicsObjects();
}
EHANDLE CPhysicsShadowClone::GetClonedEntity( void ) { return m_hClonedEntity; }
//damage relays to source entity
bool CPhysicsShadowClone::PassesDamageFilter( const CTakeDamageInfo &info ) { CBaseEntity *pClonedEntity = m_hClonedEntity.Get();
if( pClonedEntity ) return pClonedEntity->PassesDamageFilter( info ); else return BaseClass::PassesDamageFilter( info ); }
bool CPhysicsShadowClone::CanBeHitByMeleeAttack( CBaseEntity *pAttacker ) { CBaseEntity *pClonedEntity = m_hClonedEntity.Get();
if( pClonedEntity ) return pClonedEntity->CanBeHitByMeleeAttack( pAttacker ); else return BaseClass::CanBeHitByMeleeAttack( pAttacker ); }
int CPhysicsShadowClone::OnTakeDamage( const CTakeDamageInfo &info ) { CBaseEntity *pClonedEntity = m_hClonedEntity.Get();
if( pClonedEntity ) return pClonedEntity->OnTakeDamage( info ); else return BaseClass::OnTakeDamage( info ); }
int CPhysicsShadowClone::TakeHealth( float flHealth, int bitsDamageType ) { CBaseEntity *pClonedEntity = m_hClonedEntity.Get();
if( pClonedEntity ) return pClonedEntity->TakeHealth( flHealth, bitsDamageType ); else return BaseClass::TakeHealth( flHealth, bitsDamageType ); }
void CPhysicsShadowClone::Event_Killed( const CTakeDamageInfo &info ) { CBaseEntity *pClonedEntity = m_hClonedEntity.Get();
if( pClonedEntity ) pClonedEntity->Event_Killed( info ); else BaseClass::Event_Killed( info ); }
CPhysicsShadowClone *CPhysicsShadowClone::CreateShadowClone( IPhysicsEnvironment *pInPhysicsEnvironment, EHANDLE hEntToClone, const char *szDebugMarker, const matrix3x4_t *pTransformationMatrix /*= NULL*/ ) { AssertMsg( szDebugMarker != NULL, "All shadow clones must have a debug marker for where it came from in debug builds." );
if( !sv_use_shadow_clones.GetBool() ) return NULL;
CBaseEntity *pClonedEntity = hEntToClone.Get(); if( pClonedEntity == NULL ) return NULL;
AssertMsg( IsShadowClone( pClonedEntity ) == false, "Shouldn't attempt to clone clones" );
if( pClonedEntity->IsMarkedForDeletion() ) return NULL;
//if( pClonedEntity->IsPlayer() )
// return NULL;
IPhysicsObject *pPhysics = pClonedEntity->VPhysicsGetObject();
if( pPhysics == NULL ) return NULL;
if( pClonedEntity->GetSolid() == SOLID_BSP ) return NULL;
if( pClonedEntity->GetSolidFlags() & (FSOLID_NOT_SOLID | FSOLID_TRIGGER) ) return NULL;
if( pClonedEntity->GetFlags() & (FL_WORLDBRUSH | FL_STATICPROP) ) return NULL;
/*if( FClassnameIs( pClonedEntity, "func_door" ) )
{ //only clone func_door's that are in front of the portal
return NULL; }*/
// Too many shadow clones breaks the game (too many entities)
if( g_iShadowCloneCount >= MAX_SHADOW_CLONE_COUNT ) { AssertMsg( false, "Too many shadow clones, consider upping the limit or reducing the level's physics props" ); return NULL; } ++g_iShadowCloneCount;
CPhysicsShadowClone *pClone = (CPhysicsShadowClone*)CreateEntityByName("physicsshadowclone"); s_IsShadowClone[pClone->entindex()] = true; pClone->m_pOwnerPhysEnvironment = pInPhysicsEnvironment; pClone->m_hClonedEntity = hEntToClone; DBG_CODE_NOSCOPE( pClone->m_szDebugMarker = szDebugMarker; );
CPhysicsShadowCloneLL *pCloneLLEntry = s_SCLLManager.Alloc(); pCloneLLEntry->pClone = pClone; pCloneLLEntry->pNext = s_EntityClones[pClonedEntity->entindex()]; s_EntityClones[pClonedEntity->entindex()] = pCloneLLEntry;
if( pTransformationMatrix ) { pClone->m_matrixShadowTransform.Init( *pTransformationMatrix ); pClone->m_bShadowTransformIsIdentity = pClone->m_matrixShadowTransform.IsIdentity();
if( !pClone->m_bShadowTransformIsIdentity ) { if( pClone->m_matrixShadowTransform.InverseGeneral( pClone->m_matrixShadowTransform_Inverse ) == false ) { pClone->m_matrixShadowTransform.InverseTR( pClone->m_matrixShadowTransform_Inverse ); //probably not the right matrix, but we're out of options
} } }
DispatchSpawn( pClone );
return pClone; }
void CPhysicsShadowClone::Free( void ) { VPhysicsDestroyObject();
UTIL_Remove( this );
//Too many shadow clones breaks the game (too many entities)
--g_iShadowCloneCount; }
void CPhysicsShadowClone::FullSyncAllClones( void ) { for( int i = s_ActiveShadowClones.Count(); --i >= 0; ) { s_ActiveShadowClones[i]->FullSync( true ); } }
IPhysicsObject *CPhysicsShadowClone::TranslatePhysicsToClonedEnt( const IPhysicsObject *pPhysics ) { if( m_hClonedEntity.Get() != NULL ) { for( int i = m_CloneLinks.Count(); --i >= 0; ) { if( m_CloneLinks[i].pClone == pPhysics ) return m_CloneLinks[i].pSource; } }
return NULL; }
void CPhysicsShadowClone::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) { //the baseclass just screenshakes, makes sounds, and outputs dust, we rely on the original entity to do this when applicable
}
bool CPhysicsShadowClone::IsShadowClone( const CBaseEntity *pEntity ) { return s_IsShadowClone[pEntity->entindex()]; }
CPhysicsShadowCloneLL *CPhysicsShadowClone::GetClonesOfEntity( const CBaseEntity *pEntity ) { return s_EntityClones[pEntity->entindex()]; }
void CPhysicsShadowClone::DestroyClonedPhys( IPhysicsObject *pPhys ) { for( int i = m_CloneLinks.Count(); --i >= 0; ) { if( pPhys == m_CloneLinks[i].pSource ) { m_pOwnerPhysEnvironment->DestroyObject( m_CloneLinks[i].pClone ); //also destroys shadow controller
m_CloneLinks.FastRemove( i ); } } }
void CPhysicsShadowClone::DestroyClonedCollideable( CPhysCollide *pCollide ) { for( int i = m_CloneLinks.Count(); --i >= 0; ) { if( pCollide == m_CloneLinks[i].pSource->GetCollide() ) { m_pOwnerPhysEnvironment->DestroyObject( m_CloneLinks[i].pClone ); //also destroys shadow controller
m_CloneLinks.FastRemove( i ); } } }
void CPhysicsShadowClone::NotifyDestroy( IPhysicsObject *pDestroyingPhys, CBaseEntity *pOwningEntity ) { if( pOwningEntity ) { CPhysicsShadowCloneLL *pCloneLL = GetClonesOfEntity( pOwningEntity ); while( pCloneLL ) { pCloneLL->pClone->DestroyClonedPhys( pDestroyingPhys ); pCloneLL = pCloneLL->pNext; } } else { for( int i = 0; i != s_ActiveShadowClones.Count(); ++i ) { s_ActiveShadowClones[i]->DestroyClonedPhys( pDestroyingPhys ); } } }
void CPhysicsShadowClone::NotifyDestroy( CPhysCollide *pDestroyingCollide, CBaseEntity *pOwningEntity ) { if( pOwningEntity ) { CPhysicsShadowCloneLL *pCloneLL = GetClonesOfEntity( pOwningEntity ); while( pCloneLL ) { pCloneLL->pClone->DestroyClonedCollideable( pDestroyingCollide ); pCloneLL = pCloneLL->pNext; } } else { for( int i = 0; i != s_ActiveShadowClones.Count(); ++i ) { s_ActiveShadowClones[i]->DestroyClonedCollideable( pDestroyingCollide ); } } }
void DrawDebugOverlayForShadowClone( CPhysicsShadowClone *pClone ) { unsigned char iColorIntensity = (pClone->IsInAssumedSyncState())?(127):(255);
int iRed = (pClone->IsUntransformedClone())?(0):(iColorIntensity); int iGreen = iColorIntensity; int iBlue = iColorIntensity;
CPortalSimulator *pSimulator = CPortalSimulator::GetSimulatorThatOwnsEntity( pClone ); if( pSimulator ) { Color debugColor = pSimulator->GetInternalData().Debugging.overlayColor; iRed = debugColor.r(); iGreen = debugColor.g(); iBlue = debugColor.b(); }
for( int i = 0; i != pClone->m_CloneLinks.Count(); ++i ) { IPhysicsObject *pPhys = pClone->m_CloneLinks[i].pClone; if( pPhys ) { const CPhysCollide *pCollide = pPhys->GetCollide(); if( pCollide ) { Vector origin; QAngle angles; pPhys->GetPosition( &origin, &angles ); Vector *outVerts; int vertCount = physcollision->CreateDebugMesh( pCollide, &outVerts ); int triCount = vertCount / 3; int vert = 0; VMatrix tmp = SetupMatrixOrgAngles( origin, angles ); int i; for ( i = 0; i < vertCount; i++ ) { outVerts[i] = tmp.VMul4x3( outVerts[i] ); }
for ( i = 0; i < triCount; i++ ) { NDebugOverlay::Line( outVerts[vert], outVerts[vert + 1], iRed, iGreen, iBlue, true, 0.0f ); NDebugOverlay::Line( outVerts[vert + 1], outVerts[vert + 2], iRed, iGreen, iBlue, true, 0.0f ); NDebugOverlay::Line( outVerts[vert + 2], outVerts[vert], iRed, iGreen, iBlue, true, 0.0f ); vert += 3; } physcollision->DestroyDebugMesh( vertCount, outVerts ); } } }
NDebugOverlay::EntityBounds( pClone, iRed, iGreen, iBlue, (iColorIntensity>>2), 0.0f ); }
bool CTraceFilterTranslateClones::ShouldHitEntity( IHandleEntity *pEntity, int contentsMask ) { CBaseEntity *pEnt = EntityFromEntityHandle( pEntity ); if( CPhysicsShadowClone::IsShadowClone( pEnt ) ) { CBaseEntity *pClonedEntity = ((CPhysicsShadowClone *)pEnt)->GetClonedEntity(); CPortalSimulator *pSimulator = CPortalSimulator::GetSimulatorThatOwnsEntity( pEnt )->GetLinkedPortalSimulator(); if( pSimulator->GetInternalData().Simulation.Dynamic.EntFlags[pClonedEntity->entindex()] & PSEF_IS_IN_PORTAL_HOLE ) return m_pActualFilter->ShouldHitEntity( pClonedEntity, contentsMask ); else return false; } else { return m_pActualFilter->ShouldHitEntity( pEntity, contentsMask ); } }
TraceType_t CTraceFilterTranslateClones::GetTraceType() const { return m_pActualFilter->GetTraceType(); }
|