Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

980 lines
32 KiB

//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#include "cbase.h"
#include "C_PortalGhostRenderable.h"
#include "PortalRender.h"
#include "c_portal_player.h"
#include "model_types.h"
#include "c_basecombatweapon.h"
#include "c_combatweaponworldclone.h"
#include "toolframework_client.h"
ConVar portal_ghosts_disable( "portal_ghosts_disable", "0", 0, "Disables rendering of ghosted objects in portal environments" );
inline float GhostedRenderableOriginTime( C_BaseEntity *pGhostedRenderable, float fCurTime )
{
return pGhostedRenderable->GetOriginInterpolator().GetInterpolatedTime( pGhostedRenderable->GetEffectiveInterpolationCurTime( fCurTime ) );
}
#define GHOST_RENDERABLE_TEN_TON_HAMMER 0
#if (GHOST_RENDERABLE_TEN_TON_HAMMER == 1)
void GhostResetEverything( C_PortalGhostRenderable *pGhost )
{
C_BaseEntity *pGhostedRenderable = pGhost->m_hGhostedRenderable;
if( pGhostedRenderable )
{
pGhostedRenderable->MarkRenderHandleDirty();
if( pGhost->m_bSourceIsBaseAnimating )
{
//((C_BaseAnimating *)pGhostedRenderable)->MarkForThreadedBoneSetup();
((C_BaseAnimating *)pGhostedRenderable)->InvalidateBoneCache();
}
pGhostedRenderable->CollisionProp()->MarkPartitionHandleDirty();
pGhostedRenderable->CollisionProp()->MarkSurroundingBoundsDirty();
pGhostedRenderable->AddEFlags( EFL_DIRTY_ABSTRANSFORM | EFL_DIRTY_SPATIAL_PARTITION );
g_pClientLeafSystem->DisableCachedRenderBounds( pGhostedRenderable->RenderHandle(), true );
g_pClientLeafSystem->RenderableChanged( pGhostedRenderable->RenderHandle() );
if( C_BaseEntity::IsAbsQueriesValid() )
{
pGhostedRenderable->CollisionProp()->UpdatePartition();
//pGhostedRenderable->SetupBones( NULL, -1, BONE_USED_BY_ANYTHING, gpGlobals->curtime );
}
}
pGhost->MarkRenderHandleDirty();
//pGhost->MarkForThreadedBoneSetup();
pGhost->InvalidateBoneCache();
pGhost->CollisionProp()->MarkPartitionHandleDirty();
pGhost->CollisionProp()->MarkSurroundingBoundsDirty();
pGhost->AddEFlags( EFL_DIRTY_ABSTRANSFORM | EFL_DIRTY_SPATIAL_PARTITION );
g_pClientLeafSystem->DisableCachedRenderBounds( pGhost->RenderHandle(), true );
g_pClientLeafSystem->RenderableChanged( pGhost->RenderHandle() );
if( C_BaseEntity::IsAbsQueriesValid() )
{
pGhost->CollisionProp()->UpdatePartition();
//pGhost->SetupBones( NULL, -1, BONE_USED_BY_ANYTHING, gpGlobals->curtime );
}
}
#endif //#if (GHOST_RENDERABLE_TEN_TON_HAMMER == 1)
C_PortalGhostRenderable::C_PortalGhostRenderable( C_Portal_Base2D *pOwningPortal, C_BaseEntity *pGhostSource, const VMatrix &matGhostTransform, float *pSharedRenderClipPlane, C_BasePlayer *pPlayer ) :
m_hGhostedRenderable( pGhostSource ),
m_matGhostTransform( matGhostTransform ),
m_pSharedRenderClipPlane( pSharedRenderClipPlane ),
m_pPortalExitRenderClipPlane( NULL ),
m_hHoldingPlayer( pPlayer ),
m_pOwningPortal( pOwningPortal )
{
m_fRenderableRange[0] = -FLT_MAX;
m_fRenderableRange[1] = FLT_MAX;
m_fNoTransformBeforeTime = -FLT_MAX;
m_fDisablePositionChecksUntilTime = -FLT_MAX;
#if( DEBUG_GHOSTRENDERABLES == 1 )
if( pOwningPortal->m_bIsPortal2 )
{
m_iDebugColor[0] = 255;
m_iDebugColor[1] = 0;
m_iDebugColor[2] = 0;
}
else
{
m_iDebugColor[0] = 0;
m_iDebugColor[1] = 0;
m_iDebugColor[2] = 255;
}
m_iDebugColor[3] = 16;
#endif
m_bSourceIsBaseAnimating = (dynamic_cast<C_BaseAnimating *>(pGhostSource) != NULL);
RenderWithViewModels( pGhostSource->IsRenderingWithViewModels() );
SetModelName( m_hGhostedRenderable->GetModelName() );
m_bCombatWeapon = (dynamic_cast<C_BaseCombatWeapon *>(pGhostSource) != NULL);
SetModelIndex( m_bCombatWeapon ? ((C_BaseCombatWeapon *)pGhostSource)->GetWorldModelIndex() : pGhostSource->GetModelIndex() );
m_bCombatWeaponWorldClone = ( dynamic_cast< C_CombatWeaponClone* >( pGhostSource ) != NULL );
m_bPlayerHeldClone = ( dynamic_cast< C_PlayerHeldObjectClone* >( pGhostSource ) != NULL );
SetSize( pGhostSource->CollisionProp()->OBBMins(), pGhostSource->CollisionProp()->OBBMaxs() );
Assert( m_hGhostedRenderable.Get() != NULL );
}
C_PortalGhostRenderable::~C_PortalGhostRenderable( void )
{
m_hGhostedRenderable = NULL;
}
void C_PortalGhostRenderable::UpdateOnRemove( void )
{
m_hGhostedRenderable = NULL;
BaseClass::UpdateOnRemove();
}
void C_PortalGhostRenderable::PerFrameUpdate( void )
{
C_BaseEntity *pGhostedRenderable = m_hGhostedRenderable;
if( !pGhostedRenderable )
return;
SetModelName( pGhostedRenderable->GetModelName() );
SetModelIndex( m_bCombatWeapon ? ((C_BaseCombatWeapon *)pGhostedRenderable)->GetWorldModelIndex() : pGhostedRenderable->GetModelIndex() );
SetEffects( pGhostedRenderable->GetEffects() | EF_NOINTERP );
m_flAnimTime = pGhostedRenderable->m_flAnimTime;
if( m_bSourceIsBaseAnimating && !m_bCombatWeapon )
{
C_BaseAnimating *pSource = (C_BaseAnimating *)pGhostedRenderable;
SetCycle( pSource->GetCycle() );
SetSequence( pSource->GetSequence() );
SetBody( pSource->GetBody() );
SetSkin( pSource->GetSkin() );
}
SetSize( pGhostedRenderable->CollisionProp()->OBBMins(), pGhostedRenderable->CollisionProp()->OBBMaxs() );
// Set position and angles relative to the object it's ghosting
Vector ptNewOrigin;
QAngle qNewAngles;
if( GhostedRenderableOriginTime( pGhostedRenderable, gpGlobals->curtime ) >= m_fNoTransformBeforeTime )
{
ptNewOrigin = m_matGhostTransform * pGhostedRenderable->GetNetworkOrigin();
qNewAngles = TransformAnglesToWorldSpace( pGhostedRenderable->GetNetworkAngles(), m_matGhostTransform.As3x4() );
}
else
{
ptNewOrigin = pGhostedRenderable->GetNetworkOrigin();
qNewAngles = pGhostedRenderable->GetNetworkAngles();
}
SetNetworkOrigin( ptNewOrigin );
SetLocalOrigin( ptNewOrigin );
SetAbsOrigin( ptNewOrigin );
SetNetworkAngles( qNewAngles );
SetLocalAngles( qNewAngles );
SetAbsAngles( qNewAngles );
g_pClientLeafSystem->RenderableChanged( RenderHandle() );
#if (GHOST_RENDERABLE_TEN_TON_HAMMER == 1)
GhostResetEverything( this );
#endif
}
Vector const& C_PortalGhostRenderable::GetRenderOrigin( void )
{
C_BaseEntity *pGhostedRenderable = m_hGhostedRenderable;
if( pGhostedRenderable == NULL )
return m_ReferencedReturns.vRenderOrigin;
if( GhostedRenderableOriginTime( pGhostedRenderable, gpGlobals->curtime ) < m_fNoTransformBeforeTime )
{
m_ReferencedReturns.vRenderOrigin = pGhostedRenderable->GetRenderOrigin();
}
else
{
m_ReferencedReturns.vRenderOrigin = m_matGhostTransform * pGhostedRenderable->GetRenderOrigin();
}
return m_ReferencedReturns.vRenderOrigin;
}
QAngle const& C_PortalGhostRenderable::GetRenderAngles( void )
{
C_BaseEntity *pGhostedRenderable = m_hGhostedRenderable;
if( pGhostedRenderable == NULL )
return m_ReferencedReturns.qRenderAngle;
if( GhostedRenderableOriginTime( pGhostedRenderable, gpGlobals->curtime ) < m_fNoTransformBeforeTime )
{
m_ReferencedReturns.qRenderAngle = pGhostedRenderable->GetRenderAngles();
}
else
{
m_ReferencedReturns.qRenderAngle = TransformAnglesToWorldSpace( pGhostedRenderable->GetRenderAngles(), m_matGhostTransform.As3x4() );
}
return m_ReferencedReturns.qRenderAngle;
}
bool C_PortalGhostRenderable::SetupBones( matrix3x4a_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime )
{
C_BaseEntity *pGhostedRenderable = m_hGhostedRenderable;
if( pGhostedRenderable == NULL )
return false;
int iOldModelIndex = 0, iWorldModelIndex = 0;
bool bChangeModelIndex = m_bCombatWeapon &&
((iOldModelIndex = ((C_BaseCombatWeapon *)pGhostedRenderable)->GetModelIndex()) != (iWorldModelIndex = ((C_BaseCombatWeapon *)pGhostedRenderable)->GetWorldModelIndex()));
if( bChangeModelIndex )
{
((C_BaseCombatWeapon *)pGhostedRenderable)->SetModelIndex( iWorldModelIndex );
}
if( pGhostedRenderable->SetupBones( pBoneToWorldOut, nMaxBones, boneMask, currentTime ) )
{
if( pBoneToWorldOut && (GhostedRenderableOriginTime( pGhostedRenderable, currentTime ) >= m_fNoTransformBeforeTime) )
{
matrix3x4a_t matGhostTransform;
matGhostTransform = m_matGhostTransform.As3x4();
CStudioHdr *hdr = GetModelPtr();
int nBoneCount = hdr->numbones();
nBoneCount = MIN( nMaxBones, nBoneCount );
for( int i = 0; i != nBoneCount; ++i )
{
ConcatTransforms_Aligned( matGhostTransform, pBoneToWorldOut[i], pBoneToWorldOut[i] );
}
}
if( bChangeModelIndex )
{
((C_BaseCombatWeapon *)pGhostedRenderable)->SetModelIndex( iOldModelIndex );
}
return true;
}
if( bChangeModelIndex )
{
((C_BaseCombatWeapon *)pGhostedRenderable)->SetModelIndex( iOldModelIndex );
}
return false;
}
C_BaseAnimating *C_PortalGhostRenderable::GetBoneSetupDependancy( void )
{
return m_bSourceIsBaseAnimating ? (C_BaseAnimating *)(m_hGhostedRenderable.Get()) : NULL;
}
void C_PortalGhostRenderable::GetRenderBounds( Vector& mins, Vector& maxs )
{
if( m_hGhostedRenderable == NULL )
{
mins = maxs = vec3_origin;
return;
}
m_hGhostedRenderable->GetRenderBounds( mins, maxs );
}
void C_PortalGhostRenderable::GetRenderBoundsWorldspace( Vector& mins, Vector& maxs )
{
C_BaseEntity *pGhostedRenderable = m_hGhostedRenderable;
if( pGhostedRenderable == NULL )
{
mins = maxs = vec3_origin;
return;
}
if( GhostedRenderableOriginTime( pGhostedRenderable, gpGlobals->curtime ) < m_fNoTransformBeforeTime )
return pGhostedRenderable->GetRenderBoundsWorldspace( mins, maxs );
Vector vTempMins, vTempMaxs;
pGhostedRenderable->GetRenderBoundsWorldspace( vTempMins, vTempMaxs );
TransformAABB( m_matGhostTransform.As3x4(), vTempMins, vTempMaxs, mins, maxs );
}
bool C_PortalGhostRenderable::ShouldReceiveProjectedTextures( int flags )
{
return false;
}
void C_PortalGhostRenderable::GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType )
{
C_BaseEntity *pGhostedRenderable = m_hGhostedRenderable;
if( pGhostedRenderable == NULL )
{
mins = maxs = vec3_origin;
return;
}
if( GhostedRenderableOriginTime( pGhostedRenderable, gpGlobals->curtime ) < m_fNoTransformBeforeTime )
return pGhostedRenderable->GetShadowRenderBounds( mins, maxs, shadowType );
Vector vTempMins, vTempMaxs;
pGhostedRenderable->GetShadowRenderBounds( vTempMins, vTempMaxs, shadowType );
TransformAABB( m_matGhostTransform.As3x4(), vTempMins, vTempMaxs, mins, maxs );
}
/*bool C_PortalGhostRenderable::GetShadowCastDistance( float *pDist, ShadowType_t shadowType ) const
{
if( m_hGhostedRenderable == NULL )
return false;
return m_hGhostedRenderable->GetShadowCastDistance( pDist, shadowType );
}
bool C_PortalGhostRenderable::GetShadowCastDirection( Vector *pDirection, ShadowType_t shadowType ) const
{
if( m_hGhostedRenderable == NULL )
return false;
if( m_hGhostedRenderable->GetShadowCastDirection( pDirection, shadowType ) )
{
if( pDirection )
*pDirection = m_matGhostTransform.ApplyRotation( *pDirection );
return true;
}
return false;
}*/
const matrix3x4_t & C_PortalGhostRenderable::RenderableToWorldTransform()
{
C_BaseEntity *pGhostedRenderable = m_hGhostedRenderable;
if( pGhostedRenderable == NULL )
return m_ReferencedReturns.matRenderableToWorldTransform;
if( GhostedRenderableOriginTime( pGhostedRenderable, gpGlobals->curtime ) < m_fNoTransformBeforeTime )
{
m_ReferencedReturns.matRenderableToWorldTransform = pGhostedRenderable->RenderableToWorldTransform();
}
else
{
ConcatTransforms( m_matGhostTransform.As3x4(), pGhostedRenderable->RenderableToWorldTransform(), m_ReferencedReturns.matRenderableToWorldTransform );
}
return m_ReferencedReturns.matRenderableToWorldTransform;
}
bool C_PortalGhostRenderable::GetAttachment( int number, Vector &origin, QAngle &angles )
{
C_BaseEntity *pGhostedRenderable = m_hGhostedRenderable;
if( pGhostedRenderable == NULL )
return false;
if( GhostedRenderableOriginTime( pGhostedRenderable, gpGlobals->curtime ) < m_fNoTransformBeforeTime )
return pGhostedRenderable->GetAttachment( number, origin, angles );
if( pGhostedRenderable->GetAttachment( number, origin, angles ) )
{
origin = m_matGhostTransform * origin;
angles = TransformAnglesToWorldSpace( angles, m_matGhostTransform.As3x4() );
return true;
}
return false;
}
bool C_PortalGhostRenderable::GetAttachment( int number, matrix3x4_t &matrix )
{
C_BaseEntity *pGhostedRenderable = m_hGhostedRenderable;
if( pGhostedRenderable == NULL )
return false;
if( GhostedRenderableOriginTime( pGhostedRenderable, gpGlobals->curtime ) < m_fNoTransformBeforeTime )
return pGhostedRenderable->GetAttachment( number, matrix );
if( pGhostedRenderable->GetAttachment( number, matrix ) )
{
ConcatTransforms( m_matGhostTransform.As3x4(), matrix, matrix );
return true;
}
return false;
}
bool C_PortalGhostRenderable::GetAttachment( int number, Vector &origin )
{
C_BaseEntity *pGhostedRenderable = m_hGhostedRenderable;
if( pGhostedRenderable == NULL )
return false;
if( GhostedRenderableOriginTime( pGhostedRenderable, gpGlobals->curtime ) < m_fNoTransformBeforeTime )
return pGhostedRenderable->GetAttachment( number, origin );
if( pGhostedRenderable->GetAttachment( number, origin ) )
{
origin = m_matGhostTransform * origin;
return true;
}
return false;
}
bool C_PortalGhostRenderable::GetAttachmentVelocity( int number, Vector &originVel, Quaternion &angleVel )
{
C_BaseEntity *pGhostedRenderable = m_hGhostedRenderable;
if( pGhostedRenderable == NULL )
return false;
if( GhostedRenderableOriginTime( pGhostedRenderable, gpGlobals->curtime ) < m_fNoTransformBeforeTime )
return pGhostedRenderable->GetAttachmentVelocity( number, originVel, angleVel );
Vector ghostVel;
if( pGhostedRenderable->GetAttachmentVelocity( number, ghostVel, angleVel ) )
{
Vector3DMultiply( m_matGhostTransform, ghostVel, originVel );
Vector3DMultiply( m_matGhostTransform, *(Vector*)( &angleVel ), *(Vector*)( &angleVel ) );
return true;
}
return false;
}
bool C_PortalGhostRenderable::ShouldDrawForThisView( void )
{
if ( portal_ghosts_disable.GetBool() )
return false;
C_BaseEntity *pGhostedRenderable = m_hGhostedRenderable.Get();
if( pGhostedRenderable == NULL )
{
return false;
}
float fInterpTime = GhostedRenderableOriginTime( pGhostedRenderable, gpGlobals->curtime );
if( (fInterpTime < m_fRenderableRange[0]) ||
(fInterpTime >= m_fRenderableRange[1]) )
return false;
if ( m_bSourceIsBaseAnimating )
{
C_Portal_Player *pViewPlayer = ToPortalPlayer( GetSplitScreenViewPlayer() );
C_Portal_Player *pHoldingPlayer = ToPortalPlayer( m_hHoldingPlayer.Get() );
if ( pHoldingPlayer && (pHoldingPlayer == pViewPlayer) && !pViewPlayer->ShouldDrawLocalPlayer() )
{
if ( !pHoldingPlayer->IsAlive() )
{
// Dead player uses a ragdoll to draw, so don't ghost the dead entity
return false;
}
else if( g_pPortalRender->GetViewRecursionLevel() == 0 )
{
if ( pHoldingPlayer->m_bEyePositionIsTransformedByPortal )
return false;
C_PlayerHeldObjectClone *pClone = NULL;
if ( m_bPlayerHeldClone )
{
pClone = assert_cast< C_PlayerHeldObjectClone* >( m_hGhostedRenderable.Get() );
if( pClone && pClone->m_bOnOppositeSideOfPortal )
return false;
}
}
else if ( g_pPortalRender->GetViewRecursionLevel() == 1 )
{
C_PlayerHeldObjectClone *pClone = NULL;
if ( m_bPlayerHeldClone )
{
pClone = assert_cast< C_PlayerHeldObjectClone* >( m_hGhostedRenderable.Get() );
}
if ( (!pHoldingPlayer->m_bEyePositionIsTransformedByPortal && (g_pPortalRender->GetCurrentViewEntryPortal() == m_pOwningPortal)) &&
!( pClone && pClone->m_bOnOppositeSideOfPortal ) )
return false;
}
}
}
return true;
}
int C_PortalGhostRenderable::DrawModel( int flags, const RenderableInstance_t &instance )
{
if( !ShouldDrawForThisView() )
return 0;
#if( DEBUG_GHOSTRENDERABLES == 1 )
if( m_iDebugColor[3] != 0 )
{
//NDebugOverlay::BoxAngles( GetRenderOrigin(), m_hGhostedRenderable->CollisionProp()->OBBMins(), m_hGhostedRenderable->CollisionProp()->OBBMaxs(), GetRenderAngles(), m_iDebugColor[0], m_iDebugColor[1], m_iDebugColor[2], m_iDebugColor[3], 0.0f );
NDebugOverlay::Sphere( GetNetworkOrigin(), 5.0f, m_iDebugColor[0], m_iDebugColor[1], m_iDebugColor[2], true, 0.0f );
}
#endif
if ( m_bSourceIsBaseAnimating )
{
return C_BaseAnimating::DrawModel( flags, instance );
}
else
{
DrawBrushModelMode_t mode = DBM_DRAW_ALL;
if ( flags & STUDIO_TWOPASS )
{
mode = ( flags & STUDIO_TRANSPARENCY ) ? DBM_DRAW_TRANSLUCENT_ONLY : DBM_DRAW_OPAQUE_ONLY;
}
render->DrawBrushModelEx( m_hGhostedRenderable,
(model_t *)m_hGhostedRenderable->GetModel(),
GetRenderOrigin(),
GetRenderAngles(),
mode );
return 1;
}
return 0;
}
ModelInstanceHandle_t C_PortalGhostRenderable::GetModelInstance()
{
// HACK: This causes problems if the ghosted renderable is
// has it's model instance destructed mid-render... this is currently
// the case for the portalgun view model due to model switching
// so we're not going to use the ghost's model instance for combat weapon ents.
// This will only mean the decal state is wrong, the worldmodel doesn't get decals anyway.
// Real fix would be to 'sync' the decal state between this and the ghosted ent, but
// this will fix it for now.
if ( m_hGhostedRenderable && (m_bCombatWeapon == false) && (m_bCombatWeaponWorldClone == false) )
return m_hGhostedRenderable->GetModelInstance();
return BaseClass::GetModelInstance();
}
RenderableTranslucencyType_t C_PortalGhostRenderable::ComputeTranslucencyType( void )
{
if ( m_hGhostedRenderable == NULL )
return RENDERABLE_IS_OPAQUE;
return m_hGhostedRenderable->ComputeTranslucencyType();
}
int C_PortalGhostRenderable::GetRenderFlags()
{
if( m_hGhostedRenderable == NULL )
return false;
return m_hGhostedRenderable->GetRenderFlags();
}
/*const model_t* C_PortalGhostRenderable::GetModel( ) const
{
if( m_hGhostedRenderable == NULL )
return NULL;
return m_hGhostedRenderable->GetModel();
}
int C_PortalGhostRenderable::GetBody()
{
if( m_hGhostedRenderable == NULL )
return 0;
return m_hGhostedRenderable->GetBody();
}*/
void C_PortalGhostRenderable::GetColorModulation( float* color )
{
if( m_hGhostedRenderable == NULL )
return;
#if(DEBUG_GHOSTRENDERABLES == 1)
if( color[3] != 0 )
{
color[0] = m_iDebugColor[0] / 255.0f;
color[1] = m_iDebugColor[1] / 255.0f;
color[2] = m_iDebugColor[2] / 255.0f;
return;
}
#endif
return m_hGhostedRenderable->GetColorModulation( color );
}
/*ShadowType_t C_PortalGhostRenderable::ShadowCastType()
{
if( m_hGhostedRenderable == NULL )
return SHADOWS_NONE;
return m_hGhostedRenderable->ShadowCastType();
}*/
int C_PortalGhostRenderable::LookupAttachment( const char *pAttachmentName )
{
if( m_hGhostedRenderable == NULL )
return -1;
return m_hGhostedRenderable->LookupAttachment( pAttachmentName );
}
/*
int C_PortalGhostRenderable::GetSkin()
{
if( m_hGhostedRenderable == NULL )
return -1;
return m_hGhostedRenderable->GetSkin();
}
*/
float *C_PortalGhostRenderable::GetRenderClipPlane( void )
{
if( !ShouldDrawForThisView() ) //Fix for systems that do not support clip planes. The occluding depth box should be avoided if it's not going to be useful
return NULL;
if( m_pPortalExitRenderClipPlane && //have an exit portal special clip plane
g_pPortalRender->IsRenderingPortal() && //rendering through a portal
(g_pPortalRender->GetCurrentViewEntryPortal() == m_pOwningPortal) ) //we're being drawn on the exit side
{
return m_pPortalExitRenderClipPlane;
}
return m_pSharedRenderClipPlane;
}
//-----------------------------------------------------------------------------
// Handle recording for the SFM
//-----------------------------------------------------------------------------
void C_PortalGhostRenderable::GetToolRecordingState( KeyValues *msg )
{
VPROF_BUDGET( "C_PortalGhostRenderable::GetToolRecordingState", VPROF_BUDGETGROUP_TOOLS );
BaseClass::GetToolRecordingState( msg );
C_Portal_Player *pViewPlayer = ToPortalPlayer( GetSplitScreenViewPlayer() );
if ( m_hHoldingPlayer.Get() && m_hHoldingPlayer.Get() == pViewPlayer )
{
msg->SetInt( "worldmodel", 2 ); //world model that should only draw in third person
}
}
bool C_PortalGhostRenderable::ShouldCloneEntity( C_BaseEntity *pEntity, C_Portal_Base2D *pPortal, bool bUsePositionChecks )
{
if( pEntity->IsEffectActive( EF_NODRAW ) )
return false;
bool bIsMovable = false;
C_BaseEntity *pMoveEntity = pEntity;
MoveType_t moveType = MOVETYPE_NONE;
//unmoveables and doors can never get halfway in the portal
while ( pMoveEntity )
{
moveType = pMoveEntity->GetMoveType();
if ( !( moveType == MOVETYPE_NONE || moveType == MOVETYPE_PUSH ) )
{
bIsMovable = true;
pMoveEntity = NULL;
}
else
pMoveEntity = pMoveEntity->GetMoveParent();
}
if ( !bIsMovable )
return false;
if( (moveType == MOVETYPE_NOCLIP) && dynamic_cast<C_Portal_Base2D *>(pEntity) != NULL ) //potentially "mobile" portals in p2 can use MOVETYPE_NOCLIP
return false;
Assert( dynamic_cast<C_Portal_Base2D *>(pEntity) == NULL ); //should have been killed with (pEntity->GetMoveType() == MOVETYPE_NONE) check. Infinite recursion is infinitely bad.
if( ToBaseViewModel(pEntity) )
return false; //avoid ghosting view models
if( pEntity->IsRenderingWithViewModels() )
return false; //avoid ghosting anything that draws with viewmodels
bool bActivePlayerWeapon = false;
C_BaseCombatWeapon *pWeapon = ToBaseCombatWeapon( pEntity );
if ( pWeapon )
{
C_Portal_Player *pPortalPlayer = ToPortalPlayer( pWeapon->GetOwner() );
if ( pPortalPlayer )
{
if ( pWeapon->m_iState != WEAPON_IS_ACTIVE )
{
return false; // don't ghost player owned non active weapons
}
else
{
bActivePlayerWeapon = true;
}
}
}
// Weapon is not teleportable, and we don't want to make that function use a dynamic cast to test for it,
// so do the simple thing and just ignore this check for player-held weapons
if( !bActivePlayerWeapon && !CPortal_Base2D_Shared::IsEntityTeleportable( pEntity ) )
return false;
if( bUsePositionChecks )
{
Vector ptEntCenter = pEntity->WorldSpaceCenter();
float fEntCenterDist = (pPortal->m_plane_Origin.normal.Dot( ptEntCenter ) - pPortal->m_plane_Origin.dist);
if( fEntCenterDist < -5.0f )
{
//view model held objects don't actually teleport to the other side of a portal when their center crosses the plane.
//Alternate test is to ensure that a line drawn from the player center to the clone intersects the portal
C_Portal_Player *pHoldingPlayer = (C_Portal_Player *)GetPlayerHoldingEntity( pEntity );
if( (pHoldingPlayer == NULL) || //not held
!pHoldingPlayer->IsUsingVMGrab() || //not in ViewModel grab mode
dynamic_cast<C_PlayerHeldObjectClone *>(pEntity) ) //clones actually handle the teleportation correctly on the client.
{
return false; //entity is behind the portal, most likely behind the wall the portal is placed on
}
else
{
Vector vPlayerCenter = pHoldingPlayer->WorldSpaceCenter();
float fPlayerCenterDist = (pPortal->m_plane_Origin.normal.Dot( vPlayerCenter ) - pPortal->m_plane_Origin.dist);
if( fPlayerCenterDist < 0.0f )
return false;
float fTotalDist = fPlayerCenterDist - fEntCenterDist;
if( fTotalDist == 0.0f ) //should be >= 5.0 at all times, but I've been bitten too many times by impossibly 0 cases
return false;
Vector vPlaneIntersect = (ptEntCenter * (fPlayerCenterDist/fTotalDist)) - (vPlayerCenter * (fEntCenterDist/fTotalDist));
Vector vPortalCenterToIntersect = vPlaneIntersect - pPortal->m_ptOrigin;
if( (fabs( vPortalCenterToIntersect.Dot( pPortal->m_vRight ) ) > pPortal->GetHalfWidth()) ||
(fabs( vPortalCenterToIntersect.Dot( pPortal->m_vUp ) ) > pPortal->GetHalfHeight()) )
{
return false;
}
//else intersects on the portal quad and the test passes
}
}
if ( bActivePlayerWeapon )
{
// Both the player AND the weapon must be in the portal hole.
//
// We test the player's collision AABB against the portal hole because it's guaranteed not to intersect
// multiple adjacent portal holes at the same time in a degenerate case.
//
// We test the player's weapon hitbox against the portal hole because it doesn't have a good collision AABB
// and because we'll only bother to even do this test if the player's AABB is acutally inside the portal hole.
//
// If we only test the weapon, we get false positives (phantom renderables) when its bounds span multiple adjacent
// portal holes.
if( !pPortal->m_PortalSimulator.EntityHitBoxExtentIsInPortalHole( pWeapon->GetOwner(), true /* bUseCollisionAABB */ ) ||
!pPortal->m_PortalSimulator.EntityHitBoxExtentIsInPortalHole( (C_BaseAnimating*)pEntity, false /* bUseCollisionAABB */ ) )
return false;
}
else if( pEntity->IsPlayer() )
{
if( !pPortal->m_PortalSimulator.EntityHitBoxExtentIsInPortalHole( (C_BaseAnimating*)pEntity, true /* bUseCollisionAABB */ ) )
return false;
}
else
{
if( !pPortal->m_PortalSimulator.EntityIsInPortalHole( pEntity ) )
return false;
}
}
return true;
}
C_PortalGhostRenderable *C_PortalGhostRenderable::CreateGhostRenderable( C_BaseEntity *pEntity, C_Portal_Base2D *pPortal )
{
Assert( ShouldCloneEntity( pEntity, pPortal, false ) );
C_BasePlayer *pPlayerOwner = NULL;
bool bRenderableIsPlayer = pEntity->IsPlayer();
if ( bRenderableIsPlayer )
{
pPlayerOwner = ToBasePlayer( pEntity );
}
else
{
C_BaseCombatWeapon *pWeapon = ToBaseCombatWeapon( pEntity );
if ( pWeapon )
{
C_Portal_Player *pOwningPlayer = ToPortalPlayer( pWeapon->GetOwner() );
if ( pOwningPlayer )
{
pPlayerOwner = pOwningPlayer;
}
}
else
{
C_PlayerHeldObjectClone *pClone = dynamic_cast< C_PlayerHeldObjectClone* >( pEntity );
if ( pClone )
{
C_Portal_Player *pOwningPlayer = ToPortalPlayer( pClone->m_hPlayer );
if ( pOwningPlayer )
{
pPlayerOwner = pOwningPlayer;
}
}
}
}
#if( DEBUG_GHOSTRENDERABLES == 1 )
{
Vector vTransformedOrigin;
QAngle qTransformedAngles;
VectorTransform( pEntity->GetRenderOrigin(), pPortal->m_matrixThisToLinked.As3x4(), vTransformedOrigin );
qTransformedAngles = TransformAnglesToWorldSpace( pEntity->GetRenderAngles(), pPortal->m_matrixThisToLinked.As3x4() );
NDebugOverlay::BoxAngles( vTransformedOrigin, pEntity->CollisionProp()->OBBMins(), pEntity->CollisionProp()->OBBMaxs(), qTransformedAngles, 0, 255, 0, 16, 0.0f );
}
#endif
C_PortalGhostRenderable *pNewGhost = new C_PortalGhostRenderable( pPortal,
pEntity,
pPortal->m_matrixThisToLinked,
bRenderableIsPlayer ? pPortal->m_fGhostRenderablesClipForPlayer : pPortal->m_fGhostRenderablesClip,
pPlayerOwner );
if( !pNewGhost->InitializeAsClientEntity( pEntity->GetModelName(), false ) )
{
pNewGhost->Release();
return NULL;
}
Assert( pNewGhost );
if( pPlayerOwner )
{
pNewGhost->SetOwnerEntity( pPlayerOwner );
}
if( pNewGhost->m_pSharedRenderClipPlane == pPortal->m_fGhostRenderablesClipForPlayer )
{
pNewGhost->m_pPortalExitRenderClipPlane = pPortal->m_fGhostRenderablesClip;
}
pPortal->m_GhostRenderables.AddToTail( pNewGhost );
{
// HACK - I just copied the CClientTools::OnEntityCreated code here,
// since the ghosts aren't really entities - they don't have an entindex,
// they're not in the entitylist, and they get created during Simulate(),
// which isn't valid for real entities, since it changes the simulate list
// -jd
if ( ToolsEnabled() && clienttools->IsInRecordingMode() )
{
// Send deletion message to tool interface
KeyValues *kv = new KeyValues( "created" );
HTOOLHANDLE h = clienttools->AttachToEntity( pNewGhost );
ToolFramework_PostToolMessage( h, kv );
kv->deleteThis();
}
}
g_pClientLeafSystem->DisableCachedRenderBounds( pNewGhost->RenderHandle(), true );
pNewGhost->PerFrameUpdate();
return pNewGhost;
}
C_PortalGhostRenderable *C_PortalGhostRenderable::CreateInversion( C_PortalGhostRenderable *pSrc, C_Portal_Base2D *pSourcePortal, float fTime )
{
if( !(pSourcePortal && pSrc) )
return NULL;
C_Portal_Base2D *pRemotePortal = pSourcePortal->m_hLinkedPortal.Get();
if( !pRemotePortal )
return NULL;
C_BaseEntity *pRootEntity = pSrc->m_hGhostedRenderable;
if( !pRootEntity )
return NULL;
C_PortalGhostRenderable *pNewGhost = NULL;
//use existing if we already have one
for( int i = 0; i != pRemotePortal->m_GhostRenderables.Count(); ++i )
{
if( pRemotePortal->m_GhostRenderables[i]->m_hGhostedRenderable == pRootEntity )
{
pNewGhost = pRemotePortal->m_GhostRenderables[i];
//reset time based variables
pNewGhost->m_fRenderableRange[0] = -FLT_MAX;
pNewGhost->m_fRenderableRange[1] = FLT_MAX;
pNewGhost->m_fNoTransformBeforeTime = -FLT_MAX;
break;
}
}
if( pNewGhost == NULL )
{
pNewGhost = new C_PortalGhostRenderable( pRemotePortal, pSrc->m_hGhostedRenderable, pRemotePortal->m_matrixThisToLinked,
pSrc->m_pSharedRenderClipPlane == pSourcePortal->m_fGhostRenderablesClipForPlayer ? pRemotePortal->m_fGhostRenderablesClipForPlayer : pRemotePortal->m_fGhostRenderablesClip, pSrc->m_hHoldingPlayer );
if( !pNewGhost->InitializeAsClientEntity( pRootEntity->GetModelName(), false ) )
{
pNewGhost->Release();
return NULL;
}
if( pSrc->m_hHoldingPlayer )
{
pNewGhost->SetOwnerEntity( pSrc->m_hHoldingPlayer );
}
if( pSrc->m_pPortalExitRenderClipPlane != NULL )
{
if( pSrc->m_pPortalExitRenderClipPlane == pSourcePortal->m_fGhostRenderablesClipForPlayer )
{
pNewGhost->m_pPortalExitRenderClipPlane = pRemotePortal->m_fGhostRenderablesClipForPlayer;
}
else if( pSrc->m_pPortalExitRenderClipPlane == pSourcePortal->m_fGhostRenderablesClip )
{
pNewGhost->m_pPortalExitRenderClipPlane = pRemotePortal->m_fGhostRenderablesClip;
}
else
{
Assert( false );
}
}
pRemotePortal->m_GhostRenderables.AddToTail( pNewGhost );
#if( DEBUG_GHOSTRENDERABLES == 1 )
{
Vector vTransformedOrigin;
QAngle qTransformedAngles;
VectorTransform( pSrc->m_hGhostedRenderable->GetNetworkOrigin(), pRemotePortal->m_matrixThisToLinked.As3x4(), vTransformedOrigin );
qTransformedAngles = TransformAnglesToWorldSpace( pSrc->m_hGhostedRenderable->GetNetworkAngles(), pRemotePortal->m_matrixThisToLinked.As3x4() );
NDebugOverlay::BoxAngles( vTransformedOrigin, pSrc->m_hGhostedRenderable->CollisionProp()->OBBMins(), pSrc->m_hGhostedRenderable->CollisionProp()->OBBMaxs(), qTransformedAngles, 0, 255, 0, 16, 0.0f );
}
#endif
}
//these times are in effective origin interpolator time m_hGhostedRenderable->GetOriginInterpolator().GetInterpolatedTime( m_hGhostedRenderable->GetEffectiveInterpolationCurTime() )
pNewGhost->m_fRenderableRange[0] = fTime;
pNewGhost->m_fRenderableRange[1] = FLT_MAX;
pSrc->m_fRenderableRange[0] = -FLT_MAX;
pSrc->m_fRenderableRange[1] = fTime;
pNewGhost->m_fNoTransformBeforeTime = fTime;
pSrc->m_fDisablePositionChecksUntilTime = fTime + (TICK_INTERVAL * 2.0f) + pSrc->m_hGhostedRenderable->GetOriginInterpolator().GetInterpolationAmount();
pNewGhost->m_fDisablePositionChecksUntilTime = pSrc->m_fDisablePositionChecksUntilTime;
g_pClientLeafSystem->DisableCachedRenderBounds( pNewGhost->RenderHandle(), true );
pNewGhost->PerFrameUpdate();
return pNewGhost;
}