diff options
Diffstat (limited to 'game/shared/portal/portal_util_shared.cpp')
| -rw-r--r-- | game/shared/portal/portal_util_shared.cpp | 1774 |
1 files changed, 1774 insertions, 0 deletions
diff --git a/game/shared/portal/portal_util_shared.cpp b/game/shared/portal/portal_util_shared.cpp new file mode 100644 index 0000000..7e61178 --- /dev/null +++ b/game/shared/portal/portal_util_shared.cpp @@ -0,0 +1,1774 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "portal_util_shared.h" +#include "prop_portal_shared.h" +#include "portal_shareddefs.h" +#include "portal_collideable_enumerator.h" +#include "beam_shared.h" +#include "collisionutils.h" +#include "util_shared.h" +#ifndef CLIENT_DLL + #include "util.h" + #include "ndebugoverlay.h" + #include "env_debughistory.h" +#else + #include "c_portal_player.h" +#endif +#include "PortalSimulation.h" + +bool g_bAllowForcePortalTrace = false; +bool g_bForcePortalTrace = false; +bool g_bBulletPortalTrace = false; + +ConVar sv_portal_trace_vs_world ("sv_portal_trace_vs_world", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "Use traces against portal environment world geometry" ); +ConVar sv_portal_trace_vs_displacements ("sv_portal_trace_vs_displacements", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "Use traces against portal environment displacement geometry" ); +ConVar sv_portal_trace_vs_holywall ("sv_portal_trace_vs_holywall", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "Use traces against portal environment carved wall" ); +ConVar sv_portal_trace_vs_staticprops ("sv_portal_trace_vs_staticprops", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "Use traces against portal environment static prop geometry" ); +ConVar sv_use_find_closest_passable_space ("sv_use_find_closest_passable_space", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "Enables heavy-handed player teleporting stuck fix code." ); +ConVar sv_use_transformed_collideables("sv_use_transformed_collideables", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "Disables traces against remote portal moving entities using transforms to bring them into local space." ); +class CTransformedCollideable : public ICollideable //wraps an existing collideable, but transforms everything that pertains to world space by another transform +{ +public: + VMatrix m_matTransform; //the transformation we apply to the wrapped collideable + VMatrix m_matInvTransform; //cached inverse of m_matTransform + + ICollideable *m_pWrappedCollideable; //the collideable we're transforming without it knowing + + struct CTC_ReferenceVars_t + { + Vector m_vCollisionOrigin; + QAngle m_qCollisionAngles; + matrix3x4_t m_matCollisionToWorldTransform; + matrix3x4_t m_matRootParentToWorldTransform; + }; + + mutable CTC_ReferenceVars_t m_ReferencedVars; //when returning a const reference, it needs to point to something, so here we go + + //abstract functions which require no transforms, just pass them along to the wrapped collideable + virtual IHandleEntity *GetEntityHandle() { return m_pWrappedCollideable->GetEntityHandle(); } + virtual const Vector& OBBMinsPreScaled() const { return m_pWrappedCollideable->OBBMinsPreScaled(); } + virtual const Vector& OBBMaxsPreScaled() const { return m_pWrappedCollideable->OBBMaxsPreScaled(); } + virtual const Vector& OBBMins() const { return m_pWrappedCollideable->OBBMins(); } + virtual const Vector& OBBMaxs() const { return m_pWrappedCollideable->OBBMaxs(); } + virtual int GetCollisionModelIndex() { return m_pWrappedCollideable->GetCollisionModelIndex(); } + virtual const model_t* GetCollisionModel() { return m_pWrappedCollideable->GetCollisionModel(); } + virtual SolidType_t GetSolid() const { return m_pWrappedCollideable->GetSolid(); } + virtual int GetSolidFlags() const { return m_pWrappedCollideable->GetSolidFlags(); } + virtual IClientUnknown* GetIClientUnknown() { return m_pWrappedCollideable->GetIClientUnknown(); } + virtual int GetCollisionGroup() const { return m_pWrappedCollideable->GetCollisionGroup(); } + virtual bool ShouldTouchTrigger( int triggerSolidFlags ) const { return m_pWrappedCollideable->ShouldTouchTrigger(triggerSolidFlags); } + + //slightly trickier functions + virtual void WorldSpaceTriggerBounds( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) const; + virtual bool TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ); + virtual bool TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ); + virtual const Vector& GetCollisionOrigin() const; + virtual const QAngle& GetCollisionAngles() const; + virtual const matrix3x4_t& CollisionToWorldTransform() const; + virtual void WorldSpaceSurroundingBounds( Vector *pVecMins, Vector *pVecMaxs ); + virtual const matrix3x4_t *GetRootParentToWorldTransform() const; +}; + +void CTransformedCollideable::WorldSpaceTriggerBounds( Vector *pVecWorldMins, Vector *pVecWorldMaxs ) const +{ + m_pWrappedCollideable->WorldSpaceTriggerBounds( pVecWorldMins, pVecWorldMaxs ); + + if( pVecWorldMins ) + *pVecWorldMins = m_matTransform * (*pVecWorldMins); + + if( pVecWorldMaxs ) + *pVecWorldMaxs = m_matTransform * (*pVecWorldMaxs); +} + +bool CTransformedCollideable::TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ) +{ + //TODO: Transform the ray by inverse matTransform and transform the trace results by matTransform? AABB Errors arise by transforming the ray. + return m_pWrappedCollideable->TestCollision( ray, fContentsMask, tr ); +} + +bool CTransformedCollideable::TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ) +{ + //TODO: Transform the ray by inverse matTransform and transform the trace results by matTransform? AABB Errors arise by transforming the ray. + return m_pWrappedCollideable->TestHitboxes( ray, fContentsMask, tr ); +} + +const Vector& CTransformedCollideable::GetCollisionOrigin() const +{ + m_ReferencedVars.m_vCollisionOrigin = m_matTransform * m_pWrappedCollideable->GetCollisionOrigin(); + return m_ReferencedVars.m_vCollisionOrigin; +} + +const QAngle& CTransformedCollideable::GetCollisionAngles() const +{ + m_ReferencedVars.m_qCollisionAngles = TransformAnglesToWorldSpace( m_pWrappedCollideable->GetCollisionAngles(), m_matTransform.As3x4() ); + return m_ReferencedVars.m_qCollisionAngles; +} + +const matrix3x4_t& CTransformedCollideable::CollisionToWorldTransform() const +{ + //1-2 order correct? + ConcatTransforms( m_matTransform.As3x4(), m_pWrappedCollideable->CollisionToWorldTransform(), m_ReferencedVars.m_matCollisionToWorldTransform ); + return m_ReferencedVars.m_matCollisionToWorldTransform; +} + +void CTransformedCollideable::WorldSpaceSurroundingBounds( Vector *pVecMins, Vector *pVecMaxs ) +{ + if( (pVecMins == NULL) && (pVecMaxs == NULL) ) + return; + + Vector vMins, vMaxs; + m_pWrappedCollideable->WorldSpaceSurroundingBounds( &vMins, &vMaxs ); + + TransformAABB( m_matTransform.As3x4(), vMins, vMaxs, vMins, vMaxs ); + + if( pVecMins ) + *pVecMins = vMins; + if( pVecMaxs ) + *pVecMaxs = vMaxs; +} + +const matrix3x4_t* CTransformedCollideable::GetRootParentToWorldTransform() const +{ + const matrix3x4_t *pWrappedVersion = m_pWrappedCollideable->GetRootParentToWorldTransform(); + if( pWrappedVersion == NULL ) + return NULL; + + ConcatTransforms( m_matTransform.As3x4(), *pWrappedVersion, m_ReferencedVars.m_matRootParentToWorldTransform ); + return &m_ReferencedVars.m_matRootParentToWorldTransform; +} + +Color UTIL_Portal_Color( int iPortal ) +{ + switch ( iPortal ) + { + case 0: + // GRAVITY BEAM + return Color( 242, 202, 167, 255 ); + + case 1: + // PORTAL 1 + return Color( 64, 160, 255, 255 ); + + case 2: + // PORTAL 2 + return Color( 255, 160, 32, 255 ); + } + + return Color( 255, 255, 255, 255 ); +} + +void UTIL_Portal_Trace_Filter( CTraceFilterSimpleClassnameList *traceFilterPortalShot ) +{ + traceFilterPortalShot->AddClassnameToIgnore( "prop_physics" ); + traceFilterPortalShot->AddClassnameToIgnore( "func_physbox" ); + traceFilterPortalShot->AddClassnameToIgnore( "npc_portal_turret_floor" ); + traceFilterPortalShot->AddClassnameToIgnore( "prop_energy_ball" ); + traceFilterPortalShot->AddClassnameToIgnore( "npc_security_camera" ); + traceFilterPortalShot->AddClassnameToIgnore( "player" ); + traceFilterPortalShot->AddClassnameToIgnore( "simple_physics_prop" ); + traceFilterPortalShot->AddClassnameToIgnore( "simple_physics_brush" ); + traceFilterPortalShot->AddClassnameToIgnore( "prop_ragdoll" ); + traceFilterPortalShot->AddClassnameToIgnore( "prop_glados_core" ); + traceFilterPortalShot->AddClassnameToIgnore( "updateitem2" ); +} + + +CProp_Portal* UTIL_Portal_FirstAlongRay( const Ray_t &ray, float &fMustBeCloserThan ) +{ + CProp_Portal *pIntersectedPortal = NULL; + + int iPortalCount = CProp_Portal_Shared::AllPortals.Count(); + if( iPortalCount != 0 ) + { + CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base(); + + for( int i = 0; i != iPortalCount; ++i ) + { + CProp_Portal *pTempPortal = pPortals[i]; + if( pTempPortal->IsActivedAndLinked() ) + { + float fIntersection = UTIL_IntersectRayWithPortal( ray, pTempPortal ); + if( fIntersection >= 0.0f && fIntersection < fMustBeCloserThan ) + { + //within range, now check directionality + if( pTempPortal->m_plane_Origin.normal.Dot( ray.m_Delta ) < 0.0f ) + { + //qualifies for consideration, now it just has to compete for closest + pIntersectedPortal = pTempPortal; + fMustBeCloserThan = fIntersection; + } + } + } + } + } + + return pIntersectedPortal; +} + + +bool UTIL_Portal_TraceRay_Bullets( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace, bool bTraceHolyWall ) +{ + if( !pPortal || !pPortal->IsActivedAndLinked() ) + { + //not in a portal environment, use regular traces + enginetrace->TraceRay( ray, fMask, pTraceFilter, pTrace ); + return false; + } + + trace_t trReal; + + enginetrace->TraceRay( ray, fMask, pTraceFilter, &trReal ); + + Vector vRayNormal = ray.m_Delta; + VectorNormalize( vRayNormal ); + + Vector vPortalForward; + pPortal->GetVectors( &vPortalForward, 0, 0 ); + + // If the ray isn't going into the front of the portal, just use the real trace + if ( vPortalForward.Dot( vRayNormal ) > 0.0f ) + { + *pTrace = trReal; + return false; + } + + // If the real trace collides before the portal plane, just use the real trace + float fPortalFraction = UTIL_IntersectRayWithPortal( ray, pPortal ); + + if ( fPortalFraction == -1.0f || trReal.fraction + 0.0001f < fPortalFraction ) + { + // Didn't intersect or the real trace intersected closer + *pTrace = trReal; + return false; + } + + Ray_t rayPostPortal; + rayPostPortal = ray; + rayPostPortal.m_Start = ray.m_Start + ray.m_Delta * fPortalFraction; + rayPostPortal.m_Delta = ray.m_Delta * ( 1.0f - fPortalFraction ); + + VMatrix matThisToLinked = pPortal->MatrixThisToLinked(); + + Ray_t rayTransformed; + UTIL_Portal_RayTransform( matThisToLinked, rayPostPortal, rayTransformed ); + + // After a bullet traces through a portal it can hit the player that fired it + CTraceFilterSimple *pSimpleFilter = dynamic_cast<CTraceFilterSimple*>(pTraceFilter); + const IHandleEntity *pPassEntity = NULL; + if ( pSimpleFilter ) + { + pPassEntity = pSimpleFilter->GetPassEntity(); + pSimpleFilter->SetPassEntity( 0 ); + } + + trace_t trPostPortal; + enginetrace->TraceRay( rayTransformed, fMask, pTraceFilter, &trPostPortal ); + + if ( pSimpleFilter ) + { + pSimpleFilter->SetPassEntity( pPassEntity ); + } + + //trPostPortal.startpos = ray.m_Start; + UTIL_Portal_PointTransform( matThisToLinked, ray.m_Start, trPostPortal.startpos ); + trPostPortal.fraction = trPostPortal.fraction * ( 1.0f - fPortalFraction ) + fPortalFraction; + + *pTrace = trPostPortal; + + return true; +} + +CProp_Portal* UTIL_Portal_TraceRay_Beam( const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, float *pfFraction ) +{ + // Do a regular trace + trace_t tr; + UTIL_TraceLine( ray.m_Start, ray.m_Start + ray.m_Delta, fMask, pTraceFilter, &tr ); + float fMustBeCloserThan = tr.fraction + 0.0001f; + + CProp_Portal *pIntersectedPortal = UTIL_Portal_FirstAlongRay( ray, fMustBeCloserThan ); + + *pfFraction = fMustBeCloserThan; //will be real trace distance if it didn't hit a portal + return pIntersectedPortal; +} + + +bool UTIL_Portal_Trace_Beam( const CBeam *pBeam, Vector &vecStart, Vector &vecEnd, Vector &vecIntersectionStart, Vector &vecIntersectionEnd, ITraceFilter *pTraceFilter ) +{ + vecStart = pBeam->GetAbsStartPos(); + vecEnd = pBeam->GetAbsEndPos(); + + // Trace to see if we've intersected a portal + float fEndFraction; + Ray_t rayBeam; + + bool bIsReversed = ( pBeam->GetBeamFlags() & FBEAM_REVERSED ) != 0x0; + + if ( !bIsReversed ) + rayBeam.Init( vecStart, vecEnd ); + else + rayBeam.Init( vecEnd, vecStart ); + + CProp_Portal *pPortal = UTIL_Portal_TraceRay_Beam( rayBeam, MASK_SHOT, pTraceFilter, &fEndFraction ); + + // If we intersected a portal we need to modify the start and end points to match the actual trace through portal drawing extents + if ( !pPortal ) + return false; + + // Modify the start and end points to match the actual trace through portal drawing extents + vecStart = rayBeam.m_Start; + + Vector vecIntersection = rayBeam.m_Start + rayBeam.m_Delta * fEndFraction; + + int iNumLoops = 0; + + // Loop through the portals (at most 16 times) + while ( pPortal && iNumLoops < 16 ) + { + // Get the point that we hit a portal or wall + vecIntersectionStart = vecIntersection; + + VMatrix matThisToLinked = pPortal->MatrixThisToLinked(); + + // Get the transformed positions of the sub beam in the other portal's space + UTIL_Portal_PointTransform( matThisToLinked, vecIntersectionStart, vecIntersectionEnd ); + UTIL_Portal_PointTransform( matThisToLinked, rayBeam.m_Start + rayBeam.m_Delta, vecEnd ); + + CTraceFilterSkipClassname traceFilter( pPortal->m_hLinkedPortal, "prop_energy_ball", COLLISION_GROUP_NONE ); + + rayBeam.Init( vecIntersectionEnd, vecEnd ); + pPortal = UTIL_Portal_TraceRay_Beam( rayBeam, MASK_SHOT, &traceFilter, &fEndFraction ); + vecIntersection = rayBeam.m_Start + rayBeam.m_Delta * fEndFraction; + + ++iNumLoops; + } + + vecEnd = vecIntersection; + + return true; +} + + +void UTIL_Portal_TraceRay_With( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace, bool bTraceHolyWall ) +{ + //check to see if the player is theoretically in a portal environment + if( !pPortal || !pPortal->m_PortalSimulator.IsReadyToSimulate() ) + { + //not in a portal environment, use regular traces + enginetrace->TraceRay( ray, fMask, pTraceFilter, pTrace ); + } + else + { + + trace_t RealTrace; + enginetrace->TraceRay( ray, fMask, pTraceFilter, &RealTrace ); + + trace_t PortalTrace; + UTIL_Portal_TraceRay( pPortal, ray, fMask, pTraceFilter, &PortalTrace, bTraceHolyWall ); + + if( !g_bForcePortalTrace && !RealTrace.startsolid && PortalTrace.fraction <= RealTrace.fraction ) + { + *pTrace = RealTrace; + return; + } + + if ( g_bAllowForcePortalTrace ) + { + g_bForcePortalTrace = true; + } + + *pTrace = PortalTrace; + + // If this ray has a delta, make sure its towards the portal before we try to trace across portals + Vector vDirection = ray.m_Delta; + VectorNormalize( vDirection ); + Vector vPortalForward; + pPortal->GetVectors( &vPortalForward, 0, 0 ); + + float flDot = -1.0f; + if ( ray.m_IsSwept ) + { + flDot = vDirection.Dot( vPortalForward ); + } + + // TODO: Translate extents of rays properly, tracing extruded box rays across portals causes collision bugs + // Until this is fixed, we'll only test true rays across portals + if ( flDot < 0.0f && /*PortalTrace.fraction == 1.0f &&*/ ray.m_IsRay) + { + // Check if we're hitting stuff on the other side of the portal + trace_t PortalLinkedTrace; + UTIL_PortalLinked_TraceRay( pPortal, ray, fMask, pTraceFilter, &PortalLinkedTrace, bTraceHolyWall ); + + if ( PortalLinkedTrace.fraction < pTrace->fraction ) + { + // Only collide with the cross-portal objects if this trace crossed a portal + if ( UTIL_DidTraceTouchPortals( ray, PortalLinkedTrace ) ) + { + *pTrace = PortalLinkedTrace; + } + } + } + + if( pTrace->fraction < 1.0f ) + { + pTrace->contents = RealTrace.contents; + pTrace->surface = RealTrace.surface; + } + + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Tests if a ray touches the surface of any portals +// Input : ray - the ray to be tested against portal surfaces +// trace - a filled-in trace corresponding to the parameter ray +// Output : bool - false if the 'ray' parameter failed to hit any portal surface +// pOutLocal - the portal touched (if any) +// pOutRemote - the portal linked to the portal touched +//----------------------------------------------------------------------------- +bool UTIL_DidTraceTouchPortals( const Ray_t& ray, const trace_t& trace, CProp_Portal** pOutLocal, CProp_Portal** pOutRemote ) +{ + int iPortalCount = CProp_Portal_Shared::AllPortals.Count(); + if( iPortalCount == 0 ) + { + if( pOutLocal ) + *pOutLocal = NULL; + + if( pOutRemote ) + *pOutRemote = NULL; + + return false; + } + + CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base(); + CProp_Portal *pIntersectedPortal = NULL; + + if( ray.m_IsSwept ) + { + float fMustBeCloserThan = trace.fraction + 0.0001f; + + pIntersectedPortal = UTIL_Portal_FirstAlongRay( ray, fMustBeCloserThan ); + } + + if( (pIntersectedPortal == NULL) && !ray.m_IsRay ) + { + //haven't hit anything yet, try again with box tests + + Vector ptRayEndPoint = trace.endpos - ray.m_StartOffset; // The trace added the start offset to the end position, so remove it for the box test + CProp_Portal **pBoxIntersectsPortals = (CProp_Portal **)stackalloc( sizeof(CProp_Portal *) * iPortalCount ); + int iBoxIntersectsPortalsCount = 0; + + for( int i = 0; i != iPortalCount; ++i ) + { + CProp_Portal *pTempPortal = pPortals[i]; + if( (pTempPortal->m_bActivated) && + (pTempPortal->m_hLinkedPortal.Get() != NULL) ) + { + if( UTIL_IsBoxIntersectingPortal( ptRayEndPoint, ray.m_Extents, pTempPortal, 0.00f ) ) + { + pBoxIntersectsPortals[iBoxIntersectsPortalsCount] = pTempPortal; + ++iBoxIntersectsPortalsCount; + } + } + } + + if( iBoxIntersectsPortalsCount > 0 ) + { + pIntersectedPortal = pBoxIntersectsPortals[0]; + + if( iBoxIntersectsPortalsCount > 1 ) + { + //hit more than one, use the closest + float fDistToBeat = (ptRayEndPoint - pIntersectedPortal->GetAbsOrigin()).LengthSqr(); + + for( int i = 1; i != iBoxIntersectsPortalsCount; ++i ) + { + float fDist = (ptRayEndPoint - pBoxIntersectsPortals[i]->GetAbsOrigin()).LengthSqr(); + if( fDist < fDistToBeat ) + { + pIntersectedPortal = pBoxIntersectsPortals[i]; + fDistToBeat = fDist; + } + } + } + } + } + + if( pIntersectedPortal == NULL ) + { + if( pOutLocal ) + *pOutLocal = NULL; + + if( pOutRemote ) + *pOutRemote = NULL; + + return false; + } + else + { + // Record the touched portals and return + if( pOutLocal ) + *pOutLocal = pIntersectedPortal; + + if( pOutRemote ) + *pOutRemote = pIntersectedPortal->m_hLinkedPortal.Get(); + + return true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Redirects the trace to either a trace that uses portal environments, or if a +// global boolean is set, trace with a special bullets trace. +// NOTE: UTIL_Portal_TraceRay_With will use the default world trace if it gets a NULL portal pointer +// Input : &ray - the ray to use to trace +// fMask - collision mask +// *pTraceFilter - customizable filter on the trace +// *pTrace - trace struct to fill with output info +//----------------------------------------------------------------------------- +CProp_Portal* UTIL_Portal_TraceRay( const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace, bool bTraceHolyWall ) +{ + float fMustBeCloserThan = 2.0f; + CProp_Portal *pIntersectedPortal = UTIL_Portal_FirstAlongRay( ray, fMustBeCloserThan ); + + if ( g_bBulletPortalTrace ) + { + if ( UTIL_Portal_TraceRay_Bullets( pIntersectedPortal, ray, fMask, pTraceFilter, pTrace, bTraceHolyWall ) ) + return pIntersectedPortal; + + // Bullet didn't actually go through portal + return NULL; + + } + else + { + UTIL_Portal_TraceRay_With( pIntersectedPortal, ray, fMask, pTraceFilter, pTrace, bTraceHolyWall ); + return pIntersectedPortal; + } +} + +CProp_Portal* UTIL_Portal_TraceRay( const Ray_t &ray, unsigned int fMask, const IHandleEntity *ignore, int collisionGroup, trace_t *pTrace, bool bTraceHolyWall ) +{ + CTraceFilterSimple traceFilter( ignore, collisionGroup ); + return UTIL_Portal_TraceRay( ray, fMask, &traceFilter, pTrace, bTraceHolyWall ); +} + + +//----------------------------------------------------------------------------- +// Purpose: This version of traceray only traces against the portal environment of the specified portal. +// Input : *pPortal - the portal whose physics we will trace against +// &ray - the ray to trace with +// fMask - collision mask +// *pTraceFilter - customizable filter to determine what it hits +// *pTrace - the trace struct to fill in with results +// bTraceHolyWall - if this trace is to test against the 'holy wall' geometry +//----------------------------------------------------------------------------- +void UTIL_Portal_TraceRay( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace, bool bTraceHolyWall ) +{ +#ifdef CLIENT_DLL + Assert( (GameRules() == NULL) || GameRules()->IsMultiplayer() ); +#endif + Assert( pPortal->m_PortalSimulator.IsReadyToSimulate() ); //a trace shouldn't make it down this far if the portal is incapable of changing the results of the trace + + CTraceFilterHitAll traceFilterHitAll; + if ( !pTraceFilter ) + { + pTraceFilter = &traceFilterHitAll; + } + + pTrace->fraction = 2.0f; + pTrace->startsolid = true; + pTrace->allsolid = true; + + trace_t TempTrace; + int counter; + + const CPortalSimulator &portalSimulator = pPortal->m_PortalSimulator; + CPortalSimulator *pLinkedPortalSimulator = portalSimulator.GetLinkedPortalSimulator(); + + //bool bTraceDisplacements = sv_portal_trace_vs_displacements.GetBool(); + bool bTraceStaticProps = sv_portal_trace_vs_staticprops.GetBool(); + if( sv_portal_trace_vs_holywall.GetBool() == false ) + bTraceHolyWall = false; + + bool bTraceTransformedGeometry = ( (pLinkedPortalSimulator != NULL) && bTraceHolyWall && portalSimulator.RayIsInPortalHole( ray ) ); + + bool bCopyBackBrushTraceData = false; + + + + // Traces vs world + if( pTraceFilter->GetTraceType() != TRACE_ENTITIES_ONLY ) + { + //trace_t RealTrace; + //enginetrace->TraceRay( ray, fMask, pTraceFilter, &RealTrace ); + if( portalSimulator.m_DataAccess.Simulation.Static.World.Brushes.pCollideable && sv_portal_trace_vs_world.GetBool() ) + { + physcollision->TraceBox( ray, portalSimulator.m_DataAccess.Simulation.Static.World.Brushes.pCollideable, vec3_origin, vec3_angle, pTrace ); + bCopyBackBrushTraceData = true; + } + + if( bTraceHolyWall ) + { + if( portalSimulator.m_DataAccess.Simulation.Static.Wall.Local.Tube.pCollideable ) + { + physcollision->TraceBox( ray, portalSimulator.m_DataAccess.Simulation.Static.Wall.Local.Tube.pCollideable, vec3_origin, vec3_angle, &TempTrace ); + + if( (TempTrace.startsolid == false) && (TempTrace.fraction < pTrace->fraction) ) //never allow something to be stuck in the tube, it's more of a last-resort guide than a real collideable + { + *pTrace = TempTrace; + bCopyBackBrushTraceData = true; + } + } + + if( portalSimulator.m_DataAccess.Simulation.Static.Wall.Local.Brushes.pCollideable ) + { + physcollision->TraceBox( ray, portalSimulator.m_DataAccess.Simulation.Static.Wall.Local.Brushes.pCollideable, vec3_origin, vec3_angle, &TempTrace ); + if( (TempTrace.fraction < pTrace->fraction) ) + { + *pTrace = TempTrace; + bCopyBackBrushTraceData = true; + } + } + + //if( portalSimulator.m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pCollideable && sv_portal_trace_vs_world.GetBool() ) + if( bTraceTransformedGeometry && pLinkedPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable ) + { + physcollision->TraceBox( ray, pLinkedPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable, portalSimulator.m_DataAccess.Placement.ptaap_LinkedToThis.ptOriginTransform, portalSimulator.m_DataAccess.Placement.ptaap_LinkedToThis.qAngleTransform, &TempTrace ); + if( (TempTrace.fraction < pTrace->fraction) ) + { + *pTrace = TempTrace; + bCopyBackBrushTraceData = true; + } + } + } + + if( bCopyBackBrushTraceData ) + { + pTrace->surface = portalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.surface; + pTrace->contents = portalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.contents; + pTrace->m_pEnt = portalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.pEntity; + + bCopyBackBrushTraceData = false; + } + } + + // Traces vs entities + if( pTraceFilter->GetTraceType() != TRACE_WORLD_ONLY ) + { + bool bFilterStaticProps = (pTraceFilter->GetTraceType() == TRACE_EVERYTHING_FILTER_PROPS); + + //solid entities + CPortalCollideableEnumerator enumerator( pPortal ); + partition->EnumerateElementsAlongRay( PARTITION_ENGINE_SOLID_EDICTS | PARTITION_ENGINE_STATIC_PROPS, ray, false, &enumerator ); + for( counter = 0; counter != enumerator.m_iHandleCount; ++counter ) + { + if( staticpropmgr->IsStaticProp( enumerator.m_pHandles[counter] ) ) + { + //if( bFilterStaticProps && !pTraceFilter->ShouldHitEntity( enumerator.m_pHandles[counter], fMask ) ) + continue; //static props are handled separately, with clipped versions + } + else if ( !pTraceFilter->ShouldHitEntity( enumerator.m_pHandles[counter], fMask ) ) + { + continue; + } + + enginetrace->ClipRayToEntity( ray, fMask, enumerator.m_pHandles[counter], &TempTrace ); + if( (TempTrace.fraction < pTrace->fraction) ) + *pTrace = TempTrace; + } + + + + + if( bTraceStaticProps ) + { + //local clipped static props + { + int iLocalStaticCount = portalSimulator.m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); + if( iLocalStaticCount != 0 && portalSimulator.m_DataAccess.Simulation.Static.World.StaticProps.bCollisionExists ) + { + const PS_SD_Static_World_StaticProps_ClippedProp_t *pCurrentProp = portalSimulator.m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations.Base(); + const PS_SD_Static_World_StaticProps_ClippedProp_t *pStop = pCurrentProp + iLocalStaticCount; + Vector vTransform = vec3_origin; + QAngle qTransform = vec3_angle; + + do + { + if( (!bFilterStaticProps) || pTraceFilter->ShouldHitEntity( pCurrentProp->pSourceProp, fMask ) ) + { + physcollision->TraceBox( ray, pCurrentProp->pCollide, vTransform, qTransform, &TempTrace ); + if( (TempTrace.fraction < pTrace->fraction) ) + { + *pTrace = TempTrace; + pTrace->surface.flags = pCurrentProp->iTraceSurfaceFlags; + pTrace->surface.surfaceProps = pCurrentProp->iTraceSurfaceProps; + pTrace->surface.name = pCurrentProp->szTraceSurfaceName; + pTrace->contents = pCurrentProp->iTraceContents; + pTrace->m_pEnt = pCurrentProp->pTraceEntity; + } + } + + ++pCurrentProp; + } + while( pCurrentProp != pStop ); + } + } + + if( bTraceHolyWall ) + { + //remote clipped static props transformed into our wall space + if( bTraceTransformedGeometry && (pTraceFilter->GetTraceType() != TRACE_WORLD_ONLY) && sv_portal_trace_vs_staticprops.GetBool() ) + { + int iLocalStaticCount = pLinkedPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); + if( iLocalStaticCount != 0 ) + { + const PS_SD_Static_World_StaticProps_ClippedProp_t *pCurrentProp = pLinkedPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations.Base(); + const PS_SD_Static_World_StaticProps_ClippedProp_t *pStop = pCurrentProp + iLocalStaticCount; + Vector vTransform = portalSimulator.m_DataAccess.Placement.ptaap_LinkedToThis.ptOriginTransform; + QAngle qTransform = portalSimulator.m_DataAccess.Placement.ptaap_LinkedToThis.qAngleTransform; + + do + { + if( (!bFilterStaticProps) || pTraceFilter->ShouldHitEntity( pCurrentProp->pSourceProp, fMask ) ) + { + physcollision->TraceBox( ray, pCurrentProp->pCollide, vTransform, qTransform, &TempTrace ); + if( (TempTrace.fraction < pTrace->fraction) ) + { + *pTrace = TempTrace; + pTrace->surface.flags = pCurrentProp->iTraceSurfaceFlags; + pTrace->surface.surfaceProps = pCurrentProp->iTraceSurfaceProps; + pTrace->surface.name = pCurrentProp->szTraceSurfaceName; + pTrace->contents = pCurrentProp->iTraceContents; + pTrace->m_pEnt = pCurrentProp->pTraceEntity; + } + } + + ++pCurrentProp; + } + while( pCurrentProp != pStop ); + } + } + } + } + } + + if( pTrace->fraction > 1.0f ) //this should only happen if there was absolutely nothing to trace against + { + //AssertMsg( 0, "Nothing to trace against" ); + memset( pTrace, 0, sizeof( trace_t ) ); + pTrace->fraction = 1.0f; + pTrace->startpos = ray.m_Start - ray.m_StartOffset; + pTrace->endpos = pTrace->startpos + ray.m_Delta; + } + else if ( pTrace->fraction < 0 ) + { + // For all brush traces, use the 'portal backbrush' surface surface contents + // BUGBUG: Doing this is a great solution because brushes near a portal + // will have their contents and surface properties homogenized to the brush the portal ray hit. + pTrace->contents = portalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.contents; + pTrace->surface = portalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.surface; + pTrace->m_pEnt = portalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.pEntity; + } +} + +void UTIL_Portal_TraceRay( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, const IHandleEntity *ignore, int collisionGroup, trace_t *pTrace, bool bTraceHolyWall ) +{ + CTraceFilterSimple traceFilter( ignore, collisionGroup ); + UTIL_Portal_TraceRay( pPortal, ray, fMask, &traceFilter, pTrace, bTraceHolyWall ); +} + +//----------------------------------------------------------------------------- +// Purpose: Trace a ray 'past' a portal's surface, hitting objects in the linked portal's collision environment +// Input : *pPortal - The portal being traced 'through' +// &ray - The ray being traced +// fMask - trace mask to cull results +// *pTraceFilter - trace filter to cull results +// *pTrace - Empty trace to return the result (value will be overwritten) +//----------------------------------------------------------------------------- +void UTIL_PortalLinked_TraceRay( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace, bool bTraceHolyWall ) +{ +#ifdef CLIENT_DLL + Assert( (GameRules() == NULL) || GameRules()->IsMultiplayer() ); +#endif + // Transform the specified ray to the remote portal's space + Ray_t rayTransformed; + UTIL_Portal_RayTransform( pPortal->MatrixThisToLinked(), ray, rayTransformed ); + + AssertMsg ( ray.m_IsRay, "Ray with extents across portal tracing not implemented!" ); + + const CPortalSimulator &portalSimulator = pPortal->m_PortalSimulator; + CProp_Portal *pLinkedPortal = (CProp_Portal*)(pPortal->m_hLinkedPortal.Get()); + if( (pLinkedPortal == NULL) || (portalSimulator.RayIsInPortalHole( ray ) == false) ) + { + memset( pTrace, 0, sizeof(trace_t)); + pTrace->fraction = 1.0f; + pTrace->fractionleftsolid = 0; + + pTrace->contents = pPortal->m_PortalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.contents; + pTrace->surface = pPortal->m_PortalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.surface; + pTrace->m_pEnt = pPortal->m_PortalSimulator.m_DataAccess.Simulation.Static.SurfaceProperties.pEntity; + return; + } + UTIL_Portal_TraceRay( pLinkedPortal, rayTransformed, fMask, pTraceFilter, pTrace, bTraceHolyWall ); + + // Transform the ray's start, end and plane back into this portal's space, + // because we react to the collision as it is displayed, and the image is displayed with this local portal's orientation. + VMatrix matLinkedToThis = pLinkedPortal->MatrixThisToLinked(); + UTIL_Portal_PointTransform( matLinkedToThis, pTrace->startpos, pTrace->startpos ); + UTIL_Portal_PointTransform( matLinkedToThis, pTrace->endpos, pTrace->endpos ); + UTIL_Portal_PlaneTransform( matLinkedToThis, pTrace->plane, pTrace->plane ); +} + +void UTIL_PortalLinked_TraceRay( const CProp_Portal *pPortal, const Ray_t &ray, unsigned int fMask, const IHandleEntity *ignore, int collisionGroup, trace_t *pTrace, bool bTraceHolyWall ) +{ + CTraceFilterSimple traceFilter( ignore, collisionGroup ); + UTIL_PortalLinked_TraceRay( pPortal, ray, fMask, &traceFilter, pTrace, bTraceHolyWall ); +} + +//----------------------------------------------------------------------------- +// Purpose: A version of trace entity which detects portals and translates the trace through portals +//----------------------------------------------------------------------------- +void UTIL_Portal_TraceEntity( CBaseEntity *pEntity, const Vector &vecAbsStart, const Vector &vecAbsEnd, + unsigned int mask, ITraceFilter *pFilter, trace_t *pTrace ) +{ +#ifdef CLIENT_DLL + Assert( (GameRules() == NULL) || GameRules()->IsMultiplayer() ); + Assert( pEntity->IsPlayer() ); + + CPortalSimulator *pPortalSimulator = NULL; + if( pEntity->IsPlayer() ) + { + C_Prop_Portal *pPortal = ((C_Portal_Player *)pEntity)->m_hPortalEnvironment.Get(); + if( pPortal ) + pPortalSimulator = &pPortal->m_PortalSimulator; + } +#else + CPortalSimulator *pPortalSimulator = CPortalSimulator::GetSimulatorThatOwnsEntity( pEntity ); +#endif + + memset( pTrace, 0, sizeof(trace_t)); + pTrace->fraction = 1.0f; + pTrace->fractionleftsolid = 0; + + ICollideable* pCollision = enginetrace->GetCollideable( pEntity ); + + // If main is simulating this object, trace as UTIL_TraceEntity would + trace_t realTrace; + QAngle qCollisionAngles = pCollision->GetCollisionAngles(); + enginetrace->SweepCollideable( pCollision, vecAbsStart, vecAbsEnd, qCollisionAngles, mask, pFilter, &realTrace ); + + // For the below box test, we need to add the tolerance onto the extents, because the underlying + // box on plane side test doesn't use the parameter tolerance. + float flTolerance = 0.1f; + Vector vEntExtents = pEntity->WorldAlignSize() * 0.5 + Vector ( flTolerance, flTolerance, flTolerance ); + Vector vColCenter = realTrace.endpos + ( pEntity->WorldAlignMaxs() + pEntity->WorldAlignMins() ) * 0.5f; + + // If this entity is not simulated in a portal environment, trace as normal + if( pPortalSimulator == NULL ) + { + // If main is simulating this object, trace as UTIL_TraceEntity would + *pTrace = realTrace; + } + else + { + CPortalSimulator *pLinkedPortalSimulator = pPortalSimulator->GetLinkedPortalSimulator(); + + Ray_t entRay; + entRay.Init( vecAbsStart, vecAbsEnd, pCollision->OBBMins(), pCollision->OBBMaxs() ); + +#if 0 // this trace for brush ents made sense at one time, but it's 'overcolliding' during portal transitions (bugzilla#25) + if( realTrace.m_pEnt && (realTrace.m_pEnt->GetMoveType() != MOVETYPE_NONE) ) //started by hitting something moving which wouldn't be detected in the following traces + { + float fFirstPortalFraction = 2.0f; + CProp_Portal *pFirstPortal = UTIL_Portal_FirstAlongRay( entRay, fFirstPortalFraction ); + + if ( !pFirstPortal ) + *pTrace = realTrace; + else + { + Vector vFirstPortalForward; + pFirstPortal->GetVectors( &vFirstPortalForward, NULL, NULL ); + if ( vFirstPortalForward.Dot( realTrace.endpos - pFirstPortal->GetAbsOrigin() ) > 0.0f ) + *pTrace = realTrace; + } + } +#endif + + // We require both environments to be active in order to trace against them + Assert ( pCollision ); + if ( !pCollision ) + { + return; + } + + // World, displacements and holy wall are stored in separate collideables + // Traces against each and keep the closest intersection (if any) + trace_t tempTrace; + + // Hit the world + if ( pFilter->GetTraceType() != TRACE_ENTITIES_ONLY ) + { + if( pPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable && + sv_portal_trace_vs_world.GetBool() ) + { + //physcollision->TraceCollide( vecAbsStart, vecAbsEnd, pCollision, qCollisionAngles, + // pPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable, vec3_origin, vec3_angle, &tempTrace ); + + physcollision->TraceBox( entRay, MASK_ALL, NULL, pPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable, vec3_origin, vec3_angle, &tempTrace ); + + if ( tempTrace.startsolid || (tempTrace.fraction < pTrace->fraction) ) + { + *pTrace = tempTrace; + } + } + + //if( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.RemoteTransformedToLocal.Brushes.pCollideable && + if( pLinkedPortalSimulator && + pLinkedPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable && + sv_portal_trace_vs_world.GetBool() && + sv_portal_trace_vs_holywall.GetBool() ) + { + //physcollision->TraceCollide( vecAbsStart, vecAbsEnd, pCollision, qCollisionAngles, + // pLinkedPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable, pPortalSimulator->m_DataAccess.Placement.ptaap_LinkedToThis.ptOriginTransform, pPortalSimulator->m_DataAccess.Placement.ptaap_LinkedToThis.qAngleTransform, &tempTrace ); + + physcollision->TraceBox( entRay, MASK_ALL, NULL, pLinkedPortalSimulator->m_DataAccess.Simulation.Static.World.Brushes.pCollideable, pPortalSimulator->m_DataAccess.Placement.ptaap_LinkedToThis.ptOriginTransform, pPortalSimulator->m_DataAccess.Placement.ptaap_LinkedToThis.qAngleTransform, &tempTrace ); + + if ( tempTrace.startsolid || (tempTrace.fraction < pTrace->fraction) ) + { + *pTrace = tempTrace; + } + } + + if ( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pCollideable && + sv_portal_trace_vs_holywall.GetBool() ) + { + //physcollision->TraceCollide( vecAbsStart, vecAbsEnd, pCollision, qCollisionAngles, + // pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pCollideable, vec3_origin, vec3_angle, &tempTrace ); + + physcollision->TraceBox( entRay, MASK_ALL, NULL, pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Brushes.pCollideable, vec3_origin, vec3_angle, &tempTrace ); + + if ( tempTrace.startsolid || (tempTrace.fraction < pTrace->fraction) ) + { + if( tempTrace.fraction == 0.0f ) + tempTrace.startsolid = true; + + if( tempTrace.fractionleftsolid == 1.0f ) + tempTrace.allsolid = true; + + *pTrace = tempTrace; + } + } + + if ( pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Tube.pCollideable && + sv_portal_trace_vs_holywall.GetBool() ) + { + //physcollision->TraceCollide( vecAbsStart, vecAbsEnd, pCollision, qCollisionAngles, + // pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Tube.pCollideable, vec3_origin, vec3_angle, &tempTrace ); + + physcollision->TraceBox( entRay, MASK_ALL, NULL, pPortalSimulator->m_DataAccess.Simulation.Static.Wall.Local.Tube.pCollideable, vec3_origin, vec3_angle, &tempTrace ); + + if( (tempTrace.startsolid == false) && (tempTrace.fraction < pTrace->fraction) ) //never allow something to be stuck in the tube, it's more of a last-resort guide than a real collideable + { + *pTrace = tempTrace; + } + } + + // For all brush traces, use the 'portal backbrush' surface surface contents + // BUGBUG: Doing this is a great solution because brushes near a portal + // will have their contents and surface properties homogenized to the brush the portal ray hit. + if ( pTrace->startsolid || (pTrace->fraction < 1.0f) ) + { + pTrace->surface = pPortalSimulator->m_DataAccess.Simulation.Static.SurfaceProperties.surface; + pTrace->contents = pPortalSimulator->m_DataAccess.Simulation.Static.SurfaceProperties.contents; + pTrace->m_pEnt = pPortalSimulator->m_DataAccess.Simulation.Static.SurfaceProperties.pEntity; + } + } + + // Trace vs entities + if ( pFilter->GetTraceType() != TRACE_WORLD_ONLY ) + { + if( sv_portal_trace_vs_staticprops.GetBool() && (pFilter->GetTraceType() != TRACE_ENTITIES_ONLY) ) + { + bool bFilterStaticProps = (pFilter->GetTraceType() == TRACE_EVERYTHING_FILTER_PROPS); + + //local clipped static props + { + int iLocalStaticCount = pPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations.Count(); + if( iLocalStaticCount != 0 && pPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.bCollisionExists ) + { + const PS_SD_Static_World_StaticProps_ClippedProp_t *pCurrentProp = pPortalSimulator->m_DataAccess.Simulation.Static.World.StaticProps.ClippedRepresentations.Base(); + const PS_SD_Static_World_StaticProps_ClippedProp_t *pStop = pCurrentProp + iLocalStaticCount; + Vector vTransform = vec3_origin; + QAngle qTransform = vec3_angle; + + do + { + if( (!bFilterStaticProps) || pFilter->ShouldHitEntity( pCurrentProp->pSourceProp, mask ) ) + { + //physcollision->TraceCollide( vecAbsStart, vecAbsEnd, pCollision, qCollisionAngles, + // pCurrentProp->pCollide, vTransform, qTransform, &tempTrace ); + + physcollision->TraceBox( entRay, MASK_ALL, NULL, pCurrentProp->pCollide, vTransform, qTransform, &tempTrace ); + + if( tempTrace.startsolid || (tempTrace.fraction < pTrace->fraction) ) + { + *pTrace = tempTrace; + pTrace->surface.flags = pCurrentProp->iTraceSurfaceFlags; + pTrace->surface.surfaceProps = pCurrentProp->iTraceSurfaceProps; + pTrace->surface.name = pCurrentProp->szTraceSurfaceName; + pTrace->contents = pCurrentProp->iTraceContents; + pTrace->m_pEnt = pCurrentProp->pTraceEntity; + } + } + + ++pCurrentProp; + } + while( pCurrentProp != pStop ); + } + } + + if( pLinkedPortalSimulator && pPortalSimulator->EntityIsInPortalHole( pEntity ) ) + { + +#ifndef CLIENT_DLL + if( sv_use_transformed_collideables.GetBool() ) //if this never gets turned off, it should be removed before release + { + //moving entities near the remote portal + CBaseEntity *pEnts[1024]; + int iEntCount = pLinkedPortalSimulator->GetMoveableOwnedEntities( pEnts, 1024 ); + + CTransformedCollideable transformedCollideable; + transformedCollideable.m_matTransform = pLinkedPortalSimulator->m_DataAccess.Placement.matThisToLinked; + transformedCollideable.m_matInvTransform = pLinkedPortalSimulator->m_DataAccess.Placement.matLinkedToThis; + for( int i = 0; i != iEntCount; ++i ) + { + CBaseEntity *pRemoteEntity = pEnts[i]; + if( pRemoteEntity->GetSolid() == SOLID_NONE ) + continue; + + transformedCollideable.m_pWrappedCollideable = pRemoteEntity->GetCollideable(); + Assert( transformedCollideable.m_pWrappedCollideable != NULL ); + + //enginetrace->ClipRayToCollideable( entRay, mask, &transformedCollideable, pTrace ); + + enginetrace->ClipRayToCollideable( entRay, mask, &transformedCollideable, &tempTrace ); + if( tempTrace.startsolid || (tempTrace.fraction < pTrace->fraction) ) + { + *pTrace = tempTrace; + } + } + } +#endif //#ifndef CLIENT_DLL + } + } + } + + if( pTrace->fraction == 1.0f ) + { + memset( pTrace, 0, sizeof( trace_t ) ); + pTrace->fraction = 1.0f; + pTrace->startpos = vecAbsStart; + pTrace->endpos = vecAbsEnd; + } +//#endif + } +} + +void UTIL_Portal_PointTransform( const VMatrix matThisToLinked, const Vector &ptSource, Vector &ptTransformed ) +{ + ptTransformed = matThisToLinked * ptSource; +} + +void UTIL_Portal_VectorTransform( const VMatrix matThisToLinked, const Vector &vSource, Vector &vTransformed ) +{ + vTransformed = matThisToLinked.ApplyRotation( vSource ); +} + +void UTIL_Portal_AngleTransform( const VMatrix matThisToLinked, const QAngle &qSource, QAngle &qTransformed ) +{ + qTransformed = TransformAnglesToWorldSpace( qSource, matThisToLinked.As3x4() ); +} + +void UTIL_Portal_RayTransform( const VMatrix matThisToLinked, const Ray_t &raySource, Ray_t &rayTransformed ) +{ + rayTransformed = raySource; + + UTIL_Portal_PointTransform( matThisToLinked, raySource.m_Start, rayTransformed.m_Start ); + UTIL_Portal_VectorTransform( matThisToLinked, raySource.m_StartOffset, rayTransformed.m_StartOffset ); + UTIL_Portal_VectorTransform( matThisToLinked, raySource.m_Delta, rayTransformed.m_Delta ); + + //BUGBUG: Extents are axis aligned, so rotating it won't necessarily give us what we're expecting + UTIL_Portal_VectorTransform( matThisToLinked, raySource.m_Extents, rayTransformed.m_Extents ); + + //HACKHACK: Negative extents hang in traces, make each positive because we rotated it above + if ( rayTransformed.m_Extents.x < 0.0f ) + { + rayTransformed.m_Extents.x = -rayTransformed.m_Extents.x; + } + if ( rayTransformed.m_Extents.y < 0.0f ) + { + rayTransformed.m_Extents.y = -rayTransformed.m_Extents.y; + } + if ( rayTransformed.m_Extents.z < 0.0f ) + { + rayTransformed.m_Extents.z = -rayTransformed.m_Extents.z; + } + +} + +void UTIL_Portal_PlaneTransform( const VMatrix matThisToLinked, const cplane_t &planeSource, cplane_t &planeTransformed ) +{ + planeTransformed = planeSource; + + Vector vTrans; + UTIL_Portal_VectorTransform( matThisToLinked, planeSource.normal, planeTransformed.normal ); + planeTransformed.dist = planeSource.dist * DotProduct( planeTransformed.normal, planeTransformed.normal ); + planeTransformed.dist += DotProduct( planeTransformed.normal, matThisToLinked.GetTranslation( vTrans ) ); +} + +void UTIL_Portal_PlaneTransform( const VMatrix matThisToLinked, const VPlane &planeSource, VPlane &planeTransformed ) +{ + Vector vTranformedNormal; + float fTransformedDist; + + Vector vTrans; + UTIL_Portal_VectorTransform( matThisToLinked, planeSource.m_Normal, vTranformedNormal ); + fTransformedDist = planeSource.m_Dist * DotProduct( vTranformedNormal, vTranformedNormal ); + fTransformedDist += DotProduct( vTranformedNormal, matThisToLinked.GetTranslation( vTrans ) ); + + planeTransformed.Init( vTranformedNormal, fTransformedDist ); +} + +void UTIL_Portal_Triangles( const Vector &ptPortalCenter, const QAngle &qPortalAngles, Vector pvTri1[ 3 ], Vector pvTri2[ 3 ] ) +{ + // Get points to make triangles + Vector vRight, vUp; + AngleVectors( qPortalAngles, NULL, &vRight, &vUp ); + + Vector vTopEdge = vUp * PORTAL_HALF_HEIGHT; + Vector vBottomEdge = -vTopEdge; + Vector vRightEdge = vRight * PORTAL_HALF_WIDTH; + Vector vLeftEdge = -vRightEdge; + + Vector vTopLeft = ptPortalCenter + vTopEdge + vLeftEdge; + Vector vTopRight = ptPortalCenter + vTopEdge + vRightEdge; + Vector vBottomLeft = ptPortalCenter + vBottomEdge + vLeftEdge; + Vector vBottomRight = ptPortalCenter + vBottomEdge + vRightEdge; + + // Make triangles + pvTri1[ 0 ] = vTopRight; + pvTri1[ 1 ] = vTopLeft; + pvTri1[ 2 ] = vBottomLeft; + + pvTri2[ 0 ] = vTopRight; + pvTri2[ 1 ] = vBottomLeft; + pvTri2[ 2 ] = vBottomRight; +} + +void UTIL_Portal_Triangles( const CProp_Portal *pPortal, Vector pvTri1[ 3 ], Vector pvTri2[ 3 ] ) +{ + UTIL_Portal_Triangles( pPortal->GetAbsOrigin(), pPortal->GetAbsAngles(), pvTri1, pvTri2 ); +} + +float UTIL_Portal_DistanceThroughPortal( const CProp_Portal *pPortal, const Vector &vPoint1, const Vector &vPoint2 ) +{ + return FastSqrt( UTIL_Portal_DistanceThroughPortalSqr( pPortal, vPoint1, vPoint2 ) ); +} + +float UTIL_Portal_DistanceThroughPortalSqr( const CProp_Portal *pPortal, const Vector &vPoint1, const Vector &vPoint2 ) +{ + if ( !pPortal || !pPortal->m_bActivated ) + return -1.0f; + + CProp_Portal *pPortalLinked = pPortal->m_hLinkedPortal; + if ( !pPortalLinked || !pPortalLinked->m_bActivated ) + return -1.0f; + + return vPoint1.DistToSqr( pPortal->GetAbsOrigin() ) + pPortalLinked->GetAbsOrigin().DistToSqr( vPoint2 ); +} + +float UTIL_Portal_ShortestDistance( const Vector &vPoint1, const Vector &vPoint2, CProp_Portal **pShortestDistPortal_Out /*= NULL*/, bool bRequireStraightLine /*= false*/ ) +{ + return FastSqrt( UTIL_Portal_ShortestDistanceSqr( vPoint1, vPoint2, pShortestDistPortal_Out, bRequireStraightLine ) ); +} + +float UTIL_Portal_ShortestDistanceSqr( const Vector &vPoint1, const Vector &vPoint2, CProp_Portal **pShortestDistPortal_Out /*= NULL*/, bool bRequireStraightLine /*= false*/ ) +{ + float fMinDist = vPoint1.DistToSqr( vPoint2 ); + + int iPortalCount = CProp_Portal_Shared::AllPortals.Count(); + if( iPortalCount == 0 ) + { + if( pShortestDistPortal_Out ) + *pShortestDistPortal_Out = NULL; + + return fMinDist; + } + CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base(); + CProp_Portal *pShortestDistPortal = NULL; + + for( int i = 0; i != iPortalCount; ++i ) + { + CProp_Portal *pTempPortal = pPortals[i]; + if( pTempPortal->m_bActivated ) + { + CProp_Portal *pLinkedPortal = pTempPortal->m_hLinkedPortal.Get(); + if( pLinkedPortal != NULL ) + { + Vector vPoint1Transformed = pTempPortal->MatrixThisToLinked() * vPoint1; + + float fDirectDist = vPoint1Transformed.DistToSqr( vPoint2 ); + if( fDirectDist < fMinDist ) + { + //worth investigating further + //find out if it's a straight line through the portal, or if we have to wrap around a corner + float fPoint1TransformedDist = pLinkedPortal->m_plane_Origin.normal.Dot( vPoint1Transformed ) - pLinkedPortal->m_plane_Origin.dist; + float fPoint2Dist = pLinkedPortal->m_plane_Origin.normal.Dot( vPoint2 ) - pLinkedPortal->m_plane_Origin.dist; + + bool bStraightLine = true; + if( (fPoint1TransformedDist > 0.0f) || (fPoint2Dist < 0.0f) ) //straight line through portal impossible, part of the line has to backtrack to get to the portal surface + bStraightLine = false; + + if( bStraightLine ) //if we're not already doing some crazy wrapping, find an intersection point + { + float fTotalDist = fPoint2Dist - fPoint1TransformedDist; //fPoint1TransformedDist is known to be negative + Vector ptPlaneIntersection; + + if( fTotalDist != 0.0f ) + { + float fInvTotalDist = 1.0f / fTotalDist; + ptPlaneIntersection = (vPoint1Transformed * (fPoint2Dist * fInvTotalDist)) + (vPoint2 * ((-fPoint1TransformedDist) * fInvTotalDist)); + } + else + { + ptPlaneIntersection = vPoint1Transformed; + } + + Vector vRight, vUp; + pLinkedPortal->GetVectors( NULL, &vRight, &vUp ); + + Vector ptLinkedCenter = pLinkedPortal->GetAbsOrigin(); + Vector vCenterToIntersection = ptPlaneIntersection - ptLinkedCenter; + float fRight = vRight.Dot( vCenterToIntersection ); + float fUp = vUp.Dot( vCenterToIntersection ); + + float fAbsRight = fabs( fRight ); + float fAbsUp = fabs( fUp ); + if( (fAbsRight > PORTAL_HALF_WIDTH) || + (fAbsUp > PORTAL_HALF_HEIGHT) ) + bStraightLine = false; + + if( bStraightLine == false ) + { + if( bRequireStraightLine ) + continue; + + //find the offending extent and shorten both extents to bring it into the portal quad + float fNormalizer; + if( fAbsRight > PORTAL_HALF_WIDTH ) + { + fNormalizer = fAbsRight/PORTAL_HALF_WIDTH; + + if( fAbsUp > PORTAL_HALF_HEIGHT ) + { + float fUpNormalizer = fAbsUp/PORTAL_HALF_HEIGHT; + if( fUpNormalizer > fNormalizer ) + fNormalizer = fUpNormalizer; + } + } + else + { + fNormalizer = fAbsUp/PORTAL_HALF_HEIGHT; + } + + vCenterToIntersection *= (1.0f/fNormalizer); + ptPlaneIntersection = ptLinkedCenter + vCenterToIntersection; + + float fWrapDist = vPoint1Transformed.DistToSqr( ptPlaneIntersection ) + vPoint2.DistToSqr( ptPlaneIntersection ); + if( fWrapDist < fMinDist ) + { + fMinDist = fWrapDist; + pShortestDistPortal = pTempPortal; + } + } + else + { + //it's a straight shot from point 1 to 2 through the portal + fMinDist = fDirectDist; + pShortestDistPortal = pTempPortal; + } + } + else + { + if( bRequireStraightLine ) + continue; + + //do some crazy wrapped line intersection algorithm + + //for now, just do the cheap and easy solution + float fWrapDist = vPoint1.DistToSqr( pTempPortal->GetAbsOrigin() ) + pLinkedPortal->GetAbsOrigin().DistToSqr( vPoint2 ); + if( fWrapDist < fMinDist ) + { + fMinDist = fWrapDist; + pShortestDistPortal = pTempPortal; + } + } + } + } + } + } + + return fMinDist; +} + +void UTIL_Portal_AABB( const CProp_Portal *pPortal, Vector &vMin, Vector &vMax ) +{ + Vector vOrigin = pPortal->GetAbsOrigin(); + QAngle qAngles = pPortal->GetAbsAngles(); + + Vector vOBBForward; + Vector vOBBRight; + Vector vOBBUp; + + AngleVectors( qAngles, &vOBBForward, &vOBBRight, &vOBBUp ); + + //scale the extents to usable sizes + vOBBForward *= PORTAL_HALF_DEPTH; + vOBBRight *= PORTAL_HALF_WIDTH; + vOBBUp *= PORTAL_HALF_HEIGHT; + + vOrigin -= vOBBForward + vOBBRight + vOBBUp; + + vOBBForward *= 2.0f; + vOBBRight *= 2.0f; + vOBBUp *= 2.0f; + + vMin = vMax = vOrigin; + + for( int i = 1; i != 8; ++i ) + { + Vector ptTest = vOrigin; + if( i & (1 << 0) ) ptTest += vOBBForward; + if( i & (1 << 1) ) ptTest += vOBBRight; + if( i & (1 << 2) ) ptTest += vOBBUp; + + if( ptTest.x < vMin.x ) vMin.x = ptTest.x; + if( ptTest.y < vMin.y ) vMin.y = ptTest.y; + if( ptTest.z < vMin.z ) vMin.z = ptTest.z; + if( ptTest.x > vMax.x ) vMax.x = ptTest.x; + if( ptTest.y > vMax.y ) vMax.y = ptTest.y; + if( ptTest.z > vMax.z ) vMax.z = ptTest.z; + } +} + +float UTIL_IntersectRayWithPortal( const Ray_t &ray, const CProp_Portal *pPortal ) +{ + if ( !pPortal || !pPortal->m_bActivated ) + { + return -1.0f; + } + + Vector vForward; + pPortal->GetVectors( &vForward, NULL, NULL ); + + // Discount rays not coming from the front of the portal + float fDot = DotProduct( vForward, ray.m_Delta ); + if ( fDot > 0.0f ) + { + return -1.0f; + } + + Vector pvTri1[ 3 ], pvTri2[ 3 ]; + + UTIL_Portal_Triangles( pPortal, pvTri1, pvTri2 ); + + float fT; + + // Test triangle 1 + fT = IntersectRayWithTriangle( ray, pvTri1[ 0 ], pvTri1[ 1 ], pvTri1[ 2 ], false ); + + // If there was an intersection return the T + if ( fT >= 0.0f ) + return fT; + + // Return the result of collision with the other face triangle + return IntersectRayWithTriangle( ray, pvTri2[ 0 ], pvTri2[ 1 ], pvTri2[ 2 ], false ); +} + +bool UTIL_IntersectRayWithPortalOBB( const CProp_Portal *pPortal, const Ray_t &ray, trace_t *pTrace ) +{ + return IntersectRayWithOBB( ray, pPortal->GetAbsOrigin(), pPortal->GetAbsAngles(), CProp_Portal_Shared::vLocalMins, CProp_Portal_Shared::vLocalMaxs, 0.0f, pTrace ); +} + +bool UTIL_IntersectRayWithPortalOBBAsAABB( const CProp_Portal *pPortal, const Ray_t &ray, trace_t *pTrace ) +{ + Vector vAABBMins, vAABBMaxs; + + UTIL_Portal_AABB( pPortal, vAABBMins, vAABBMaxs ); + + return IntersectRayWithBox( ray, vAABBMins, vAABBMaxs, 0.0f, pTrace ); +} + +bool UTIL_IsBoxIntersectingPortal( const Vector &vecBoxCenter, const Vector &vecBoxExtents, const Vector &ptPortalCenter, const QAngle &qPortalAngles, float flTolerance ) +{ + Vector pvTri1[ 3 ], pvTri2[ 3 ]; + + UTIL_Portal_Triangles( ptPortalCenter, qPortalAngles, pvTri1, pvTri2 ); + + cplane_t plane; + + ComputeTrianglePlane( pvTri1[ 0 ], pvTri1[ 1 ], pvTri1[ 2 ], plane.normal, plane.dist ); + plane.type = PLANE_ANYZ; + plane.signbits = SignbitsForPlane( &plane ); + + if ( IsBoxIntersectingTriangle( vecBoxCenter, vecBoxExtents, pvTri1[ 0 ], pvTri1[ 1 ], pvTri1[ 2 ], plane, flTolerance ) ) + { + return true; + } + + ComputeTrianglePlane( pvTri2[ 0 ], pvTri2[ 1 ], pvTri2[ 2 ], plane.normal, plane.dist ); + plane.type = PLANE_ANYZ; + plane.signbits = SignbitsForPlane( &plane ); + + return IsBoxIntersectingTriangle( vecBoxCenter, vecBoxExtents, pvTri2[ 0 ], pvTri2[ 1 ], pvTri2[ 2 ], plane, flTolerance ); +} + +bool UTIL_IsBoxIntersectingPortal( const Vector &vecBoxCenter, const Vector &vecBoxExtents, const CProp_Portal *pPortal, float flTolerance ) +{ + if( pPortal == NULL ) + return false; + + return UTIL_IsBoxIntersectingPortal( vecBoxCenter, vecBoxExtents, pPortal->GetAbsOrigin(), pPortal->GetAbsAngles(), flTolerance ); +} + +CProp_Portal *UTIL_IntersectEntityExtentsWithPortal( const CBaseEntity *pEntity ) +{ + int iPortalCount = CProp_Portal_Shared::AllPortals.Count(); + if( iPortalCount == 0 ) + return NULL; + + Vector vMin, vMax; + pEntity->CollisionProp()->WorldSpaceAABB( &vMin, &vMax ); + Vector ptCenter = ( vMin + vMax ) * 0.5f; + Vector vExtents = ( vMax - vMin ) * 0.5f; + + CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base(); + for( int i = 0; i != iPortalCount; ++i ) + { + CProp_Portal *pTempPortal = pPortals[i]; + if( pTempPortal->m_bActivated && + (pTempPortal->m_hLinkedPortal.Get() != NULL) && + UTIL_IsBoxIntersectingPortal( ptCenter, vExtents, pTempPortal ) ) + { + return pPortals[i]; + } + } + + return NULL; +} + +void UTIL_Portal_NDebugOverlay( const Vector &ptPortalCenter, const QAngle &qPortalAngles, int r, int g, int b, int a, bool noDepthTest, float duration ) +{ +#ifndef CLIENT_DLL + Vector pvTri1[ 3 ], pvTri2[ 3 ]; + + UTIL_Portal_Triangles( ptPortalCenter, qPortalAngles, pvTri1, pvTri2 ); + + NDebugOverlay::Triangle( pvTri1[ 0 ], pvTri1[ 1 ], pvTri1[ 2 ], r, g, b, a, noDepthTest, duration ); + NDebugOverlay::Triangle( pvTri2[ 0 ], pvTri2[ 1 ], pvTri2[ 2 ], r, g, b, a, noDepthTest, duration ); +#endif //#ifndef CLIENT_DLL +} + +void UTIL_Portal_NDebugOverlay( const CProp_Portal *pPortal, int r, int g, int b, int a, bool noDepthTest, float duration ) +{ +#ifndef CLIENT_DLL + UTIL_Portal_NDebugOverlay( pPortal->GetAbsOrigin(), pPortal->GetAbsAngles(), r, g, b, a, noDepthTest, duration ); +#endif //#ifndef CLIENT_DLL +} + + +bool FindClosestPassableSpace( CBaseEntity *pEntity, const Vector &vIndecisivePush, unsigned int fMask ) //assumes the object is already in a mostly passable space +{ + if ( sv_use_find_closest_passable_space.GetBool() == false ) + return true; + + // Don't ever do this to entities with a move parent + if ( pEntity->GetMoveParent() ) + return true; + +#ifndef CLIENT_DLL + ADD_DEBUG_HISTORY( HISTORY_PLAYER_DAMAGE, UTIL_VarArgs( "RUNNING FIND CLOSEST PASSABLE SPACE on %s..\n", pEntity->GetDebugName() ) ); +#endif + + Vector ptExtents[8]; //ordering is going to be like 3 bits, where 0 is a min on the related axis, and 1 is a max on the same axis, axis order x y z + + float fExtentsValidation[8]; //some points are more valid than others, and this is our measure + + + Vector vEntityMaxs;// = pEntity->WorldAlignMaxs(); + Vector vEntityMins;// = pEntity->WorldAlignMins(); + CCollisionProperty *pEntityCollision = pEntity->CollisionProp(); + pEntityCollision->WorldSpaceAABB( &vEntityMins, &vEntityMaxs ); + + + + Vector ptEntityCenter = ((vEntityMins + vEntityMaxs) / 2.0f); + vEntityMins -= ptEntityCenter; + vEntityMaxs -= ptEntityCenter; + + Vector ptEntityOriginalCenter = ptEntityCenter; + + ptEntityCenter.z += 0.001f; //to satisfy m_IsSwept on first pass + + int iEntityCollisionGroup = pEntity->GetCollisionGroup(); + + trace_t traces[2]; + Ray_t entRay; + //entRay.Init( ptEntityCenter, ptEntityCenter, vEntityMins, vEntityMaxs ); + entRay.m_Extents = vEntityMaxs; + entRay.m_IsRay = false; + entRay.m_IsSwept = true; + entRay.m_StartOffset = vec3_origin; + + Vector vOriginalExtents = vEntityMaxs; + + Vector vGrowSize = vEntityMaxs / 101.0f; + vEntityMaxs -= vGrowSize; + vEntityMins += vGrowSize; + + + Ray_t testRay; + testRay.m_Extents = vGrowSize; + testRay.m_IsRay = false; + testRay.m_IsSwept = true; + testRay.m_StartOffset = vec3_origin; + + + + unsigned int iFailCount; + for( iFailCount = 0; iFailCount != 100; ++iFailCount ) + { + entRay.m_Start = ptEntityCenter; + entRay.m_Delta = ptEntityOriginalCenter - ptEntityCenter; + + UTIL_TraceRay( entRay, fMask, pEntity, iEntityCollisionGroup, &traces[0] ); + if( traces[0].startsolid == false ) + { + Vector vNewPos = traces[0].endpos + (pEntity->GetAbsOrigin() - ptEntityOriginalCenter); +#ifdef CLIENT_DLL + pEntity->SetAbsOrigin( vNewPos ); +#else + pEntity->Teleport( &vNewPos, NULL, NULL ); +#endif + return true; //current placement worked + } + + bool bExtentInvalid[8]; + for( int i = 0; i != 8; ++i ) + { + fExtentsValidation[i] = 0.0f; + ptExtents[i] = ptEntityCenter; + ptExtents[i].x += ((i & (1<<0)) ? vEntityMaxs.x : vEntityMins.x); + ptExtents[i].y += ((i & (1<<1)) ? vEntityMaxs.y : vEntityMins.y); + ptExtents[i].z += ((i & (1<<2)) ? vEntityMaxs.z : vEntityMins.z); + + bExtentInvalid[i] = enginetrace->PointOutsideWorld( ptExtents[i] ); + } + + unsigned int counter, counter2; + for( counter = 0; counter != 7; ++counter ) + { + for( counter2 = counter + 1; counter2 != 8; ++counter2 ) + { + + testRay.m_Delta = ptExtents[counter2] - ptExtents[counter]; + + if( bExtentInvalid[counter] ) + traces[0].startsolid = true; + else + { + testRay.m_Start = ptExtents[counter]; + UTIL_TraceRay( testRay, fMask, pEntity, iEntityCollisionGroup, &traces[0] ); + } + + if( bExtentInvalid[counter2] ) + traces[1].startsolid = true; + else + { + testRay.m_Start = ptExtents[counter2]; + testRay.m_Delta = -testRay.m_Delta; + UTIL_TraceRay( testRay, fMask, pEntity, iEntityCollisionGroup, &traces[1] ); + } + + float fDistance = testRay.m_Delta.Length(); + + for( int i = 0; i != 2; ++i ) + { + int iExtent = (i==0)?(counter):(counter2); + + if( traces[i].startsolid ) + { + fExtentsValidation[iExtent] -= 100.0f; + } + else + { + fExtentsValidation[iExtent] += traces[i].fraction * fDistance; + } + } + } + } + + Vector vNewOriginDirection( 0.0f, 0.0f, 0.0f ); + float fTotalValidation = 0.0f; + for( counter = 0; counter != 8; ++counter ) + { + if( fExtentsValidation[counter] > 0.0f ) + { + vNewOriginDirection += (ptExtents[counter] - ptEntityCenter) * fExtentsValidation[counter]; + fTotalValidation += fExtentsValidation[counter]; + } + } + + if( fTotalValidation != 0.0f ) + { + ptEntityCenter += (vNewOriginDirection / fTotalValidation); + + //increase sizing + testRay.m_Extents += vGrowSize; + vEntityMaxs -= vGrowSize; + vEntityMins = -vEntityMaxs; + } + else + { + //no point was valid, apply the indecisive vector + ptEntityCenter += vIndecisivePush; + + //reset sizing + testRay.m_Extents = vGrowSize; + vEntityMaxs = vOriginalExtents; + vEntityMins = -vEntityMaxs; + } + } + + // X360TBD: Hits in portal devtest + AssertMsg( IsX360() || iFailCount != 100, "FindClosestPassableSpace() failure." ); + return false; +} + +bool UTIL_Portal_EntityIsInPortalHole( const CProp_Portal *pPortal, CBaseEntity *pEntity ) +{ + CCollisionProperty *pCollisionProp = pEntity->CollisionProp(); + Vector vMins = pCollisionProp->OBBMins(); + Vector vMaxs = pCollisionProp->OBBMaxs(); + Vector vForward, vUp, vRight; + AngleVectors( pCollisionProp->GetCollisionAngles(), &vForward, &vRight, &vUp ); + Vector ptOrigin = pEntity->GetAbsOrigin(); + + Vector ptOBBCenter = pEntity->GetAbsOrigin() + (vMins + vMaxs * 0.5f); + Vector vExtents = (vMaxs - vMins) * 0.5f; + + vForward *= vExtents.x; + vRight *= vExtents.y; + vUp *= vExtents.z; + + Vector vPortalForward, vPortalRight, vPortalUp; + pPortal->GetVectors( &vPortalForward, &vPortalRight, &vPortalUp ); + Vector ptPortalCenter = pPortal->GetAbsOrigin(); + + return OBBHasFullyContainedIntersectionWithQuad( vForward, vRight, vUp, ptOBBCenter, + vPortalForward, vPortalForward.Dot( ptPortalCenter ), ptPortalCenter, + vPortalRight, PORTAL_HALF_WIDTH + 1.0f, vPortalUp, PORTAL_HALF_HEIGHT + 1.0f ); +} + + +#ifdef CLIENT_DLL +void UTIL_TransformInterpolatedAngle( CInterpolatedVar< QAngle > &qInterped, matrix3x4_t matTransform, bool bSkipNewest ) +{ + int iHead = qInterped.GetHead(); + if( !qInterped.IsValidIndex( iHead ) ) + return; + +#ifdef DBGFLAG_ASSERT + float fHeadTime; + qInterped.GetHistoryValue( iHead, fHeadTime ); +#endif + + float fTime; + QAngle *pCurrent; + int iCurrent; + + if( bSkipNewest ) + iCurrent = qInterped.GetNext( iHead ); + else + iCurrent = iHead; + + while( (pCurrent = qInterped.GetHistoryValue( iCurrent, fTime )) != NULL ) + { + Assert( (fTime <= fHeadTime) || (iCurrent == iHead) ); //asserting that head is always newest + + if( fTime < gpGlobals->curtime ) + *pCurrent = TransformAnglesToWorldSpace( *pCurrent, matTransform ); + + iCurrent = qInterped.GetNext( iCurrent ); + if( iCurrent == iHead ) + break; + } + + qInterped.Interpolate( gpGlobals->curtime ); +} + +void UTIL_TransformInterpolatedPosition( CInterpolatedVar< Vector > &vInterped, VMatrix matTransform, bool bSkipNewest ) +{ + int iHead = vInterped.GetHead(); + if( !vInterped.IsValidIndex( iHead ) ) + return; + +#ifdef DBGFLAG_ASSERT + float fHeadTime; + vInterped.GetHistoryValue( iHead, fHeadTime ); +#endif + + float fTime; + Vector *pCurrent; + int iCurrent; + + if( bSkipNewest ) + iCurrent = vInterped.GetNext( iHead ); + else + iCurrent = iHead; + + while( (pCurrent = vInterped.GetHistoryValue( iCurrent, fTime )) != NULL ) + { + Assert( (fTime <= fHeadTime) || (iCurrent == iHead) ); + + if( fTime < gpGlobals->curtime ) + *pCurrent = matTransform * (*pCurrent); + + iCurrent = vInterped.GetNext( iCurrent ); + if( iCurrent == iHead ) + break; + } + + vInterped.Interpolate( gpGlobals->curtime ); +} +#endif + + +#ifndef CLIENT_DLL + +void CC_Debug_FixMyPosition( void ) +{ + CBaseEntity *pPlayer = UTIL_GetCommandClient(); + + FindClosestPassableSpace( pPlayer, vec3_origin ); +} + +static ConCommand debug_fixmyposition("debug_fixmyposition", CC_Debug_FixMyPosition, "Runs FindsClosestPassableSpace() on player.", FCVAR_CHEAT ); +#endif |