2020-04-22 18:56:21 +02:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//=============================================================================//
# include "engine/IEngineTrace.h"
# include "icliententitylist.h"
# include "ispatialpartitioninternal.h"
# include "icliententity.h"
# include "cmodel_engine.h"
# include "dispcoll_common.h"
# include "staticpropmgr.h"
# include "server.h"
# include "edict.h"
# include "gl_model_private.h"
# include "world.h"
# include "vphysics_interface.h"
# include "client_class.h"
# include "server_class.h"
# include "debugoverlay.h"
# include "collisionutils.h"
# include "tier0/vprof.h"
# include "convar.h"
# include "mathlib/polyhedron.h"
# include "sys_dll.h"
# include "vphysics/virtualmesh.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Various statistics to gather
//-----------------------------------------------------------------------------
enum
{
TRACE_STAT_COUNTER_TRACERAY = 0 ,
TRACE_STAT_COUNTER_POINTCONTENTS ,
TRACE_STAT_COUNTER_ENUMERATE ,
NUM_TRACE_STAT_COUNTER
} ;
//-----------------------------------------------------------------------------
// Used to visualize raycasts going on
//-----------------------------------------------------------------------------
# ifdef _DEBUG
ConVar debugrayenable ( " debugrayenable " , " 0 " , NULL , " Use this to enable ray testing. To reset: bind \" F1 \" \" clearalloverlays; debugrayreset 0; host_framerate 66.66666667 \" " ) ;
ConVar debugrayreset ( " debugrayreset " , " 0 " ) ;
ConVar debugraylimit ( " debugraylimit " , " 500 " , NULL , " number of rays per frame that you have to hit before displaying them all " ) ;
static CUtlVector < Ray_t > s_FrameRays ;
# endif
# define BENCHMARK_RAY_TEST 0
# if BENCHMARK_RAY_TEST
static CUtlVector < Ray_t > s_BenchmarkRays ;
# endif
//-----------------------------------------------------------------------------
// Implementation of IEngineTrace
//-----------------------------------------------------------------------------
abstract_class CEngineTrace : public IEngineTrace
{
public :
CEngineTrace ( ) { m_pRootMoveParent = NULL ; }
// Returns the contents mask at a particular world-space position
virtual int GetPointContents ( const Vector & vecAbsPosition , IHandleEntity * * ppEntity ) ;
virtual int GetPointContents_Collideable ( ICollideable * pCollide , const Vector & vecAbsPosition ) ;
// Traces a ray against a particular edict
virtual void ClipRayToEntity ( const Ray_t & ray , unsigned int fMask , IHandleEntity * pEntity , trace_t * pTrace ) ;
// A version that simply accepts a ray (can work as a traceline or tracehull)
virtual void TraceRay ( const Ray_t & ray , unsigned int fMask , ITraceFilter * pTraceFilter , trace_t * pTrace ) ;
// A version that sets up the leaf and entity lists and allows you to pass those in for collision.
virtual void SetupLeafAndEntityListRay ( const Ray_t & ray , CTraceListData & traceData ) ;
virtual void SetupLeafAndEntityListBox ( const Vector & vecBoxMin , const Vector & vecBoxMax , CTraceListData & traceData ) ;
virtual void TraceRayAgainstLeafAndEntityList ( const Ray_t & ray , CTraceListData & traceData , unsigned int fMask , ITraceFilter * pTraceFilter , trace_t * pTrace ) ;
// A version that sweeps a collideable through the world
// abs start + abs end represents the collision origins you want to sweep the collideable through
// vecAngles represents the collision angles of the collideable during the sweep
virtual void SweepCollideable ( ICollideable * pCollide , const Vector & vecAbsStart , const Vector & vecAbsEnd ,
const QAngle & vecAngles , unsigned int fMask , ITraceFilter * pTraceFilter , trace_t * pTrace ) ;
// Enumerates over all entities along a ray
// If triggers == true, it enumerates all triggers along a ray
virtual void EnumerateEntities ( const Ray_t & ray , bool triggers , IEntityEnumerator * pEnumerator ) ;
// Same thing, but enumerate entitys within a box
virtual void EnumerateEntities ( const Vector & vecAbsMins , const Vector & vecAbsMaxs , IEntityEnumerator * pEnumerator ) ;
// FIXME: Different versions for client + server. Eventually we need to make these go away
virtual void HandleEntityToCollideable ( IHandleEntity * pHandleEntity , ICollideable * * ppCollide , const char * * ppDebugName ) = 0 ;
virtual ICollideable * GetWorldCollideable ( ) = 0 ;
// Traces a ray against a particular edict
virtual void ClipRayToCollideable ( const Ray_t & ray , unsigned int fMask , ICollideable * pEntity , trace_t * pTrace ) ;
// HACKHACK: Temp
virtual int GetStatByIndex ( int index , bool bClear ) ;
//finds brushes in an AABB, prone to some false positives
virtual void GetBrushesInAABB ( const Vector & vMins , const Vector & vMaxs , CUtlVector < int > * pOutput , int iContentsMask = 0xFFFFFFFF ) ;
//Creates a CPhysCollide out of all displacements wholly or partially contained in the specified AABB
virtual CPhysCollide * GetCollidableFromDisplacementsInAABB ( const Vector & vMins , const Vector & vMaxs ) ;
//retrieve brush planes and contents, returns true if data is being returned in the output pointers, false if the brush doesn't exist
virtual bool GetBrushInfo ( int iBrush , CUtlVector < Vector4D > * pPlanesOut , int * pContentsOut ) ;
virtual bool PointOutsideWorld ( const Vector & ptTest ) ; //Tests a point to see if it's outside any playable area
// Walks bsp to find the leaf containing the specified point
virtual int GetLeafContainingPoint ( const Vector & ptTest ) ;
private :
// FIXME: Different versions for client + server. Eventually we need to make these go away
virtual void SetTraceEntity ( ICollideable * pCollideable , trace_t * pTrace ) = 0 ;
virtual ICollideable * GetCollideable ( IHandleEntity * pEntity ) = 0 ;
virtual int SpatialPartitionMask ( ) const = 0 ;
virtual int SpatialPartitionTriggerMask ( ) const = 0 ;
// Figure out point contents for entities at a particular position
int EntityContents ( const Vector & vecAbsPosition ) ;
// Should we perform the custom raytest?
bool ShouldPerformCustomRayTest ( const Ray_t & ray , ICollideable * pCollideable ) const ;
// Performs the custom raycast
bool ClipRayToCustom ( const Ray_t & ray , unsigned int fMask , ICollideable * pCollideable , trace_t * pTrace ) ;
// Perform vphysics trace
bool ClipRayToVPhysics ( const Ray_t & ray , unsigned int fMask , ICollideable * pCollideable , studiohdr_t * pStudioHdr , trace_t * pTrace ) ;
// Perform hitbox trace
bool ClipRayToHitboxes ( const Ray_t & ray , unsigned int fMask , ICollideable * pCollideable , trace_t * pTrace ) ;
// Perform bsp trace
bool ClipRayToBSP ( const Ray_t & ray , unsigned int fMask , ICollideable * pCollideable , trace_t * pTrace ) ;
// bbox
bool ClipRayToBBox ( const Ray_t & ray , unsigned int fMask , ICollideable * pCollideable , trace_t * pTrace ) ;
// OBB
bool ClipRayToOBB ( const Ray_t & ray , unsigned int fMask , ICollideable * pEntity , trace_t * pTrace ) ;
// Clips a trace to another trace
bool ClipTraceToTrace ( trace_t & clipTrace , trace_t * pFinalTrace ) ;
private :
int m_traceStatCounters [ NUM_TRACE_STAT_COUNTER ] ;
const matrix3x4_t * m_pRootMoveParent ;
friend void RayBench ( const CCommand & args ) ;
} ;
class CEngineTraceServer : public CEngineTrace
{
private :
virtual void HandleEntityToCollideable ( IHandleEntity * pEnt , ICollideable * * ppCollide , const char * * ppDebugName ) ;
virtual void SetTraceEntity ( ICollideable * pCollideable , trace_t * pTrace ) ;
virtual int SpatialPartitionMask ( ) const ;
virtual int SpatialPartitionTriggerMask ( ) const ;
virtual ICollideable * GetWorldCollideable ( ) ;
friend void RayBench ( const CCommand & args ) ;
public :
// IEngineTrace
virtual ICollideable * GetCollideable ( IHandleEntity * pEntity ) ;
} ;
# ifndef SWDS
class CEngineTraceClient : public CEngineTrace
{
private :
virtual void HandleEntityToCollideable ( IHandleEntity * pEnt , ICollideable * * ppCollide , const char * * ppDebugName ) ;
virtual void SetTraceEntity ( ICollideable * pCollideable , trace_t * pTrace ) ;
virtual int SpatialPartitionMask ( ) const ;
virtual int SpatialPartitionTriggerMask ( ) const ;
virtual ICollideable * GetWorldCollideable ( ) ;
public :
// IEngineTrace
virtual ICollideable * GetCollideable ( IHandleEntity * pEntity ) ;
} ;
# endif
//-----------------------------------------------------------------------------
// Expose CVEngineServer to the game + client DLLs
//-----------------------------------------------------------------------------
static CEngineTraceServer s_EngineTraceServer ;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR ( CEngineTraceServer , IEngineTrace , INTERFACEVERSION_ENGINETRACE_SERVER , s_EngineTraceServer ) ;
# ifndef SWDS
static CEngineTraceClient s_EngineTraceClient ;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR ( CEngineTraceClient , IEngineTrace , INTERFACEVERSION_ENGINETRACE_CLIENT , s_EngineTraceClient ) ;
# endif
//-----------------------------------------------------------------------------
// Expose CVEngineServer to the engine.
//-----------------------------------------------------------------------------
IEngineTrace * g_pEngineTraceServer = & s_EngineTraceServer ;
# ifndef SWDS
IEngineTrace * g_pEngineTraceClient = & s_EngineTraceClient ;
# endif
//-----------------------------------------------------------------------------
// Client-server neutral method of getting at collideables
//-----------------------------------------------------------------------------
# ifndef SWDS
ICollideable * CEngineTraceClient : : GetCollideable ( IHandleEntity * pEntity )
{
Assert ( pEntity ) ;
ICollideable * pProp = StaticPropMgr ( ) - > GetStaticProp ( pEntity ) ;
if ( pProp )
return pProp ;
IClientUnknown * pUnk = entitylist - > GetClientUnknownFromHandle ( pEntity - > GetRefEHandle ( ) ) ;
return pUnk - > GetCollideable ( ) ;
}
# endif
ICollideable * CEngineTraceServer : : GetCollideable ( IHandleEntity * pEntity )
{
Assert ( pEntity ) ;
ICollideable * pProp = StaticPropMgr ( ) - > GetStaticProp ( pEntity ) ;
if ( pProp )
return pProp ;
IServerUnknown * pNetUnknown = static_cast < IServerUnknown * > ( pEntity ) ;
return pNetUnknown - > GetCollideable ( ) ;
}
//-----------------------------------------------------------------------------
// Spatial partition masks for iteration
//-----------------------------------------------------------------------------
# ifndef SWDS
int CEngineTraceClient : : SpatialPartitionMask ( ) const
{
return PARTITION_CLIENT_SOLID_EDICTS ;
}
# endif
int CEngineTraceServer : : SpatialPartitionMask ( ) const
{
return PARTITION_ENGINE_SOLID_EDICTS ;
}
# ifndef SWDS
int CEngineTraceClient : : SpatialPartitionTriggerMask ( ) const
{
return 0 ;
}
# endif
int CEngineTraceServer : : SpatialPartitionTriggerMask ( ) const
{
return PARTITION_ENGINE_TRIGGER_EDICTS ;
}
//-----------------------------------------------------------------------------
// Spatial partition enumerator looking for entities that we may lie within
//-----------------------------------------------------------------------------
class CPointContentsEnum : public IPartitionEnumerator
{
public :
CPointContentsEnum ( CEngineTrace * pEngineTrace , const Vector & pos ) : m_Contents ( CONTENTS_EMPTY )
{
m_pEngineTrace = pEngineTrace ;
m_Pos = pos ;
m_pCollide = NULL ;
}
static inline bool TestEntity (
CEngineTrace * pEngineTrace ,
ICollideable * pCollide ,
const Vector & vPos ,
int * pContents ,
ICollideable * * pWorldCollideable )
{
// Deal with static props
// NOTE: I could have added static props to a different list and
// enumerated them separately, but that would have been less efficient
if ( StaticPropMgr ( ) - > IsStaticProp ( pCollide - > GetEntityHandle ( ) ) )
{
Ray_t ray ;
trace_t trace ;
ray . Init ( vPos , vPos ) ;
pEngineTrace - > ClipRayToCollideable ( ray , MASK_ALL , pCollide , & trace ) ;
if ( trace . startsolid )
{
// We're in a static prop; that's solid baby
// Pretend we hit the world
* pContents = CONTENTS_SOLID ;
* pWorldCollideable = pEngineTrace - > GetWorldCollideable ( ) ;
return true ;
}
return false ;
}
// We only care about solid volumes
if ( ( pCollide - > GetSolidFlags ( ) & FSOLID_VOLUME_CONTENTS ) = = 0 )
return false ;
model_t * pModel = ( model_t * ) pCollide - > GetCollisionModel ( ) ;
if ( pModel & & pModel - > type = = mod_brush )
{
Assert ( pCollide - > GetCollisionModelIndex ( ) < MAX_MODELS & & pCollide - > GetCollisionModelIndex ( ) > = 0 ) ;
int nHeadNode = GetModelHeadNode ( pCollide ) ;
int contents = CM_TransformedPointContents ( vPos , nHeadNode ,
pCollide - > GetCollisionOrigin ( ) , pCollide - > GetCollisionAngles ( ) ) ;
if ( contents ! = CONTENTS_EMPTY )
{
// Return the contents of the first thing we hit
* pContents = contents ;
* pWorldCollideable = pCollide ;
return true ;
}
}
return false ;
}
IterationRetval_t EnumElement ( IHandleEntity * pHandleEntity )
{
ICollideable * pCollide ;
const char * pDbgName ;
m_pEngineTrace - > HandleEntityToCollideable ( pHandleEntity , & pCollide , & pDbgName ) ;
if ( ! pCollide )
return ITERATION_CONTINUE ;
if ( CPointContentsEnum : : TestEntity ( m_pEngineTrace , pCollide , m_Pos , & m_Contents , & m_pCollide ) )
return ITERATION_STOP ;
else
return ITERATION_CONTINUE ;
}
private :
static int GetModelHeadNode ( ICollideable * pCollide )
{
int modelindex = pCollide - > GetCollisionModelIndex ( ) ;
if ( modelindex > = MAX_MODELS | | modelindex < 0 )
return - 1 ;
model_t * pModel = ( model_t * ) pCollide - > GetCollisionModel ( ) ;
if ( ! pModel )
return - 1 ;
if ( cmodel_t * pCModel = CM_InlineModelNumber ( modelindex - 1 ) )
return pCModel - > headnode ;
else
return - 1 ;
}
public :
int m_Contents ;
ICollideable * m_pCollide ;
private :
CEngineTrace * m_pEngineTrace ;
Vector m_Pos ;
} ;
//-----------------------------------------------------------------------------
// Returns the contents mask at a particular world-space position
//-----------------------------------------------------------------------------
int CEngineTrace : : GetPointContents ( const Vector & vecAbsPosition , IHandleEntity * * ppEntity )
{
VPROF ( " CEngineTrace_GetPointContents " ) ;
// VPROF_BUDGET( "CEngineTrace_GetPointContents", "CEngineTrace_GetPointContents" );
m_traceStatCounters [ TRACE_STAT_COUNTER_POINTCONTENTS ] + + ;
// First check the collision model
int nContents = CM_PointContents ( vecAbsPosition , 0 ) ;
if ( nContents & MASK_CURRENT )
{
nContents = CONTENTS_WATER ;
}
if ( nContents ! = CONTENTS_SOLID )
{
CPointContentsEnum contentsEnum ( this , vecAbsPosition ) ;
SpatialPartition ( ) - > EnumerateElementsAtPoint ( SpatialPartitionMask ( ) ,
vecAbsPosition , false , & contentsEnum ) ;
int nEntityContents = contentsEnum . m_Contents ;
if ( nEntityContents & MASK_CURRENT )
nContents = CONTENTS_WATER ;
if ( nEntityContents ! = CONTENTS_EMPTY )
{
if ( ppEntity )
{
* ppEntity = contentsEnum . m_pCollide - > GetEntityHandle ( ) ;
}
return nEntityContents ;
}
}
if ( ppEntity )
{
* ppEntity = GetWorldCollideable ( ) - > GetEntityHandle ( ) ;
}
return nContents ;
}
int CEngineTrace : : GetPointContents_Collideable ( ICollideable * pCollide , const Vector & vecAbsPosition )
{
int contents = CONTENTS_EMPTY ;
ICollideable * pDummy ;
CPointContentsEnum : : TestEntity ( this , pCollide , vecAbsPosition , & contents , & pDummy ) ;
return contents ;
}
//-----------------------------------------------------------------------------
// Should we perform the custom raytest?
//-----------------------------------------------------------------------------
inline bool CEngineTrace : : ShouldPerformCustomRayTest ( const Ray_t & ray , ICollideable * pCollideable ) const
{
// No model? The entity's got its own collision detector maybe
// Does the entity force box or ray tests to go through its code?
return ( ( pCollideable - > GetSolid ( ) = = SOLID_CUSTOM ) | |
( ray . m_IsRay & & ( pCollideable - > GetSolidFlags ( ) & FSOLID_CUSTOMRAYTEST ) ) | |
( ! ray . m_IsRay & & ( pCollideable - > GetSolidFlags ( ) & FSOLID_CUSTOMBOXTEST ) ) ) ;
}
//-----------------------------------------------------------------------------
// Performs the custom raycast
//-----------------------------------------------------------------------------
bool CEngineTrace : : ClipRayToCustom ( const Ray_t & ray , unsigned int fMask , ICollideable * pCollideable , trace_t * pTrace )
{
if ( pCollideable - > TestCollision ( ray , fMask , * pTrace ) )
{
return true ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Performs the hitbox raycast, returns true if the hitbox test was made
//-----------------------------------------------------------------------------
bool CEngineTrace : : ClipRayToHitboxes ( const Ray_t & ray , unsigned int fMask , ICollideable * pCollideable , trace_t * pTrace )
{
trace_t hitboxTrace ;
CM_ClearTrace ( & hitboxTrace ) ;
// Keep track of the contents of what was hit initially
hitboxTrace . contents = pTrace - > contents ;
VectorAdd ( ray . m_Start , ray . m_StartOffset , hitboxTrace . startpos ) ;
VectorAdd ( hitboxTrace . startpos , ray . m_Delta , hitboxTrace . endpos ) ;
// If the hitboxes weren't even tested, then just use the original trace
if ( ! pCollideable - > TestHitboxes ( ray , fMask , hitboxTrace ) )
return false ;
// If they *were* tested and missed, clear the original trace
if ( ! hitboxTrace . DidHit ( ) )
{
CM_ClearTrace ( pTrace ) ;
pTrace - > startpos = hitboxTrace . startpos ;
pTrace - > endpos = hitboxTrace . endpos ;
}
else if ( pCollideable - > GetSolid ( ) ! = SOLID_VPHYSICS )
{
// If we also hit the hitboxes, maintain fractionleftsolid +
// startpos because those are reasonable enough values and the
// hitbox code doesn't set those itself.
Vector vecStartPos = pTrace - > startpos ;
float flFractionLeftSolid = pTrace - > fractionleftsolid ;
* pTrace = hitboxTrace ;
if ( hitboxTrace . startsolid )
{
pTrace - > startpos = vecStartPos ;
pTrace - > fractionleftsolid = flFractionLeftSolid ;
}
}
else
{
// Fill out the trace hitbox details
pTrace - > contents = hitboxTrace . contents ;
pTrace - > hitgroup = hitboxTrace . hitgroup ;
pTrace - > hitbox = hitboxTrace . hitbox ;
pTrace - > physicsbone = hitboxTrace . physicsbone ;
pTrace - > surface = hitboxTrace . surface ;
Assert ( pTrace - > physicsbone > = 0 ) ;
// Fill out the surfaceprop details from the hitbox. Use the physics bone instead of the hitbox bone
Assert ( pTrace - > surface . flags = = SURF_HITBOX ) ;
}
return true ;
}
int CEngineTrace : : GetStatByIndex ( int index , bool bClear )
{
if ( index > = NUM_TRACE_STAT_COUNTER )
return 0 ;
int out = m_traceStatCounters [ index ] ;
if ( bClear )
{
m_traceStatCounters [ index ] = 0 ;
}
return out ;
}
static void FASTCALL GetBrushesInAABB_ParseLeaf ( const Vector * pExtents , CCollisionBSPData * pBSPData , cleaf_t * pLeaf , CUtlVector < int > * pOutput , int iContentsMask , int * pCounters )
{
for ( unsigned int i = 0 ; i ! = pLeaf - > numleafbrushes ; + + i )
{
int iBrushNumber = pBSPData - > map_leafbrushes [ pLeaf - > firstleafbrush + i ] ;
cbrush_t * pBrush = & pBSPData - > map_brushes [ iBrushNumber ] ;
if ( pCounters [ iBrushNumber ] )
continue ;
pCounters [ iBrushNumber ] = 1 ;
if ( ( pBrush - > contents & iContentsMask ) = = 0 )
continue ;
if ( pBrush - > IsBox ( ) )
{
cboxbrush_t * pBox = & pBSPData - > map_boxbrushes [ pBrush - > GetBox ( ) ] ;
if ( IsBoxIntersectingBox ( pBox - > mins , pBox - > maxs , pExtents [ 0 ] , pExtents [ 7 ] ) )
{
pOutput - > AddToTail ( iBrushNumber ) ;
}
}
else
{
unsigned int j ;
for ( j = 0 ; j ! = pBrush - > numsides ; + + j )
{
cplane_t * pPlane = pBSPData - > map_brushsides [ pBrush - > firstbrushside + j ] . plane ;
if ( ( pExtents [ pPlane - > signbits ] . Dot ( pPlane - > normal ) - pPlane - > dist ) > 0.0f )
break ; //the bounding box extent that was most likely to be encapsulated by the plane is outside the halfspace, brush not in bbox
}
if ( j = = pBrush - > numsides )
pOutput - > AddToTail ( iBrushNumber ) ; //brush was most likely in bbox
}
}
}
void CEngineTrace : : GetBrushesInAABB ( const Vector & vMins , const Vector & vMaxs , CUtlVector < int > * pOutput , int iContentsMask )
{
if ( pOutput = = NULL ) return ;
CCollisionBSPData * pBSPData = GetCollisionBSPData ( ) ;
Vector ptBBoxExtents [ 8 ] ; //for fast plane checking
for ( int i = 0 ; i ! = 8 ; + + i )
{
//set these up to be opposite that of cplane_t's signbits for it's normal
ptBBoxExtents [ i ] . x = ( i & ( 1 < < 0 ) ) ? ( vMaxs . x ) : ( vMins . x ) ;
ptBBoxExtents [ i ] . y = ( i & ( 1 < < 1 ) ) ? ( vMaxs . y ) : ( vMins . y ) ;
ptBBoxExtents [ i ] . z = ( i & ( 1 < < 2 ) ) ? ( vMaxs . z ) : ( vMins . z ) ;
}
int * pLeafList = ( int * ) stackalloc ( pBSPData - > numleafs * 2 * sizeof ( int ) ) ; // *2 just in case
int iNumLeafs = CM_BoxLeafnums ( vMins , vMaxs , pLeafList , pBSPData - > numleafs * 2 , NULL ) ;
CUtlVector < int > counters ;
counters . SetSize ( pBSPData - > numbrushes ) ;
memset ( counters . Base ( ) , 0 , pBSPData - > numbrushes * sizeof ( int ) ) ;
for ( int i = 0 ; i ! = iNumLeafs ; + + i )
GetBrushesInAABB_ParseLeaf ( ptBBoxExtents , pBSPData , & pBSPData - > map_leafs [ pLeafList [ i ] ] , pOutput , iContentsMask , counters . Base ( ) ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Used to copy the collision information of all displacement surfaces in a specified box
// Input : vMins - min vector of the AABB
// vMaxs - max vector of the AABB
// Output : CPhysCollide* the collision mesh created from all the displacements partially contained in the specified box
// Note: We're not clipping to the box. Collidable may be larger than the box provided.
//-----------------------------------------------------------------------------
CPhysCollide * CEngineTrace : : GetCollidableFromDisplacementsInAABB ( const Vector & vMins , const Vector & vMaxs )
{
CCollisionBSPData * pBSPData = GetCollisionBSPData ( ) ;
int * pLeafList = ( int * ) stackalloc ( pBSPData - > numleafs * sizeof ( int ) ) ;
int iLeafCount = CM_BoxLeafnums ( vMins , vMaxs , pLeafList , pBSPData - > numleafs , NULL ) ;
// Get all the triangles for displacement surfaces in this box, add them to a polysoup
CPhysPolysoup * pDispCollideSoup = physcollision - > PolysoupCreate ( ) ;
// Count total triangles added to this poly soup- Can't support more than 65435.
int iTriCount = 0 ;
TraceInfo_t * pTraceInfo = BeginTrace ( ) ;
TraceCounter_t * pCounters = pTraceInfo - > GetDispCounters ( ) ;
int count = pTraceInfo - > GetCount ( ) ;
// For each leaf in which the box lies, Get all displacements in that leaf and use their triangles to create the mesh
for ( int i = 0 ; i < iLeafCount ; + + i )
{
// Current leaf
cleaf_t curLeaf = pBSPData - > map_leafs [ pLeafList [ i ] ] ;
// Test box against all displacements in the leaf.
for ( int k = 0 ; k < curLeaf . dispCount ; k + + )
{
int dispIndex = pBSPData - > map_dispList [ curLeaf . dispListStart + k ] ;
CDispCollTree * pDispTree = & g_pDispCollTrees [ dispIndex ] ;
// make sure we only check this brush once per trace/stab
if ( ! pTraceInfo - > Visit ( pDispTree - > m_iCounter , count , pCounters ) )
continue ;
// If this displacement doesn't touch our test box, don't add it to the list.
if ( ! IsBoxIntersectingBox ( vMins , vMaxs , pDispTree - > m_mins , pDispTree - > m_maxs ) )
continue ;
// The the triangle mesh for this displacement surface
virtualmeshlist_t meshTriList ;
pDispTree - > GetVirtualMeshList ( & meshTriList ) ;
Assert ( meshTriList . indexCount % 3 = = 0 ) ;
Assert ( meshTriList . indexCount ! = 0 ) ;
Assert ( meshTriList . indexCount / 3 = = meshTriList . triangleCount ) ;
// Don't allow more than 64k triangles in a collision model
// TODO: Return a list of collidables? How often do we break 64k triangles?
iTriCount + = meshTriList . triangleCount ;
if ( iTriCount > 65535 )
{
AssertMsg ( 0 , " Displacement surfaces have too many triangles to duplicate in GetCollidableFromDisplacementsInBox. " ) ;
EndTrace ( pTraceInfo ) ;
return NULL ;
}
for ( int j = 0 ; j < meshTriList . indexCount ; j + = 3 )
{
// Don't index past the index list
Assert ( j + 2 < meshTriList . indexCount ) ;
if ( j + 2 > = meshTriList . indexCount )
{
EndTrace ( pTraceInfo ) ;
physcollision - > PolysoupDestroy ( pDispCollideSoup ) ;
return NULL ;
}
unsigned short i0 = meshTriList . indices [ j + 0 ] ;
unsigned short i1 = meshTriList . indices [ j + 1 ] ;
unsigned short i2 = meshTriList . indices [ j + 2 ] ;
// Don't index past the end of the vert list
Assert ( i0 < meshTriList . vertexCount & & i1 < meshTriList . vertexCount & & i2 < meshTriList . vertexCount ) ;
if ( i0 > = meshTriList . vertexCount | | i1 > = meshTriList . vertexCount | | i2 > = meshTriList . vertexCount )
{
EndTrace ( pTraceInfo ) ;
physcollision - > PolysoupDestroy ( pDispCollideSoup ) ;
return NULL ;
}
Vector v0 = meshTriList . pVerts [ i0 ] ;
Vector v1 = meshTriList . pVerts [ i1 ] ;
Vector v2 = meshTriList . pVerts [ i2 ] ;
Assert ( v0 . IsValid ( ) & & v1 . IsValid ( ) & & v2 . IsValid ( ) ) ;
// We don't need exact clipping to the box... Include any triangle that has at least one vert on the inside
if ( IsPointInBox ( v0 , vMins , vMaxs ) | | IsPointInBox ( v1 , vMins , vMaxs ) | | IsPointInBox ( v2 , vMins , vMaxs ) )
{
// This is for collision only, so we don't need to worry about blending-- Use the first surface prop.
int nProp = pDispTree - > GetSurfaceProps ( 0 ) ;
physcollision - > PolysoupAddTriangle ( pDispCollideSoup , v0 , v1 , v2 , nProp ) ;
}
} // triangle loop
} // for each displacement in leaf
} // for each leaf
EndTrace ( pTraceInfo ) ;
CPhysCollide * pCollide = physcollision - > ConvertPolysoupToCollide ( pDispCollideSoup , false ) ;
// clean up poly soup
physcollision - > PolysoupDestroy ( pDispCollideSoup ) ;
return pCollide ;
}
bool CEngineTrace : : GetBrushInfo ( int iBrush , CUtlVector < Vector4D > * pPlanesOut , int * pContentsOut )
{
CCollisionBSPData * pBSPData = GetCollisionBSPData ( ) ;
if ( iBrush < 0 | | iBrush > = pBSPData - > numbrushes )
return false ;
cbrush_t * pBrush = & pBSPData - > map_brushes [ iBrush ] ;
if ( pPlanesOut )
{
pPlanesOut - > RemoveAll ( ) ;
Vector4D p ;
if ( pBrush - > IsBox ( ) )
{
cboxbrush_t * pBox = & pBSPData - > map_boxbrushes [ pBrush - > GetBox ( ) ] ;
for ( int i = 0 ; i < 6 ; i + + )
{
p . Init ( 0 , 0 , 0 , 0 ) ;
if ( i < 3 )
{
p [ i ] = 1.0f ;
p [ 3 ] = pBox - > maxs [ i ] ;
}
else
{
p [ i - 3 ] = - 1.0f ;
p [ 3 ] = - pBox - > mins [ i - 3 ] ;
}
pPlanesOut - > AddToTail ( p ) ;
}
}
else
{
cbrushside_t * stopside = & pBSPData - > map_brushsides [ pBrush - > firstbrushside ] ;
// Note: Don't do this in the [] since the final one on the last brushside will be past the end of the array end by one index
stopside + = pBrush - > numsides ;
for ( cbrushside_t * side = & pBSPData - > map_brushsides [ pBrush - > firstbrushside ] ; side ! = stopside ; + + side )
{
Vector4D pVec ( side - > plane - > normal . x , side - > plane - > normal . y , side - > plane - > normal . z , side - > plane - > dist ) ;
pPlanesOut - > AddToTail ( pVec ) ;
}
}
}
if ( pContentsOut )
* pContentsOut = pBrush - > contents ;
return true ;
}
//Tests a point to see if it's outside any playable area
bool CEngineTrace : : PointOutsideWorld ( const Vector & ptTest )
{
int iLeaf = CM_PointLeafnum ( ptTest ) ;
Assert ( iLeaf > = 0 ) ;
CCollisionBSPData * pBSPData = GetCollisionBSPData ( ) ;
if ( pBSPData - > map_leafs [ iLeaf ] . cluster = = - 1 )
return true ;
return false ;
}
//-----------------------------------------------------------------------------
// Purpose: Expose to the game dll a method for finding the leaf which contains a given point
// Input : &vPos - Returns the leaf which contains this point
// Output : int - The handle to the leaf
//-----------------------------------------------------------------------------
int CEngineTrace : : GetLeafContainingPoint ( const Vector & vPos )
{
return CM_PointLeafnum ( vPos ) ;
}
//-----------------------------------------------------------------------------
// Convex info for studio + brush models
//-----------------------------------------------------------------------------
class CBrushConvexInfo : public IConvexInfo
{
public :
CBrushConvexInfo ( )
{
m_pBSPData = GetCollisionBSPData ( ) ;
}
virtual unsigned int GetContents ( int convexGameData )
{
return m_pBSPData - > map_brushes [ convexGameData ] . contents ;
}
private :
CCollisionBSPData * m_pBSPData ;
} ;
class CStudioConvexInfo : public IConvexInfo
{
public :
CStudioConvexInfo ( studiohdr_t * pStudioHdr )
{
m_pStudioHdr = pStudioHdr ;
}
virtual unsigned int GetContents ( int convexGameData )
{
if ( convexGameData = = 0 )
{
return m_pStudioHdr - > contents ;
}
Assert ( convexGameData < = m_pStudioHdr - > numbones ) ;
mstudiobone_t * pBone = m_pStudioHdr - > pBone ( convexGameData - 1 ) ;
return pBone - > contents ;
}
private :
studiohdr_t * m_pStudioHdr ;
} ;
//-----------------------------------------------------------------------------
// Perform vphysics trace
//-----------------------------------------------------------------------------
bool CEngineTrace : : ClipRayToVPhysics ( const Ray_t & ray , unsigned int fMask , ICollideable * pEntity , studiohdr_t * pStudioHdr , trace_t * pTrace )
{
if ( pEntity - > GetSolid ( ) ! = SOLID_VPHYSICS )
return false ;
bool bTraced = false ;
// use the vphysics model for rotated brushes and vphysics simulated objects
const model_t * pModel = pEntity - > GetCollisionModel ( ) ;
if ( ! pModel )
return false ;
if ( pStudioHdr )
{
CStudioConvexInfo studioConvex ( pStudioHdr ) ;
vcollide_t * pCollide = g_pMDLCache - > GetVCollide ( pModel - > studio ) ;
if ( pCollide & & pCollide - > solidCount )
{
physcollision - > TraceBox (
ray ,
fMask ,
& studioConvex ,
pCollide - > solids [ 0 ] , // UNDONE: Support other solid indices?!?!?!? (forced zero)
pEntity - > GetCollisionOrigin ( ) ,
pEntity - > GetCollisionAngles ( ) ,
pTrace ) ;
bTraced = true ;
}
}
else
{
Assert ( pModel - > type ! = mod_studio ) ;
// use the regular code for raytraces against brushes
// do ray traces with normal code, but use vphysics to do box traces
if ( ! ray . m_IsRay | | pModel - > type ! = mod_brush )
{
int nModelIndex = pEntity - > GetCollisionModelIndex ( ) ;
// BUGBUG: This only works when the vcollide in question is the first solid in the model
vcollide_t * pCollide = CM_VCollideForModel ( nModelIndex , ( model_t * ) pModel ) ;
if ( pCollide & & pCollide - > solidCount )
{
CBrushConvexInfo brushConvex ;
IConvexInfo * pConvexInfo = ( pModel - > type ) = = mod_brush ? & brushConvex : NULL ;
physcollision - > TraceBox (
ray ,
fMask ,
pConvexInfo ,
pCollide - > solids [ 0 ] , // UNDONE: Support other solid indices?!?!?!? (forced zero)
pEntity - > GetCollisionOrigin ( ) ,
pEntity - > GetCollisionAngles ( ) ,
pTrace ) ;
bTraced = true ;
}
}
}
return bTraced ;
}
//-----------------------------------------------------------------------------
// Perform bsp trace
//-----------------------------------------------------------------------------
bool CEngineTrace : : ClipRayToBSP ( const Ray_t & ray , unsigned int fMask , ICollideable * pEntity , trace_t * pTrace )
{
int nModelIndex = pEntity - > GetCollisionModelIndex ( ) ;
cmodel_t * pCModel = CM_InlineModelNumber ( nModelIndex - 1 ) ;
int nHeadNode = pCModel - > headnode ;
CM_TransformedBoxTrace ( ray , nHeadNode , fMask , pEntity - > GetCollisionOrigin ( ) , pEntity - > GetCollisionAngles ( ) , * pTrace ) ;
return true ;
}
// NOTE: Switched over to SIMD ray/box test since there is a bug we haven't hunted down yet in the scalar version
bool CEngineTrace : : ClipRayToBBox ( const Ray_t & ray , unsigned int fMask , ICollideable * pEntity , trace_t * pTrace )
{
extern bool IntersectRayWithBox ( const Ray_t & ray , const VectorAligned & inInvDelta , const VectorAligned & inBoxMins , const VectorAligned & inBoxMaxs , trace_t * RESTRICT pTrace ) ;
if ( pEntity - > GetSolid ( ) ! = SOLID_BBOX )
return false ;
// We can't use the OBBMins/Maxs unless the collision angles are world-aligned
Assert ( pEntity - > GetCollisionAngles ( ) = = vec3_angle ) ;
VectorAligned vecAbsMins , vecAbsMaxs ;
VectorAligned vecInvDelta ;
// NOTE: If m_pRootMoveParent is set, then the boxes should be rotated into the root parent's space
if ( ! ray . m_IsRay & & m_pRootMoveParent )
{
Ray_t ray_l ;
ray_l . m_Extents = ray . m_Extents ;
VectorIRotate ( ray . m_Delta , * m_pRootMoveParent , ray_l . m_Delta ) ;
ray_l . m_StartOffset . Init ( ) ;
VectorITransform ( ray . m_Start , * m_pRootMoveParent , ray_l . m_Start ) ;
vecInvDelta = ray_l . InvDelta ( ) ;
Vector localEntityOrigin ;
VectorITransform ( pEntity - > GetCollisionOrigin ( ) , * m_pRootMoveParent , localEntityOrigin ) ;
ray_l . m_IsRay = ray . m_IsRay ;
ray_l . m_IsSwept = ray . m_IsSwept ;
VectorAdd ( localEntityOrigin , pEntity - > OBBMins ( ) , vecAbsMins ) ;
VectorAdd ( localEntityOrigin , pEntity - > OBBMaxs ( ) , vecAbsMaxs ) ;
IntersectRayWithBox ( ray_l , vecInvDelta , vecAbsMins , vecAbsMaxs , pTrace ) ;
if ( pTrace - > DidHit ( ) )
{
Vector temp ;
VectorCopy ( pTrace - > plane . normal , temp ) ;
VectorRotate ( temp , * m_pRootMoveParent , pTrace - > plane . normal ) ;
VectorAdd ( ray . m_Start , ray . m_StartOffset , pTrace - > startpos ) ;
if ( pTrace - > fraction = = 1 )
{
VectorAdd ( pTrace - > startpos , ray . m_Delta , pTrace - > endpos ) ;
}
else
{
VectorMA ( pTrace - > startpos , pTrace - > fraction , ray . m_Delta , pTrace - > endpos ) ;
}
pTrace - > plane . dist = DotProduct ( pTrace - > endpos , pTrace - > plane . normal ) ;
if ( pTrace - > fractionleftsolid < 1 )
{
pTrace - > startpos + = ray . m_Delta * pTrace - > fractionleftsolid ;
}
}
else
{
VectorAdd ( ray . m_Start , ray . m_StartOffset , pTrace - > startpos ) ;
}
return true ;
}
vecInvDelta = ray . InvDelta ( ) ;
VectorAdd ( pEntity - > GetCollisionOrigin ( ) , pEntity - > OBBMins ( ) , vecAbsMins ) ;
VectorAdd ( pEntity - > GetCollisionOrigin ( ) , pEntity - > OBBMaxs ( ) , vecAbsMaxs ) ;
IntersectRayWithBox ( ray , vecInvDelta , vecAbsMins , vecAbsMaxs , pTrace ) ;
return true ;
}
bool CEngineTrace : : ClipRayToOBB ( const Ray_t & ray , unsigned int fMask , ICollideable * pEntity , trace_t * pTrace )
{
if ( pEntity - > GetSolid ( ) ! = SOLID_OBB )
return false ;
// NOTE: This is busted because it doesn't compute fractionleftsolid, which at the
// moment is required for the engine trace system.
IntersectRayWithOBB ( ray , pEntity - > GetCollisionOrigin ( ) , pEntity - > GetCollisionAngles ( ) ,
pEntity - > OBBMins ( ) , pEntity - > OBBMaxs ( ) , DIST_EPSILON , pTrace ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Main entry point for clipping rays to entities
//-----------------------------------------------------------------------------
# ifndef SWDS
void CEngineTraceClient : : SetTraceEntity ( ICollideable * pCollideable , trace_t * pTrace )
{
if ( ! pTrace - > DidHit ( ) )
return ;
// FIXME: This is only necessary because of traces occurring during
// LevelInit (a suspect time to be tracing)
if ( ! pCollideable )
{
pTrace - > m_pEnt = NULL ;
return ;
}
IClientUnknown * pUnk = ( IClientUnknown * ) pCollideable - > GetEntityHandle ( ) ;
if ( ! StaticPropMgr ( ) - > IsStaticProp ( pUnk ) )
{
pTrace - > m_pEnt = ( CBaseEntity * ) ( pUnk - > GetIClientEntity ( ) ) ;
}
else
{
// For static props, point to the world, hitbox is the prop index
pTrace - > m_pEnt = ( CBaseEntity * ) ( entitylist - > GetClientEntity ( 0 ) ) ;
pTrace - > hitbox = StaticPropMgr ( ) - > GetStaticPropIndex ( pUnk ) + 1 ;
}
}
# endif
void CEngineTraceServer : : SetTraceEntity ( ICollideable * pCollideable , trace_t * pTrace )
{
if ( ! pTrace - > DidHit ( ) )
return ;
IHandleEntity * pHandleEntity = pCollideable - > GetEntityHandle ( ) ;
if ( ! StaticPropMgr ( ) - > IsStaticProp ( pHandleEntity ) )
{
pTrace - > m_pEnt = ( CBaseEntity * ) ( pHandleEntity ) ;
}
else
{
// For static props, point to the world, hitbox is the prop index
pTrace - > m_pEnt = ( CBaseEntity * ) ( sv . edicts - > GetIServerEntity ( ) ) ;
pTrace - > hitbox = StaticPropMgr ( ) - > GetStaticPropIndex ( pHandleEntity ) + 1 ;
}
}
//-----------------------------------------------------------------------------
// Traces a ray against a particular edict
//-----------------------------------------------------------------------------
void CEngineTrace : : ClipRayToCollideable ( const Ray_t & ray , unsigned int fMask , ICollideable * pEntity , trace_t * pTrace )
{
CM_ClearTrace ( pTrace ) ;
VectorAdd ( ray . m_Start , ray . m_StartOffset , pTrace - > startpos ) ;
VectorAdd ( pTrace - > startpos , ray . m_Delta , pTrace - > endpos ) ;
const model_t * pModel = pEntity - > GetCollisionModel ( ) ;
bool bIsStudioModel = false ;
studiohdr_t * pStudioHdr = NULL ;
if ( pModel & & pModel - > type = = mod_studio )
{
bIsStudioModel = true ;
pStudioHdr = ( studiohdr_t * ) modelloader - > GetExtraData ( ( model_t * ) pModel ) ;
// Cull if the collision mask isn't set + we're not testing hitboxes.
if ( ( ( fMask & CONTENTS_HITBOX ) = = 0 ) )
{
if ( ( fMask & pStudioHdr - > contents ) = = 0 )
return ;
}
}
const matrix3x4_t * pOldRoot = m_pRootMoveParent ;
if ( pEntity - > GetSolidFlags ( ) & FSOLID_ROOT_PARENT_ALIGNED )
{
m_pRootMoveParent = pEntity - > GetRootParentToWorldTransform ( ) ;
}
bool bTraced = false ;
bool bCustomPerformed = false ;
if ( ShouldPerformCustomRayTest ( ray , pEntity ) )
{
ClipRayToCustom ( ray , fMask , pEntity , pTrace ) ;
bTraced = true ;
bCustomPerformed = true ;
}
else
{
bTraced = ClipRayToVPhysics ( ray , fMask , pEntity , pStudioHdr , pTrace ) ;
}
// FIXME: Why aren't we using solid type to check what kind of collisions to test against?!?!
if ( ! bTraced & & pModel & & pModel - > type = = mod_brush )
{
bTraced = ClipRayToBSP ( ray , fMask , pEntity , pTrace ) ;
}
if ( ! bTraced )
{
bTraced = ClipRayToOBB ( ray , fMask , pEntity , pTrace ) ;
}
// Hitboxes..
bool bTracedHitboxes = false ;
if ( bIsStudioModel & & ( fMask & CONTENTS_HITBOX ) )
{
// Until hitboxes are no longer implemented as custom raytests,
// don't bother to do the work twice
if ( ! bCustomPerformed )
{
bTraced = ClipRayToHitboxes ( ray , fMask , pEntity , pTrace ) ;
if ( bTraced )
{
// Hitboxes will set the surface properties
bTracedHitboxes = true ;
}
}
}
if ( ! bTraced )
{
ClipRayToBBox ( ray , fMask , pEntity , pTrace ) ;
}
if ( bIsStudioModel & & ! bTracedHitboxes & & pTrace - > DidHit ( ) & & ( ! bCustomPerformed | | pTrace - > surface . surfaceProps = = 0 ) )
{
pTrace - > contents = pStudioHdr - > contents ;
// use the default surface properties
pTrace - > surface . name = " **studio** " ;
pTrace - > surface . flags = 0 ;
pTrace - > surface . surfaceProps = physprop - > GetSurfaceIndex ( pStudioHdr - > pszSurfaceProp ( ) ) ;
}
if ( ! pTrace - > m_pEnt & & pTrace - > DidHit ( ) )
{
SetTraceEntity ( pEntity , pTrace ) ;
}
# ifdef _DEBUG
Vector vecOffset , vecEndTest ;
VectorAdd ( ray . m_Start , ray . m_StartOffset , vecOffset ) ;
VectorMA ( vecOffset , pTrace - > fractionleftsolid , ray . m_Delta , vecEndTest ) ;
Assert ( VectorsAreEqual ( vecEndTest , pTrace - > startpos , 0.1f ) ) ;
VectorMA ( vecOffset , pTrace - > fraction , ray . m_Delta , vecEndTest ) ;
Assert ( VectorsAreEqual ( vecEndTest , pTrace - > endpos , 0.1f ) ) ;
# endif
m_pRootMoveParent = pOldRoot ;
}
//-----------------------------------------------------------------------------
// Main entry point for clipping rays to entities
//-----------------------------------------------------------------------------
void CEngineTrace : : ClipRayToEntity ( const Ray_t & ray , unsigned int fMask , IHandleEntity * pEntity , trace_t * pTrace )
{
ClipRayToCollideable ( ray , fMask , GetCollideable ( pEntity ) , pTrace ) ;
}
//-----------------------------------------------------------------------------
// Grabs all entities along a ray
//-----------------------------------------------------------------------------
class CEntitiesAlongRay : public IPartitionEnumerator
{
public :
CEntitiesAlongRay ( ) : m_EntityHandles ( 0 , 32 ) { }
void Reset ( )
{
m_EntityHandles . RemoveAll ( ) ;
}
IterationRetval_t EnumElement ( IHandleEntity * pHandleEntity )
{
m_EntityHandles . AddToTail ( pHandleEntity ) ;
return ITERATION_CONTINUE ;
}
CUtlVector < IHandleEntity * > m_EntityHandles ;
} ;
class CEntityListAlongRay : public IPartitionEnumerator
{
public :
enum { MAX_ENTITIES_ALONGRAY = 1024 } ;
CEntityListAlongRay ( )
{
m_nCount = 0 ;
}
void Reset ( )
{
m_nCount = 0 ;
}
int Count ( )
{
return m_nCount ;
}
IterationRetval_t EnumElement ( IHandleEntity * pHandleEntity )
{
if ( m_nCount < MAX_ENTITIES_ALONGRAY )
{
m_EntityHandles [ m_nCount ] = pHandleEntity ;
m_nCount + + ;
}
else
{
DevMsg ( 1 , " Max entity count along ray exceeded! \n " ) ;
}
return ITERATION_CONTINUE ;
}
int m_nCount ;
IHandleEntity * m_EntityHandles [ MAX_ENTITIES_ALONGRAY ] ;
} ;
//-----------------------------------------------------------------------------
// Makes sure the final trace is clipped to the clip trace
// Returns true if clipping occurred
//-----------------------------------------------------------------------------
bool CEngineTrace : : ClipTraceToTrace ( trace_t & clipTrace , trace_t * pFinalTrace )
{
if ( clipTrace . allsolid | | clipTrace . startsolid | | ( clipTrace . fraction < pFinalTrace - > fraction ) )
{
if ( pFinalTrace - > startsolid )
{
float flFractionLeftSolid = pFinalTrace - > fractionleftsolid ;
Vector vecStartPos = pFinalTrace - > startpos ;
* pFinalTrace = clipTrace ;
pFinalTrace - > startsolid = true ;
if ( flFractionLeftSolid > clipTrace . fractionleftsolid )
{
pFinalTrace - > fractionleftsolid = flFractionLeftSolid ;
pFinalTrace - > startpos = vecStartPos ;
}
}
else
{
* pFinalTrace = clipTrace ;
}
return true ;
}
if ( clipTrace . startsolid )
{
pFinalTrace - > startsolid = true ;
if ( clipTrace . fractionleftsolid > pFinalTrace - > fractionleftsolid )
{
pFinalTrace - > fractionleftsolid = clipTrace . fractionleftsolid ;
pFinalTrace - > startpos = clipTrace . startpos ;
}
}
return false ;
}
//-----------------------------------------------------------------------------
// Converts a user id to a collideable + username
//-----------------------------------------------------------------------------
void CEngineTraceServer : : HandleEntityToCollideable ( IHandleEntity * pHandleEntity , ICollideable * * ppCollide , const char * * ppDebugName )
{
* ppCollide = StaticPropMgr ( ) - > GetStaticProp ( pHandleEntity ) ;
if ( * ppCollide )
{
* ppDebugName = " static prop " ;
return ;
}
IServerUnknown * pServerUnknown = static_cast < IServerUnknown * > ( pHandleEntity ) ;
if ( ! pServerUnknown | | ! pServerUnknown - > GetNetworkable ( ) )
{
* ppCollide = NULL ;
* ppDebugName = " <null> " ;
return ;
}
* ppCollide = pServerUnknown - > GetCollideable ( ) ;
* ppDebugName = pServerUnknown - > GetNetworkable ( ) - > GetClassName ( ) ;
}
# ifndef SWDS
void CEngineTraceClient : : HandleEntityToCollideable ( IHandleEntity * pHandleEntity , ICollideable * * ppCollide , const char * * ppDebugName )
{
* ppCollide = StaticPropMgr ( ) - > GetStaticProp ( pHandleEntity ) ;
if ( * ppCollide )
{
* ppDebugName = " static prop " ;
return ;
}
IClientUnknown * pUnk = static_cast < IClientUnknown * > ( pHandleEntity ) ;
if ( ! pUnk )
{
* ppCollide = NULL ;
* ppDebugName = " <null> " ;
return ;
}
* ppCollide = pUnk - > GetCollideable ( ) ;
* ppDebugName = " client entity " ;
IClientNetworkable * pNetwork = pUnk - > GetClientNetworkable ( ) ;
if ( pNetwork )
{
if ( pNetwork - > GetClientClass ( ) )
{
* ppDebugName = pNetwork - > GetClientClass ( ) - > m_pNetworkName ;
}
}
}
# endif
//-----------------------------------------------------------------------------
// Returns the world collideable for trace setting
//-----------------------------------------------------------------------------
# ifndef SWDS
ICollideable * CEngineTraceClient : : GetWorldCollideable ( )
{
IClientEntity * pUnk = entitylist - > GetClientEntity ( 0 ) ;
AssertOnce ( pUnk ) ;
return pUnk ? pUnk - > GetCollideable ( ) : NULL ;
}
# endif
ICollideable * CEngineTraceServer : : GetWorldCollideable ( )
{
if ( ! sv . edicts )
return NULL ;
return sv . edicts - > GetCollideable ( ) ;
}
//-----------------------------------------------------------------------------
// Debugging code to render all ray casts since the last time this call was made
//-----------------------------------------------------------------------------
void EngineTraceRenderRayCasts ( )
{
2024-07-15 23:27:31 +02:00
# if defined _DEBUG
2020-04-22 18:56:21 +02:00
if ( debugrayenable . GetBool ( ) & & s_FrameRays . Count ( ) > debugraylimit . GetInt ( ) & & ! debugrayreset . GetInt ( ) )
{
Warning ( " m_FrameRays.Count() == %d \n " , s_FrameRays . Count ( ) ) ;
debugrayreset . SetValue ( 1 ) ;
int i ;
for ( i = 0 ; i < s_FrameRays . Count ( ) ; i + + )
{
Ray_t & ray = s_FrameRays [ i ] ;
if ( ray . m_Extents . x ! = 0.0f | | ray . m_Extents . y ! = 0.0f | | ray . m_Extents . z ! = 0.0f )
{
CDebugOverlay : : AddLineOverlay ( ray . m_Start , ray . m_Start + ray . m_Delta , 255 , 0 , 0 , 255 , true , 3600.0f ) ;
}
else
{
CDebugOverlay : : AddLineOverlay ( ray . m_Start , ray . m_Start + ray . m_Delta , 255 , 255 , 0 , 255 , true , 3600.0f ) ;
}
}
}
s_FrameRays . RemoveAll ( ) ;
# endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CEngineTrace : : SetupLeafAndEntityListRay ( const Ray_t & ray , CTraceListData & traceData )
{
if ( ! ray . m_IsSwept )
{
Vector vecMin , vecMax ;
VectorSubtract ( ray . m_Start , ray . m_Extents , vecMin ) ;
VectorAdd ( ray . m_Start , ray . m_Extents , vecMax ) ;
SetupLeafAndEntityListBox ( vecMin , vecMax , traceData ) ;
return ;
}
// Get the leaves that intersect the ray.
traceData . LeafCountReset ( ) ;
CM_RayLeafnums ( ray , traceData . m_aLeafList . Base ( ) , traceData . LeafCountMax ( ) , traceData . m_nLeafCount ) ;
// Find all the entities in the voxels that intersect this ray.
traceData . EntityCountReset ( ) ;
SpatialPartition ( ) - > EnumerateElementsAlongRay ( SpatialPartitionMask ( ) , ray , false , & traceData ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Gives an AABB and returns a leaf and entity list.
//-----------------------------------------------------------------------------
void CEngineTrace : : SetupLeafAndEntityListBox ( const Vector & vecBoxMin , const Vector & vecBoxMax , CTraceListData & traceData )
{
// Get the leaves that intersect this box.
int iTopNode = - 1 ;
traceData . LeafCountReset ( ) ;
traceData . m_nLeafCount = CM_BoxLeafnums ( vecBoxMin , vecBoxMax , traceData . m_aLeafList . Base ( ) , traceData . LeafCountMax ( ) , & iTopNode ) ;
// Find all entities in the voxels that intersect this box.
traceData . EntityCountReset ( ) ;
SpatialPartition ( ) - > EnumerateElementsInBox ( SpatialPartitionMask ( ) , vecBoxMin , vecBoxMax , false , & traceData ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// NOTE: the fMask is redundant with the stuff below, what do I want to do???
//-----------------------------------------------------------------------------
void CEngineTrace : : TraceRayAgainstLeafAndEntityList ( const Ray_t & ray , CTraceListData & traceData ,
unsigned int fMask , ITraceFilter * pTraceFilter , trace_t * pTrace )
{
// Setup the trace data.
CM_ClearTrace ( pTrace ) ;
// Make sure we have some kind of trace filter.
CTraceFilterHitAll traceFilter ;
if ( ! pTraceFilter )
{
pTraceFilter = & traceFilter ;
}
// Collide with the world.
if ( pTraceFilter - > GetTraceType ( ) ! = TRACE_ENTITIES_ONLY )
{
ICollideable * pCollide = GetWorldCollideable ( ) ;
// Make sure the world entity is unrotated
// FIXME: BAH! The !pCollide test here is because of
// CStaticProp::PrecacheLighting.. it's occurring too early
// need to fix that later
Assert ( ! pCollide | | pCollide - > GetCollisionOrigin ( ) = = vec3_origin ) ;
Assert ( ! pCollide | | pCollide - > GetCollisionAngles ( ) = = vec3_angle ) ;
CM_BoxTraceAgainstLeafList ( ray , traceData . m_aLeafList . Base ( ) , traceData . LeafCount ( ) , fMask , true , * pTrace ) ;
SetTraceEntity ( pCollide , pTrace ) ;
// Blocked by the world or early out because we only are tracing against the world.
if ( ( pTrace - > fraction = = 0 ) | | ( pTraceFilter - > GetTraceType ( ) = = TRACE_WORLD_ONLY ) )
return ;
}
else
{
// Set initial start and endpos. This is necessary if the world isn't traced against,
// because we may not trace against anything below.
VectorAdd ( ray . m_Start , ray . m_StartOffset , pTrace - > startpos ) ;
VectorAdd ( pTrace - > startpos , ray . m_Delta , pTrace - > endpos ) ;
}
// Save the world collision fraction.
float flWorldFraction = pTrace - > fraction ;
// Create a ray that extends only until we hit the world and adjust the trace accordingly
Ray_t entityRay = ray ;
VectorScale ( entityRay . m_Delta , pTrace - > fraction , entityRay . m_Delta ) ;
// We know this is safe because if pTrace->fraction == 0, we would have exited above.
pTrace - > fractionleftsolid / = pTrace - > fraction ;
pTrace - > fraction = 1.0 ;
// Collide with entities.
bool bNoStaticProps = pTraceFilter - > GetTraceType ( ) = = TRACE_ENTITIES_ONLY ;
bool bFilterStaticProps = pTraceFilter - > GetTraceType ( ) = = TRACE_EVERYTHING_FILTER_PROPS ;
trace_t trace ;
ICollideable * pCollideable ;
const char * pDebugName ;
for ( int iEntity = 0 ; iEntity < traceData . m_nEntityCount ; + + iEntity )
{
// Generate a collideable.
IHandleEntity * pHandleEntity = traceData . m_aEntityList [ iEntity ] ;
HandleEntityToCollideable ( pHandleEntity , & pCollideable , & pDebugName ) ;
// Check for error condition.
if ( ! IsSolid ( pCollideable - > GetSolid ( ) , pCollideable - > GetSolidFlags ( ) ) )
{
Assert ( 0 ) ;
Msg ( " %s in solid list (not solid) \n " , pDebugName ) ;
continue ;
}
if ( ! StaticPropMgr ( ) - > IsStaticProp ( pHandleEntity ) )
{
if ( ! pTraceFilter - > ShouldHitEntity ( pHandleEntity , fMask ) )
continue ;
}
else
{
// FIXME: Could remove this check here by
// using a different spatial partition mask. Look into it
// if we want more speedups here.
if ( bNoStaticProps )
continue ;
if ( bFilterStaticProps )
{
if ( ! pTraceFilter - > ShouldHitEntity ( pHandleEntity , fMask ) )
continue ;
}
}
ClipRayToCollideable ( entityRay , fMask , pCollideable , & trace ) ;
// Make sure the ray is always shorter than it currently is
ClipTraceToTrace ( trace , pTrace ) ;
// Stop if we're in allsolid
if ( pTrace - > allsolid )
break ;
}
// Fix up the fractions so they are appropriate given the original unclipped-to-world ray.
pTrace - > fraction * = flWorldFraction ;
pTrace - > fractionleftsolid * = flWorldFraction ;
if ( ! ray . m_IsRay )
{
// Make sure no fractionleftsolid can be used with box sweeps.
VectorAdd ( ray . m_Start , ray . m_StartOffset , pTrace - > startpos ) ;
pTrace - > fractionleftsolid = 0 ;
}
}
# if BENCHMARK_RAY_TEST
CON_COMMAND ( ray_save , " Save the rays " )
{
int count = s_BenchmarkRays . Count ( ) ;
if ( count )
{
FileHandle_t hFile = g_pFileSystem - > Open ( " rays.bin " , " wb " ) ;
if ( hFile )
{
g_pFileSystem - > Write ( & count , sizeof ( count ) , hFile ) ;
g_pFileSystem - > Write ( s_BenchmarkRays . Base ( ) , sizeof ( s_BenchmarkRays [ 0 ] ) * count , hFile ) ;
g_pFileSystem - > Close ( hFile ) ;
}
}
Msg ( " Saved %d rays \n " , count ) ;
}
CON_COMMAND ( ray_load , " Load the rays " )
{
s_BenchmarkRays . RemoveAll ( ) ;
FileHandle_t hFile = g_pFileSystem - > Open ( " rays.bin " , " rb " ) ;
if ( hFile )
{
int count = 0 ;
g_pFileSystem - > Read ( & count , sizeof ( count ) , hFile ) ;
if ( count )
{
s_BenchmarkRays . EnsureCount ( count ) ;
g_pFileSystem - > Read ( s_BenchmarkRays . Base ( ) , sizeof ( s_BenchmarkRays [ 0 ] ) * count , hFile ) ;
}
g_pFileSystem - > Close ( hFile ) ;
}
Msg ( " Loaded %d rays \n " , s_BenchmarkRays . Count ( ) ) ;
}
CON_COMMAND ( ray_clear , " Clear the current rays " )
{
s_BenchmarkRays . RemoveAll ( ) ;
Msg ( " Reset rays! \n " ) ;
}
CON_COMMAND_EXTERN ( ray_bench , RayBench , " Time the rays " )
{
# if VPROF_LEVEL > 0
g_VProfCurrentProfile . Start ( ) ;
g_VProfCurrentProfile . Reset ( ) ;
g_VProfCurrentProfile . ResetPeaks ( ) ;
# endif
{
double tStart = Plat_FloatTime ( ) ;
trace_t trace ;
int hit = 0 ;
int miss = 0 ;
int rayVsProp = 0 ;
int boxVsProp = 0 ;
for ( int i = 0 ; i < s_BenchmarkRays . Count ( ) ; i + + )
{
CM_BoxTrace ( s_BenchmarkRays [ i ] , 0 , MASK_SOLID , true , trace ) ;
if ( 0 )
{
VPROF ( " QueryStaticProps " ) ;
// Create a ray that extends only until we hit the world and adjust the trace accordingly
Ray_t entityRay = s_BenchmarkRays [ i ] ;
VectorScale ( entityRay . m_Delta , trace . fraction , entityRay . m_Delta ) ;
CEntityListAlongRay enumerator ;
enumerator . Reset ( ) ;
SpatialPartition ( ) - > EnumerateElementsAlongRay ( PARTITION_ENGINE_SOLID_EDICTS , entityRay , false , & enumerator ) ;
trace_t tr ;
ICollideable * pCollideable ;
int nCount = enumerator . Count ( ) ;
const char * pDebugName = NULL ;
//float flWorldFraction = trace.fraction;
if ( 0 )
{
VPROF ( " IntersectStaticProps " ) ;
for ( int i = 0 ; i < nCount ; + + i )
{
// Generate a collideable
IHandleEntity * pHandleEntity = enumerator . m_EntityHandles [ i ] ;
if ( ! StaticPropMgr ( ) - > IsStaticProp ( pHandleEntity ) )
continue ;
if ( entityRay . m_IsRay )
rayVsProp + + ;
else
boxVsProp + + ;
s_EngineTraceServer . HandleEntityToCollideable ( pHandleEntity , & pCollideable , & pDebugName ) ;
s_EngineTraceServer . ClipRayToCollideable ( entityRay , MASK_SOLID , pCollideable , & tr ) ;
// Make sure the ray is always shorter than it currently is
s_EngineTraceServer . ClipTraceToTrace ( tr , & trace ) ;
}
}
}
if ( trace . DidHit ( ) )
hit + + ;
else
miss + + ;
# if VPROF_LEVEL > 0
g_VProfCurrentProfile . MarkFrame ( ) ;
# endif
}
double tEnd = Plat_FloatTime ( ) ;
float ms = ( tEnd - tStart ) * 1000.0f ;
int swept = 0 ;
int point = 0 ;
for ( int i = 0 ; i < s_BenchmarkRays . Count ( ) ; i + + )
{
swept + = s_BenchmarkRays [ i ] . m_IsSwept ? 1 : 0 ;
point + = s_BenchmarkRays [ i ] . m_IsRay ? 1 : 0 ;
}
Msg ( " RAY TEST: %d hits, %d misses, %.2fms (%d rays, %d sweeps) (%d ray/prop, %d box/prop) \n " , hit , miss , ms , point , swept , rayVsProp , boxVsProp ) ;
}
# if VPROF_LEVEL > 0
g_VProfCurrentProfile . MarkFrame ( ) ;
g_VProfCurrentProfile . Stop ( ) ;
g_VProfCurrentProfile . OutputReport ( VPRT_FULL & ~ VPRT_HIERARCHY , NULL ) ;
# endif
}
# endif
//-----------------------------------------------------------------------------
// A version that simply accepts a ray (can work as a traceline or tracehull)
//-----------------------------------------------------------------------------
void CEngineTrace : : TraceRay ( const Ray_t & ray , unsigned int fMask , ITraceFilter * pTraceFilter , trace_t * pTrace )
{
2024-07-15 23:27:31 +02:00
# if defined _DEBUG
2020-04-22 18:56:21 +02:00
if ( debugrayenable . GetBool ( ) )
{
s_FrameRays . AddToTail ( ray ) ;
}
# endif
# if BENCHMARK_RAY_TEST
if ( s_BenchmarkRays . Count ( ) < 15000 )
{
s_BenchmarkRays . EnsureCapacity ( 15000 ) ;
s_BenchmarkRays . AddToTail ( ray ) ;
}
# endif
tmZone ( TELEMETRY_LEVEL1 , TMZF_NONE , " %s:%d " , __FUNCTION__ , __LINE__ ) ;
VPROF_INCREMENT_COUNTER ( " TraceRay " , 1 ) ;
m_traceStatCounters [ TRACE_STAT_COUNTER_TRACERAY ] + + ;
// VPROF_BUDGET( "CEngineTrace::TraceRay", "Ray/Hull Trace" );
CTraceFilterHitAll traceFilter ;
if ( ! pTraceFilter )
{
pTraceFilter = & traceFilter ;
}
CM_ClearTrace ( pTrace ) ;
// Collide with the world.
if ( pTraceFilter - > GetTraceType ( ) ! = TRACE_ENTITIES_ONLY )
{
ICollideable * pCollide = GetWorldCollideable ( ) ;
Assert ( pCollide ) ;
// Make sure the world entity is unrotated
// FIXME: BAH! The !pCollide test here is because of
// CStaticProp::PrecacheLighting.. it's occurring too early
// need to fix that later
Assert ( ! pCollide | | pCollide - > GetCollisionOrigin ( ) = = vec3_origin ) ;
Assert ( ! pCollide | | pCollide - > GetCollisionAngles ( ) = = vec3_angle ) ;
CM_BoxTrace ( ray , 0 , fMask , true , * pTrace ) ;
SetTraceEntity ( pCollide , pTrace ) ;
// inside world, no need to check being inside anything else
if ( pTrace - > startsolid )
return ;
// Early out if we only trace against the world
if ( pTraceFilter - > GetTraceType ( ) = = TRACE_WORLD_ONLY )
return ;
}
else
{
// Set initial start + endpos, necessary if the world isn't traced against
// because we may not trace against *anything* below.
VectorAdd ( ray . m_Start , ray . m_StartOffset , pTrace - > startpos ) ;
VectorAdd ( pTrace - > startpos , ray . m_Delta , pTrace - > endpos ) ;
}
// Save the world collision fraction.
float flWorldFraction = pTrace - > fraction ;
float flWorldFractionLeftSolidScale = flWorldFraction ;
// Create a ray that extends only until we hit the world
// and adjust the trace accordingly
Ray_t entityRay = ray ;
if ( pTrace - > fraction = = 0 )
{
entityRay . m_Delta . Init ( ) ;
flWorldFractionLeftSolidScale = pTrace - > fractionleftsolid ;
pTrace - > fractionleftsolid = 1.0f ;
pTrace - > fraction = 1.0f ;
}
else
{
// Explicitly compute end so that this computation happens at the quantization of
// the output (endpos). That way we won't miss any intersections we would get
// by feeding these results back in to the tracer
// This is not the same as entityRay.m_Delta *= pTrace->fraction which happens
// at a quantization that is more precise as m_Start moves away from the origin
Vector end ;
VectorMA ( entityRay . m_Start , pTrace - > fraction , entityRay . m_Delta , end ) ;
VectorSubtract ( end , entityRay . m_Start , entityRay . m_Delta ) ;
// We know this is safe because pTrace->fraction != 0
pTrace - > fractionleftsolid / = pTrace - > fraction ;
pTrace - > fraction = 1.0 ;
}
// Collide with entities along the ray
// FIXME: Hitbox code causes this to be re-entrant for the IK stuff.
// If we could eliminate that, this could be static and therefore
// not have to reallocate memory all the time
CEntityListAlongRay enumerator ;
enumerator . Reset ( ) ;
SpatialPartition ( ) - > EnumerateElementsAlongRay ( SpatialPartitionMask ( ) , entityRay , false , & enumerator ) ;
bool bNoStaticProps = pTraceFilter - > GetTraceType ( ) = = TRACE_ENTITIES_ONLY ;
bool bFilterStaticProps = pTraceFilter - > GetTraceType ( ) = = TRACE_EVERYTHING_FILTER_PROPS ;
trace_t tr ;
ICollideable * pCollideable ;
const char * pDebugName ;
int nCount = enumerator . Count ( ) ;
for ( int i = 0 ; i < nCount ; + + i )
{
// Generate a collideable
IHandleEntity * pHandleEntity = enumerator . m_EntityHandles [ i ] ;
HandleEntityToCollideable ( pHandleEntity , & pCollideable , & pDebugName ) ;
// Check for error condition
if ( IsPC ( ) & & IsDebug ( ) & & ! IsSolid ( pCollideable - > GetSolid ( ) , pCollideable - > GetSolidFlags ( ) ) )
{
Assert ( 0 ) ;
Msg ( " %s in solid list (not solid) \n " , pDebugName ) ;
continue ;
}
if ( ! StaticPropMgr ( ) - > IsStaticProp ( pHandleEntity ) )
{
if ( ! pTraceFilter - > ShouldHitEntity ( pHandleEntity , fMask ) )
continue ;
}
else
{
// FIXME: Could remove this check here by
// using a different spatial partition mask. Look into it
// if we want more speedups here.
if ( bNoStaticProps )
continue ;
if ( bFilterStaticProps )
{
if ( ! pTraceFilter - > ShouldHitEntity ( pHandleEntity , fMask ) )
continue ;
}
}
ClipRayToCollideable ( entityRay , fMask , pCollideable , & tr ) ;
// Make sure the ray is always shorter than it currently is
ClipTraceToTrace ( tr , pTrace ) ;
// Stop if we're in allsolid
if ( pTrace - > allsolid )
break ;
}
// Fix up the fractions so they are appropriate given the original
// unclipped-to-world ray
pTrace - > fraction * = flWorldFraction ;
pTrace - > fractionleftsolid * = flWorldFractionLeftSolidScale ;
# ifdef _DEBUG
Vector vecOffset , vecEndTest ;
VectorAdd ( ray . m_Start , ray . m_StartOffset , vecOffset ) ;
VectorMA ( vecOffset , pTrace - > fractionleftsolid , ray . m_Delta , vecEndTest ) ;
Assert ( VectorsAreEqual ( vecEndTest , pTrace - > startpos , 0.1f ) ) ;
VectorMA ( vecOffset , pTrace - > fraction , ray . m_Delta , vecEndTest ) ;
Assert ( VectorsAreEqual ( vecEndTest , pTrace - > endpos , 0.1f ) ) ;
// Assert( !ray.m_IsRay || pTrace->allsolid || pTrace->fraction >= pTrace->fractionleftsolid );
# endif
if ( ! ray . m_IsRay )
{
// Make sure no fractionleftsolid can be used with box sweeps
VectorAdd ( ray . m_Start , ray . m_StartOffset , pTrace - > startpos ) ;
pTrace - > fractionleftsolid = 0 ;
# ifdef _DEBUG
pTrace - > fractionleftsolid = VEC_T_NAN ;
# endif
}
}
//-----------------------------------------------------------------------------
// A version that sweeps a collideable through the world
//-----------------------------------------------------------------------------
void CEngineTrace : : SweepCollideable ( ICollideable * pCollide ,
const Vector & vecAbsStart , const Vector & vecAbsEnd , const QAngle & vecAngles ,
unsigned int fMask , ITraceFilter * pTraceFilter , trace_t * pTrace )
{
const matrix3x4_t * pOldRoot = m_pRootMoveParent ;
Ray_t ray ;
Assert ( vecAngles = = vec3_angle ) ;
if ( pCollide - > GetSolidFlags ( ) & FSOLID_ROOT_PARENT_ALIGNED )
{
m_pRootMoveParent = pCollide - > GetRootParentToWorldTransform ( ) ;
}
ray . Init ( vecAbsStart , vecAbsEnd , pCollide - > OBBMins ( ) , pCollide - > OBBMaxs ( ) ) ;
TraceRay ( ray , fMask , pTraceFilter , pTrace ) ;
m_pRootMoveParent = pOldRoot ;
}
//-----------------------------------------------------------------------------
// Lets clients know about all edicts along a ray
//-----------------------------------------------------------------------------
class CEnumerationFilter : public IPartitionEnumerator
{
public :
CEnumerationFilter ( CEngineTrace * pEngineTrace , IEntityEnumerator * pEnumerator ) :
m_pEngineTrace ( pEngineTrace ) , m_pEnumerator ( pEnumerator ) { }
IterationRetval_t EnumElement ( IHandleEntity * pHandleEntity )
{
// Don't enumerate static props
if ( StaticPropMgr ( ) - > IsStaticProp ( pHandleEntity ) )
return ITERATION_CONTINUE ;
if ( ! m_pEnumerator - > EnumEntity ( pHandleEntity ) )
{
return ITERATION_STOP ;
}
return ITERATION_CONTINUE ;
}
private :
IEntityEnumerator * m_pEnumerator ;
CEngineTrace * m_pEngineTrace ;
} ;
//-----------------------------------------------------------------------------
// Enumerates over all entities along a ray
// If triggers == true, it enumerates all triggers along a ray
//-----------------------------------------------------------------------------
void CEngineTrace : : EnumerateEntities ( const Ray_t & ray , bool bTriggers , IEntityEnumerator * pEnumerator )
{
m_traceStatCounters [ TRACE_STAT_COUNTER_ENUMERATE ] + + ;
// FIXME: If we store CBaseHandles directly in the spatial partition, this method
// basically becomes obsolete. The spatial partition can be queried directly.
CEnumerationFilter enumerator ( this , pEnumerator ) ;
int fMask = ! bTriggers ? SpatialPartitionMask ( ) : SpatialPartitionTriggerMask ( ) ;
// NOTE: Triggers currently don't exist on the client
if ( fMask )
{
SpatialPartition ( ) - > EnumerateElementsAlongRay ( fMask , ray , false , & enumerator ) ;
}
}
//-----------------------------------------------------------------------------
// Lets clients know about all entities in a box
//-----------------------------------------------------------------------------
void CEngineTrace : : EnumerateEntities ( const Vector & vecAbsMins , const Vector & vecAbsMaxs , IEntityEnumerator * pEnumerator )
{
m_traceStatCounters [ TRACE_STAT_COUNTER_ENUMERATE ] + + ;
// FIXME: If we store CBaseHandles directly in the spatial partition, this method
// basically becomes obsolete. The spatial partition can be queried directly.
CEnumerationFilter enumerator ( this , pEnumerator ) ;
SpatialPartition ( ) - > EnumerateElementsInBox ( SpatialPartitionMask ( ) ,
vecAbsMins , vecAbsMaxs , false , & enumerator ) ;
}
class CEntList : public IEntityEnumerator
{
public :
virtual bool EnumEntity ( IHandleEntity * pHandleEntity )
{
IServerUnknown * pNetEntity = static_cast < IServerUnknown * > ( pHandleEntity ) ;
ICollideable * pCollide = pNetEntity - > GetCollideable ( ) ;
if ( ! pCollide )
return true ;
Vector vecCenter ;
VectorMA ( MainViewOrigin ( ) , 100.0f , MainViewForward ( ) , vecCenter ) ;
float flDist = ( vecCenter - pCollide - > GetCollisionOrigin ( ) ) . LengthSqr ( ) ;
if ( flDist < m_flClosestDist )
{
m_flClosestDist = flDist ;
m_pClosest = pCollide ;
}
return true ;
}
ICollideable * m_pClosest ;
float m_flClosestDist ;
} ;
# ifdef _DEBUG
//-----------------------------------------------------------------------------
// A method to test out sweeps
//-----------------------------------------------------------------------------
CON_COMMAND ( test_sweepaabb , " method to test out sweeps " )
{
Vector vecStartPoint ;
VectorMA ( MainViewOrigin ( ) , 50.0f , MainViewForward ( ) , vecStartPoint ) ;
Vector endPoint ;
VectorMA ( MainViewOrigin ( ) , COORD_EXTENT * 1.74f , MainViewForward ( ) , endPoint ) ;
Ray_t ray ;
ray . Init ( vecStartPoint , endPoint ) ;
trace_t tr ;
// CTraceFilterHitAll traceFilter;
// g_pEngineTraceServer->TraceRay( ray, MASK_ALL, &traceFilter, &tr );
CEntList list ;
list . m_pClosest = NULL ;
list . m_flClosestDist = FLT_MAX ;
g_pEngineTraceServer - > EnumerateEntities ( MainViewOrigin ( ) - Vector ( 200 , 200 , 200 ) , MainViewOrigin ( ) + Vector ( 200 , 200 , 200 ) , & list ) ;
if ( ! list . m_pClosest )
return ;
// Visualize the intersection test
ICollideable * pCollide = list . m_pClosest ;
if ( pCollide - > GetCollisionOrigin ( ) = = vec3_origin )
return ;
QAngle test ( 0 , 45 , 0 ) ;
# ifndef SWDS
CDebugOverlay : : AddBoxOverlay ( pCollide - > GetCollisionOrigin ( ) ,
pCollide - > OBBMins ( ) , pCollide - > OBBMaxs ( ) ,
test /*pCollide->GetCollisionAngles()*/ , 0 , 0 , 255 , 128 , 5.0f ) ;
# endif
VectorMA ( MainViewOrigin ( ) , 200.0f , MainViewForward ( ) , endPoint ) ;
ray . Init ( vecStartPoint , endPoint , Vector ( - 10 , - 20 , - 10 ) , Vector ( 30 , 30 , 20 ) ) ;
bool bIntersect = IntersectRayWithOBB ( ray , pCollide - > GetCollisionOrigin ( ) , test , pCollide - > OBBMins ( ) ,
pCollide - > OBBMaxs ( ) , 0.0f , & tr ) ;
unsigned char r , g , b , a ;
b = 0 ;
a = 255 ;
r = bIntersect ? 255 : 0 ;
g = bIntersect ? 0 : 255 ;
# ifndef SWDS
CDebugOverlay : : AddSweptBoxOverlay ( tr . startpos , tr . endpos ,
Vector ( - 10 , - 20 , - 10 ) , Vector ( 30 , 30 , 20 ) , vec3_angle , r , g , b , a , 5.0 ) ;
# endif
}
# endif