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
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;
|
|
}
|
|
|
|
|
|
|
|
|