Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2010 lines
63 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #include "engine/IEngineTrace.h"
  10. #include "icliententitylist.h"
  11. #include "ispatialpartitioninternal.h"
  12. #include "icliententity.h"
  13. #include "cmodel_engine.h"
  14. #include "dispcoll_common.h"
  15. #include "staticpropmgr.h"
  16. #include "server.h"
  17. #include "edict.h"
  18. #include "gl_model_private.h"
  19. #include "world.h"
  20. #include "vphysics_interface.h"
  21. #include "client_class.h"
  22. #include "server_class.h"
  23. #include "debugoverlay.h"
  24. #include "collisionutils.h"
  25. #include "tier0/vprof.h"
  26. #include "convar.h"
  27. #include "mathlib/polyhedron.h"
  28. #include "sys_dll.h"
  29. #include "vphysics/virtualmesh.h"
  30. // memdbgon must be the last include file in a .cpp file!!!
  31. #include "tier0/memdbgon.h"
  32. //-----------------------------------------------------------------------------
  33. // Various statistics to gather
  34. //-----------------------------------------------------------------------------
  35. enum
  36. {
  37. TRACE_STAT_COUNTER_TRACERAY = 0,
  38. TRACE_STAT_COUNTER_POINTCONTENTS,
  39. TRACE_STAT_COUNTER_ENUMERATE,
  40. NUM_TRACE_STAT_COUNTER
  41. };
  42. //-----------------------------------------------------------------------------
  43. // Used to visualize raycasts going on
  44. //-----------------------------------------------------------------------------
  45. #ifdef _DEBUG
  46. ConVar debugrayenable( "debugrayenable", "0", NULL, "Use this to enable ray testing. To reset: bind \"F1\" \"clearalloverlays; debugrayreset 0; host_framerate 66.66666667\"" );
  47. ConVar debugrayreset( "debugrayreset", "0" );
  48. ConVar debugraylimit( "debugraylimit", "500", NULL, "number of rays per frame that you have to hit before displaying them all" );
  49. static CUtlVector<Ray_t> s_FrameRays;
  50. #endif
  51. #define BENCHMARK_RAY_TEST 0
  52. #if BENCHMARK_RAY_TEST
  53. static CUtlVector<Ray_t> s_BenchmarkRays;
  54. #endif
  55. //-----------------------------------------------------------------------------
  56. // Implementation of IEngineTrace
  57. //-----------------------------------------------------------------------------
  58. abstract_class CEngineTrace : public IEngineTrace
  59. {
  60. public:
  61. CEngineTrace() { m_pRootMoveParent = NULL; }
  62. // Returns the contents mask at a particular world-space position
  63. virtual int GetPointContents( const Vector &vecAbsPosition, IHandleEntity** ppEntity );
  64. virtual int GetPointContents_Collideable( ICollideable *pCollide, const Vector &vecAbsPosition );
  65. // Traces a ray against a particular edict
  66. virtual void ClipRayToEntity( const Ray_t &ray, unsigned int fMask, IHandleEntity *pEntity, trace_t *pTrace );
  67. // A version that simply accepts a ray (can work as a traceline or tracehull)
  68. virtual void TraceRay( const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace );
  69. // A version that sets up the leaf and entity lists and allows you to pass those in for collision.
  70. virtual void SetupLeafAndEntityListRay( const Ray_t &ray, CTraceListData &traceData );
  71. virtual void SetupLeafAndEntityListBox( const Vector &vecBoxMin, const Vector &vecBoxMax, CTraceListData &traceData );
  72. virtual void TraceRayAgainstLeafAndEntityList( const Ray_t &ray, CTraceListData &traceData, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace );
  73. // A version that sweeps a collideable through the world
  74. // abs start + abs end represents the collision origins you want to sweep the collideable through
  75. // vecAngles represents the collision angles of the collideable during the sweep
  76. virtual void SweepCollideable( ICollideable *pCollide, const Vector &vecAbsStart, const Vector &vecAbsEnd,
  77. const QAngle &vecAngles, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace );
  78. // Enumerates over all entities along a ray
  79. // If triggers == true, it enumerates all triggers along a ray
  80. virtual void EnumerateEntities( const Ray_t &ray, bool triggers, IEntityEnumerator *pEnumerator );
  81. // Same thing, but enumerate entitys within a box
  82. virtual void EnumerateEntities( const Vector &vecAbsMins, const Vector &vecAbsMaxs, IEntityEnumerator *pEnumerator );
  83. // FIXME: Different versions for client + server. Eventually we need to make these go away
  84. virtual void HandleEntityToCollideable( IHandleEntity *pHandleEntity, ICollideable **ppCollide, const char **ppDebugName ) = 0;
  85. virtual ICollideable *GetWorldCollideable() = 0;
  86. // Traces a ray against a particular edict
  87. virtual void ClipRayToCollideable( const Ray_t &ray, unsigned int fMask, ICollideable *pEntity, trace_t *pTrace );
  88. // HACKHACK: Temp
  89. virtual int GetStatByIndex( int index, bool bClear );
  90. //finds brushes in an AABB, prone to some false positives
  91. virtual void GetBrushesInAABB( const Vector &vMins, const Vector &vMaxs, CUtlVector<int> *pOutput, int iContentsMask = 0xFFFFFFFF );
  92. //Creates a CPhysCollide out of all displacements wholly or partially contained in the specified AABB
  93. virtual CPhysCollide* GetCollidableFromDisplacementsInAABB( const Vector& vMins, const Vector& vMaxs );
  94. //retrieve brush planes and contents, returns true if data is being returned in the output pointers, false if the brush doesn't exist
  95. virtual bool GetBrushInfo( int iBrush, CUtlVector<Vector4D> *pPlanesOut, int *pContentsOut );
  96. virtual bool PointOutsideWorld( const Vector &ptTest ); //Tests a point to see if it's outside any playable area
  97. // Walks bsp to find the leaf containing the specified point
  98. virtual int GetLeafContainingPoint( const Vector &ptTest );
  99. private:
  100. // FIXME: Different versions for client + server. Eventually we need to make these go away
  101. virtual void SetTraceEntity( ICollideable *pCollideable, trace_t *pTrace ) = 0;
  102. virtual ICollideable *GetCollideable( IHandleEntity *pEntity ) = 0;
  103. virtual int SpatialPartitionMask() const = 0;
  104. virtual int SpatialPartitionTriggerMask() const = 0;
  105. // Figure out point contents for entities at a particular position
  106. int EntityContents( const Vector &vecAbsPosition );
  107. // Should we perform the custom raytest?
  108. bool ShouldPerformCustomRayTest( const Ray_t& ray, ICollideable *pCollideable ) const;
  109. // Performs the custom raycast
  110. bool ClipRayToCustom( const Ray_t& ray, unsigned int fMask, ICollideable *pCollideable, trace_t* pTrace );
  111. // Perform vphysics trace
  112. bool ClipRayToVPhysics( const Ray_t &ray, unsigned int fMask, ICollideable *pCollideable, studiohdr_t *pStudioHdr, trace_t *pTrace );
  113. // Perform hitbox trace
  114. bool ClipRayToHitboxes( const Ray_t& ray, unsigned int fMask, ICollideable *pCollideable, trace_t* pTrace );
  115. // Perform bsp trace
  116. bool ClipRayToBSP( const Ray_t &ray, unsigned int fMask, ICollideable *pCollideable, trace_t *pTrace );
  117. // bbox
  118. bool ClipRayToBBox( const Ray_t &ray, unsigned int fMask, ICollideable *pCollideable, trace_t *pTrace );
  119. // OBB
  120. bool ClipRayToOBB( const Ray_t &ray, unsigned int fMask, ICollideable *pEntity, trace_t *pTrace );
  121. // Clips a trace to another trace
  122. bool ClipTraceToTrace( trace_t &clipTrace, trace_t *pFinalTrace );
  123. private:
  124. int m_traceStatCounters[NUM_TRACE_STAT_COUNTER];
  125. const matrix3x4_t *m_pRootMoveParent;
  126. friend void RayBench( const CCommand &args );
  127. };
  128. class CEngineTraceServer : public CEngineTrace
  129. {
  130. private:
  131. virtual void HandleEntityToCollideable( IHandleEntity *pEnt, ICollideable **ppCollide, const char **ppDebugName );
  132. virtual void SetTraceEntity( ICollideable *pCollideable, trace_t *pTrace );
  133. virtual int SpatialPartitionMask() const;
  134. virtual int SpatialPartitionTriggerMask() const;
  135. virtual ICollideable *GetWorldCollideable();
  136. friend void RayBench( const CCommand &args );
  137. public:
  138. // IEngineTrace
  139. virtual ICollideable *GetCollideable( IHandleEntity *pEntity );
  140. };
  141. #ifndef SWDS
  142. class CEngineTraceClient : public CEngineTrace
  143. {
  144. private:
  145. virtual void HandleEntityToCollideable( IHandleEntity *pEnt, ICollideable **ppCollide, const char **ppDebugName );
  146. virtual void SetTraceEntity( ICollideable *pCollideable, trace_t *pTrace );
  147. virtual int SpatialPartitionMask() const;
  148. virtual int SpatialPartitionTriggerMask() const;
  149. virtual ICollideable *GetWorldCollideable();
  150. public:
  151. // IEngineTrace
  152. virtual ICollideable *GetCollideable( IHandleEntity *pEntity );
  153. };
  154. #endif
  155. //-----------------------------------------------------------------------------
  156. // Expose CVEngineServer to the game + client DLLs
  157. //-----------------------------------------------------------------------------
  158. static CEngineTraceServer s_EngineTraceServer;
  159. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CEngineTraceServer, IEngineTrace, INTERFACEVERSION_ENGINETRACE_SERVER, s_EngineTraceServer);
  160. #ifndef SWDS
  161. static CEngineTraceClient s_EngineTraceClient;
  162. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CEngineTraceClient, IEngineTrace, INTERFACEVERSION_ENGINETRACE_CLIENT, s_EngineTraceClient);
  163. #endif
  164. //-----------------------------------------------------------------------------
  165. // Expose CVEngineServer to the engine.
  166. //-----------------------------------------------------------------------------
  167. IEngineTrace *g_pEngineTraceServer = &s_EngineTraceServer;
  168. #ifndef SWDS
  169. IEngineTrace *g_pEngineTraceClient = &s_EngineTraceClient;
  170. #endif
  171. //-----------------------------------------------------------------------------
  172. // Client-server neutral method of getting at collideables
  173. //-----------------------------------------------------------------------------
  174. #ifndef SWDS
  175. ICollideable *CEngineTraceClient::GetCollideable( IHandleEntity *pEntity )
  176. {
  177. Assert( pEntity );
  178. ICollideable *pProp = StaticPropMgr()->GetStaticProp( pEntity );
  179. if ( pProp )
  180. return pProp;
  181. IClientUnknown *pUnk = entitylist->GetClientUnknownFromHandle( pEntity->GetRefEHandle() );
  182. return pUnk->GetCollideable();
  183. }
  184. #endif
  185. ICollideable *CEngineTraceServer::GetCollideable( IHandleEntity *pEntity )
  186. {
  187. Assert( pEntity );
  188. ICollideable *pProp = StaticPropMgr()->GetStaticProp( pEntity );
  189. if ( pProp )
  190. return pProp;
  191. IServerUnknown *pNetUnknown = static_cast<IServerUnknown*>(pEntity);
  192. return pNetUnknown->GetCollideable();
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Spatial partition masks for iteration
  196. //-----------------------------------------------------------------------------
  197. #ifndef SWDS
  198. int CEngineTraceClient::SpatialPartitionMask() const
  199. {
  200. return PARTITION_CLIENT_SOLID_EDICTS;
  201. }
  202. #endif
  203. int CEngineTraceServer::SpatialPartitionMask() const
  204. {
  205. return PARTITION_ENGINE_SOLID_EDICTS;
  206. }
  207. #ifndef SWDS
  208. int CEngineTraceClient::SpatialPartitionTriggerMask() const
  209. {
  210. return 0;
  211. }
  212. #endif
  213. int CEngineTraceServer::SpatialPartitionTriggerMask() const
  214. {
  215. return PARTITION_ENGINE_TRIGGER_EDICTS;
  216. }
  217. //-----------------------------------------------------------------------------
  218. // Spatial partition enumerator looking for entities that we may lie within
  219. //-----------------------------------------------------------------------------
  220. class CPointContentsEnum : public IPartitionEnumerator
  221. {
  222. public:
  223. CPointContentsEnum( CEngineTrace *pEngineTrace, const Vector &pos ) : m_Contents(CONTENTS_EMPTY)
  224. {
  225. m_pEngineTrace = pEngineTrace;
  226. m_Pos = pos;
  227. m_pCollide = NULL;
  228. }
  229. static inline bool TestEntity(
  230. CEngineTrace *pEngineTrace,
  231. ICollideable *pCollide,
  232. const Vector &vPos,
  233. int *pContents,
  234. ICollideable **pWorldCollideable )
  235. {
  236. // Deal with static props
  237. // NOTE: I could have added static props to a different list and
  238. // enumerated them separately, but that would have been less efficient
  239. if ( StaticPropMgr()->IsStaticProp( pCollide->GetEntityHandle() ) )
  240. {
  241. Ray_t ray;
  242. trace_t trace;
  243. ray.Init( vPos, vPos );
  244. pEngineTrace->ClipRayToCollideable( ray, MASK_ALL, pCollide, &trace );
  245. if (trace.startsolid)
  246. {
  247. // We're in a static prop; that's solid baby
  248. // Pretend we hit the world
  249. *pContents = CONTENTS_SOLID;
  250. *pWorldCollideable = pEngineTrace->GetWorldCollideable();
  251. return true;
  252. }
  253. return false;
  254. }
  255. // We only care about solid volumes
  256. if ((pCollide->GetSolidFlags() & FSOLID_VOLUME_CONTENTS) == 0)
  257. return false;
  258. model_t* pModel = (model_t*)pCollide->GetCollisionModel();
  259. if ( pModel && pModel->type == mod_brush )
  260. {
  261. Assert( pCollide->GetCollisionModelIndex() < MAX_MODELS && pCollide->GetCollisionModelIndex() >= 0 );
  262. int nHeadNode = GetModelHeadNode( pCollide );
  263. int contents = CM_TransformedPointContents( vPos, nHeadNode,
  264. pCollide->GetCollisionOrigin(), pCollide->GetCollisionAngles() );
  265. if (contents != CONTENTS_EMPTY)
  266. {
  267. // Return the contents of the first thing we hit
  268. *pContents = contents;
  269. *pWorldCollideable = pCollide;
  270. return true;
  271. }
  272. }
  273. return false;
  274. }
  275. IterationRetval_t EnumElement( IHandleEntity *pHandleEntity )
  276. {
  277. ICollideable *pCollide;
  278. const char *pDbgName;
  279. m_pEngineTrace->HandleEntityToCollideable( pHandleEntity, &pCollide, &pDbgName );
  280. if (!pCollide)
  281. return ITERATION_CONTINUE;
  282. if ( CPointContentsEnum::TestEntity( m_pEngineTrace, pCollide, m_Pos, &m_Contents, &m_pCollide ) )
  283. return ITERATION_STOP;
  284. else
  285. return ITERATION_CONTINUE;
  286. }
  287. private:
  288. static int GetModelHeadNode( ICollideable *pCollide )
  289. {
  290. int modelindex = pCollide->GetCollisionModelIndex();
  291. if(modelindex >= MAX_MODELS || modelindex < 0)
  292. return -1;
  293. model_t *pModel = (model_t*)pCollide->GetCollisionModel();
  294. if(!pModel)
  295. return -1;
  296. if(cmodel_t *pCModel = CM_InlineModelNumber(modelindex-1))
  297. return pCModel->headnode;
  298. else
  299. return -1;
  300. }
  301. public:
  302. int m_Contents;
  303. ICollideable *m_pCollide;
  304. private:
  305. CEngineTrace *m_pEngineTrace;
  306. Vector m_Pos;
  307. };
  308. //-----------------------------------------------------------------------------
  309. // Returns the contents mask at a particular world-space position
  310. //-----------------------------------------------------------------------------
  311. int CEngineTrace::GetPointContents( const Vector &vecAbsPosition, IHandleEntity** ppEntity )
  312. {
  313. VPROF( "CEngineTrace_GetPointContents" );
  314. // VPROF_BUDGET( "CEngineTrace_GetPointContents", "CEngineTrace_GetPointContents" );
  315. m_traceStatCounters[TRACE_STAT_COUNTER_POINTCONTENTS]++;
  316. // First check the collision model
  317. int nContents = CM_PointContents( vecAbsPosition, 0 );
  318. if ( nContents & MASK_CURRENT )
  319. {
  320. nContents = CONTENTS_WATER;
  321. }
  322. if ( nContents != CONTENTS_SOLID )
  323. {
  324. CPointContentsEnum contentsEnum(this, vecAbsPosition);
  325. SpatialPartition()->EnumerateElementsAtPoint( SpatialPartitionMask(),
  326. vecAbsPosition, false, &contentsEnum );
  327. int nEntityContents = contentsEnum.m_Contents;
  328. if ( nEntityContents & MASK_CURRENT )
  329. nContents = CONTENTS_WATER;
  330. if ( nEntityContents != CONTENTS_EMPTY )
  331. {
  332. if (ppEntity)
  333. {
  334. *ppEntity = contentsEnum.m_pCollide->GetEntityHandle();
  335. }
  336. return nEntityContents;
  337. }
  338. }
  339. if (ppEntity)
  340. {
  341. *ppEntity = GetWorldCollideable()->GetEntityHandle();
  342. }
  343. return nContents;
  344. }
  345. int CEngineTrace::GetPointContents_Collideable( ICollideable *pCollide, const Vector &vecAbsPosition )
  346. {
  347. int contents = CONTENTS_EMPTY;
  348. ICollideable *pDummy;
  349. CPointContentsEnum::TestEntity( this, pCollide, vecAbsPosition, &contents, &pDummy );
  350. return contents;
  351. }
  352. //-----------------------------------------------------------------------------
  353. // Should we perform the custom raytest?
  354. //-----------------------------------------------------------------------------
  355. inline bool CEngineTrace::ShouldPerformCustomRayTest( const Ray_t& ray, ICollideable *pCollideable ) const
  356. {
  357. // No model? The entity's got its own collision detector maybe
  358. // Does the entity force box or ray tests to go through its code?
  359. return( (pCollideable->GetSolid() == SOLID_CUSTOM) ||
  360. (ray.m_IsRay && (pCollideable->GetSolidFlags() & FSOLID_CUSTOMRAYTEST )) ||
  361. (!ray.m_IsRay && (pCollideable->GetSolidFlags() & FSOLID_CUSTOMBOXTEST )) );
  362. }
  363. //-----------------------------------------------------------------------------
  364. // Performs the custom raycast
  365. //-----------------------------------------------------------------------------
  366. bool CEngineTrace::ClipRayToCustom( const Ray_t& ray, unsigned int fMask, ICollideable *pCollideable, trace_t* pTrace )
  367. {
  368. if ( pCollideable->TestCollision( ray, fMask, *pTrace ))
  369. {
  370. return true;
  371. }
  372. return false;
  373. }
  374. //-----------------------------------------------------------------------------
  375. // Performs the hitbox raycast, returns true if the hitbox test was made
  376. //-----------------------------------------------------------------------------
  377. bool CEngineTrace::ClipRayToHitboxes( const Ray_t& ray, unsigned int fMask, ICollideable *pCollideable, trace_t* pTrace )
  378. {
  379. trace_t hitboxTrace;
  380. CM_ClearTrace( &hitboxTrace );
  381. // Keep track of the contents of what was hit initially
  382. hitboxTrace.contents = pTrace->contents;
  383. VectorAdd( ray.m_Start, ray.m_StartOffset, hitboxTrace.startpos );
  384. VectorAdd( hitboxTrace.startpos, ray.m_Delta, hitboxTrace.endpos );
  385. // At the moment, it has to be a true ray to work with hitboxes
  386. if ( !ray.m_IsRay )
  387. return false;
  388. // If the hitboxes weren't even tested, then just use the original trace
  389. if (!pCollideable->TestHitboxes( ray, fMask, hitboxTrace ))
  390. return false;
  391. // If they *were* tested and missed, clear the original trace
  392. if (!hitboxTrace.DidHit())
  393. {
  394. CM_ClearTrace( pTrace );
  395. pTrace->startpos = hitboxTrace.startpos;
  396. pTrace->endpos = hitboxTrace.endpos;
  397. }
  398. else if ( pCollideable->GetSolid() != SOLID_VPHYSICS )
  399. {
  400. // If we also hit the hitboxes, maintain fractionleftsolid +
  401. // startpos because those are reasonable enough values and the
  402. // hitbox code doesn't set those itself.
  403. Vector vecStartPos = pTrace->startpos;
  404. float flFractionLeftSolid = pTrace->fractionleftsolid;
  405. *pTrace = hitboxTrace;
  406. if (hitboxTrace.startsolid)
  407. {
  408. pTrace->startpos = vecStartPos;
  409. pTrace->fractionleftsolid = flFractionLeftSolid;
  410. }
  411. }
  412. else
  413. {
  414. // Fill out the trace hitbox details
  415. pTrace->contents = hitboxTrace.contents;
  416. pTrace->hitgroup = hitboxTrace.hitgroup;
  417. pTrace->hitbox = hitboxTrace.hitbox;
  418. pTrace->physicsbone = hitboxTrace.physicsbone;
  419. pTrace->surface = hitboxTrace.surface;
  420. Assert( pTrace->physicsbone >= 0 );
  421. // Fill out the surfaceprop details from the hitbox. Use the physics bone instead of the hitbox bone
  422. Assert(pTrace->surface.flags == SURF_HITBOX);
  423. }
  424. return true;
  425. }
  426. int CEngineTrace::GetStatByIndex( int index, bool bClear )
  427. {
  428. if ( index >= NUM_TRACE_STAT_COUNTER )
  429. return 0;
  430. int out = m_traceStatCounters[index];
  431. if ( bClear )
  432. {
  433. m_traceStatCounters[index] = 0;
  434. }
  435. return out;
  436. }
  437. static void FASTCALL GetBrushesInAABB_ParseLeaf( const Vector *pExtents, CCollisionBSPData *pBSPData, cleaf_t *pLeaf, CUtlVector<int> *pOutput, int iContentsMask, int *pCounters )
  438. {
  439. for( unsigned int i = 0; i != pLeaf->numleafbrushes; ++i )
  440. {
  441. int iBrushNumber = pBSPData->map_leafbrushes[pLeaf->firstleafbrush + i];
  442. cbrush_t *pBrush = &pBSPData->map_brushes[iBrushNumber];
  443. if( pCounters[iBrushNumber] )
  444. continue;
  445. pCounters[iBrushNumber] = 1;
  446. if( (pBrush->contents & iContentsMask) == 0 )
  447. continue;
  448. if ( pBrush->IsBox() )
  449. {
  450. cboxbrush_t *pBox = &pBSPData->map_boxbrushes[pBrush->GetBox()];
  451. if ( IsBoxIntersectingBox(pBox->mins, pBox->maxs, pExtents[0], pExtents[7]) )
  452. {
  453. pOutput->AddToTail(iBrushNumber);
  454. }
  455. }
  456. else
  457. {
  458. unsigned int j;
  459. for( j = 0; j != pBrush->numsides; ++j )
  460. {
  461. cplane_t *pPlane = pBSPData->map_brushsides[pBrush->firstbrushside + j].plane;
  462. if( (pExtents[pPlane->signbits].Dot( pPlane->normal ) - pPlane->dist) > 0.0f )
  463. break; //the bounding box extent that was most likely to be encapsulated by the plane is outside the halfspace, brush not in bbox
  464. }
  465. if( j == pBrush->numsides )
  466. pOutput->AddToTail( iBrushNumber ); //brush was most likely in bbox
  467. }
  468. }
  469. }
  470. void CEngineTrace::GetBrushesInAABB( const Vector &vMins, const Vector &vMaxs, CUtlVector<int> *pOutput, int iContentsMask )
  471. {
  472. if( pOutput == NULL ) return;
  473. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  474. Vector ptBBoxExtents[8]; //for fast plane checking
  475. for( int i = 0; i != 8; ++i )
  476. {
  477. //set these up to be opposite that of cplane_t's signbits for it's normal
  478. ptBBoxExtents[i].x = (i & (1<<0)) ? (vMaxs.x) : (vMins.x);
  479. ptBBoxExtents[i].y = (i & (1<<1)) ? (vMaxs.y) : (vMins.y);
  480. ptBBoxExtents[i].z = (i & (1<<2)) ? (vMaxs.z) : (vMins.z);
  481. }
  482. int *pLeafList = (int *)stackalloc( pBSPData->numleafs * 2 * sizeof( int ) ); // *2 just in case
  483. int iNumLeafs = CM_BoxLeafnums( vMins, vMaxs, pLeafList, pBSPData->numleafs * 2, NULL );
  484. CUtlVector<int> counters;
  485. counters.SetSize( pBSPData->numbrushes );
  486. memset( counters.Base(), 0, pBSPData->numbrushes * sizeof(int) );
  487. for( int i = 0; i != iNumLeafs; ++i )
  488. GetBrushesInAABB_ParseLeaf( ptBBoxExtents, pBSPData, &pBSPData->map_leafs[pLeafList[i]], pOutput, iContentsMask, counters.Base() );
  489. }
  490. //-----------------------------------------------------------------------------
  491. // Purpose: Used to copy the collision information of all displacement surfaces in a specified box
  492. // Input : vMins - min vector of the AABB
  493. // vMaxs - max vector of the AABB
  494. // Output : CPhysCollide* the collision mesh created from all the displacements partially contained in the specified box
  495. // Note: We're not clipping to the box. Collidable may be larger than the box provided.
  496. //-----------------------------------------------------------------------------
  497. CPhysCollide* CEngineTrace::GetCollidableFromDisplacementsInAABB( const Vector& vMins, const Vector& vMaxs )
  498. {
  499. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  500. int *pLeafList = (int *)stackalloc( pBSPData->numleafs * sizeof( int ) );
  501. int iLeafCount = CM_BoxLeafnums( vMins, vMaxs, pLeafList, pBSPData->numleafs, NULL );
  502. // Get all the triangles for displacement surfaces in this box, add them to a polysoup
  503. CPhysPolysoup *pDispCollideSoup = physcollision->PolysoupCreate();
  504. // Count total triangles added to this poly soup- Can't support more than 65435.
  505. int iTriCount = 0;
  506. TraceInfo_t *pTraceInfo = BeginTrace();
  507. TraceCounter_t *pCounters = pTraceInfo->GetDispCounters();
  508. int count = pTraceInfo->GetCount();
  509. // For each leaf in which the box lies, Get all displacements in that leaf and use their triangles to create the mesh
  510. for ( int i = 0; i < iLeafCount; ++i )
  511. {
  512. // Current leaf
  513. cleaf_t curLeaf = pBSPData->map_leafs[ pLeafList[i] ];
  514. // Test box against all displacements in the leaf.
  515. for( int k = 0; k < curLeaf.dispCount; k++ )
  516. {
  517. int dispIndex = pBSPData->map_dispList[curLeaf.dispListStart + k];
  518. CDispCollTree *pDispTree = &g_pDispCollTrees[dispIndex];
  519. // make sure we only check this brush once per trace/stab
  520. if ( !pTraceInfo->Visit( pDispTree->m_iCounter, count, pCounters ) )
  521. continue;
  522. // If this displacement doesn't touch our test box, don't add it to the list.
  523. if ( !IsBoxIntersectingBox( vMins, vMaxs, pDispTree->m_mins, pDispTree->m_maxs) )
  524. continue;
  525. // The the triangle mesh for this displacement surface
  526. virtualmeshlist_t meshTriList;
  527. pDispTree->GetVirtualMeshList( &meshTriList );
  528. Assert ( meshTriList.indexCount%3 == 0 );
  529. Assert ( meshTriList.indexCount != 0 );
  530. Assert ( meshTriList.indexCount/3 == meshTriList.triangleCount );
  531. // Don't allow more than 64k triangles in a collision model
  532. // TODO: Return a list of collidables? How often do we break 64k triangles?
  533. iTriCount += meshTriList.triangleCount;
  534. if ( iTriCount > 65535 )
  535. {
  536. AssertMsg ( 0, "Displacement surfaces have too many triangles to duplicate in GetCollidableFromDisplacementsInBox." );
  537. EndTrace( pTraceInfo );
  538. return NULL;
  539. }
  540. for ( int j = 0; j < meshTriList.indexCount; j+=3 )
  541. {
  542. // Don't index past the index list
  543. Assert( j+2 < meshTriList.indexCount );
  544. if ( j+2 >= meshTriList.indexCount )
  545. {
  546. EndTrace( pTraceInfo );
  547. physcollision->PolysoupDestroy( pDispCollideSoup );
  548. return NULL;
  549. }
  550. unsigned short i0 = meshTriList.indices[j+0];
  551. unsigned short i1 = meshTriList.indices[j+1];
  552. unsigned short i2 = meshTriList.indices[j+2];
  553. // Don't index past the end of the vert list
  554. Assert ( i0 < meshTriList.vertexCount && i1 < meshTriList.vertexCount && i2 < meshTriList.vertexCount );
  555. if ( i0 >= meshTriList.vertexCount || i1 >= meshTriList.vertexCount || i2 >= meshTriList.vertexCount )
  556. {
  557. EndTrace( pTraceInfo );
  558. physcollision->PolysoupDestroy( pDispCollideSoup );
  559. return NULL;
  560. }
  561. Vector v0 = meshTriList.pVerts[ i0 ];
  562. Vector v1 = meshTriList.pVerts[ i1 ];
  563. Vector v2 = meshTriList.pVerts[ i2 ];
  564. Assert ( v0.IsValid() && v1.IsValid() && v2.IsValid() );
  565. // We don't need exact clipping to the box... Include any triangle that has at least one vert on the inside
  566. if ( IsPointInBox( v0, vMins, vMaxs ) || IsPointInBox( v1, vMins, vMaxs ) || IsPointInBox( v2, vMins, vMaxs ) )
  567. {
  568. // This is for collision only, so we don't need to worry about blending-- Use the first surface prop.
  569. int nProp = pDispTree->GetSurfaceProps(0);
  570. physcollision->PolysoupAddTriangle( pDispCollideSoup, v0, v1, v2, nProp );
  571. }
  572. }// triangle loop
  573. }// for each displacement in leaf
  574. }// for each leaf
  575. EndTrace( pTraceInfo );
  576. CPhysCollide* pCollide = physcollision->ConvertPolysoupToCollide ( pDispCollideSoup, false );
  577. // clean up poly soup
  578. physcollision->PolysoupDestroy( pDispCollideSoup );
  579. return pCollide;
  580. }
  581. bool CEngineTrace::GetBrushInfo( int iBrush, CUtlVector<Vector4D> *pPlanesOut, int *pContentsOut )
  582. {
  583. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  584. if( iBrush < 0 || iBrush >= pBSPData->numbrushes )
  585. return false;
  586. cbrush_t *pBrush = &pBSPData->map_brushes[iBrush];
  587. if( pPlanesOut )
  588. {
  589. pPlanesOut->RemoveAll();
  590. Vector4D p;
  591. if ( pBrush->IsBox() )
  592. {
  593. cboxbrush_t *pBox = &pBSPData->map_boxbrushes[pBrush->GetBox()];
  594. for ( int i = 0; i < 6; i++ )
  595. {
  596. p.Init(0,0,0,0);
  597. if ( i < 3 )
  598. {
  599. p[i] = 1.0f;
  600. p[3] = pBox->maxs[i];
  601. }
  602. else
  603. {
  604. p[i-3] = -1.0f;
  605. p[3] = -pBox->mins[i-3];
  606. }
  607. pPlanesOut->AddToTail( p );
  608. }
  609. }
  610. else
  611. {
  612. cbrushside_t *stopside = &pBSPData->map_brushsides[pBrush->firstbrushside];
  613. // 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
  614. stopside += pBrush->numsides;
  615. for( cbrushside_t *side = &pBSPData->map_brushsides[pBrush->firstbrushside]; side != stopside; ++side )
  616. {
  617. Vector4D pVec( side->plane->normal.x, side->plane->normal.y, side->plane->normal.z, side->plane->dist );
  618. pPlanesOut->AddToTail( pVec );
  619. }
  620. }
  621. }
  622. if( pContentsOut )
  623. *pContentsOut = pBrush->contents;
  624. return true;
  625. }
  626. //Tests a point to see if it's outside any playable area
  627. bool CEngineTrace::PointOutsideWorld( const Vector &ptTest )
  628. {
  629. int iLeaf = CM_PointLeafnum( ptTest );
  630. Assert( iLeaf >= 0 );
  631. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  632. if( pBSPData->map_leafs[iLeaf].cluster == -1 )
  633. return true;
  634. return false;
  635. }
  636. //-----------------------------------------------------------------------------
  637. // Purpose: Expose to the game dll a method for finding the leaf which contains a given point
  638. // Input : &vPos - Returns the leaf which contains this point
  639. // Output : int - The handle to the leaf
  640. //-----------------------------------------------------------------------------
  641. int CEngineTrace::GetLeafContainingPoint( const Vector &vPos )
  642. {
  643. return CM_PointLeafnum( vPos );
  644. }
  645. //-----------------------------------------------------------------------------
  646. // Convex info for studio + brush models
  647. //-----------------------------------------------------------------------------
  648. class CBrushConvexInfo : public IConvexInfo
  649. {
  650. public:
  651. CBrushConvexInfo()
  652. {
  653. m_pBSPData = GetCollisionBSPData();
  654. }
  655. virtual unsigned int GetContents( int convexGameData )
  656. {
  657. return m_pBSPData->map_brushes[convexGameData].contents;
  658. }
  659. private:
  660. CCollisionBSPData *m_pBSPData;
  661. };
  662. class CStudioConvexInfo : public IConvexInfo
  663. {
  664. public:
  665. CStudioConvexInfo( studiohdr_t *pStudioHdr )
  666. {
  667. m_pStudioHdr = pStudioHdr;
  668. }
  669. virtual unsigned int GetContents( int convexGameData )
  670. {
  671. if ( convexGameData == 0 )
  672. {
  673. return m_pStudioHdr->contents;
  674. }
  675. Assert( convexGameData <= m_pStudioHdr->numbones );
  676. mstudiobone_t *pBone = m_pStudioHdr->pBone(convexGameData - 1);
  677. return pBone->contents;
  678. }
  679. private:
  680. studiohdr_t *m_pStudioHdr;
  681. };
  682. //-----------------------------------------------------------------------------
  683. // Perform vphysics trace
  684. //-----------------------------------------------------------------------------
  685. bool CEngineTrace::ClipRayToVPhysics( const Ray_t &ray, unsigned int fMask, ICollideable *pEntity, studiohdr_t *pStudioHdr, trace_t *pTrace )
  686. {
  687. if ( pEntity->GetSolid() != SOLID_VPHYSICS )
  688. return false;
  689. bool bTraced = false;
  690. // use the vphysics model for rotated brushes and vphysics simulated objects
  691. const model_t *pModel = pEntity->GetCollisionModel();
  692. if ( !pModel )
  693. return false;
  694. if ( pStudioHdr )
  695. {
  696. CStudioConvexInfo studioConvex( pStudioHdr );
  697. vcollide_t *pCollide = g_pMDLCache->GetVCollide( pModel->studio );
  698. if ( pCollide && pCollide->solidCount )
  699. {
  700. physcollision->TraceBox(
  701. ray,
  702. fMask,
  703. &studioConvex,
  704. pCollide->solids[0], // UNDONE: Support other solid indices?!?!?!? (forced zero)
  705. pEntity->GetCollisionOrigin(),
  706. pEntity->GetCollisionAngles(),
  707. pTrace );
  708. bTraced = true;
  709. }
  710. }
  711. else
  712. {
  713. Assert(pModel->type != mod_studio);
  714. // use the regular code for raytraces against brushes
  715. // do ray traces with normal code, but use vphysics to do box traces
  716. if ( !ray.m_IsRay || pModel->type != mod_brush )
  717. {
  718. int nModelIndex = pEntity->GetCollisionModelIndex();
  719. // BUGBUG: This only works when the vcollide in question is the first solid in the model
  720. vcollide_t *pCollide = CM_VCollideForModel( nModelIndex, (model_t*)pModel );
  721. if ( pCollide && pCollide->solidCount )
  722. {
  723. CBrushConvexInfo brushConvex;
  724. IConvexInfo *pConvexInfo = (pModel->type) == mod_brush ? &brushConvex : NULL;
  725. physcollision->TraceBox(
  726. ray,
  727. fMask,
  728. pConvexInfo,
  729. pCollide->solids[0], // UNDONE: Support other solid indices?!?!?!? (forced zero)
  730. pEntity->GetCollisionOrigin(),
  731. pEntity->GetCollisionAngles(),
  732. pTrace );
  733. bTraced = true;
  734. }
  735. }
  736. }
  737. return bTraced;
  738. }
  739. //-----------------------------------------------------------------------------
  740. // Perform bsp trace
  741. //-----------------------------------------------------------------------------
  742. bool CEngineTrace::ClipRayToBSP( const Ray_t &ray, unsigned int fMask, ICollideable *pEntity, trace_t *pTrace )
  743. {
  744. int nModelIndex = pEntity->GetCollisionModelIndex();
  745. cmodel_t *pCModel = CM_InlineModelNumber( nModelIndex - 1 );
  746. int nHeadNode = pCModel->headnode;
  747. CM_TransformedBoxTrace( ray, nHeadNode, fMask, pEntity->GetCollisionOrigin(), pEntity->GetCollisionAngles(), *pTrace );
  748. return true;
  749. }
  750. // NOTE: Switched over to SIMD ray/box test since there is a bug we haven't hunted down yet in the scalar version
  751. bool CEngineTrace::ClipRayToBBox( const Ray_t &ray, unsigned int fMask, ICollideable *pEntity, trace_t *pTrace )
  752. {
  753. extern bool IntersectRayWithBox( const Ray_t &ray, const VectorAligned &inInvDelta, const VectorAligned &inBoxMins, const VectorAligned &inBoxMaxs, trace_t *RESTRICT pTrace );
  754. if ( pEntity->GetSolid() != SOLID_BBOX )
  755. return false;
  756. // We can't use the OBBMins/Maxs unless the collision angles are world-aligned
  757. Assert( pEntity->GetCollisionAngles() == vec3_angle );
  758. VectorAligned vecAbsMins, vecAbsMaxs;
  759. VectorAligned vecInvDelta;
  760. // NOTE: If m_pRootMoveParent is set, then the boxes should be rotated into the root parent's space
  761. if ( !ray.m_IsRay && m_pRootMoveParent )
  762. {
  763. Ray_t ray_l;
  764. ray_l.m_Extents = ray.m_Extents;
  765. VectorIRotate( ray.m_Delta, *m_pRootMoveParent, ray_l.m_Delta );
  766. ray_l.m_StartOffset.Init();
  767. VectorITransform( ray.m_Start, *m_pRootMoveParent, ray_l.m_Start );
  768. vecInvDelta = ray_l.InvDelta();
  769. Vector localEntityOrigin;
  770. VectorITransform( pEntity->GetCollisionOrigin(), *m_pRootMoveParent, localEntityOrigin );
  771. ray_l.m_IsRay = ray.m_IsRay;
  772. ray_l.m_IsSwept = ray.m_IsSwept;
  773. VectorAdd( localEntityOrigin, pEntity->OBBMins(), vecAbsMins );
  774. VectorAdd( localEntityOrigin, pEntity->OBBMaxs(), vecAbsMaxs );
  775. IntersectRayWithBox( ray_l, vecInvDelta, vecAbsMins, vecAbsMaxs, pTrace );
  776. if ( pTrace->DidHit() )
  777. {
  778. Vector temp;
  779. VectorCopy (pTrace->plane.normal, temp);
  780. VectorRotate( temp, *m_pRootMoveParent, pTrace->plane.normal );
  781. VectorAdd( ray.m_Start, ray.m_StartOffset, pTrace->startpos );
  782. if (pTrace->fraction == 1)
  783. {
  784. VectorAdd( pTrace->startpos, ray.m_Delta, pTrace->endpos);
  785. }
  786. else
  787. {
  788. VectorMA( pTrace->startpos, pTrace->fraction, ray.m_Delta, pTrace->endpos );
  789. }
  790. pTrace->plane.dist = DotProduct( pTrace->endpos, pTrace->plane.normal );
  791. if ( pTrace->fractionleftsolid < 1 )
  792. {
  793. pTrace->startpos += ray.m_Delta * pTrace->fractionleftsolid;
  794. }
  795. }
  796. else
  797. {
  798. VectorAdd( ray.m_Start, ray.m_StartOffset, pTrace->startpos );
  799. }
  800. return true;
  801. }
  802. vecInvDelta = ray.InvDelta();
  803. VectorAdd( pEntity->GetCollisionOrigin(), pEntity->OBBMins(), vecAbsMins );
  804. VectorAdd( pEntity->GetCollisionOrigin(), pEntity->OBBMaxs(), vecAbsMaxs );
  805. IntersectRayWithBox( ray, vecInvDelta, vecAbsMins, vecAbsMaxs, pTrace);
  806. return true;
  807. }
  808. bool CEngineTrace::ClipRayToOBB( const Ray_t &ray, unsigned int fMask, ICollideable *pEntity, trace_t *pTrace )
  809. {
  810. if ( pEntity->GetSolid() != SOLID_OBB )
  811. return false;
  812. // NOTE: This is busted because it doesn't compute fractionleftsolid, which at the
  813. // moment is required for the engine trace system.
  814. IntersectRayWithOBB( ray, pEntity->GetCollisionOrigin(), pEntity->GetCollisionAngles(),
  815. pEntity->OBBMins(), pEntity->OBBMaxs(), DIST_EPSILON, pTrace );
  816. return true;
  817. }
  818. //-----------------------------------------------------------------------------
  819. // Main entry point for clipping rays to entities
  820. //-----------------------------------------------------------------------------
  821. #ifndef SWDS
  822. void CEngineTraceClient::SetTraceEntity( ICollideable *pCollideable, trace_t *pTrace )
  823. {
  824. if ( !pTrace->DidHit() )
  825. return;
  826. // FIXME: This is only necessary because of traces occurring during
  827. // LevelInit (a suspect time to be tracing)
  828. if (!pCollideable)
  829. {
  830. pTrace->m_pEnt = NULL;
  831. return;
  832. }
  833. IClientUnknown *pUnk = (IClientUnknown*)pCollideable->GetEntityHandle();
  834. if ( !StaticPropMgr()->IsStaticProp( pUnk ) )
  835. {
  836. pTrace->m_pEnt = (CBaseEntity*)(pUnk->GetIClientEntity());
  837. }
  838. else
  839. {
  840. // For static props, point to the world, hitbox is the prop index
  841. pTrace->m_pEnt = (CBaseEntity*)(entitylist->GetClientEntity(0));
  842. pTrace->hitbox = StaticPropMgr()->GetStaticPropIndex( pUnk ) + 1;
  843. }
  844. }
  845. #endif
  846. void CEngineTraceServer::SetTraceEntity( ICollideable *pCollideable, trace_t *pTrace )
  847. {
  848. if ( !pTrace->DidHit() )
  849. return;
  850. IHandleEntity *pHandleEntity = pCollideable->GetEntityHandle();
  851. if ( !StaticPropMgr()->IsStaticProp( pHandleEntity ) )
  852. {
  853. pTrace->m_pEnt = (CBaseEntity*)(pHandleEntity);
  854. }
  855. else
  856. {
  857. // For static props, point to the world, hitbox is the prop index
  858. pTrace->m_pEnt = (CBaseEntity*)(sv.edicts->GetIServerEntity());
  859. pTrace->hitbox = StaticPropMgr()->GetStaticPropIndex( pHandleEntity ) + 1;
  860. }
  861. }
  862. //-----------------------------------------------------------------------------
  863. // Traces a ray against a particular edict
  864. //-----------------------------------------------------------------------------
  865. void CEngineTrace::ClipRayToCollideable( const Ray_t &ray, unsigned int fMask, ICollideable *pEntity, trace_t *pTrace )
  866. {
  867. CM_ClearTrace( pTrace );
  868. VectorAdd( ray.m_Start, ray.m_StartOffset, pTrace->startpos );
  869. VectorAdd( pTrace->startpos, ray.m_Delta, pTrace->endpos );
  870. const model_t *pModel = pEntity->GetCollisionModel();
  871. bool bIsStudioModel = false;
  872. studiohdr_t *pStudioHdr = NULL;
  873. if ( pModel && pModel->type == mod_studio )
  874. {
  875. bIsStudioModel = true;
  876. pStudioHdr = (studiohdr_t *)modelloader->GetExtraData( (model_t*)pModel );
  877. // Cull if the collision mask isn't set + we're not testing hitboxes.
  878. if ( (( fMask & CONTENTS_HITBOX ) == 0) )
  879. {
  880. if ( ( fMask & pStudioHdr->contents ) == 0)
  881. return;
  882. }
  883. }
  884. const matrix3x4_t *pOldRoot = m_pRootMoveParent;
  885. if ( pEntity->GetSolidFlags() & FSOLID_ROOT_PARENT_ALIGNED )
  886. {
  887. m_pRootMoveParent = pEntity->GetRootParentToWorldTransform();
  888. }
  889. bool bTraced = false;
  890. bool bCustomPerformed = false;
  891. if ( ShouldPerformCustomRayTest( ray, pEntity ) )
  892. {
  893. ClipRayToCustom( ray, fMask, pEntity, pTrace );
  894. bTraced = true;
  895. bCustomPerformed = true;
  896. }
  897. else
  898. {
  899. bTraced = ClipRayToVPhysics( ray, fMask, pEntity, pStudioHdr, pTrace );
  900. }
  901. // FIXME: Why aren't we using solid type to check what kind of collisions to test against?!?!
  902. if ( !bTraced && pModel && pModel->type == mod_brush )
  903. {
  904. bTraced = ClipRayToBSP( ray, fMask, pEntity, pTrace );
  905. }
  906. if ( !bTraced )
  907. {
  908. bTraced = ClipRayToOBB( ray, fMask, pEntity, pTrace );
  909. }
  910. // Hitboxes..
  911. bool bTracedHitboxes = false;
  912. if ( bIsStudioModel && (fMask & CONTENTS_HITBOX) )
  913. {
  914. // Until hitboxes are no longer implemented as custom raytests,
  915. // don't bother to do the work twice
  916. if (!bCustomPerformed)
  917. {
  918. bTraced = ClipRayToHitboxes( ray, fMask, pEntity, pTrace );
  919. if ( bTraced )
  920. {
  921. // Hitboxes will set the surface properties
  922. bTracedHitboxes = true;
  923. }
  924. }
  925. }
  926. if ( !bTraced )
  927. {
  928. ClipRayToBBox( ray, fMask, pEntity, pTrace );
  929. }
  930. if ( bIsStudioModel && !bTracedHitboxes && pTrace->DidHit() && (!bCustomPerformed || pTrace->surface.surfaceProps == 0) )
  931. {
  932. pTrace->contents = pStudioHdr->contents;
  933. // use the default surface properties
  934. pTrace->surface.name = "**studio**";
  935. pTrace->surface.flags = 0;
  936. pTrace->surface.surfaceProps = physprop->GetSurfaceIndex( pStudioHdr->pszSurfaceProp() );
  937. }
  938. if (!pTrace->m_pEnt && pTrace->DidHit())
  939. {
  940. SetTraceEntity( pEntity, pTrace );
  941. }
  942. #ifdef _DEBUG
  943. Vector vecOffset, vecEndTest;
  944. VectorAdd( ray.m_Start, ray.m_StartOffset, vecOffset );
  945. VectorMA( vecOffset, pTrace->fractionleftsolid, ray.m_Delta, vecEndTest );
  946. Assert( VectorsAreEqual( vecEndTest, pTrace->startpos, 0.1f ) );
  947. VectorMA( vecOffset, pTrace->fraction, ray.m_Delta, vecEndTest );
  948. Assert( VectorsAreEqual( vecEndTest, pTrace->endpos, 0.1f ) );
  949. #endif
  950. m_pRootMoveParent = pOldRoot;
  951. }
  952. //-----------------------------------------------------------------------------
  953. // Main entry point for clipping rays to entities
  954. //-----------------------------------------------------------------------------
  955. void CEngineTrace::ClipRayToEntity( const Ray_t &ray, unsigned int fMask, IHandleEntity *pEntity, trace_t *pTrace )
  956. {
  957. ClipRayToCollideable( ray, fMask, GetCollideable(pEntity), pTrace );
  958. }
  959. //-----------------------------------------------------------------------------
  960. // Grabs all entities along a ray
  961. //-----------------------------------------------------------------------------
  962. class CEntitiesAlongRay : public IPartitionEnumerator
  963. {
  964. public:
  965. CEntitiesAlongRay( ) : m_EntityHandles(0, 32) {}
  966. void Reset()
  967. {
  968. m_EntityHandles.RemoveAll();
  969. }
  970. IterationRetval_t EnumElement( IHandleEntity *pHandleEntity )
  971. {
  972. m_EntityHandles.AddToTail( pHandleEntity );
  973. return ITERATION_CONTINUE;
  974. }
  975. CUtlVector< IHandleEntity * > m_EntityHandles;
  976. };
  977. class CEntityListAlongRay : public IPartitionEnumerator
  978. {
  979. public:
  980. enum { MAX_ENTITIES_ALONGRAY = 1024 };
  981. CEntityListAlongRay()
  982. {
  983. m_nCount = 0;
  984. }
  985. void Reset()
  986. {
  987. m_nCount = 0;
  988. }
  989. int Count()
  990. {
  991. return m_nCount;
  992. }
  993. IterationRetval_t EnumElement( IHandleEntity *pHandleEntity )
  994. {
  995. if ( m_nCount < MAX_ENTITIES_ALONGRAY )
  996. {
  997. m_EntityHandles[m_nCount] = pHandleEntity;
  998. m_nCount++;
  999. }
  1000. else
  1001. {
  1002. DevMsg( 1, "Max entity count along ray exceeded!\n" );
  1003. }
  1004. return ITERATION_CONTINUE;
  1005. }
  1006. int m_nCount;
  1007. IHandleEntity *m_EntityHandles[MAX_ENTITIES_ALONGRAY];
  1008. };
  1009. //-----------------------------------------------------------------------------
  1010. // Makes sure the final trace is clipped to the clip trace
  1011. // Returns true if clipping occurred
  1012. //-----------------------------------------------------------------------------
  1013. bool CEngineTrace::ClipTraceToTrace( trace_t &clipTrace, trace_t *pFinalTrace )
  1014. {
  1015. if (clipTrace.allsolid || clipTrace.startsolid || (clipTrace.fraction < pFinalTrace->fraction))
  1016. {
  1017. if (pFinalTrace->startsolid)
  1018. {
  1019. float flFractionLeftSolid = pFinalTrace->fractionleftsolid;
  1020. Vector vecStartPos = pFinalTrace->startpos;
  1021. *pFinalTrace = clipTrace;
  1022. pFinalTrace->startsolid = true;
  1023. if ( flFractionLeftSolid > clipTrace.fractionleftsolid )
  1024. {
  1025. pFinalTrace->fractionleftsolid = flFractionLeftSolid;
  1026. pFinalTrace->startpos = vecStartPos;
  1027. }
  1028. }
  1029. else
  1030. {
  1031. *pFinalTrace = clipTrace;
  1032. }
  1033. return true;
  1034. }
  1035. if (clipTrace.startsolid)
  1036. {
  1037. pFinalTrace->startsolid = true;
  1038. if ( clipTrace.fractionleftsolid > pFinalTrace->fractionleftsolid )
  1039. {
  1040. pFinalTrace->fractionleftsolid = clipTrace.fractionleftsolid;
  1041. pFinalTrace->startpos = clipTrace.startpos;
  1042. }
  1043. }
  1044. return false;
  1045. }
  1046. //-----------------------------------------------------------------------------
  1047. // Converts a user id to a collideable + username
  1048. //-----------------------------------------------------------------------------
  1049. void CEngineTraceServer::HandleEntityToCollideable( IHandleEntity *pHandleEntity, ICollideable **ppCollide, const char **ppDebugName )
  1050. {
  1051. *ppCollide = StaticPropMgr()->GetStaticProp( pHandleEntity );
  1052. if ( *ppCollide )
  1053. {
  1054. *ppDebugName = "static prop";
  1055. return;
  1056. }
  1057. IServerUnknown *pServerUnknown = static_cast<IServerUnknown*>(pHandleEntity);
  1058. if ( !pServerUnknown || ! pServerUnknown->GetNetworkable())
  1059. {
  1060. *ppCollide = NULL;
  1061. *ppDebugName = "<null>";
  1062. return;
  1063. }
  1064. *ppCollide = pServerUnknown->GetCollideable();
  1065. *ppDebugName = pServerUnknown->GetNetworkable()->GetClassName();
  1066. }
  1067. #ifndef SWDS
  1068. void CEngineTraceClient::HandleEntityToCollideable( IHandleEntity *pHandleEntity, ICollideable **ppCollide, const char **ppDebugName )
  1069. {
  1070. *ppCollide = StaticPropMgr()->GetStaticProp( pHandleEntity );
  1071. if ( *ppCollide )
  1072. {
  1073. *ppDebugName = "static prop";
  1074. return;
  1075. }
  1076. IClientUnknown *pUnk = static_cast<IClientUnknown*>(pHandleEntity);
  1077. if ( !pUnk )
  1078. {
  1079. *ppCollide = NULL;
  1080. *ppDebugName = "<null>";
  1081. return;
  1082. }
  1083. *ppCollide = pUnk->GetCollideable();
  1084. *ppDebugName = "client entity";
  1085. IClientNetworkable *pNetwork = pUnk->GetClientNetworkable();
  1086. if (pNetwork)
  1087. {
  1088. if (pNetwork->GetClientClass())
  1089. {
  1090. *ppDebugName = pNetwork->GetClientClass()->m_pNetworkName;
  1091. }
  1092. }
  1093. }
  1094. #endif
  1095. //-----------------------------------------------------------------------------
  1096. // Returns the world collideable for trace setting
  1097. //-----------------------------------------------------------------------------
  1098. #ifndef SWDS
  1099. ICollideable *CEngineTraceClient::GetWorldCollideable()
  1100. {
  1101. IClientEntity *pUnk = entitylist->GetClientEntity( 0 );
  1102. AssertOnce( pUnk );
  1103. return pUnk ? pUnk->GetCollideable() : NULL;
  1104. }
  1105. #endif
  1106. ICollideable *CEngineTraceServer::GetWorldCollideable()
  1107. {
  1108. if (!sv.edicts)
  1109. return NULL;
  1110. return sv.edicts->GetCollideable();
  1111. }
  1112. //-----------------------------------------------------------------------------
  1113. // Debugging code to render all ray casts since the last time this call was made
  1114. //-----------------------------------------------------------------------------
  1115. void EngineTraceRenderRayCasts()
  1116. {
  1117. #if defined _DEBUG && !defined SWDS
  1118. if( debugrayenable.GetBool() && s_FrameRays.Count() > debugraylimit.GetInt() && !debugrayreset.GetInt() )
  1119. {
  1120. Warning( "m_FrameRays.Count() == %d\n", s_FrameRays.Count() );
  1121. debugrayreset.SetValue( 1 );
  1122. int i;
  1123. for( i = 0; i < s_FrameRays.Count(); i++ )
  1124. {
  1125. Ray_t &ray = s_FrameRays[i];
  1126. if( ray.m_Extents.x != 0.0f || ray.m_Extents.y != 0.0f || ray.m_Extents.z != 0.0f )
  1127. {
  1128. CDebugOverlay::AddLineOverlay( ray.m_Start, ray.m_Start + ray.m_Delta, 255, 0, 0, 255, true, 3600.0f );
  1129. }
  1130. else
  1131. {
  1132. CDebugOverlay::AddLineOverlay( ray.m_Start, ray.m_Start + ray.m_Delta, 255, 255, 0, 255, true, 3600.0f );
  1133. }
  1134. }
  1135. }
  1136. s_FrameRays.RemoveAll( );
  1137. #endif
  1138. }
  1139. //-----------------------------------------------------------------------------
  1140. // Purpose:
  1141. //-----------------------------------------------------------------------------
  1142. void CEngineTrace::SetupLeafAndEntityListRay( const Ray_t &ray, CTraceListData &traceData )
  1143. {
  1144. if ( !ray.m_IsSwept )
  1145. {
  1146. Vector vecMin, vecMax;
  1147. VectorSubtract( ray.m_Start, ray.m_Extents, vecMin );
  1148. VectorAdd( ray.m_Start, ray.m_Extents, vecMax );
  1149. SetupLeafAndEntityListBox( vecMin, vecMax, traceData );
  1150. return;
  1151. }
  1152. // Get the leaves that intersect the ray.
  1153. traceData.LeafCountReset();
  1154. CM_RayLeafnums( ray, traceData.m_aLeafList.Base(), traceData.LeafCountMax(), traceData.m_nLeafCount );
  1155. // Find all the entities in the voxels that intersect this ray.
  1156. traceData.EntityCountReset();
  1157. SpatialPartition()->EnumerateElementsAlongRay( SpatialPartitionMask(), ray, false, &traceData );
  1158. }
  1159. //-----------------------------------------------------------------------------
  1160. // Purpose: Gives an AABB and returns a leaf and entity list.
  1161. //-----------------------------------------------------------------------------
  1162. void CEngineTrace::SetupLeafAndEntityListBox( const Vector &vecBoxMin, const Vector &vecBoxMax, CTraceListData &traceData )
  1163. {
  1164. // Get the leaves that intersect this box.
  1165. int iTopNode = -1;
  1166. traceData.LeafCountReset();
  1167. traceData.m_nLeafCount = CM_BoxLeafnums( vecBoxMin, vecBoxMax, traceData.m_aLeafList.Base(), traceData.LeafCountMax(), &iTopNode );
  1168. // Find all entities in the voxels that intersect this box.
  1169. traceData.EntityCountReset();
  1170. SpatialPartition()->EnumerateElementsInBox( SpatialPartitionMask(), vecBoxMin, vecBoxMax, false, &traceData );
  1171. }
  1172. //-----------------------------------------------------------------------------
  1173. // Purpose:
  1174. // NOTE: the fMask is redundant with the stuff below, what do I want to do???
  1175. //-----------------------------------------------------------------------------
  1176. void CEngineTrace::TraceRayAgainstLeafAndEntityList( const Ray_t &ray, CTraceListData &traceData,
  1177. unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace )
  1178. {
  1179. // Setup the trace data.
  1180. CM_ClearTrace ( pTrace );
  1181. // Make sure we have some kind of trace filter.
  1182. CTraceFilterHitAll traceFilter;
  1183. if ( !pTraceFilter )
  1184. {
  1185. pTraceFilter = &traceFilter;
  1186. }
  1187. // Collide with the world.
  1188. if ( pTraceFilter->GetTraceType() != TRACE_ENTITIES_ONLY )
  1189. {
  1190. ICollideable *pCollide = GetWorldCollideable();
  1191. // Make sure the world entity is unrotated
  1192. // FIXME: BAH! The !pCollide test here is because of
  1193. // CStaticProp::PrecacheLighting.. it's occurring too early
  1194. // need to fix that later
  1195. Assert( !pCollide || pCollide->GetCollisionOrigin() == vec3_origin );
  1196. Assert( !pCollide || pCollide->GetCollisionAngles() == vec3_angle );
  1197. CM_BoxTraceAgainstLeafList( ray, traceData.m_aLeafList.Base(), traceData.LeafCount(), fMask, true, *pTrace );
  1198. SetTraceEntity( pCollide, pTrace );
  1199. // Blocked by the world or early out because we only are tracing against the world.
  1200. if ( ( pTrace->fraction == 0 ) || ( pTraceFilter->GetTraceType() == TRACE_WORLD_ONLY ) )
  1201. return;
  1202. }
  1203. else
  1204. {
  1205. // Set initial start and endpos. This is necessary if the world isn't traced against,
  1206. // because we may not trace against anything below.
  1207. VectorAdd( ray.m_Start, ray.m_StartOffset, pTrace->startpos );
  1208. VectorAdd( pTrace->startpos, ray.m_Delta, pTrace->endpos );
  1209. }
  1210. // Save the world collision fraction.
  1211. float flWorldFraction = pTrace->fraction;
  1212. // Create a ray that extends only until we hit the world and adjust the trace accordingly
  1213. Ray_t entityRay = ray;
  1214. VectorScale( entityRay.m_Delta, pTrace->fraction, entityRay.m_Delta );
  1215. // We know this is safe because if pTrace->fraction == 0, we would have exited above.
  1216. pTrace->fractionleftsolid /= pTrace->fraction;
  1217. pTrace->fraction = 1.0;
  1218. // Collide with entities.
  1219. bool bNoStaticProps = pTraceFilter->GetTraceType() == TRACE_ENTITIES_ONLY;
  1220. bool bFilterStaticProps = pTraceFilter->GetTraceType() == TRACE_EVERYTHING_FILTER_PROPS;
  1221. trace_t trace;
  1222. ICollideable *pCollideable;
  1223. const char *pDebugName;
  1224. for ( int iEntity = 0; iEntity < traceData.m_nEntityCount; ++iEntity )
  1225. {
  1226. // Generate a collideable.
  1227. IHandleEntity *pHandleEntity = traceData.m_aEntityList[iEntity];
  1228. HandleEntityToCollideable( pHandleEntity, &pCollideable, &pDebugName );
  1229. // Check for error condition.
  1230. if ( !IsSolid( pCollideable->GetSolid(), pCollideable->GetSolidFlags() ) )
  1231. {
  1232. Assert( 0 );
  1233. Msg("%s in solid list (not solid)\n", pDebugName );
  1234. continue;
  1235. }
  1236. if ( !StaticPropMgr()->IsStaticProp( pHandleEntity ) )
  1237. {
  1238. if ( !pTraceFilter->ShouldHitEntity( pHandleEntity, fMask ) )
  1239. continue;
  1240. }
  1241. else
  1242. {
  1243. // FIXME: Could remove this check here by
  1244. // using a different spatial partition mask. Look into it
  1245. // if we want more speedups here.
  1246. if ( bNoStaticProps )
  1247. continue;
  1248. if ( bFilterStaticProps )
  1249. {
  1250. if ( !pTraceFilter->ShouldHitEntity( pHandleEntity, fMask ) )
  1251. continue;
  1252. }
  1253. }
  1254. ClipRayToCollideable( entityRay, fMask, pCollideable, &trace );
  1255. // Make sure the ray is always shorter than it currently is
  1256. ClipTraceToTrace( trace, pTrace );
  1257. // Stop if we're in allsolid
  1258. if ( pTrace->allsolid )
  1259. break;
  1260. }
  1261. // Fix up the fractions so they are appropriate given the original unclipped-to-world ray.
  1262. pTrace->fraction *= flWorldFraction;
  1263. pTrace->fractionleftsolid *= flWorldFraction;
  1264. if ( !ray.m_IsRay )
  1265. {
  1266. // Make sure no fractionleftsolid can be used with box sweeps.
  1267. VectorAdd( ray.m_Start, ray.m_StartOffset, pTrace->startpos );
  1268. pTrace->fractionleftsolid = 0;
  1269. }
  1270. }
  1271. #if BENCHMARK_RAY_TEST
  1272. CON_COMMAND( ray_save, "Save the rays" )
  1273. {
  1274. int count = s_BenchmarkRays.Count();
  1275. if ( count )
  1276. {
  1277. FileHandle_t hFile = g_pFileSystem->Open("rays.bin", "wb");
  1278. if ( hFile )
  1279. {
  1280. g_pFileSystem->Write( &count, sizeof(count), hFile );
  1281. g_pFileSystem->Write( s_BenchmarkRays.Base(), sizeof(s_BenchmarkRays[0])*count, hFile );
  1282. g_pFileSystem->Close( hFile );
  1283. }
  1284. }
  1285. Msg("Saved %d rays\n", count );
  1286. }
  1287. CON_COMMAND( ray_load, "Load the rays" )
  1288. {
  1289. s_BenchmarkRays.RemoveAll();
  1290. FileHandle_t hFile = g_pFileSystem->Open("rays.bin", "rb");
  1291. if ( hFile )
  1292. {
  1293. int count = 0;
  1294. g_pFileSystem->Read( &count, sizeof(count), hFile );
  1295. if ( count )
  1296. {
  1297. s_BenchmarkRays.EnsureCount( count );
  1298. g_pFileSystem->Read( s_BenchmarkRays.Base(), sizeof(s_BenchmarkRays[0])*count, hFile );
  1299. }
  1300. g_pFileSystem->Close( hFile );
  1301. }
  1302. Msg("Loaded %d rays\n", s_BenchmarkRays.Count() );
  1303. }
  1304. CON_COMMAND( ray_clear, "Clear the current rays" )
  1305. {
  1306. s_BenchmarkRays.RemoveAll();
  1307. Msg("Reset rays!\n");
  1308. }
  1309. CON_COMMAND_EXTERN( ray_bench, RayBench, "Time the rays" )
  1310. {
  1311. #if VPROF_LEVEL > 0
  1312. g_VProfCurrentProfile.Start();
  1313. g_VProfCurrentProfile.Reset();
  1314. g_VProfCurrentProfile.ResetPeaks();
  1315. #endif
  1316. {
  1317. double tStart = Plat_FloatTime();
  1318. trace_t trace;
  1319. int hit = 0;
  1320. int miss = 0;
  1321. int rayVsProp = 0;
  1322. int boxVsProp = 0;
  1323. for ( int i = 0; i < s_BenchmarkRays.Count(); i++ )
  1324. {
  1325. CM_BoxTrace( s_BenchmarkRays[i], 0, MASK_SOLID, true, trace );
  1326. if ( 0 )
  1327. {
  1328. VPROF("QueryStaticProps");
  1329. // Create a ray that extends only until we hit the world and adjust the trace accordingly
  1330. Ray_t entityRay = s_BenchmarkRays[i];
  1331. VectorScale( entityRay.m_Delta, trace.fraction, entityRay.m_Delta );
  1332. CEntityListAlongRay enumerator;
  1333. enumerator.Reset();
  1334. SpatialPartition()->EnumerateElementsAlongRay( PARTITION_ENGINE_SOLID_EDICTS, entityRay, false, &enumerator );
  1335. trace_t tr;
  1336. ICollideable *pCollideable;
  1337. int nCount = enumerator.Count();
  1338. const char *pDebugName = NULL;
  1339. //float flWorldFraction = trace.fraction;
  1340. if ( 0 )
  1341. {
  1342. VPROF("IntersectStaticProps");
  1343. for ( int i = 0; i < nCount; ++i )
  1344. {
  1345. // Generate a collideable
  1346. IHandleEntity *pHandleEntity = enumerator.m_EntityHandles[i];
  1347. if ( !StaticPropMgr()->IsStaticProp( pHandleEntity ) )
  1348. continue;
  1349. if ( entityRay.m_IsRay )
  1350. rayVsProp++;
  1351. else
  1352. boxVsProp++;
  1353. s_EngineTraceServer.HandleEntityToCollideable( pHandleEntity, &pCollideable, &pDebugName );
  1354. s_EngineTraceServer.ClipRayToCollideable( entityRay, MASK_SOLID, pCollideable, &tr );
  1355. // Make sure the ray is always shorter than it currently is
  1356. s_EngineTraceServer.ClipTraceToTrace( tr, &trace );
  1357. }
  1358. }
  1359. }
  1360. if ( trace.DidHit() )
  1361. hit++;
  1362. else
  1363. miss++;
  1364. #if VPROF_LEVEL > 0
  1365. g_VProfCurrentProfile.MarkFrame();
  1366. #endif
  1367. }
  1368. double tEnd = Plat_FloatTime();
  1369. float ms = (tEnd - tStart) * 1000.0f;
  1370. int swept = 0;
  1371. int point = 0;
  1372. for ( int i = 0; i < s_BenchmarkRays.Count(); i++ )
  1373. {
  1374. swept += s_BenchmarkRays[i].m_IsSwept ? 1 : 0;
  1375. point += s_BenchmarkRays[i].m_IsRay ? 1 : 0;
  1376. }
  1377. 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 );
  1378. }
  1379. #if VPROF_LEVEL > 0
  1380. g_VProfCurrentProfile.MarkFrame();
  1381. g_VProfCurrentProfile.Stop();
  1382. g_VProfCurrentProfile.OutputReport( VPRT_FULL & ~VPRT_HIERARCHY, NULL );
  1383. #endif
  1384. }
  1385. #endif
  1386. //-----------------------------------------------------------------------------
  1387. // A version that simply accepts a ray (can work as a traceline or tracehull)
  1388. //-----------------------------------------------------------------------------
  1389. void CEngineTrace::TraceRay( const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace )
  1390. {
  1391. #if defined _DEBUG && !defined SWDS
  1392. if( debugrayenable.GetBool() )
  1393. {
  1394. s_FrameRays.AddToTail( ray );
  1395. }
  1396. #endif
  1397. #if BENCHMARK_RAY_TEST
  1398. if( s_BenchmarkRays.Count() < 15000 )
  1399. {
  1400. s_BenchmarkRays.EnsureCapacity(15000);
  1401. s_BenchmarkRays.AddToTail( ray );
  1402. }
  1403. #endif
  1404. tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s:%d", __FUNCTION__, __LINE__ );
  1405. VPROF_INCREMENT_COUNTER( "TraceRay", 1 );
  1406. m_traceStatCounters[TRACE_STAT_COUNTER_TRACERAY]++;
  1407. // VPROF_BUDGET( "CEngineTrace::TraceRay", "Ray/Hull Trace" );
  1408. CTraceFilterHitAll traceFilter;
  1409. if ( !pTraceFilter )
  1410. {
  1411. pTraceFilter = &traceFilter;
  1412. }
  1413. CM_ClearTrace( pTrace );
  1414. // Collide with the world.
  1415. if ( pTraceFilter->GetTraceType() != TRACE_ENTITIES_ONLY )
  1416. {
  1417. ICollideable *pCollide = GetWorldCollideable();
  1418. Assert( pCollide );
  1419. // Make sure the world entity is unrotated
  1420. // FIXME: BAH! The !pCollide test here is because of
  1421. // CStaticProp::PrecacheLighting.. it's occurring too early
  1422. // need to fix that later
  1423. Assert(!pCollide || pCollide->GetCollisionOrigin() == vec3_origin );
  1424. Assert(!pCollide || pCollide->GetCollisionAngles() == vec3_angle );
  1425. CM_BoxTrace( ray, 0, fMask, true, *pTrace );
  1426. SetTraceEntity( pCollide, pTrace );
  1427. // inside world, no need to check being inside anything else
  1428. if ( pTrace->startsolid )
  1429. return;
  1430. // Early out if we only trace against the world
  1431. if ( pTraceFilter->GetTraceType() == TRACE_WORLD_ONLY )
  1432. return;
  1433. }
  1434. else
  1435. {
  1436. // Set initial start + endpos, necessary if the world isn't traced against
  1437. // because we may not trace against *anything* below.
  1438. VectorAdd( ray.m_Start, ray.m_StartOffset, pTrace->startpos );
  1439. VectorAdd( pTrace->startpos, ray.m_Delta, pTrace->endpos );
  1440. }
  1441. // Save the world collision fraction.
  1442. float flWorldFraction = pTrace->fraction;
  1443. float flWorldFractionLeftSolidScale = flWorldFraction;
  1444. // Create a ray that extends only until we hit the world
  1445. // and adjust the trace accordingly
  1446. Ray_t entityRay = ray;
  1447. if ( pTrace->fraction == 0 )
  1448. {
  1449. entityRay.m_Delta.Init();
  1450. flWorldFractionLeftSolidScale = pTrace->fractionleftsolid;
  1451. pTrace->fractionleftsolid = 1.0f;
  1452. pTrace->fraction = 1.0f;
  1453. }
  1454. else
  1455. {
  1456. // Explicitly compute end so that this computation happens at the quantization of
  1457. // the output (endpos). That way we won't miss any intersections we would get
  1458. // by feeding these results back in to the tracer
  1459. // This is not the same as entityRay.m_Delta *= pTrace->fraction which happens
  1460. // at a quantization that is more precise as m_Start moves away from the origin
  1461. Vector end;
  1462. VectorMA( entityRay.m_Start, pTrace->fraction, entityRay.m_Delta, end );
  1463. VectorSubtract(end, entityRay.m_Start, entityRay.m_Delta);
  1464. // We know this is safe because pTrace->fraction != 0
  1465. pTrace->fractionleftsolid /= pTrace->fraction;
  1466. pTrace->fraction = 1.0;
  1467. }
  1468. // Collide with entities along the ray
  1469. // FIXME: Hitbox code causes this to be re-entrant for the IK stuff.
  1470. // If we could eliminate that, this could be static and therefore
  1471. // not have to reallocate memory all the time
  1472. CEntityListAlongRay enumerator;
  1473. enumerator.Reset();
  1474. SpatialPartition()->EnumerateElementsAlongRay( SpatialPartitionMask(), entityRay, false, &enumerator );
  1475. bool bNoStaticProps = pTraceFilter->GetTraceType() == TRACE_ENTITIES_ONLY;
  1476. bool bFilterStaticProps = pTraceFilter->GetTraceType() == TRACE_EVERYTHING_FILTER_PROPS;
  1477. trace_t tr;
  1478. ICollideable *pCollideable;
  1479. const char *pDebugName;
  1480. int nCount = enumerator.Count();
  1481. for ( int i = 0; i < nCount; ++i )
  1482. {
  1483. // Generate a collideable
  1484. IHandleEntity *pHandleEntity = enumerator.m_EntityHandles[i];
  1485. HandleEntityToCollideable( pHandleEntity, &pCollideable, &pDebugName );
  1486. // Check for error condition
  1487. if ( IsPC() && IsDebug() && !IsSolid( pCollideable->GetSolid(), pCollideable->GetSolidFlags() ) )
  1488. {
  1489. Assert( 0 );
  1490. Msg( "%s in solid list (not solid)\n", pDebugName );
  1491. continue;
  1492. }
  1493. if ( !StaticPropMgr()->IsStaticProp( pHandleEntity ) )
  1494. {
  1495. if ( !pTraceFilter->ShouldHitEntity( pHandleEntity, fMask ) )
  1496. continue;
  1497. }
  1498. else
  1499. {
  1500. // FIXME: Could remove this check here by
  1501. // using a different spatial partition mask. Look into it
  1502. // if we want more speedups here.
  1503. if ( bNoStaticProps )
  1504. continue;
  1505. if ( bFilterStaticProps )
  1506. {
  1507. if ( !pTraceFilter->ShouldHitEntity( pHandleEntity, fMask ) )
  1508. continue;
  1509. }
  1510. }
  1511. ClipRayToCollideable( entityRay, fMask, pCollideable, &tr );
  1512. // Make sure the ray is always shorter than it currently is
  1513. ClipTraceToTrace( tr, pTrace );
  1514. // Stop if we're in allsolid
  1515. if (pTrace->allsolid)
  1516. break;
  1517. }
  1518. // Fix up the fractions so they are appropriate given the original
  1519. // unclipped-to-world ray
  1520. pTrace->fraction *= flWorldFraction;
  1521. pTrace->fractionleftsolid *= flWorldFractionLeftSolidScale;
  1522. #ifdef _DEBUG
  1523. Vector vecOffset, vecEndTest;
  1524. VectorAdd( ray.m_Start, ray.m_StartOffset, vecOffset );
  1525. VectorMA( vecOffset, pTrace->fractionleftsolid, ray.m_Delta, vecEndTest );
  1526. Assert( VectorsAreEqual( vecEndTest, pTrace->startpos, 0.1f ) );
  1527. VectorMA( vecOffset, pTrace->fraction, ray.m_Delta, vecEndTest );
  1528. Assert( VectorsAreEqual( vecEndTest, pTrace->endpos, 0.1f ) );
  1529. // Assert( !ray.m_IsRay || pTrace->allsolid || pTrace->fraction >= pTrace->fractionleftsolid );
  1530. #endif
  1531. if ( !ray.m_IsRay )
  1532. {
  1533. // Make sure no fractionleftsolid can be used with box sweeps
  1534. VectorAdd( ray.m_Start, ray.m_StartOffset, pTrace->startpos );
  1535. pTrace->fractionleftsolid = 0;
  1536. #ifdef _DEBUG
  1537. pTrace->fractionleftsolid = VEC_T_NAN;
  1538. #endif
  1539. }
  1540. }
  1541. //-----------------------------------------------------------------------------
  1542. // A version that sweeps a collideable through the world
  1543. //-----------------------------------------------------------------------------
  1544. void CEngineTrace::SweepCollideable( ICollideable *pCollide,
  1545. const Vector &vecAbsStart, const Vector &vecAbsEnd, const QAngle &vecAngles,
  1546. unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace )
  1547. {
  1548. const matrix3x4_t *pOldRoot = m_pRootMoveParent;
  1549. Ray_t ray;
  1550. Assert( vecAngles == vec3_angle );
  1551. if ( pCollide->GetSolidFlags() & FSOLID_ROOT_PARENT_ALIGNED )
  1552. {
  1553. m_pRootMoveParent = pCollide->GetRootParentToWorldTransform();
  1554. }
  1555. ray.Init( vecAbsStart, vecAbsEnd, pCollide->OBBMins(), pCollide->OBBMaxs() );
  1556. TraceRay( ray, fMask, pTraceFilter, pTrace );
  1557. m_pRootMoveParent = pOldRoot;
  1558. }
  1559. //-----------------------------------------------------------------------------
  1560. // Lets clients know about all edicts along a ray
  1561. //-----------------------------------------------------------------------------
  1562. class CEnumerationFilter : public IPartitionEnumerator
  1563. {
  1564. public:
  1565. CEnumerationFilter( CEngineTrace *pEngineTrace, IEntityEnumerator* pEnumerator ) :
  1566. m_pEngineTrace(pEngineTrace), m_pEnumerator(pEnumerator) {}
  1567. IterationRetval_t EnumElement( IHandleEntity *pHandleEntity )
  1568. {
  1569. // Don't enumerate static props
  1570. if ( StaticPropMgr()->IsStaticProp( pHandleEntity ) )
  1571. return ITERATION_CONTINUE;
  1572. if ( !m_pEnumerator->EnumEntity( pHandleEntity ) )
  1573. {
  1574. return ITERATION_STOP;
  1575. }
  1576. return ITERATION_CONTINUE;
  1577. }
  1578. private:
  1579. IEntityEnumerator* m_pEnumerator;
  1580. CEngineTrace *m_pEngineTrace;
  1581. };
  1582. //-----------------------------------------------------------------------------
  1583. // Enumerates over all entities along a ray
  1584. // If triggers == true, it enumerates all triggers along a ray
  1585. //-----------------------------------------------------------------------------
  1586. void CEngineTrace::EnumerateEntities( const Ray_t &ray, bool bTriggers, IEntityEnumerator *pEnumerator )
  1587. {
  1588. m_traceStatCounters[TRACE_STAT_COUNTER_ENUMERATE]++;
  1589. // FIXME: If we store CBaseHandles directly in the spatial partition, this method
  1590. // basically becomes obsolete. The spatial partition can be queried directly.
  1591. CEnumerationFilter enumerator( this, pEnumerator );
  1592. int fMask = !bTriggers ? SpatialPartitionMask() : SpatialPartitionTriggerMask();
  1593. // NOTE: Triggers currently don't exist on the client
  1594. if (fMask)
  1595. {
  1596. SpatialPartition()->EnumerateElementsAlongRay( fMask, ray, false, &enumerator );
  1597. }
  1598. }
  1599. //-----------------------------------------------------------------------------
  1600. // Lets clients know about all entities in a box
  1601. //-----------------------------------------------------------------------------
  1602. void CEngineTrace::EnumerateEntities( const Vector &vecAbsMins, const Vector &vecAbsMaxs, IEntityEnumerator *pEnumerator )
  1603. {
  1604. m_traceStatCounters[TRACE_STAT_COUNTER_ENUMERATE]++;
  1605. // FIXME: If we store CBaseHandles directly in the spatial partition, this method
  1606. // basically becomes obsolete. The spatial partition can be queried directly.
  1607. CEnumerationFilter enumerator( this, pEnumerator );
  1608. SpatialPartition()->EnumerateElementsInBox( SpatialPartitionMask(),
  1609. vecAbsMins, vecAbsMaxs, false, &enumerator );
  1610. }
  1611. class CEntList : public IEntityEnumerator
  1612. {
  1613. public:
  1614. virtual bool EnumEntity( IHandleEntity *pHandleEntity )
  1615. {
  1616. IServerUnknown *pNetEntity = static_cast<IServerUnknown*>(pHandleEntity);
  1617. ICollideable *pCollide = pNetEntity->GetCollideable();
  1618. if ( !pCollide )
  1619. return true;
  1620. Vector vecCenter;
  1621. VectorMA( MainViewOrigin(), 100.0f, MainViewForward(), vecCenter );
  1622. float flDist = (vecCenter - pCollide->GetCollisionOrigin()).LengthSqr();
  1623. if (flDist < m_flClosestDist)
  1624. {
  1625. m_flClosestDist = flDist;
  1626. m_pClosest = pCollide;
  1627. }
  1628. return true;
  1629. }
  1630. ICollideable *m_pClosest;
  1631. float m_flClosestDist;
  1632. };
  1633. #ifdef _DEBUG
  1634. //-----------------------------------------------------------------------------
  1635. // A method to test out sweeps
  1636. //-----------------------------------------------------------------------------
  1637. CON_COMMAND( test_sweepaabb, "method to test out sweeps" )
  1638. {
  1639. Vector vecStartPoint;
  1640. VectorMA( MainViewOrigin(), 50.0f, MainViewForward(), vecStartPoint );
  1641. Vector endPoint;
  1642. VectorMA( MainViewOrigin(), COORD_EXTENT * 1.74f, MainViewForward(), endPoint );
  1643. Ray_t ray;
  1644. ray.Init( vecStartPoint, endPoint );
  1645. trace_t tr;
  1646. // CTraceFilterHitAll traceFilter;
  1647. // g_pEngineTraceServer->TraceRay( ray, MASK_ALL, &traceFilter, &tr );
  1648. CEntList list;
  1649. list.m_pClosest = NULL;
  1650. list.m_flClosestDist = FLT_MAX;
  1651. g_pEngineTraceServer->EnumerateEntities( MainViewOrigin() - Vector( 200, 200, 200 ), MainViewOrigin() + Vector( 200, 200, 200 ), &list );
  1652. if ( !list.m_pClosest )
  1653. return;
  1654. // Visualize the intersection test
  1655. ICollideable *pCollide = list.m_pClosest;
  1656. if ( pCollide->GetCollisionOrigin() == vec3_origin )
  1657. return;
  1658. QAngle test( 0, 45, 0 );
  1659. #ifndef SWDS
  1660. CDebugOverlay::AddBoxOverlay( pCollide->GetCollisionOrigin(),
  1661. pCollide->OBBMins(), pCollide->OBBMaxs(),
  1662. test /*pCollide->GetCollisionAngles()*/, 0, 0, 255, 128, 5.0f );
  1663. #endif
  1664. VectorMA( MainViewOrigin(), 200.0f, MainViewForward(), endPoint );
  1665. ray.Init( vecStartPoint, endPoint, Vector( -10, -20, -10 ), Vector( 30, 30, 20 ) );
  1666. bool bIntersect = IntersectRayWithOBB( ray, pCollide->GetCollisionOrigin(), test, pCollide->OBBMins(),
  1667. pCollide->OBBMaxs(), 0.0f, &tr );
  1668. unsigned char r, g, b, a;
  1669. b = 0;
  1670. a = 255;
  1671. r = bIntersect ? 255 : 0;
  1672. g = bIntersect ? 0 : 255;
  1673. #ifndef SWDS
  1674. CDebugOverlay::AddSweptBoxOverlay( tr.startpos, tr.endpos,
  1675. Vector( -10, -20, -10 ), Vector( 30, 30, 20 ), vec3_angle, r, g, b, a, 5.0 );
  1676. #endif
  1677. }
  1678. #endif