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

2300 lines
79 KiB

  1. //========= Copyright (c) Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //============================================================================//
  6. #include "cbase.h"
  7. #include <algorithm>
  8. #include <functional>
  9. #include "paint_blobs_shared.h"
  10. #include "debugoverlay_shared.h"
  11. #include "portal_base2d_shared.h"
  12. #include "paint_cleanser_manager.h"
  13. #include "fmtstr.h"
  14. #include "paintable_entity.h"
  15. #include "portal_player_shared.h"
  16. #include "vprof.h"
  17. #include "datacache/imdlcache.h"
  18. #include "raytrace.h"
  19. #include "prop_portal_shared.h"
  20. #include "mathlib/ssequaternion.h"
  21. // define this when we want to prefetch blob data in PaintBlobUpdate()
  22. //#define BLOB_PREFETCH
  23. // define this to debug blob SIMD update
  24. //#define BLOB_SIMD_DEBUG
  25. #ifdef BLOB_SIMD_DEBUG
  26. #define BLOB_IN_BEAM_ERROR 1.f
  27. #endif
  28. #ifdef BLOB_PREFETCH
  29. #include "cache_hints.h"
  30. #endif
  31. #ifdef CLIENT_DLL
  32. #include "c_trigger_paint_cleanser.h"
  33. #include "c_paintblob.h"
  34. #include "c_world.h"
  35. const Color g_BlobDebugColor( 0, 255, 255 );
  36. ConVar debug_paint_client_blobs( "debug_paint_client_blobs", "0" );
  37. #else
  38. #include "trigger_paint_cleanser.h"
  39. #include "cpaintblob.h"
  40. #include "world.h"
  41. ConVar debug_paint_server_blobs( "debug_paint_server_blobs", "0", FCVAR_DEVELOPMENTONLY );
  42. ConVar debug_paintblobs_streaking( "debug_paintblobs_streaking", "0", FCVAR_DEVELOPMENTONLY );
  43. extern ConVar phys_pushscale;
  44. const Color g_BlobDebugColor( 255, 0, 255 );
  45. #endif
  46. // memdbgon must be the last include file in a .cpp file!!!
  47. #include "tier0/memdbgon.h"
  48. #define VPROF_BUDGETGROUP_PAINTBLOB _T("Paintblob")
  49. ConVar paintblob_collision_box_size("paintblob_collision_box_size", "60.f", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY);
  50. ConVar paintblob_gravity_scale( "paintblob_gravity_scale", "1.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "The gravity scale of the paint blobs." );
  51. ConVar paintblob_air_drag( "paintblob_air_drag", "0.1f", FCVAR_REPLICATED | FCVAR_CHEAT, "The air drag applied to the paint blobs." );
  52. ConVar paintblob_minimum_portal_exit_velocity( "paintblob_minimum_portal_exit_velocity", "225.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "The minimum velocity of the paint blobs on exiting portals." );
  53. // Blobulator radius scale
  54. ConVar paintblob_min_radius_scale("paintblob_min_radius_scale", "0.7f", FCVAR_REPLICATED | FCVAR_CHEAT );
  55. ConVar paintblob_max_radius_scale("paintblob_max_radius_scale", "1.0f", FCVAR_REPLICATED | FCVAR_CHEAT );
  56. // streak
  57. ConVar paintblob_radius_while_streaking( "paintblob_radius_while_streaking", "0.3f", FCVAR_REPLICATED | FCVAR_CHEAT );
  58. ConVar paintblob_streak_angle_threshold( "paintblob_streak_angle_threshold", "45.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "The angle of impact below which the paint blobs will streak paint." );
  59. ConVar paintblob_streak_trace_range( "paintblob_streak_trace_range", "20.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "The range of the trace for the paint blobs while streaking." );
  60. ConVar paintblob_streak_particles_enabled("paintblob_streak_particles_enabled", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT | FCVAR_REPLICATED );
  61. //Tractor beam
  62. ConVar paintblob_tbeam_accel( "paintblob_tbeam_accel", "200.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "The acceleration of the paint blobs while in a tractor beam to get up to tractor beam speed" );
  63. ConVar paintblob_tbeam_vortex_circulation( "paintblob_tbeam_vortex_circulation", "30000.f", FCVAR_REPLICATED | FCVAR_CHEAT );
  64. ConVar paintblob_tbeam_portal_vortex_circulation( "paintblob_tbeam_portal_vortex_circulation", "60000.f", FCVAR_REPLICATED | FCVAR_CHEAT );
  65. ConVar paintblob_tbeam_vortex_radius_rate( "paintblob_tbeam_vortex_radius_rate", "100.f", FCVAR_REPLICATED | FCVAR_CHEAT );
  66. ConVar paintblob_tbeam_vortex_accel( "paintblob_tbeam_vortex_accel", "300.f", FCVAR_REPLICATED | FCVAR_CHEAT );
  67. ConVar paintblob_tbeam_vortex_distance( "paintblob_tbeam_vortex_distance", "50.f", FCVAR_REPLICATED | FCVAR_CHEAT , "Blob will do vortex if blob's distance from start or end point of the beam is within this distance");
  68. //Limited range for blobs
  69. ConVar paintblob_limited_range( "paintblob_limited_range", "0", FCVAR_REPLICATED | FCVAR_CHEAT, "If the paintblobs have a limited range." );
  70. ConVar paintblob_lifetime( "paintblob_lifetime", "1.5f", FCVAR_REPLICATED | FCVAR_CHEAT, "The lifetime of the paintblobs if they have a limited range." );
  71. #ifdef _X360
  72. ConVar paintblob_update_per_second( "paintblob_update_per_second", "30.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "The number of times the blobs movement code is run per second." );
  73. #else
  74. ConVar paintblob_update_per_second( "paintblob_update_per_second", "60.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "The number of times the blobs movement code is run per second." );
  75. #endif
  76. ConVar debug_beam_badsection( "debug_beam_badsection", "0", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  77. const int BLOB_TRACE_STATIC_MASK = CONTENTS_SOLID | CONTENTS_WINDOW | CONTENTS_HITBOX | CONTENTS_DEBRIS | CONTENTS_WATER | CONTENTS_SLIME;
  78. const int BLOB_TRACE_DYNAMIC_MASK = CONTENTS_SOLID | CONTENTS_HITBOX | CONTENTS_MOVEABLE | CONTENTS_MONSTER | CONTENTS_DEBRIS;
  79. const int MAX_BLOB_TRACE_ENTITY_RESULTS = 64;
  80. extern ConVar sv_gravity;
  81. extern ConVar player_can_use_painted_power;
  82. extern ConVar player_paint_effects_enabled;
  83. ConVar paintblob_beam_radius_offset("paintblob_beam_radius_offset", "15.f", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
  84. float UTil_Blob_BeamRadiusOffset( float flBeamRadius )
  85. {
  86. float flOutput = flBeamRadius - paintblob_beam_radius_offset.GetFloat();
  87. Assert( flOutput > 0.f );
  88. return ( flOutput > 0.f ) ? flOutput : 1.f;
  89. }
  90. float UTil_Blob_TBeamLinearForce( float flLinearForce )
  91. {
  92. // motion controller of the beam moves other entities half speed of the blobs
  93. return 0.5f * fabs( flLinearForce );
  94. }
  95. CBasePaintBlob::CBasePaintBlob() : m_flDestVortexRadius( 0.f ),
  96. m_flCurrentVortexRadius( 0.f ),
  97. m_flCurrentVortexSpeed( 0.f ),
  98. m_flVortexDirection(1.f), // -1.f or 1.f
  99. // positions & velocities
  100. m_vecTempEndPosition( vec3_origin ),
  101. m_vecTempEndVelocity( vec3_origin ),
  102. m_vecPosition( vec3_origin ),
  103. m_vecPrevPosition( vec3_origin ),
  104. m_vecVelocity( vec3_origin ),
  105. // normal for particles effect
  106. m_vContactNormal( vec3_origin ),
  107. m_paintType( NO_POWER ),
  108. m_hOwner( NULL ),
  109. m_MoveState( PAINT_BLOB_AIR_MOVE ),
  110. // life time
  111. m_flLifeTime( 0.f ),
  112. //Streaking
  113. m_vecStreakDir( vec3_origin ),
  114. m_bStreakDirChanged( false ),
  115. m_flStreakTimer( 0.f ),
  116. m_flStreakSpeedDampenRate( 0.f ),
  117. //Tractor beam
  118. m_bInTractorBeam( false ),
  119. m_bDeleteFlag( false ),
  120. // update time
  121. m_flAccumulatedTime( 0.0 ),
  122. m_flLastUpdateTime( 0.0 ),
  123. m_flRadiusScale( 0.f ),
  124. m_bShouldPlayEffect( false ),
  125. m_bSilent( false ),
  126. // ghost blob!!!
  127. m_hPortal( NULL ),
  128. // optimize trace
  129. m_vCollisionBoxCenter( vec3_origin ),
  130. m_bCollisionBoxHitSolid( false ),
  131. m_bDrawOnly( false ),
  132. // num teleported
  133. m_bTeleportedThisFrame( false ),
  134. m_nTeleportationCount( 0 )
  135. {
  136. }
  137. CBasePaintBlob::~CBasePaintBlob()
  138. {
  139. }
  140. void CBasePaintBlob::Init( const Vector &vecOrigin, const Vector &vecVelocity, int paintType, float flMaxStreakTime, float flStreakSpeedDampenRate, CBaseEntity* pOwner, bool bSilent, bool bDrawOnly )
  141. {
  142. m_vecPosition = vecOrigin;
  143. m_vecPrevPosition = vecOrigin;
  144. SetVelocity( vecVelocity );
  145. m_paintType = static_cast<PaintPowerType>( paintType );
  146. //Set up the streaking properties of the blob
  147. m_flStreakTimer = flMaxStreakTime;
  148. m_flStreakSpeedDampenRate = flStreakSpeedDampenRate;
  149. m_vecStreakDir = vec3_origin;
  150. //Set the default move state for the blob
  151. m_MoveState = PAINT_BLOB_AIR_MOVE;
  152. // set values when blobs hit tbeam
  153. m_bInTractorBeam = false;
  154. m_flVortexDirection = Sign( RandomFloat(-1.f, 1.f) );
  155. m_flDestVortexRadius = RandomFloat( 0.1, 1.f );
  156. m_flCurrentVortexSpeed = 0.f;
  157. m_flAccumulatedTime = 0.f;
  158. m_flLastUpdateTime = gpGlobals->curtime;
  159. //Set up the radius for the blob
  160. m_flRadiusScale = RandomFloat( paintblob_min_radius_scale.GetFloat(), paintblob_max_radius_scale.GetFloat() );
  161. m_bShouldPlayEffect = false;
  162. m_bSilent = bSilent;
  163. ResetGhostState();
  164. m_vCollisionBoxCenter = vecOrigin;
  165. m_bCollisionBoxHitSolid = CheckCollisionBoxAgainstWorldAndStaticProps();
  166. m_hOwner = pOwner;
  167. m_bShouldPlaySound = false;
  168. m_bDrawOnly = bDrawOnly;
  169. }
  170. const Vector& CBasePaintBlob::GetTempEndPosition( void ) const
  171. {
  172. return m_vecTempEndPosition;
  173. }
  174. void CBasePaintBlob::SetTempEndPosition( const Vector &vecTempEndPosition )
  175. {
  176. m_vecTempEndPosition = vecTempEndPosition;
  177. }
  178. const Vector& CBasePaintBlob::GetTempEndVelocity( void ) const
  179. {
  180. return m_vecTempEndVelocity;
  181. }
  182. void CBasePaintBlob::SetTempEndVelocity( const Vector &vecTempEndVelocity )
  183. {
  184. m_vecTempEndVelocity = vecTempEndVelocity;
  185. }
  186. const Vector& CBasePaintBlob::GetPosition( void ) const
  187. {
  188. return m_vecPosition;
  189. }
  190. void CBasePaintBlob::SetPosition( const Vector &vecPosition )
  191. {
  192. m_vecPrevPosition = m_vecPosition;
  193. m_vecPosition = vecPosition;
  194. }
  195. const Vector& CBasePaintBlob::GetPrevPosition() const
  196. {
  197. return m_vecPrevPosition;
  198. }
  199. void CBasePaintBlob::SetPrevPosition( const Vector& vPrevPosition )
  200. {
  201. m_vecPrevPosition = vPrevPosition;
  202. }
  203. const Vector& CBasePaintBlob::GetVelocity( void ) const
  204. {
  205. return m_vecVelocity;
  206. }
  207. void CBasePaintBlob::SetVelocity( const Vector &vecVelocity )
  208. {
  209. m_vecVelocity = vecVelocity;
  210. }
  211. const Vector& CBasePaintBlob::GetStreakDir() const
  212. {
  213. return m_vecStreakDir;
  214. }
  215. PaintPowerType CBasePaintBlob::GetPaintPowerType( void ) const
  216. {
  217. return m_paintType;
  218. }
  219. PaintBlobMoveState CBasePaintBlob::GetMoveState( void ) const
  220. {
  221. return m_MoveState;
  222. }
  223. void CBasePaintBlob::SetMoveState( PaintBlobMoveState moveState )
  224. {
  225. m_MoveState = moveState;
  226. }
  227. float CBasePaintBlob::GetVortexDirection() const
  228. {
  229. return m_flVortexDirection;
  230. }
  231. bool CBasePaintBlob::ShouldDeleteThis() const
  232. {
  233. return m_bDeleteFlag;
  234. }
  235. void CBasePaintBlob::SetDeletionFlag( bool bDelete )
  236. {
  237. m_bDeleteFlag = bDelete;
  238. }
  239. bool CBasePaintBlob::IsStreaking( void ) const
  240. {
  241. return m_MoveState == PAINT_BLOB_STREAK_MOVE;
  242. }
  243. float CBasePaintBlob::GetLifeTime() const
  244. {
  245. return m_flLifeTime;
  246. }
  247. void CBasePaintBlob::UpdateLifeTime( float flLifeTime )
  248. {
  249. m_flLifeTime += flLifeTime;
  250. }
  251. void CBasePaintBlob::SetRadiusScale( float flRadiusScale )
  252. {
  253. m_flRadiusScale = flRadiusScale;
  254. }
  255. float CBasePaintBlob::GetRadiusScale( void ) const
  256. {
  257. if( m_MoveState == PAINT_BLOB_STREAK_MOVE )
  258. {
  259. return paintblob_radius_while_streaking.GetFloat();
  260. }
  261. return m_flRadiusScale;
  262. }
  263. void CBasePaintBlob::GetGhostMatrix( VMatrix& matGhostTransform )
  264. {
  265. CProp_Portal *pPortal = assert_cast< CProp_Portal* >( m_hPortal.Get() );
  266. Assert( pPortal );
  267. if ( pPortal )
  268. {
  269. matGhostTransform = pPortal->MatrixThisToLinked();
  270. }
  271. }
  272. CTrigger_TractorBeam* CBasePaintBlob::GetCurrentBeam() const
  273. {
  274. if ( m_beamHistory.m_beams.Count() == 0 )
  275. {
  276. return NULL;
  277. }
  278. return assert_cast< CTrigger_TractorBeam* >( m_beamHistory.m_beams[0].m_hBeamHandle.Get() );
  279. }
  280. class BlobTraceEnum : public ICountedPartitionEnumerator
  281. {
  282. public:
  283. BlobTraceEnum( CBaseEntity **pList, int listMax, int contentMask );
  284. virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity );
  285. virtual int GetCount() const;
  286. bool AddToList( CBaseEntity *pEntity );
  287. private:
  288. CBaseEntity** m_pList;
  289. int m_listMax;
  290. int m_count;
  291. int m_contentMask;
  292. };
  293. BlobTraceEnum::BlobTraceEnum( CBaseEntity **pList, int listMax, int contentMask )
  294. : m_pList( pList ),
  295. m_listMax( listMax ),
  296. m_count( 0 ),
  297. m_contentMask( m_contentMask )
  298. {
  299. }
  300. IterationRetval_t BlobTraceEnum::EnumElement( IHandleEntity *pHandleEntity )
  301. {
  302. #if defined( CLIENT_DLL )
  303. IClientEntity *pClientEntity = cl_entitylist->GetClientEntityFromHandle( pHandleEntity->GetRefEHandle() );
  304. C_BaseEntity *pEntity = pClientEntity ? pClientEntity->GetBaseEntity() : NULL;
  305. #else
  306. CBaseEntity *pEntity = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() );
  307. #endif
  308. if( pEntity )
  309. {
  310. // Does this collide with blobs?
  311. if( ( !pEntity->ShouldCollide( COLLISION_GROUP_PROJECTILE, m_contentMask ) ) /*&& bNotPortalOrTBeam*/ )
  312. return ITERATION_CONTINUE;
  313. if( !AddToList( pEntity ) )
  314. return ITERATION_STOP;
  315. }
  316. return ITERATION_CONTINUE;
  317. }
  318. int BlobTraceEnum::GetCount() const
  319. {
  320. return m_count;
  321. }
  322. bool BlobTraceEnum::AddToList( CBaseEntity *pEntity )
  323. {
  324. if( m_count >= m_listMax )
  325. {
  326. AssertMsgOnce( 0, "reached enumerated list limit. Increase limit, decrease radius, or make it so entity flags will work for you" );
  327. return false;
  328. }
  329. m_pList[m_count++] = pEntity;
  330. return true;
  331. }
  332. BlobTraceResult CBasePaintBlob::BlobHitSolid( CBaseEntity* pHitEntity )
  333. {
  334. if ( !pHitEntity )
  335. return BLOB_TRACE_HIT_NOTHING;
  336. if( pHitEntity->IsWorld() )
  337. {
  338. return BLOB_TRACE_HIT_WORLD;
  339. }
  340. // Player
  341. if( pHitEntity->IsPlayer() )
  342. {
  343. // If the blob started in the player box, it didn't hit. Otherwise, it did.
  344. // Compensate for updating out-of-sync by sweeping the box along the
  345. // displacement for the frame.
  346. Vector mins = pHitEntity->GetAbsOrigin() + pHitEntity->WorldAlignMins();
  347. Vector maxs = pHitEntity->GetAbsOrigin() + pHitEntity->WorldAlignMaxs();
  348. const Vector frameDisplacement = pHitEntity->GetAbsVelocity() * gpGlobals->frametime;
  349. ExpandAABB( mins, maxs, -frameDisplacement );
  350. // Blobs can only collide if player painting is enabled
  351. const bool usePaintEffects = player_can_use_painted_power.GetBool() || player_paint_effects_enabled.GetBool();
  352. const bool canCollideWithPlayer = usePaintEffects && !IsPointInBounds( m_vecPosition, mins, maxs );
  353. return canCollideWithPlayer ? BLOB_TRACE_HIT_PLAYER : BLOB_TRACE_HIT_NOTHING;
  354. }
  355. return BLOB_TRACE_HIT_SOMETHING;
  356. }
  357. void UTIL_Blob_TraceWorldAndStaticPropsOnly( const Ray_t& ray, trace_t& tr )
  358. {
  359. CTraceFilterWorldAndPropsOnly traceFilter;
  360. UTIL_TraceRay( ray, BLOB_TRACE_STATIC_MASK, &traceFilter, &tr );
  361. }
  362. void UTIL_Blob_EnumerateEntitiesAlongRay( const Ray_t& ray, ICountedPartitionEnumerator* pEntEnum )
  363. {
  364. #ifdef GAME_DLL
  365. if( ray.m_Delta.IsZeroFast() )
  366. ::partition->EnumerateElementsAtPoint( PARTITION_SERVER_GAME_EDICTS, ray.m_Start, false, pEntEnum );
  367. else
  368. ::partition->EnumerateElementsAlongRay( PARTITION_SERVER_GAME_EDICTS, ray, false, pEntEnum );
  369. #else
  370. if( ray.m_Delta.IsZeroFast() )
  371. ::partition->EnumerateElementsAtPoint( PARTITION_CLIENT_GAME_EDICTS, ray.m_Start, false, pEntEnum );
  372. else
  373. ::partition->EnumerateElementsAlongRay( PARTITION_CLIENT_GAME_EDICTS, ray, false, pEntEnum );
  374. #endif
  375. }
  376. bool CBasePaintBlob::CheckCollisionBoxAgainstWorldAndStaticProps()
  377. {
  378. const float flBoxSize = paintblob_collision_box_size.GetFloat();
  379. Vector vExtents( flBoxSize, flBoxSize, flBoxSize );
  380. Ray_t ray;
  381. ray.Init( m_vecPosition, m_vecTempEndPosition, -vExtents, vExtents );
  382. trace_t tr;
  383. UTIL_Blob_TraceWorldAndStaticPropsOnly( ray, tr );
  384. m_vCollisionBoxCenter = m_vecTempEndPosition;
  385. return tr.DidHit();
  386. }
  387. void CBasePaintBlob::CheckCollisionAgainstWorldAndStaticProps( BlobCollisionRecord& solidHitRecord, float& flHitFraction )
  388. {
  389. const float flBoxSize = paintblob_collision_box_size.GetFloat();
  390. Vector vExtents( flBoxSize, flBoxSize, flBoxSize );
  391. // if blob moves outside the collision box, recompute the new collision box
  392. if ( !IsPointInBounds( m_vecTempEndPosition, m_vCollisionBoxCenter - vExtents, m_vCollisionBoxCenter + vExtents ) )
  393. {
  394. m_bCollisionBoxHitSolid = CheckCollisionBoxAgainstWorldAndStaticProps();
  395. }
  396. // always check if we are hitting world this frame if the flag is set to true
  397. if ( m_bCollisionBoxHitSolid )
  398. {
  399. Ray_t ray;
  400. ray.Init( m_vecPosition, m_vecTempEndPosition );
  401. trace_t tr;
  402. UTIL_ClearTrace( tr );
  403. UTIL_Blob_TraceWorldAndStaticPropsOnly( ray, tr );
  404. if ( tr.DidHit() && tr.fraction < flHitFraction )
  405. {
  406. flHitFraction = tr.fraction;
  407. solidHitRecord.trace = tr;
  408. solidHitRecord.traceResultType = BlobHitSolid( tr.m_pEnt );
  409. solidHitRecord.targetEndPos = tr.endpos;
  410. }
  411. }
  412. }
  413. int CBasePaintBlob::CheckCollision( BlobCollisionRecord *pCollisions, int maxCollisions, const Vector &vecEndPos )
  414. {
  415. const Vector& vecStartPos = m_vecPosition;
  416. Ray_t fastRay;
  417. fastRay.Init( vecStartPos, vecEndPos );
  418. float flTempFraction = 1.f;
  419. if ( UTIL_Portal_FirstAlongRay( fastRay, flTempFraction ) == NULL )
  420. {
  421. CBaseEntity* ppEntities[ MAX_BLOB_TRACE_ENTITY_RESULTS ];
  422. BlobTraceEnum entEnum( ppEntities, ARRAYSIZE( ppEntities ), BLOB_TRACE_DYNAMIC_MASK );
  423. UTIL_Blob_EnumerateEntitiesAlongRay( fastRay, &entEnum );
  424. int nEntAlongRay = entEnum.GetCount();
  425. int nCollisionCount = 0;
  426. float firstHitSolidFraction = 1.f;
  427. BlobCollisionRecord solidHitRecord;
  428. // check against world and static props if needed
  429. CheckCollisionAgainstWorldAndStaticProps( solidHitRecord, firstHitSolidFraction );
  430. for ( int i=0; i<nEntAlongRay; ++i )
  431. {
  432. CBaseEntity* pHitEntity = ppEntities[i];
  433. if ( FClassnameIs( pHitEntity, "prop_portal" ) )
  434. {
  435. pCollisions[nCollisionCount].traceResultType = BLOB_TRACE_HIT_PROP_PORTAL;
  436. pCollisions[nCollisionCount].trace.m_pEnt = pHitEntity;
  437. pCollisions[nCollisionCount++].targetEndPos = vecEndPos;
  438. }
  439. else if ( FClassnameIs( pHitEntity, "trigger_tractorbeam" ) )
  440. {
  441. pCollisions[nCollisionCount].traceResultType = BLOB_TRACE_HIT_TRACTORBEAM;
  442. pCollisions[nCollisionCount].trace.m_pEnt = pHitEntity;
  443. pCollisions[nCollisionCount++].targetEndPos = vecEndPos;
  444. }
  445. else if ( FClassnameIs( pHitEntity, "trigger_paint_cleanser" ) )
  446. {
  447. trace_t tempTrace;
  448. enginetrace->ClipRayToEntity( fastRay, BLOB_TRACE_DYNAMIC_MASK, pHitEntity, &tempTrace );
  449. CTriggerPaintCleanser *pPaintCleanser = assert_cast< CTriggerPaintCleanser* >( pHitEntity );
  450. if ( pPaintCleanser->IsEnabled() && tempTrace.DidHit() && tempTrace.fraction < firstHitSolidFraction )
  451. {
  452. firstHitSolidFraction = tempTrace.fraction;
  453. solidHitRecord.trace = tempTrace;
  454. solidHitRecord.traceResultType = BLOB_TRACE_HIT_PAINT_CLEANSER;
  455. solidHitRecord.targetEndPos = tempTrace.endpos;
  456. }
  457. }
  458. // entity that stops the blob
  459. else if ( pHitEntity->IsSolid() )
  460. {
  461. trace_t tempTrace;
  462. enginetrace->ClipRayToEntity( fastRay, BLOB_TRACE_DYNAMIC_MASK, pHitEntity, &tempTrace );
  463. if ( tempTrace.DidHit() && tempTrace.fraction < firstHitSolidFraction )
  464. {
  465. BlobTraceResult traceResultType = BlobHitSolid( pHitEntity );
  466. if( traceResultType != BLOB_TRACE_HIT_NOTHING )
  467. {
  468. firstHitSolidFraction = tempTrace.fraction;
  469. solidHitRecord.trace = tempTrace;
  470. solidHitRecord.traceResultType = traceResultType;
  471. solidHitRecord.targetEndPos = tempTrace.endpos;
  472. }
  473. }
  474. }
  475. }
  476. // add solid collision
  477. if ( firstHitSolidFraction < 1.f )
  478. {
  479. pCollisions[nCollisionCount++] = solidHitRecord;
  480. }
  481. return nCollisionCount;
  482. }
  483. else
  484. {
  485. return CheckCollisionThroughPortal( pCollisions, maxCollisions, vecEndPos );
  486. }
  487. }
  488. int CBasePaintBlob::CheckCollisionThroughPortal( BlobCollisionRecord *pCollisions, int maxCollisions, const Vector &vecEndPos )
  489. {
  490. VPROF_BUDGET( "CBasePaintBlob::CheckCollision", VPROF_BUDGETGROUP_PAINTBLOB );
  491. if( !pCollisions )
  492. return 0;
  493. const Vector& vecStartPos = m_vecPosition;
  494. CTraceFilterHitAll traceFilter;
  495. // Reserve space for all the output
  496. CBaseEntity* hitEntities[MAX_BLOB_TRACE_ENTITY_RESULTS];
  497. int segmentIndices[MAX_BLOB_TRACE_ENTITY_RESULTS];
  498. int segmentCount = 0;
  499. const int MAX_BLOB_TRACE_SEGMENTS = 16;
  500. ComplexPortalTrace_t traceSegments[MAX_BLOB_TRACE_SEGMENTS];
  501. const int contentMask = BLOB_TRACE_DYNAMIC_MASK | BLOB_TRACE_STATIC_MASK;
  502. BlobTraceEnum traceEnum( hitEntities, ARRAYSIZE( hitEntities ), contentMask );
  503. // Run a complex trace for all entities along the ray
  504. Ray_t blobRay;
  505. blobRay.Init( vecStartPos, vecEndPos );
  506. UTIL_Portal_EntitiesAlongRayComplex( segmentIndices,
  507. &segmentCount,
  508. MIN( MAX_BLOB_TRACE_ENTITY_RESULTS, maxCollisions ),
  509. traceSegments,
  510. ARRAYSIZE( traceSegments ),
  511. blobRay,
  512. &traceEnum,
  513. &traceFilter,
  514. contentMask );
  515. // Compute the fraction of the total trace that each segment makes up
  516. float traceSegmentFractions[MAX_BLOB_TRACE_SEGMENTS];
  517. float traceTravelledLength = 0.0f;
  518. for( int i = 0; i < segmentCount; ++i )
  519. {
  520. const float segLength = (traceSegments[i].trSegment.endpos - traceSegments[i].trSegment.startpos).Length();
  521. traceSegmentFractions[i] = segLength;
  522. traceTravelledLength += segLength;
  523. }
  524. const float traceTargetLength = blobRay.m_Delta.Length();
  525. const float invTraceTargetLength = (traceTargetLength > 0.0f) ? (1.0f / traceTargetLength) : (0.0f);
  526. const float invTraceTravelledLength = (traceTravelledLength > 0.0f) ? (1.0f / traceTravelledLength) : (0.0f);
  527. for( int i = 0; i < segmentCount; ++i )
  528. {
  529. traceSegmentFractions[i] *= invTraceTravelledLength;
  530. }
  531. // Compute the fraction so far at each index
  532. float traceSegmentFractionSoFar[MAX_BLOB_TRACE_SEGMENTS];
  533. {
  534. traceSegmentFractionSoFar[0] = 0.0f;
  535. float fractionSoFar = 0.0f;
  536. for( int i = 1; i < segmentCount; ++i )
  537. {
  538. fractionSoFar += traceSegmentFractions[i - 1];
  539. traceSegmentFractionSoFar[i] = fractionSoFar;
  540. }
  541. }
  542. // Find which portals were hit and keep track of the last one to test against the world
  543. // Note: The trace only ends when it hits the world or gets to the end point.
  544. // Note: Technically, these don't all necessarily need to be processed, but if the blob
  545. // hits something and portals in the same frame, its position doesn't matter.
  546. int writeIndex = 0;
  547. CPortal_Base2D* pLastHitPortal = NULL;
  548. const int lastSegIndex = segmentCount - 1;
  549. for( int i = 0; i < segmentCount; ++i )
  550. {
  551. CPortal_Base2D* pEndPortal = traceSegments[i].pSegmentEndPortal;
  552. if( pEndPortal != NULL &&
  553. pEndPortal != pLastHitPortal &&
  554. pEndPortal->IsActivedAndLinked() )
  555. {
  556. pLastHitPortal = pEndPortal;
  557. pCollisions[writeIndex].traceResultType = BLOB_TRACE_HIT_PORTAL;
  558. pCollisions[writeIndex].trace = traceSegments[i].trSegment;
  559. pCollisions[writeIndex].trace.m_pEnt = pEndPortal;
  560. pCollisions[writeIndex++].targetEndPos = traceSegments[lastSegIndex].trSegment.endpos;
  561. }
  562. }
  563. if ( segmentCount > 1 )
  564. {
  565. SetBlobTeleportedThisFrame( true );
  566. #ifdef GAME_DLL
  567. // record the new server blob teleportation history
  568. CPaintBlob *pBlob = assert_cast< CPaintBlob* >( this );
  569. if ( pBlob )
  570. {
  571. const float flDeltaTime = gpGlobals->curtime - m_flLastUpdateTime;
  572. for ( int i=0; i<lastSegIndex; ++i )
  573. {
  574. CPortal_Base2D* pEndPortal = traceSegments[i].pSegmentEndPortal;
  575. const VMatrix& matSourceToLinked = pEndPortal->MatrixThisToLinked();
  576. const VMatrix& matLinkedToSource = pEndPortal->m_hLinkedPortal->MatrixThisToLinked();
  577. const Vector& vEnter = traceSegments[ i ].trSegment.endpos;
  578. const Vector& vExit = traceSegments[ i + 1 ].trSegment.startpos;
  579. float flTraceFraction = traceSegmentFractionSoFar[i] + traceSegmentFractions[i];
  580. pBlob->AddBlobTeleportationHistory( BlobTeleportationHistory_t( matSourceToLinked, matLinkedToSource, vEnter, vExit, m_flLastUpdateTime + flTraceFraction * flDeltaTime ) );
  581. }
  582. }
  583. #endif
  584. }
  585. AssertMsg( !pLastHitPortal || DotProduct( traceSegments[lastSegIndex].trSegment.endpos, pLastHitPortal->m_hLinkedPortal->m_plane_Origin.normal ) > pLastHitPortal->m_hLinkedPortal->m_plane_Origin.dist, "Teleporting blobs behind portal." );
  586. // Check for water (no entity on the client)
  587. const float INVALID_TRACE_FRACTION = 2.0f;
  588. float firstHitSolidFraction = INVALID_TRACE_FRACTION;
  589. BlobCollisionRecord solidHitRecord;
  590. // HACK: Non-solid entities with bone followers will be rejected in the enumerator
  591. // but not in the trace. Add the entity to the array anyway.
  592. int entityCount = traceEnum.GetCount();
  593. CBaseEntity* pLastTraceEntity = traceSegments[lastSegIndex].trSegment.m_pEnt;
  594. if( segmentCount > 0 &&
  595. pLastTraceEntity &&
  596. !pLastTraceEntity->IsWorld() &&
  597. pLastTraceEntity != hitEntities[entityCount] &&
  598. entityCount < ARRAYSIZE( hitEntities ) )
  599. {
  600. hitEntities[entityCount] = pLastTraceEntity;
  601. segmentIndices[entityCount++] = lastSegIndex;
  602. }
  603. // Output the rest of the collision records
  604. for( int i = 0; i < entityCount; ++i )
  605. {
  606. CBaseEntity* pHitEntity = hitEntities[i];
  607. const int segIndex = segmentIndices[i];
  608. BlobTraceResult traceResultType = BLOB_TRACE_HIT_NOTHING;
  609. bool bIsLastSegment = ( segIndex == lastSegIndex );
  610. if ( !pHitEntity )
  611. continue;
  612. if ( FClassnameIs( pHitEntity, "trigger_tractorbeam" ) )
  613. {
  614. // we don't care about this beam if it's not hitting in the last segment
  615. if ( !bIsLastSegment )
  616. continue;
  617. pCollisions[writeIndex].traceResultType = BLOB_TRACE_HIT_TRACTORBEAM;
  618. pCollisions[writeIndex].trace = traceSegments[segIndex].trSegment;
  619. pCollisions[writeIndex].trace.m_pEnt = pHitEntity;
  620. pCollisions[writeIndex++].targetEndPos = traceSegments[lastSegIndex].trSegment.endpos;
  621. continue;
  622. }
  623. else if ( FClassnameIs( pHitEntity, "prop_portal" ) )
  624. {
  625. // we don't care about this portal if it's not hitting in the last segment
  626. if ( !bIsLastSegment )
  627. continue;
  628. pCollisions[writeIndex].traceResultType = BLOB_TRACE_HIT_PROP_PORTAL;
  629. pCollisions[writeIndex].trace = traceSegments[segIndex].trSegment;
  630. pCollisions[writeIndex].trace.m_pEnt = pHitEntity;
  631. pCollisions[writeIndex++].targetEndPos = traceSegments[lastSegIndex].trSegment.endpos;
  632. continue;
  633. }
  634. else if ( FClassnameIs( pHitEntity, "trigger_paint_cleanser" ) )
  635. {
  636. trace_t tempTrace;
  637. Ray_t traceRay;
  638. traceRay.Init( traceSegments[segIndex].trSegment.startpos, traceSegments[segIndex].trSegment.endpos );
  639. enginetrace->ClipRayToEntity( traceRay, BLOB_TRACE_DYNAMIC_MASK, pHitEntity, &tempTrace );
  640. float fraction = traceSegmentFractionSoFar[segIndex] + traceSegmentFractions[segIndex] * tempTrace.fraction;
  641. CTriggerPaintCleanser *pPaintCleanser = assert_cast< CTriggerPaintCleanser* >( pHitEntity );
  642. if ( pPaintCleanser->IsEnabled() && tempTrace.DidHit() && fraction < firstHitSolidFraction )
  643. {
  644. firstHitSolidFraction = fraction;
  645. solidHitRecord.trace = tempTrace;
  646. solidHitRecord.traceResultType = BLOB_TRACE_HIT_PAINT_CLEANSER;
  647. solidHitRecord.targetEndPos = tempTrace.endpos;
  648. }
  649. continue;
  650. }
  651. // Any other entity
  652. else if ( pHitEntity->IsSolid() )
  653. {
  654. traceResultType = BlobHitSolid( pHitEntity );
  655. if( traceResultType == BLOB_TRACE_HIT_NOTHING )
  656. continue;
  657. }
  658. // Find the actual intersection information and fraction along the total trace
  659. float fraction = 1.0f;
  660. trace_t trace;
  661. UTIL_ClearTrace( trace );
  662. // This entity stopped the trace
  663. if( pHitEntity == traceSegments[segIndex].trSegment.m_pEnt )
  664. {
  665. trace = traceSegments[segIndex].trSegment;
  666. // Here, the trace hit the entity, so there's no need to recompute the intersection
  667. // The fraction in the trace is the fraction of the remaining ray delta.
  668. const float totalFractionSoFar = traceSegmentFractionSoFar[segIndex] * traceTravelledLength * invTraceTargetLength;
  669. const float remainingLength = traceTargetLength - totalFractionSoFar * traceTargetLength;
  670. const float remainingLengthTravelled = trace.fraction * remainingLength;
  671. fraction = totalFractionSoFar + remainingLengthTravelled * invTraceTargetLength;
  672. }
  673. // This entity was somewhere along its corresponding trace segment
  674. else if ( pHitEntity->IsSolid() )
  675. {
  676. Ray_t traceRay;
  677. traceRay.Init( traceSegments[segIndex].trSegment.startpos, traceSegments[segIndex].trSegment.endpos );
  678. enginetrace->ClipRayToEntity( traceRay, contentMask, pHitEntity, &trace );
  679. fraction = traceSegmentFractionSoFar[segIndex] + traceSegmentFractions[segIndex] * trace.fraction;
  680. }
  681. // If the trace is valid and the best so far, record the collision
  682. if( trace.DidHit() && trace.m_pEnt && fraction <= firstHitSolidFraction )
  683. {
  684. firstHitSolidFraction = fraction;
  685. solidHitRecord.trace = trace;
  686. solidHitRecord.traceResultType = traceResultType;
  687. // Use the end position of this segment as the target end position.
  688. solidHitRecord.targetEndPos = traceSegments[segIndex].trSegment.endpos;
  689. }
  690. }
  691. if( firstHitSolidFraction != INVALID_TRACE_FRACTION )
  692. {
  693. pCollisions[writeIndex++] = solidHitRecord;
  694. }
  695. return writeIndex;
  696. }
  697. void CBasePaintBlob::ResolveCollision( bool& bDeleted, const BlobCollisionRecord& collision, Vector& targetVelocity, float deltaTime )
  698. {
  699. VPROF_BUDGET( "CBasePaintBlob::ResolveCollision", VPROF_BUDGETGROUP_PAINTBLOB );
  700. //Check what the blob hit
  701. switch( collision.traceResultType )
  702. {
  703. case BLOB_TRACE_HIT_PORTAL:
  704. //If the blob went through a portal then teleport it out of the portal
  705. {
  706. if( m_MoveState != PAINT_BLOB_STREAK_MOVE )
  707. {
  708. CPortal_Base2D* pInPortal = assert_cast<CPortal_Base2D*>( collision.trace.m_pEnt );
  709. //If the portal is active and linked
  710. if( pInPortal && pInPortal->IsActivedAndLinked() )
  711. {
  712. PaintBlobMoveThroughPortal( deltaTime, pInPortal, collision.trace.startpos, collision.targetEndPos );
  713. targetVelocity = GetVelocity();
  714. }
  715. }
  716. else
  717. {
  718. SetDeletionFlag( true );
  719. bDeleted = true;
  720. }
  721. }
  722. break;
  723. case BLOB_TRACE_HIT_TRACTORBEAM:
  724. {
  725. CTrigger_TractorBeam* pBeam = assert_cast< CTrigger_TractorBeam* >( collision.trace.m_pEnt );
  726. if ( pBeam )
  727. {
  728. SetTractorBeam( pBeam );
  729. SetPosition( collision.targetEndPos );
  730. SetVelocity( targetVelocity );
  731. m_bInTractorBeam = true;
  732. }
  733. }
  734. break;
  735. case BLOB_TRACE_HIT_PROP_PORTAL:
  736. {
  737. SetPosition( collision.targetEndPos );
  738. SetVelocity( targetVelocity );
  739. CProp_Portal *pPortal = assert_cast< CProp_Portal* >( collision.trace.m_pEnt );
  740. if ( pPortal && pPortal->IsActivedAndLinked() )
  741. {
  742. m_hPortal = pPortal;
  743. }
  744. }
  745. break;
  746. case BLOB_TRACE_HIT_PAINT_CLEANSER:
  747. {
  748. PlayEffect( collision.targetEndPos, collision.trace.plane.normal );
  749. SetDeletionFlag( true );
  750. bDeleted = true;
  751. }
  752. break;
  753. case BLOB_TRACE_HIT_WORLD:
  754. case BLOB_TRACE_HIT_SOMETHING:
  755. case BLOB_TRACE_HIT_PLAYER:
  756. //If the blob hit something
  757. {
  758. SetVelocity( targetVelocity );
  759. //Remove the blob if the blob should not streak on this surface
  760. bDeleted = !PaintBlobCheckShouldStreak( collision.trace );
  761. }
  762. break;
  763. }
  764. }
  765. void CBasePaintBlob::UpdateBlobCollision( float flDeltaTime, const Vector& vecEndPos, Vector& vecEndVelocity )
  766. {
  767. //Debugging flags
  768. bool bDebuggingBlobs = false;
  769. #ifdef CLIENT_DLL
  770. if( debug_paint_client_blobs.GetBool() )
  771. #else
  772. if( debug_paint_server_blobs.GetBool() )
  773. #endif
  774. {
  775. bDebuggingBlobs = true;
  776. }
  777. // disable blob particles and blob render if the blob is streaking
  778. if ( m_MoveState == PAINT_BLOB_STREAK_MOVE && !paintblob_streak_particles_enabled.GetBool() )
  779. {
  780. m_bSilent = true;
  781. }
  782. //Check if the blob collided with anything
  783. BlobCollisionRecord collisions[MAX_BLOB_TRACE_ENTITY_RESULTS];
  784. int collisionCount = CheckCollision( collisions, MAX_BLOB_TRACE_ENTITY_RESULTS, vecEndPos );
  785. //Draw a tracer line to show the blobs path
  786. if( bDebuggingBlobs )
  787. {
  788. NDebugOverlay::Line( GetPrevPosition(), vecEndPos, g_BlobDebugColor.r(), g_BlobDebugColor.g(), g_BlobDebugColor.b(), false, 10.0f );
  789. NDebugOverlay::VertArrow( GetPrevPosition(), vecEndPos, 4.0f, 0, 255, 0, 255, true, 0.01f );
  790. }
  791. // reset state
  792. m_bInTractorBeam = false;
  793. ResetGhostState();
  794. CTrigger_TractorBeam *pPreviousCurrentBeam = GetCurrentBeam();
  795. //If the blob didn't touch anything then move it
  796. if( collisionCount == 0 )
  797. {
  798. //Move the blob to its end pos
  799. SetPosition( vecEndPos );
  800. SetVelocity( vecEndVelocity );
  801. if ( m_MoveState != PAINT_BLOB_STREAK_MOVE )
  802. {
  803. SetMoveState( PAINT_BLOB_AIR_MOVE );
  804. }
  805. }
  806. // If the blob collided with things, resolve the collisions
  807. else
  808. {
  809. bool bDeleted = false;
  810. for( int i = 0; i < collisionCount && !bDeleted; ++i )
  811. {
  812. ResolveCollision( bDeleted, collisions[i], vecEndVelocity, flDeltaTime );
  813. }
  814. }
  815. // reset beam if need
  816. if ( !m_bInTractorBeam )
  817. {
  818. SetTractorBeam( NULL );
  819. }
  820. else if ( pPreviousCurrentBeam != GetCurrentBeam() )
  821. {
  822. // add blobs to beam
  823. GetCurrentBeam()->m_blobs.AddToTail( assert_cast< CPaintBlob* >( this ) );
  824. }
  825. }
  826. void CBasePaintBlob::UpdateBlobPostCollision( float flDeltaTime )
  827. {
  828. //If the paint blob is streaking
  829. if( m_MoveState == PAINT_BLOB_STREAK_MOVE )
  830. {
  831. Vector vVelocity = m_vecVelocity;
  832. // just remove the blob if the it's trying to streak with speed == 0
  833. if ( vVelocity.IsZero() )
  834. {
  835. SetDeletionFlag( true );
  836. return;
  837. }
  838. //Update the streak timer
  839. m_flStreakTimer -= flDeltaTime;
  840. // apply streak paint
  841. bool bDeleted = PaintBlobStreakPaint( m_vecPosition );
  842. //Dampen the speed of the blobs while streaking
  843. float flSpeed = VectorNormalize( vVelocity );
  844. flSpeed -= m_flStreakSpeedDampenRate * flDeltaTime;
  845. //If the blob should still be streaking
  846. if( !bDeleted && m_flStreakTimer >= 0.0f && flSpeed >= 0.0f )
  847. {
  848. SetVelocity( vVelocity * flSpeed );
  849. //Reset the streak dir changed flag
  850. m_bStreakDirChanged = false;
  851. }
  852. else
  853. {
  854. SetDeletionFlag( true );
  855. return;
  856. }
  857. }
  858. //Reset the move state
  859. if ( m_MoveState != PAINT_BLOB_TRACTOR_BEAM_MOVE )
  860. {
  861. DecayVortexSpeed( flDeltaTime );
  862. }
  863. }
  864. void CBasePaintBlob::PaintBlobMoveThroughPortal( float flDeltaTime, CPortal_Base2D *pInPortal, const Vector &vecStartPos, const Vector &vecTransformedEndPos )
  865. {
  866. VMatrix matTransform = pInPortal->MatrixThisToLinked();
  867. Vector vTransfromedVelocity;
  868. UTIL_Portal_VectorTransform( matTransform, m_vecVelocity, vTransfromedVelocity );
  869. SetVelocity( vTransfromedVelocity );
  870. SetPosition( vecTransformedEndPos );
  871. //Make sure the blobs have a min velocity when coming out of a portal
  872. float flMinVelocity = paintblob_minimum_portal_exit_velocity.GetFloat();
  873. if( m_vecVelocity.LengthSqr() < ( flMinVelocity * flMinVelocity ) )
  874. {
  875. m_vecVelocity.NormalizeInPlace();
  876. SetVelocity( m_vecVelocity * flMinVelocity );
  877. }
  878. // increment blob teleportation count
  879. ++m_nTeleportationCount;
  880. SetMoveState( PAINT_BLOB_AIR_MOVE );
  881. }
  882. bool CBasePaintBlob::PaintBlobCheckShouldStreak( const trace_t &trace )
  883. {
  884. bool bDebuggingStreaking = false;
  885. #ifdef GAME_DLL
  886. if( debug_paintblobs_streaking.GetBool() )
  887. {
  888. bDebuggingStreaking = true;
  889. }
  890. #endif
  891. // don't streak if blob is in tractor beam or out of streak time
  892. if( m_flStreakTimer <= 0.0f || m_bInTractorBeam || m_MoveState == PAINT_BLOB_TRACTOR_BEAM_MOVE )
  893. {
  894. CPaintBlob *pBlob = assert_cast< CPaintBlob* >( this );
  895. pBlob->PaintBlobPaint( trace );
  896. SetDeletionFlag( true );
  897. return false;
  898. }
  899. if( bDebuggingStreaking )
  900. {
  901. //Draw the collision position
  902. NDebugOverlay::Cross3D( trace.startpos, 2.0f, 255 - g_BlobDebugColor.r(), 255 - g_BlobDebugColor.g(), 255 - g_BlobDebugColor.b(), true, 10.0f );
  903. NDebugOverlay::Cross3D( trace.endpos, 2.0f, g_BlobDebugColor.r(), g_BlobDebugColor.g(), g_BlobDebugColor.b(), true, 10.0f );
  904. }
  905. const Vector& vecSurfaceNormal = trace.plane.normal;
  906. Vector vecStreakVelocity = m_vecVelocity - DotProduct( vecSurfaceNormal, m_vecVelocity ) * vecSurfaceNormal;
  907. Vector vecVelocityDir = m_vecVelocity.Normalized();
  908. Vector vecStreakVelocityDir = vecStreakVelocity.Normalized();
  909. //Check the angle of impact for the blob
  910. float flBlobImpactAngle = RAD2DEG( acos( clamp( DotProduct( vecStreakVelocityDir, vecVelocityDir ), -1.f, 1.f ) ) );
  911. if( bDebuggingStreaking )
  912. {
  913. Vector vecDrawPos = trace.endpos + vecSurfaceNormal * 50;
  914. //Draw the surface normal
  915. NDebugOverlay::VertArrow( vecDrawPos, vecDrawPos + vecSurfaceNormal * 50, 2, 0, 255, 0, 255, true, 10.0f );
  916. //Draw the velocity dir of the blob
  917. NDebugOverlay::VertArrow( vecDrawPos, vecDrawPos + (vecVelocityDir * 50), 2, 0, 0, 255, 255, true, 10.0f );
  918. //Draw the streak velocity of the blob
  919. NDebugOverlay::VertArrow( vecDrawPos, vecDrawPos + vecStreakVelocityDir * 50, 2, 0, 255, 255, 255, true, 10.0f );
  920. //Display the impact angle of the blob
  921. CFmtStr msg;
  922. msg.sprintf( "Impact angle: %f\n", flBlobImpactAngle );
  923. NDebugOverlay::Text( vecDrawPos, msg, true, 10.0f );
  924. }
  925. bool bShouldStreak = false;
  926. //Check the streaking conditions
  927. if( ( vecSurfaceNormal.z > -0.5f && vecSurfaceNormal.z < 0.5f ) || //If the blob hit a wall
  928. flBlobImpactAngle <= paintblob_streak_angle_threshold.GetFloat() ) //If the impact angle of the blob is within the threshold
  929. {
  930. bShouldStreak = true;
  931. }
  932. //Check if the blob should streak paint
  933. Vector vecHitPlaneDir = trace.plane.normal.Normalized();
  934. bool bSameSurface = AlmostEqual( -m_vecStreakDir, vecHitPlaneDir );
  935. //Set the streaking data if
  936. CPaintBlob *pBlob = assert_cast< CPaintBlob* >( this );
  937. if( ( m_MoveState != PAINT_BLOB_STREAK_MOVE && bShouldStreak ) || //The blob was not already streaking and it should streak
  938. ( m_MoveState == PAINT_BLOB_STREAK_MOVE && bShouldStreak && !bSameSurface ) )//The blob was streaking and is should streak on a different surface
  939. {
  940. //Set the streaking data for the blob
  941. SetMoveState( PAINT_BLOB_STREAK_MOVE );
  942. SetVelocity( vecStreakVelocity );
  943. pBlob->PaintBlobPaint( trace );
  944. m_vecStreakDir = -( vecHitPlaneDir );
  945. return true;
  946. }
  947. pBlob->PaintBlobPaint( trace );
  948. SetDeletionFlag( true );
  949. return bShouldStreak;
  950. }
  951. bool CBasePaintBlob::PaintBlobStreakPaint( const Vector &vecBlobStartPos )
  952. {
  953. bool bRemoveBlob = false;
  954. Ray_t blobRay;
  955. blobRay.Init( vecBlobStartPos, vecBlobStartPos + m_vecStreakDir * paintblob_streak_trace_range.GetFloat() );
  956. #ifndef CLIENT_DLL
  957. if( debug_paintblobs_streaking.GetBool() )
  958. {
  959. NDebugOverlay::Line( vecBlobStartPos, vecBlobStartPos + m_vecStreakDir * paintblob_streak_trace_range.GetFloat(), 255, 0, 255, true, 10.0f );
  960. }
  961. #endif
  962. //See if the blob hit a portal or anything else
  963. trace_t trace;
  964. CTraceFilterNoPlayers traceFilter;
  965. const int contentMask = BLOB_TRACE_STATIC_MASK | BLOB_TRACE_DYNAMIC_MASK;
  966. CPortal_Base2D *pInPortal = UTIL_Portal_TraceRay( blobRay, contentMask, &traceFilter, &trace );
  967. //If the blob hit a portal
  968. if( pInPortal )
  969. {
  970. bRemoveBlob = true;
  971. }
  972. //If the blob hit something else besides the player
  973. else if( trace.DidHit() )
  974. {
  975. PaintBlobPaint( trace );
  976. }
  977. else //The blob hit nothing
  978. {
  979. //Don't remove the blob if the streak direction changed this update
  980. if( !m_bStreakDirChanged )
  981. {
  982. bRemoveBlob = true;
  983. }
  984. }
  985. return bRemoveBlob;
  986. }
  987. void CBasePaintBlob::SetTractorBeam( CTrigger_TractorBeam *pBeam )
  988. {
  989. if ( pBeam == NULL )
  990. {
  991. if ( m_MoveState != PAINT_BLOB_STREAK_MOVE )
  992. {
  993. SetMoveState( PAINT_BLOB_AIR_MOVE );
  994. }
  995. m_beamHistory.ClearAllBeams();
  996. return;
  997. }
  998. SetMoveState( PAINT_BLOB_TRACTOR_BEAM_MOVE );
  999. if ( m_beamHistory.IsDifferentBeam( pBeam ) )
  1000. {
  1001. m_beamHistory.UpdateBeam( pBeam );
  1002. Vector vecPointOnLine;
  1003. CalcClosestPointOnLineSegment( GetPosition(), pBeam->GetStartPoint(), pBeam->GetEndPoint(), vecPointOnLine );
  1004. float flDistFromBeamCenter = ( GetPosition() - vecPointOnLine ).Length();
  1005. float flFraction = 1.f / UTil_Blob_BeamRadiusOffset( pBeam->GetBeamRadius() );
  1006. m_flCurrentVortexRadius = RemapValClamped( flDistFromBeamCenter, 1.f, UTil_Blob_BeamRadiusOffset( pBeam->GetBeamRadius() ), flFraction, 1.f );
  1007. }
  1008. }
  1009. void CBasePaintBlob::DecayVortexSpeed( float flDeltaTime )
  1010. {
  1011. if ( m_flCurrentVortexSpeed > 0.f )
  1012. {
  1013. m_flCurrentVortexSpeed = clamp( m_flCurrentVortexSpeed - flDeltaTime * paintblob_tbeam_vortex_accel.GetFloat(), 0.f, m_flCurrentVortexSpeed );
  1014. }
  1015. }
  1016. const Vector& CBasePaintBlob::GetContactNormal() const
  1017. {
  1018. return m_vContactNormal;
  1019. }
  1020. void CBasePaintBlob::PlayEffect( const Vector& vPosition, const Vector& vNormal )
  1021. {
  1022. SetPosition( vPosition );
  1023. m_vContactNormal = vNormal;
  1024. m_bShouldPlayEffect = true;
  1025. }
  1026. struct BlobInBeam_t : std::unary_function< CPaintBlob*, bool >
  1027. {
  1028. inline bool operator()( const CPaintBlob* pBlob ) const
  1029. {
  1030. return pBlob->GetMoveState() == PAINT_BLOB_TRACTOR_BEAM_MOVE;
  1031. }
  1032. };
  1033. struct BlobInAir_t : std::unary_function< CPaintBlob*, bool >
  1034. {
  1035. inline bool operator()( const CPaintBlob* pBlob ) const
  1036. {
  1037. return pBlob->GetMoveState() == PAINT_BLOB_AIR_MOVE;
  1038. }
  1039. };
  1040. void SplitBlobsIntoMovementGroup( PaintBlobVector_t& blobs, PaintBlobVector_t& blobsInBeam, PaintBlobVector_t& blobsInAir, PaintBlobVector_t& blobsInStreak )
  1041. {
  1042. // split blobs in beam
  1043. CPaintBlob** begin = blobs.Base();
  1044. CPaintBlob** end = begin + blobs.Count();
  1045. CPaintBlob** middle = std::partition( begin, end, BlobInBeam_t() );
  1046. int numBlobsInBeam = middle - begin;
  1047. blobsInBeam.CopyArray( begin, numBlobsInBeam );
  1048. // split blobs in air
  1049. begin = middle;
  1050. middle = std::partition( begin, end, BlobInAir_t() );
  1051. int numBlobsInAir = middle - begin;
  1052. blobsInAir.CopyArray( begin, numBlobsInAir );
  1053. int numBlobsInStreak = end - middle;
  1054. blobsInStreak.CopyArray( middle, numBlobsInStreak );
  1055. }
  1056. class BlobsInBeamUpdate_SIMD
  1057. {
  1058. public:
  1059. BlobsInBeamUpdate_SIMD( CTrigger_TractorBeam *pBeam )
  1060. {
  1061. const PaintBlobVector_t& blobs = pBeam->m_blobs;
  1062. if ( blobs.Count() == 0 )
  1063. return;
  1064. m_flDeltaTime = gpGlobals->curtime - blobs[0]->GetLastUpdateTime();
  1065. m_pBeam = pBeam;
  1066. int numBlobs = blobs.Count();
  1067. m_data.EnsureCount( numBlobs );
  1068. for ( int i=0; i<numBlobs; ++i )
  1069. {
  1070. m_data[i].m_vPosition = blobs[i]->GetPosition();
  1071. m_data[i].m_vVelocity = blobs[i]->GetVelocity();
  1072. m_data[i].m_flCurrentVortexRadius = blobs[i]->m_flCurrentVortexRadius;
  1073. m_data[i].m_flCurrentVortexSpeed = blobs[i]->m_flCurrentVortexSpeed;
  1074. m_data[i].m_flDestVortexRadius = blobs[i]->m_flDestVortexRadius;
  1075. m_data[i].m_flVortexDirection = blobs[i]->m_flVortexDirection;
  1076. #ifdef BLOB_SIMD_DEBUG
  1077. m_data[i].m_vPosition_DEBUG = blobs[i]->GetPosition();
  1078. m_data[i].m_vVelocity_DEBUG = blobs[i]->GetVelocity();
  1079. m_data[i].m_flCurrentVortexRadius_DEBUG = blobs[i]->m_flCurrentVortexRadius;
  1080. m_data[i].m_flCurrentVortexSpeed_DEBUG = blobs[i]->m_flCurrentVortexSpeed;
  1081. #endif
  1082. }
  1083. UpdateBlobsInBeam_SIMD();
  1084. for ( int i=0; i<numBlobs; ++i )
  1085. {
  1086. blobs[i]->SetTempEndPosition( m_data[i].m_vPosition );
  1087. blobs[i]->SetTempEndVelocity( m_data[i].m_vVelocity );
  1088. blobs[i]->m_flCurrentVortexRadius = m_data[i].m_flCurrentVortexRadius;
  1089. blobs[i]->m_flCurrentVortexSpeed = m_data[i].m_flCurrentVortexSpeed;
  1090. }
  1091. }
  1092. ~BlobsInBeamUpdate_SIMD()
  1093. {
  1094. m_data.Purge();
  1095. }
  1096. private:
  1097. struct BlobBeamUpdateData_t
  1098. {
  1099. Vector m_vPosition;
  1100. Vector m_vVelocity;
  1101. // beam info
  1102. float m_flCurrentVortexRadius;
  1103. float m_flCurrentVortexSpeed;
  1104. float m_flDestVortexRadius;
  1105. float m_flVortexDirection;
  1106. #ifdef BLOB_SIMD_DEBUG
  1107. Vector m_vPosition_DEBUG;
  1108. Vector m_vVelocity_DEBUG;
  1109. float m_flCurrentVortexRadius_DEBUG;
  1110. float m_flCurrentVortexSpeed_DEBUG;
  1111. #endif
  1112. };
  1113. void UpdateBlobsInBeam_SIMD()
  1114. {
  1115. const float flDeltaTime = m_flDeltaTime;
  1116. const float flInvDeltaTime = 1.0 / m_flDeltaTime;
  1117. const float flBeamAccelDT = flDeltaTime * paintblob_tbeam_accel.GetFloat();
  1118. const float flVortexAccelDT = flDeltaTime * paintblob_tbeam_vortex_accel.GetFloat();
  1119. const float flVortexDistance = paintblob_tbeam_vortex_distance.GetFloat();
  1120. const float flTbeamCirculation = paintblob_tbeam_vortex_circulation.GetFloat();
  1121. const float flTbeamPortalCirculation = paintblob_tbeam_portal_vortex_circulation.GetFloat();
  1122. const float flVortexRadiusOffsetDT = flDeltaTime * paintblob_tbeam_vortex_radius_rate.GetFloat();
  1123. const float TWO_PI = 2.f * M_PI;
  1124. // beam constant
  1125. const Vector& vBeamStart = m_pBeam->GetStartPoint();
  1126. const Vector& vBeamEnd = m_pBeam->GetEndPoint();
  1127. Vector vBeamDir = vBeamEnd - vBeamStart;
  1128. const float flBeamLength = vBeamDir.NormalizeInPlace();
  1129. const float flHalfBeamLength = 0.5f * flBeamLength;
  1130. bool bIsBeamReversed = m_pBeam->IsReversed();
  1131. bool bGoingTowardsPortal = ( bIsBeamReversed ) ? m_pBeam->IsFromPortal() : m_pBeam->IsToPortal();
  1132. float flBeamRadius = UTil_Blob_BeamRadiusOffset( m_pBeam->GetBeamRadius() );
  1133. const float flMinVortexRadiusScale = 1.f / flBeamRadius;
  1134. #ifdef BLOB_SIMD_DEBUG
  1135. const float flBeamRadiusSqr = Square( flBeamRadius );
  1136. const float flBeamSpeed = UTil_Blob_TBeamLinearForce( m_pBeam->GetLinearForce() );
  1137. for ( int j=0; j<m_data.Count(); ++j )
  1138. {
  1139. float flSpeed = DotProduct( m_data[j].m_vVelocity_DEBUG, vBeamDir );
  1140. // if speed < 0, set it to 0
  1141. flSpeed = fsel( flSpeed, flSpeed, 0.f );
  1142. // now clamp the new speed
  1143. const float flNewSpeed = flSpeed + flBeamAccelDT;
  1144. flSpeed = fsel( flNewSpeed - flBeamSpeed, flBeamSpeed, flNewSpeed );
  1145. Vector vecEndVelocity = flSpeed * vBeamDir;
  1146. Vector vecEndPos = m_data[j].m_vPosition_DEBUG + flDeltaTime * vecEndVelocity;
  1147. // vortex, depending on where the blob is a long the beam
  1148. Vector vecPointOnLine;
  1149. float flDistOnLine;
  1150. CalcClosestPointOnLine( vecEndPos, vBeamStart, vBeamEnd, vecPointOnLine, &flDistOnLine );
  1151. flDistOnLine *= flBeamLength;
  1152. // compute circulation, if endpos is within the vortex distance, we should vortex at a faster rate (going through portal)
  1153. const float flDistFromBeamCenter = fabs( flDistOnLine - flHalfBeamLength );
  1154. float flCirculation = fsel( flHalfBeamLength - flDistFromBeamCenter - flVortexDistance, flTbeamCirculation, flTbeamPortalCirculation );
  1155. // current vortex radius
  1156. float flDestVortexRadiusScale = ( flBeamLength - flDistOnLine - flVortexDistance < 0.f && bGoingTowardsPortal ) ? flMinVortexRadiusScale : m_data[j].m_flDestVortexRadius;
  1157. float flDestVortexRadius = flDestVortexRadiusScale * flBeamRadius;
  1158. float flCurrentVortexRadiusScale = m_data[j].m_flCurrentVortexRadius_DEBUG;
  1159. float flCurrentVortexRadius = flCurrentVortexRadiusScale * flBeamRadius;
  1160. // current vortex speed
  1161. float flDestVortexSpeed = flCirculation / ( TWO_PI * flCurrentVortexRadius );
  1162. float flCurrentVortexSpeed = m_data[j].m_flCurrentVortexSpeed_DEBUG;
  1163. // compute new vortex radius
  1164. float flSign = Sign( flDestVortexRadius - flCurrentVortexRadius );
  1165. float flNewVortexRadius = flCurrentVortexRadius + flSign * flVortexRadiusOffsetDT;
  1166. flCurrentVortexRadius = fsel( flSign * ( flDestVortexRadius - flNewVortexRadius ), flNewVortexRadius, flDestVortexRadius );
  1167. // compute new vortex speed
  1168. float flVortexSpeedSign = Sign( flDestVortexSpeed - flCurrentVortexSpeed );
  1169. float flNewVortexSpeed = flCurrentVortexSpeed + flVortexSpeedSign * flVortexAccelDT;
  1170. flCurrentVortexSpeed = fsel( flVortexSpeedSign * ( flDestVortexSpeed - flNewVortexSpeed ), flNewVortexSpeed, flDestVortexSpeed );
  1171. // spiral pos around m_vecBeamDir at random speed
  1172. Vector vecVortexRadius = vecEndPos - vecPointOnLine;
  1173. float flVortexDirection = ( bIsBeamReversed ) ? -m_data[j].m_flVortexDirection : m_data[j].m_flVortexDirection;
  1174. Quaternion qRotate;
  1175. AxisAngleQuaternion(vBeamDir, flDeltaTime * flVortexDirection * flCurrentVortexSpeed, qRotate );
  1176. Vector tempVec = vecVortexRadius;
  1177. VectorRotate( tempVec, qRotate, vecVortexRadius );
  1178. vecVortexRadius = flCurrentVortexRadius * vecVortexRadius.Normalized();
  1179. Vector newPos = vecPointOnLine + vecVortexRadius;
  1180. // add angular velocity
  1181. Vector vAngularVelocity = ( newPos - vecEndPos ) * flInvDeltaTime;
  1182. // scale angular velocity by how far off the center of the beam
  1183. float flDistFromCenterSqr = ( vecPointOnLine - vecEndPos ).LengthSqr();
  1184. float flAngularVelocityScale = RemapValClamped( flDistFromCenterSqr, 0.f, flBeamRadiusSqr, 0.f, 1.f );
  1185. vecEndVelocity += flAngularVelocityScale * vAngularVelocity;
  1186. // Output
  1187. float flFinalRadiusScale = RemapValClamped( flCurrentVortexRadius, 1.f, flBeamRadius, flMinVortexRadiusScale, 1.f );
  1188. m_data[j].m_flCurrentVortexRadius_DEBUG = flFinalRadiusScale;
  1189. m_data[j].m_flCurrentVortexSpeed_DEBUG = flCurrentVortexSpeed;
  1190. m_data[j].m_vVelocity_DEBUG = vecEndVelocity;
  1191. m_data[j].m_vPosition_DEBUG = newPos;
  1192. }
  1193. #endif
  1194. // const
  1195. fltx4 f4MinVortexRadiusScale = ReplicateX4( flMinVortexRadiusScale );
  1196. fltx4 f4DeltaTime = ReplicateX4( flDeltaTime );
  1197. fltx4 f4InvDeltaTime = ReplicateX4( flInvDeltaTime );
  1198. fltx4 f4BeamAccelDT = ReplicateX4( flBeamAccelDT );
  1199. fltx4 f4VortexDistance = ReplicateX4( flVortexDistance );
  1200. fltx4 f4TbeamCirculation = ReplicateX4( flTbeamCirculation );
  1201. fltx4 f4TbeamPortalCirculation = ReplicateX4( flTbeamPortalCirculation );
  1202. fltx4 f4VortexRadiusOffsetDT = ReplicateX4( flVortexRadiusOffsetDT );
  1203. fltx4 f4VortexAccelDT = ReplicateX4( flVortexAccelDT );
  1204. // beam info SIMD
  1205. fltx4 f4BeamStart = LoadUnaligned3SIMD( m_pBeam->GetStartPoint().Base() );
  1206. fltx4 f4BeamEnd = LoadUnaligned3SIMD( m_pBeam->GetEndPoint().Base() );
  1207. fltx4 f4BeamDir = SubSIMD( f4BeamEnd, f4BeamStart );
  1208. // compute length and normalize f4BeamDir
  1209. fltx4 scLengthSqr = Dot3SIMD( f4BeamDir, f4BeamDir );
  1210. bi32x4 isSignificant = CmpGtSIMD( scLengthSqr, Four_Epsilons );
  1211. fltx4 scLengthInv = ReciprocalSqrtSIMD( scLengthSqr );
  1212. f4BeamDir = AndSIMD( isSignificant, MulSIMD( f4BeamDir, scLengthInv ) );
  1213. fltx4 f4BeamRadius = ReplicateX4( flBeamRadius );
  1214. fltx4 f4BeamRadiusSqr = MulSIMD( f4BeamRadius, f4BeamRadius );
  1215. fltx4 f4BeamSpeed = ReplicateX4( UTil_Blob_TBeamLinearForce( m_pBeam->GetLinearForce() ) );
  1216. fltx4 f4BeamLength = ReplicateX4( flBeamLength );
  1217. fltx4 f4HalfBeamLength = ReplicateX4( flHalfBeamLength );
  1218. const int32 ALIGN16 maskTRUE[4] ALIGN16_POST = { ~0, ~0, ~0, ~0 };
  1219. const int32 ALIGN16 maskFALSE[4] ALIGN16_POST = { 0, 0, 0, 0 };
  1220. bi32x4 bCmpGoingTowardsPortal = ( bGoingTowardsPortal ) ? (bi32x4)LoadAlignedSIMD( maskTRUE ) : (bi32x4)LoadAlignedSIMD( maskFALSE );
  1221. bi32x4 bCmpIsBeamReversed = ( bIsBeamReversed ) ? (bi32x4)LoadAlignedSIMD( maskTRUE ) : (bi32x4)LoadAlignedSIMD( maskFALSE );
  1222. fltx4 f4TWO_PI = ReplicateX4( TWO_PI );
  1223. FourVectors v4BeamStart = FourVectors( vBeamStart, vBeamStart, vBeamStart, vBeamStart );
  1224. FourVectors v4BeamEnd = FourVectors( vBeamEnd, vBeamEnd, vBeamEnd, vBeamEnd );
  1225. FourVectors v4BeamDir = FourVectors( vBeamDir, vBeamDir, vBeamDir, vBeamDir );
  1226. int count = m_data.Count();
  1227. int i = 0;
  1228. while ( count >= 4 )
  1229. {
  1230. FourVectors v4Velocity = FourVectors( m_data[i].m_vVelocity,
  1231. m_data[i+1].m_vVelocity,
  1232. m_data[i+2].m_vVelocity,
  1233. m_data[i+3].m_vVelocity );
  1234. // dot 4 vectors
  1235. fltx4 f4Speed = v4Velocity * v4BeamDir;
  1236. // if speed < 0, set it to 0
  1237. f4Speed = MaskedAssign( CmpLtSIMD( f4Speed, Four_Zeros ), Four_Zeros, f4Speed );
  1238. // ( f4Speed < f4NewSpeed ) ? f4Speed : f4NewSpeed;
  1239. fltx4 f4NewSpeed = AddSIMD( f4Speed, f4BeamAccelDT );
  1240. // clamp
  1241. f4Speed = MaskedAssign( CmpLtSIMD( f4NewSpeed, f4BeamSpeed ), f4NewSpeed, f4BeamSpeed );
  1242. FourVectors v4EndVelocity = Mul( v4BeamDir, f4Speed );
  1243. FourVectors v4EndPos = FourVectors( m_data[i].m_vPosition,
  1244. m_data[i+1].m_vPosition,
  1245. m_data[i+2].m_vPosition,
  1246. m_data[i+3].m_vPosition );
  1247. v4EndPos = Mul( v4EndVelocity, f4DeltaTime ) + v4EndPos;
  1248. // vortex, depending on where the blob is a long the beam
  1249. FourVectors v4VecPointOnline;
  1250. fltx4 f4DistOnLine;
  1251. FourVectors::CalcClosestPointOnLineSIMD( v4EndPos, v4BeamStart, v4BeamEnd, v4VecPointOnline, &f4DistOnLine );
  1252. f4DistOnLine = MulSIMD( f4BeamLength, f4DistOnLine );
  1253. // compute circulation, if endpos is within the vortex distance, we should vortex at a faster rate (going through portal)
  1254. fltx4 f4DistFromBeamCenter = fabs( SubSIMD( f4DistOnLine, f4HalfBeamLength ) );
  1255. bi32x4 bCmp = CmpLtSIMD( SubSIMD( SubSIMD( f4HalfBeamLength, f4DistFromBeamCenter ), f4VortexDistance ), Four_Zeros );
  1256. fltx4 f4Circulation = MaskedAssign( bCmp, f4TbeamPortalCirculation, f4TbeamCirculation );
  1257. // current vortex radius
  1258. bCmp = CmpLtSIMD( SubSIMD( SubSIMD( f4BeamLength, f4DistOnLine ), f4VortexDistance ), Four_Zeros );
  1259. fltx4 f4TempDestRadius = { m_data[i].m_flDestVortexRadius,
  1260. m_data[i+1].m_flDestVortexRadius,
  1261. m_data[i+2].m_flDestVortexRadius,
  1262. m_data[i+3].m_flDestVortexRadius };
  1263. fltx4 f4DestVortexRadiusScale = MaskedAssign( AndSIMD( bCmp, bCmpGoingTowardsPortal ), f4MinVortexRadiusScale, f4TempDestRadius );
  1264. fltx4 f4DestVortexRadius = MulSIMD( f4DestVortexRadiusScale, f4BeamRadius );
  1265. fltx4 f4CurrentVortexRadiusScale = { m_data[i].m_flCurrentVortexRadius,
  1266. m_data[i+1].m_flCurrentVortexRadius,
  1267. m_data[i+2].m_flCurrentVortexRadius,
  1268. m_data[i+3].m_flCurrentVortexRadius };
  1269. fltx4 f4CurrentVortexRadius = MulSIMD( f4CurrentVortexRadiusScale, f4BeamRadius );
  1270. // current vortex speed
  1271. fltx4 f4DestVortexSpeed = DivEstSIMD( f4Circulation, MulSIMD( f4TWO_PI, f4CurrentVortexRadius ) );
  1272. fltx4 f4CurrentVortexSpeed = { m_data[i].m_flCurrentVortexSpeed,
  1273. m_data[i+1].m_flCurrentVortexSpeed,
  1274. m_data[i+2].m_flCurrentVortexSpeed,
  1275. m_data[i+3].m_flCurrentVortexSpeed };
  1276. // compute new vortex radius
  1277. fltx4 f4RadiusSign = MaskedAssign( CmpLtSIMD( SubSIMD( f4DestVortexRadius, f4CurrentVortexRadius ), Four_Zeros ), Four_NegativeOnes, Four_Ones );
  1278. fltx4 f4NewVortexRadius = MaddSIMD( f4RadiusSign, f4VortexRadiusOffsetDT, f4CurrentVortexRadius );
  1279. bCmp = CmpGtSIMD( MulSIMD( f4RadiusSign, SubSIMD( f4DestVortexRadius, f4NewVortexRadius ) ), Four_Zeros );
  1280. f4CurrentVortexRadius = MaskedAssign( bCmp, f4NewVortexRadius, f4DestVortexRadius );
  1281. // compute new vortex speed
  1282. fltx4 f4VortexSpeedSign = MaskedAssign( CmpLtSIMD( SubSIMD( f4DestVortexSpeed, f4CurrentVortexSpeed ), Four_Zeros ), Four_NegativeOnes, Four_Ones );
  1283. fltx4 f4NewVortexSpeed = MaddSIMD( f4VortexSpeedSign, f4VortexAccelDT, f4CurrentVortexSpeed );
  1284. bCmp = CmpGtSIMD( MulSIMD( f4VortexSpeedSign, SubSIMD( f4DestVortexSpeed, f4NewVortexSpeed ) ), Four_Zeros );
  1285. f4CurrentVortexSpeed = MaskedAssign( bCmp, f4NewVortexSpeed, f4DestVortexSpeed );
  1286. // spiral pos around m_vecBeamDir at random speed
  1287. FourVectors v4VortexRadius = v4EndPos - v4VecPointOnline;
  1288. fltx4 f4VortexDirection = { m_data[i].m_flVortexDirection,
  1289. m_data[i+1].m_flVortexDirection,
  1290. m_data[i+2].m_flVortexDirection,
  1291. m_data[i+3].m_flVortexDirection };
  1292. f4VortexDirection = MaskedAssign( bCmpIsBeamReversed, NegSIMD( f4VortexDirection ), f4VortexDirection );
  1293. fltx4 f4AngleOffset = MulSIMD( f4DeltaTime, MulSIMD( f4VortexDirection, f4CurrentVortexSpeed ) );
  1294. // apply rotations
  1295. FourQuaternions q4Rotations;
  1296. q4Rotations.FromAxisAndAnglesInDegrees( f4BeamDir, f4AngleOffset );
  1297. q4Rotations.RotateFourVectors( &v4VortexRadius );
  1298. v4VortexRadius.VectorNormalize();
  1299. v4VortexRadius = Mul( v4VortexRadius, f4CurrentVortexRadius );
  1300. FourVectors v4NewPos = v4VecPointOnline + v4VortexRadius;
  1301. // add angular velocity
  1302. FourVectors v4DiffPos = v4NewPos - v4EndPos;
  1303. FourVectors v4AngularVelocity = Mul( v4DiffPos, f4InvDeltaTime );
  1304. // scale angular velocity by how far off the center of the beam
  1305. FourVectors v4DisFromCenterVec = v4VecPointOnline - v4EndPos;
  1306. fltx4 f4DistFromCenterSqr = v4DisFromCenterVec.length2(); // lengthSqr
  1307. fltx4 f4AngularVelocityScale = RemapValClampedSIMD( f4DistFromCenterSqr, Four_Zeros, f4BeamRadiusSqr, Four_Zeros, Four_Ones );
  1308. v4EndVelocity = Mul( v4AngularVelocity, f4AngularVelocityScale ) + v4EndVelocity;
  1309. // output radius
  1310. fltx4 f4FinalRadiusScale = RemapValClampedSIMD( f4NewVortexRadius, Four_Ones, f4BeamRadius, f4MinVortexRadiusScale, Four_Ones );
  1311. m_data[i].m_flCurrentVortexRadius = SubFloat( f4FinalRadiusScale, 0 );
  1312. m_data[i+1].m_flCurrentVortexRadius = SubFloat( f4FinalRadiusScale, 1 );
  1313. m_data[i+2].m_flCurrentVortexRadius = SubFloat( f4FinalRadiusScale, 2 );
  1314. m_data[i+3].m_flCurrentVortexRadius = SubFloat( f4FinalRadiusScale, 3 );
  1315. // output speed
  1316. m_data[i].m_flCurrentVortexSpeed = SubFloat( f4CurrentVortexSpeed, 0 );
  1317. m_data[i+1].m_flCurrentVortexSpeed = SubFloat( f4CurrentVortexSpeed, 1 );
  1318. m_data[i+2].m_flCurrentVortexSpeed = SubFloat( f4CurrentVortexSpeed, 2 );
  1319. m_data[i+3].m_flCurrentVortexSpeed = SubFloat( f4CurrentVortexSpeed, 3 );
  1320. // output velocity
  1321. v4EndVelocity.StoreUnalignedVector3SIMD( &m_data[i].m_vVelocity,
  1322. &m_data[i+1].m_vVelocity,
  1323. &m_data[i+2].m_vVelocity,
  1324. &m_data[i+3].m_vVelocity );
  1325. // output position
  1326. v4NewPos.StoreUnalignedVector3SIMD( &m_data[i].m_vPosition,
  1327. &m_data[i+1].m_vPosition,
  1328. &m_data[i+2].m_vPosition,
  1329. &m_data[i+3].m_vPosition );
  1330. #ifdef BLOB_SIMD_DEBUG
  1331. for ( int k=0; k<4; ++k )
  1332. {
  1333. Assert( fabs( m_data[i + k].m_flCurrentVortexRadius - m_data[i + k].m_flCurrentVortexRadius_DEBUG ) < BLOB_IN_BEAM_ERROR );
  1334. Assert( fabs( m_data[i + k].m_flCurrentVortexSpeed - m_data[i + k].m_flCurrentVortexSpeed_DEBUG ) < BLOB_IN_BEAM_ERROR );
  1335. Assert( fabs( m_data[i + k].m_vPosition.x - m_data[i + k].m_vPosition_DEBUG.x ) < BLOB_IN_BEAM_ERROR );
  1336. Assert( fabs( m_data[i + k].m_vPosition.y - m_data[i + k].m_vPosition_DEBUG.y ) < BLOB_IN_BEAM_ERROR );
  1337. Assert( fabs( m_data[i + k].m_vPosition.z - m_data[i + k].m_vPosition_DEBUG.z ) < BLOB_IN_BEAM_ERROR );
  1338. Assert( fabs( m_data[i + k].m_vVelocity.x - m_data[i + k].m_vVelocity_DEBUG.x ) < BLOB_IN_BEAM_ERROR );
  1339. Assert( fabs( m_data[i + k].m_vVelocity.y - m_data[i + k].m_vVelocity_DEBUG.y ) < BLOB_IN_BEAM_ERROR );
  1340. Assert( fabs( m_data[i + k].m_vVelocity.z - m_data[i + k].m_vVelocity_DEBUG.z ) < BLOB_IN_BEAM_ERROR );
  1341. }
  1342. #endif
  1343. count -= 4;
  1344. i += 4;
  1345. }
  1346. while ( count > 0 )
  1347. {
  1348. fltx4 f4Speed = Dot3SIMD( LoadUnaligned3SIMD( m_data[i].m_vVelocity.Base() ), f4BeamDir );
  1349. // if speed < 0, set it to 0
  1350. f4Speed = MaskedAssign( CmpLtSIMD( f4Speed, Four_Zeros ), Four_Zeros, f4Speed );
  1351. // ( f4Speed < f4NewSpeed ) ? f4Speed : f4NewSpeed;
  1352. fltx4 f4NewSpeed = AddSIMD( f4Speed, f4BeamAccelDT );
  1353. f4Speed = MaskedAssign( CmpLtSIMD( f4NewSpeed, f4BeamSpeed ), f4NewSpeed, f4BeamSpeed );
  1354. fltx4 f4EndVelocity = MulSIMD( f4Speed, f4BeamDir );
  1355. fltx4 f4EndPos = MaddSIMD( f4DeltaTime, f4EndVelocity, LoadUnaligned3SIMD( m_data[i].m_vPosition.Base() ) );
  1356. // vortex, depending on where the blob is a long the beam
  1357. Vector vecPointOnLine;
  1358. float flDistOnLine;
  1359. Vector vTempEndPos;
  1360. StoreUnaligned3SIMD( vTempEndPos.Base(), f4EndPos );
  1361. CalcClosestPointOnLine( vTempEndPos, vBeamStart, vBeamEnd, vecPointOnLine, &flDistOnLine );
  1362. flDistOnLine *= flBeamLength;
  1363. // compute circulation, if endpos is within the vortex distance, we should vortex at a faster rate (going through portal)
  1364. const float flDistFromBeamCenter = fabs( flDistOnLine - flHalfBeamLength );
  1365. float flCirculation = fsel( flHalfBeamLength - flDistFromBeamCenter - flVortexDistance, flTbeamCirculation, flTbeamPortalCirculation );
  1366. // current vortex radius
  1367. float flDestVortexRadiusScale = ( flBeamLength - flDistOnLine - flVortexDistance < 0.f && bGoingTowardsPortal ) ? flMinVortexRadiusScale : m_data[i].m_flDestVortexRadius;
  1368. float flDestVortexRadius = flDestVortexRadiusScale * flBeamRadius;
  1369. float flCurrentVortexRadiusScale = m_data[i].m_flCurrentVortexRadius;
  1370. float flCurrentVortexRadius = flCurrentVortexRadiusScale * flBeamRadius;
  1371. // current vortex speed
  1372. float flDestVortexSpeed = flCirculation / ( TWO_PI * flCurrentVortexRadius );
  1373. float flCurrentVortexSpeed = m_data[i].m_flCurrentVortexSpeed;
  1374. // compute new vortex radius
  1375. float flSign = Sign( flDestVortexRadius - flCurrentVortexRadius );
  1376. float flNewVortexRadius = flCurrentVortexRadius + flSign * flVortexRadiusOffsetDT;
  1377. flCurrentVortexRadius = fsel( flSign * ( flDestVortexRadius - flNewVortexRadius ), flNewVortexRadius, flDestVortexRadius );
  1378. // compute new vortex speed
  1379. float flVortexSpeedSign = Sign( flDestVortexSpeed - flCurrentVortexSpeed );
  1380. float flNewVortexSpeed = flCurrentVortexSpeed + flVortexSpeedSign * flVortexAccelDT;
  1381. flCurrentVortexSpeed = fsel( flVortexSpeedSign * ( flDestVortexSpeed - flNewVortexSpeed ), flNewVortexSpeed, flDestVortexSpeed );
  1382. // spiral pos around m_vecBeamDir at random speed
  1383. fltx4 f4PointOnLine = LoadUnaligned3SIMD( vecPointOnLine.Base() );
  1384. fltx4 f4VortexRadius = SubSIMD( f4EndPos, f4PointOnLine );
  1385. float flVortexDirection = ( bIsBeamReversed ) ? -m_data[i].m_flVortexDirection : m_data[i].m_flVortexDirection;
  1386. Quaternion qRotate;
  1387. AxisAngleQuaternion(vBeamDir, flDeltaTime * flVortexDirection * flCurrentVortexSpeed, qRotate );
  1388. Vector vecVortexRadius;
  1389. StoreUnaligned3SIMD( vecVortexRadius.Base(), f4VortexRadius );
  1390. Vector vecTemp = vecVortexRadius;
  1391. VectorRotate( vecTemp, qRotate, vecVortexRadius );
  1392. f4VortexRadius = LoadUnaligned3SIMD( vecVortexRadius.Base() );
  1393. f4VortexRadius = MulSIMD( ReplicateX4( flCurrentVortexRadius ), Normalized3SIMD( f4VortexRadius ) );
  1394. fltx4 f4NewPos = AddSIMD( f4PointOnLine, f4VortexRadius );
  1395. // add angular velocity
  1396. fltx4 f4DiffPos = SubSIMD( f4NewPos, f4EndPos );
  1397. fltx4 f4AngularVelocity = MulSIMD( f4DiffPos, f4InvDeltaTime );
  1398. // scale angular velocity by how far off the center of the beam
  1399. fltx4 f4DisFromCenterVec = SubSIMD( f4PointOnLine, f4EndPos );
  1400. fltx4 f4DistFromCenterSqr = Dot3SIMD( f4DisFromCenterVec, f4DisFromCenterVec ); // lengthSqr
  1401. fltx4 f4AngularVelocityScale = RemapValClampedSIMD( f4DistFromCenterSqr, Four_Zeros, f4BeamRadiusSqr, Four_Zeros, Four_Ones );
  1402. f4EndVelocity = MaddSIMD( f4AngularVelocityScale, f4AngularVelocity, f4EndVelocity );
  1403. // Output
  1404. float flFinalRadiusScale = RemapValClamped( flCurrentVortexRadius, 1.f, flBeamRadius, flMinVortexRadiusScale, 1.f );
  1405. m_data[i].m_flCurrentVortexRadius = flFinalRadiusScale;
  1406. m_data[i].m_flCurrentVortexSpeed = flCurrentVortexSpeed;
  1407. StoreUnaligned3SIMD( m_data[i].m_vVelocity.Base(), f4EndVelocity );
  1408. StoreUnaligned3SIMD( m_data[i].m_vPosition.Base(), f4NewPos );
  1409. #ifdef BLOB_SIMD_DEBUG
  1410. Assert( fabs( m_data[i].m_flCurrentVortexRadius - m_data[i].m_flCurrentVortexRadius_DEBUG ) < BLOB_IN_BEAM_ERROR );
  1411. Assert( fabs( m_data[i].m_flCurrentVortexSpeed - m_data[i].m_flCurrentVortexSpeed_DEBUG ) < BLOB_IN_BEAM_ERROR );
  1412. Assert( fabs( m_data[i].m_vPosition.x - m_data[i].m_vPosition_DEBUG.x ) < BLOB_IN_BEAM_ERROR );
  1413. Assert( fabs( m_data[i].m_vPosition.y - m_data[i].m_vPosition_DEBUG.y ) < BLOB_IN_BEAM_ERROR );
  1414. Assert( fabs( m_data[i].m_vPosition.z - m_data[i].m_vPosition_DEBUG.z ) < BLOB_IN_BEAM_ERROR );
  1415. Assert( fabs( m_data[i].m_vVelocity.x - m_data[i].m_vVelocity_DEBUG.x ) < BLOB_IN_BEAM_ERROR );
  1416. Assert( fabs( m_data[i].m_vVelocity.y - m_data[i].m_vVelocity_DEBUG.y ) < BLOB_IN_BEAM_ERROR );
  1417. Assert( fabs( m_data[i].m_vVelocity.z - m_data[i].m_vVelocity_DEBUG.z ) < BLOB_IN_BEAM_ERROR );
  1418. #endif
  1419. --count;
  1420. ++i;
  1421. }
  1422. }
  1423. float m_flDeltaTime;
  1424. CTrigger_TractorBeam *m_pBeam;
  1425. CUtlVector< BlobBeamUpdateData_t, CUtlMemoryAligned< BlobBeamUpdateData_t, 16 > > m_data;
  1426. };
  1427. class BlobsInAirUpdate_SIMD
  1428. {
  1429. public:
  1430. BlobsInAirUpdate_SIMD( const PaintBlobVector_t& blobs )
  1431. {
  1432. if ( blobs.Count() == 0 )
  1433. return;
  1434. m_flDeltaTime = gpGlobals->curtime - blobs[0]->GetLastUpdateTime();
  1435. int numBlobs = blobs.Count();
  1436. m_data.EnsureCount( numBlobs );
  1437. for ( int i=0; i<numBlobs; ++i )
  1438. {
  1439. m_data[i].m_f4Position = LoadUnaligned3SIMD( blobs[i]->GetPosition().Base() );
  1440. m_data[i].m_f4Velocity = LoadUnaligned3SIMD( blobs[i]->GetVelocity().Base() );
  1441. #ifdef BLOB_SIMD_DEBUG
  1442. m_data[i].m_vPosition = blobs[i]->GetPosition();
  1443. m_data[i].m_vVelocity = blobs[i]->GetVelocity();
  1444. #endif
  1445. }
  1446. UpdateBlobsInAir_SIMD();
  1447. for ( int i=0; i<numBlobs; ++i )
  1448. {
  1449. Vector pos;
  1450. StoreUnaligned3SIMD( pos.Base(), m_data[i].m_f4Position );
  1451. blobs[i]->SetTempEndPosition( pos );
  1452. Vector vel;
  1453. StoreUnaligned3SIMD( vel.Base(), m_data[i].m_f4Velocity );
  1454. blobs[i]->SetTempEndVelocity( vel );
  1455. }
  1456. }
  1457. ~BlobsInAirUpdate_SIMD()
  1458. {
  1459. m_data.Purge();
  1460. }
  1461. private:
  1462. struct BlobAirUpdateData_t
  1463. {
  1464. fltx4 m_f4Position;
  1465. fltx4 m_f4Velocity;
  1466. #ifdef BLOB_SIMD_DEBUG
  1467. Vector m_vPosition;
  1468. Vector m_vVelocity;
  1469. #endif
  1470. };
  1471. void UpdateBlobsInAir_SIMD()
  1472. {
  1473. #ifdef BLOB_SIMD_DEBUG
  1474. const float flDeltaTime = m_flDeltaTime;
  1475. float flNewSpeedFraction = ( 1.f - paintblob_air_drag.GetFloat() * flDeltaTime );
  1476. for ( int j=0; j<m_data.Count(); ++j )
  1477. {
  1478. Vector vecMove;
  1479. Vector vecAbsVelocity = m_data[j].m_vVelocity;
  1480. vecMove.x = vecAbsVelocity.x * flDeltaTime;
  1481. vecMove.y = vecAbsVelocity.y * flDeltaTime;
  1482. //Apply gravity
  1483. float newZVelocity = vecAbsVelocity.z - paintblob_gravity_scale.GetFloat() * sv_gravity.GetFloat() * flDeltaTime;
  1484. vecMove.z = 0.5f * ( vecAbsVelocity.z + newZVelocity ) * flDeltaTime;
  1485. vecAbsVelocity.z = newZVelocity;
  1486. //Apply air drag
  1487. m_data[j].m_vVelocity = vecAbsVelocity * flNewSpeedFraction;
  1488. m_data[j].m_vPosition = m_data[j].m_vPosition + vecMove;
  1489. }
  1490. #endif
  1491. fltx4 f4DeltaTime = ReplicateX4( m_flDeltaTime );
  1492. fltx4 f4Gravity = { 0.f, 0.f, paintblob_gravity_scale.GetFloat() * sv_gravity.GetFloat(), 0.f };
  1493. fltx4 f4GravityDT = MulSIMD( f4Gravity, f4DeltaTime );
  1494. fltx4 f4NewSpeedFraction = MsubSIMD( ReplicateX4( paintblob_air_drag.GetFloat() ), f4DeltaTime, Four_Ones );
  1495. fltx4 f4HalfDeltaTime = MulSIMD( Four_PointFives, f4DeltaTime );
  1496. int count = m_data.Count();
  1497. int i = 0;
  1498. while ( count >= 4 )
  1499. {
  1500. fltx4 f4Vel0 = m_data[i].m_f4Velocity;
  1501. fltx4 f4Vel1 = m_data[i+1].m_f4Velocity;
  1502. fltx4 f4Vel2 = m_data[i+2].m_f4Velocity;
  1503. fltx4 f4Vel3 = m_data[i+3].m_f4Velocity;
  1504. fltx4 f4NewVel0 = SubSIMD( f4Vel0, f4GravityDT );
  1505. fltx4 f4NewVel1 = SubSIMD( f4Vel1, f4GravityDT );
  1506. fltx4 f4NewVel2 = SubSIMD( f4Vel2, f4GravityDT );
  1507. fltx4 f4NewVel3 = SubSIMD( f4Vel3, f4GravityDT );
  1508. m_data[i].m_f4Velocity = MulSIMD( f4NewVel0, f4NewSpeedFraction );
  1509. m_data[i+1].m_f4Velocity = MulSIMD( f4NewVel1, f4NewSpeedFraction );
  1510. m_data[i+2].m_f4Velocity = MulSIMD( f4NewVel2, f4NewSpeedFraction );
  1511. m_data[i+3].m_f4Velocity = MulSIMD( f4NewVel3, f4NewSpeedFraction );
  1512. m_data[i].m_f4Position = MaddSIMD( AddSIMD( f4Vel0, f4NewVel0 ), f4HalfDeltaTime, m_data[i].m_f4Position );
  1513. m_data[i+1].m_f4Position = MaddSIMD( AddSIMD( f4Vel1, f4NewVel1 ), f4HalfDeltaTime, m_data[i+1].m_f4Position );
  1514. m_data[i+2].m_f4Position = MaddSIMD( AddSIMD( f4Vel2, f4NewVel2 ), f4HalfDeltaTime, m_data[i+2].m_f4Position );
  1515. m_data[i+3].m_f4Position = MaddSIMD( AddSIMD( f4Vel3, f4NewVel3 ), f4HalfDeltaTime, m_data[i+3].m_f4Position );
  1516. #ifdef BLOB_SIMD_DEBUG
  1517. Assert( fabs( SubFloat( m_data[i].m_f4Velocity, 0 ) - m_data[i].m_vVelocity.x ) < 0.001f );
  1518. Assert( fabs( SubFloat( m_data[i].m_f4Velocity, 1 ) - m_data[i].m_vVelocity.y ) < 0.001f );
  1519. Assert( fabs( SubFloat( m_data[i].m_f4Velocity, 2 ) - m_data[i].m_vVelocity.z ) < 0.001f );
  1520. Assert( fabs( SubFloat( m_data[i].m_f4Position, 0 ) - m_data[i].m_vPosition.x ) < 0.001f );
  1521. Assert( fabs( SubFloat( m_data[i].m_f4Position, 1 ) - m_data[i].m_vPosition.y ) < 0.001f );
  1522. Assert( fabs( SubFloat( m_data[i].m_f4Position, 2 ) - m_data[i].m_vPosition.z ) < 0.001f );
  1523. #endif
  1524. i += 4;
  1525. count -= 4;
  1526. }
  1527. // do the rest
  1528. while ( count > 0 )
  1529. {
  1530. fltx4 f4Vel = m_data[i].m_f4Velocity;
  1531. fltx4 f4NewVel = SubSIMD( f4Vel, f4GravityDT );
  1532. m_data[i].m_f4Velocity = MulSIMD( f4NewVel, f4NewSpeedFraction );
  1533. m_data[i].m_f4Position = MaddSIMD( AddSIMD( f4Vel, f4NewVel ), f4HalfDeltaTime, m_data[i].m_f4Position );
  1534. #ifdef BLOB_SIMD_DEBUG
  1535. Assert( fabs( SubFloat( m_data[i].m_f4Velocity, 0 ) - m_data[i].m_vVelocity.x ) < 0.001f );
  1536. Assert( fabs( SubFloat( m_data[i].m_f4Velocity, 1 ) - m_data[i].m_vVelocity.y ) < 0.001f );
  1537. Assert( fabs( SubFloat( m_data[i].m_f4Velocity, 2 ) - m_data[i].m_vVelocity.z ) < 0.001f );
  1538. Assert( fabs( SubFloat( m_data[i].m_f4Position, 0 ) - m_data[i].m_vPosition.x ) < 0.001f );
  1539. Assert( fabs( SubFloat( m_data[i].m_f4Position, 1 ) - m_data[i].m_vPosition.y ) < 0.001f );
  1540. Assert( fabs( SubFloat( m_data[i].m_f4Position, 2 ) - m_data[i].m_vPosition.z ) < 0.001f );
  1541. #endif
  1542. ++i;
  1543. --count;
  1544. }
  1545. }
  1546. float m_flDeltaTime;
  1547. CUtlVector< BlobAirUpdateData_t, CUtlMemoryAligned< BlobAirUpdateData_t, 16 > > m_data;
  1548. };
  1549. class BlobsInStreakUpdate_SIMD
  1550. {
  1551. public:
  1552. BlobsInStreakUpdate_SIMD( const PaintBlobVector_t& blobs )
  1553. {
  1554. if ( blobs.Count() == 0 )
  1555. return;
  1556. m_flDeltaTime = gpGlobals->curtime - blobs[0]->GetLastUpdateTime();
  1557. int numBlobs = blobs.Count();
  1558. m_data.EnsureCount( numBlobs );
  1559. for ( int i=0; i<numBlobs; ++i )
  1560. {
  1561. m_data[i].m_f4Position = LoadUnaligned3SIMD( blobs[i]->GetPosition().Base() );
  1562. m_data[i].m_f4Velocity = LoadUnaligned3SIMD( blobs[i]->GetVelocity().Base() );
  1563. m_data[i].m_f4StreakDir = LoadUnaligned3SIMD( blobs[i]->GetStreakDir().Base() );
  1564. #ifdef BLOB_SIMD_DEBUG
  1565. m_data[i].m_vPosition = blobs[i]->GetPosition();
  1566. m_data[i].m_vVelocity = blobs[i]->GetVelocity();
  1567. m_data[i].m_vStreakDir = blobs[i]->GetStreakDir();
  1568. #endif
  1569. }
  1570. UpdateBlobsInStreak_SIMD();
  1571. for ( int i=0; i<numBlobs; ++i )
  1572. {
  1573. Vector pos;
  1574. StoreUnaligned3SIMD( pos.Base(), m_data[i].m_f4Position );
  1575. blobs[i]->SetTempEndPosition( pos );
  1576. Vector vel;
  1577. StoreUnaligned3SIMD( vel.Base(), m_data[i].m_f4Velocity );
  1578. blobs[i]->SetTempEndVelocity( vel );
  1579. }
  1580. }
  1581. ~BlobsInStreakUpdate_SIMD()
  1582. {
  1583. m_data.Purge();
  1584. }
  1585. private:
  1586. struct BlobStreakUpdateData_t
  1587. {
  1588. fltx4 m_f4Position;
  1589. fltx4 m_f4Velocity;
  1590. fltx4 m_f4StreakDir;
  1591. #ifdef BLOB_SIMD_DEBUG
  1592. Vector m_vPosition;
  1593. Vector m_vVelocity;
  1594. Vector m_vStreakDir;
  1595. #endif
  1596. };
  1597. void UpdateBlobsInStreak_SIMD()
  1598. {
  1599. fltx4 f4DeltaTime = ReplicateX4( m_flDeltaTime );
  1600. fltx4 f4Gravity = { 0.f, 0.f, paintblob_gravity_scale.GetFloat() * sv_gravity.GetFloat(), 0.f };
  1601. fltx4 f4GravityDT = MulSIMD( f4Gravity, f4DeltaTime );
  1602. fltx4 f4HalfDeltaTime = MulSIMD( Four_PointFives, f4DeltaTime );
  1603. #ifdef BLOB_SIMD_DEBUG
  1604. const float flDeltaTime = m_flDeltaTime;
  1605. float flGravity = paintblob_gravity_scale.GetFloat() * sv_gravity.GetFloat() * flDeltaTime;
  1606. #endif
  1607. for ( int i=0; i<m_data.Count(); ++i )
  1608. {
  1609. #ifdef BLOB_SIMD_DEBUG
  1610. Vector vecMove;
  1611. Vector vecVelocity = m_data[i].m_vVelocity;
  1612. Vector vecAbsVelocity = vecVelocity;
  1613. vecMove.x = vecAbsVelocity.x * flDeltaTime;
  1614. vecMove.y = vecAbsVelocity.y * flDeltaTime;
  1615. //Apply gravity
  1616. float newZVelocity = vecAbsVelocity.z - flGravity;
  1617. vecMove.z = 0.5f * ( vecAbsVelocity.z + newZVelocity ) * flDeltaTime;
  1618. vecAbsVelocity.z = newZVelocity;
  1619. //Clip the velocity to the streak surface if the blob is streaking
  1620. Vector vecSurfaceNormal = -m_data[i].m_vStreakDir;
  1621. m_data[i].m_vVelocity = vecVelocity - DotProduct( vecSurfaceNormal, vecVelocity ) * vecSurfaceNormal;
  1622. m_data[i].m_vPosition = ( vecMove - DotProduct( vecSurfaceNormal, vecMove ) * vecSurfaceNormal ) + m_data[i].m_vPosition;
  1623. #endif
  1624. // SIMD
  1625. fltx4 f4Vel = m_data[i].m_f4Velocity;
  1626. fltx4 f4NewVel = SubSIMD( f4Vel, f4GravityDT );
  1627. fltx4 f4Move = MulSIMD( AddSIMD( f4Vel, f4NewVel ), f4HalfDeltaTime );
  1628. fltx4 f4SurfaceNormal = m_data[i].m_f4StreakDir;
  1629. m_data[i].m_f4Velocity = SubSIMD( f4Vel, MulSIMD( Dot3SIMD( f4SurfaceNormal, f4Vel ), f4SurfaceNormal ) );
  1630. m_data[i].m_f4Position = AddSIMD( MsubSIMD( Dot3SIMD( f4SurfaceNormal, f4Move), f4SurfaceNormal, f4Move ), m_data[i].m_f4Position );
  1631. #ifdef BLOB_SIMD_DEBUG
  1632. Assert( fabs( SubFloat( m_data[i].m_f4Velocity, 0 ) - m_data[i].m_vVelocity.x ) < 0.001f );
  1633. Assert( fabs( SubFloat( m_data[i].m_f4Velocity, 1 ) - m_data[i].m_vVelocity.y ) < 0.001f );
  1634. Assert( fabs( SubFloat( m_data[i].m_f4Velocity, 2 ) - m_data[i].m_vVelocity.z ) < 0.001f );
  1635. Assert( fabs( SubFloat( m_data[i].m_f4Position, 0 ) - m_data[i].m_vPosition.x ) < 0.001f );
  1636. Assert( fabs( SubFloat( m_data[i].m_f4Position, 1 ) - m_data[i].m_vPosition.y ) < 0.001f );
  1637. Assert( fabs( SubFloat( m_data[i].m_f4Position, 2 ) - m_data[i].m_vPosition.z ) < 0.001f );
  1638. #endif
  1639. }
  1640. }
  1641. float m_flDeltaTime;
  1642. CUtlVector< BlobStreakUpdateData_t, CUtlMemoryAligned< BlobStreakUpdateData_t, 16 > > m_data;
  1643. };
  1644. void PaintBlobUpdate( const PaintBlobVector_t& blobList )
  1645. {
  1646. if ( blobList.Count() == 0 )
  1647. return;
  1648. PaintBlobVector_t firstPass;
  1649. firstPass.EnsureCount( blobList.Count() );
  1650. int nFirstPassCount = 0;
  1651. for ( int i=0; i<blobList.Count(); ++i )
  1652. {
  1653. CPaintBlob *pBlob = blobList[i];
  1654. Assert( !pBlob->ShouldDeleteThis() );
  1655. float updateDeltaTime = gpGlobals->curtime - pBlob->GetLastUpdateTime();
  1656. if ( updateDeltaTime < 0.001f )
  1657. {
  1658. continue;
  1659. }
  1660. //Update the lifetime of the blob
  1661. pBlob->UpdateLifeTime( updateDeltaTime );
  1662. //If the paint blobs have a limited range
  1663. if( paintblob_limited_range.GetBool() )
  1664. {
  1665. if( pBlob->GetLifeTime() >= paintblob_lifetime.GetFloat() )
  1666. {
  1667. pBlob->SetDeletionFlag( true );
  1668. continue;
  1669. }
  1670. }
  1671. firstPass[ nFirstPassCount ] = pBlob;
  1672. ++nFirstPassCount;
  1673. }
  1674. firstPass.RemoveMultipleFromTail( blobList.Count() - nFirstPassCount );
  1675. if ( nFirstPassCount == 0 )
  1676. return;
  1677. PaintBlobVector_t blobsInBeam;
  1678. PaintBlobVector_t blobsInAir;
  1679. PaintBlobVector_t blobsInStreak;
  1680. SplitBlobsIntoMovementGroup( firstPass, blobsInBeam, blobsInAir, blobsInStreak );
  1681. // do SIMD update here
  1682. BlobsInAirUpdate_SIMD blobAirSIMD( blobsInAir );
  1683. BlobsInStreakUpdate_SIMD blobStreakSIMD( blobsInStreak );
  1684. int nSecondPassCount = 0;
  1685. PaintBlobVector_t secondPass;
  1686. secondPass.EnsureCount( firstPass.Count() );
  1687. V_memcpy( secondPass.Base(), blobsInAir.Base(), blobsInAir.Count() * sizeof( CBasePaintBlob* ) );
  1688. nSecondPassCount += blobsInAir.Count();
  1689. V_memcpy( secondPass.Base() + nSecondPassCount, blobsInStreak.Base(), blobsInStreak.Count() * sizeof( CBasePaintBlob* ) );
  1690. nSecondPassCount += blobsInStreak.Count();
  1691. int totalBlobsInBeams = 0;
  1692. for( int i = 0; i < ITriggerTractorBeamAutoList::AutoList().Count(); ++i )
  1693. {
  1694. CTrigger_TractorBeam *pBeam = static_cast< CTrigger_TractorBeam* >( ITriggerTractorBeamAutoList::AutoList()[i] );
  1695. int numBlobsInBeam = pBeam->m_blobs.Count();
  1696. if ( numBlobsInBeam == 0 )
  1697. continue;
  1698. // update tempEndPos, tempEndVel
  1699. BlobsInBeamUpdate_SIMD blobBeamSIMD( pBeam );
  1700. totalBlobsInBeams += numBlobsInBeam;
  1701. const Vector& vecBeamStart = pBeam->GetStartPoint();
  1702. const Vector& vecBeamEnd = pBeam->GetEndPoint();
  1703. float flBeamRadius = pBeam->GetBeamRadius();
  1704. // TODO: trace beam ray, if not hit anything, skip all these blobs
  1705. Ray_t blobRay;
  1706. Vector vBeamExtents( 0.f, flBeamRadius, flBeamRadius );
  1707. blobRay.Init( vecBeamStart, vecBeamEnd, -vBeamExtents, vBeamExtents );
  1708. CBaseEntity* ppEntities[ MAX_BLOB_TRACE_ENTITY_RESULTS ];
  1709. BlobTraceEnum entEnum( ppEntities, ARRAYSIZE( ppEntities ), BLOB_TRACE_DYNAMIC_MASK );
  1710. UTIL_Blob_EnumerateEntitiesAlongRay( blobRay, &entEnum );
  1711. CUtlVector< std::pair< float, float > > beamBadSectionList;
  1712. Ray_t invBlobRay;
  1713. invBlobRay.Init( vecBeamEnd, vecBeamStart, -vBeamExtents, vBeamExtents );
  1714. for ( int i=0; i<entEnum.GetCount(); ++i )
  1715. {
  1716. CBaseEntity *pHitEntity = ppEntities[i];
  1717. if ( pHitEntity == pBeam || FClassnameIs( pHitEntity, "physicsclonearea" ) )
  1718. {
  1719. continue;
  1720. }
  1721. // trace from both sides of the beam to find bad section
  1722. trace_t tr;
  1723. enginetrace->ClipRayToEntity( blobRay, BLOB_TRACE_DYNAMIC_MASK, pHitEntity, &tr );
  1724. trace_t trInv;
  1725. enginetrace->ClipRayToEntity( invBlobRay, BLOB_TRACE_DYNAMIC_MASK, pHitEntity, &trInv );
  1726. std::pair< float, float > badSection;
  1727. badSection.first = fsel( tr.fraction - 1.f, 1.f, tr.fraction );
  1728. badSection.second = fsel( trInv.fraction - 1.f, 0.f, 1.f - trInv.fraction );
  1729. // if assert fail, means we missed the entity
  1730. //Assert( badSection.first < badSection.second );
  1731. if ( badSection.first >= badSection.second )
  1732. {
  1733. //DevMsg("bad entity: %s\n", pHitEntity->GetClassname() );
  1734. continue;
  1735. }
  1736. /*else
  1737. {
  1738. DevMsg("time: %f, name: %s, range: [%f, %f]\n", gpGlobals->curtime, pHitEntity->GetClassname(), badSection.first, badSection.second );
  1739. }*/
  1740. beamBadSectionList.AddToTail( badSection );
  1741. }
  1742. if ( debug_beam_badsection.GetBool() )
  1743. {
  1744. Vector vecDir = vecBeamEnd - vecBeamStart;
  1745. for ( int i=0; i<beamBadSectionList.Count(); ++i )
  1746. {
  1747. const std::pair< float, float >& badSection = beamBadSectionList[i];
  1748. Vector vStart = vecBeamStart + badSection.first * vecDir;
  1749. Vector vEnd = vecBeamStart + badSection.second * vecDir;
  1750. NDebugOverlay::Line( vStart, vEnd, 255, 255, 0, true, 0.1f );
  1751. }
  1752. }
  1753. // check where each blob is along the beam and see if they need to go through second pass
  1754. for( int j = 0; j < numBlobsInBeam; ++j )
  1755. {
  1756. CPaintBlob *pBlob = pBeam->m_blobs[j];
  1757. const Vector& vEndPos = pBlob->GetTempEndPosition();
  1758. Vector vClosestPointOnLine;
  1759. float flFractionOnLine;
  1760. CalcClosestPointOnLine( vEndPos, vecBeamStart, vecBeamEnd, vClosestPointOnLine, &flFractionOnLine );
  1761. if ( flFractionOnLine < 0.f || flFractionOnLine > 1.f )
  1762. {
  1763. secondPass[nSecondPassCount] = pBlob;
  1764. ++nSecondPassCount;
  1765. continue;
  1766. }
  1767. bool bIsInBadSection = false;
  1768. for ( int k=0; k<beamBadSectionList.Count(); ++k )
  1769. {
  1770. const std::pair< float, float >& badSection = beamBadSectionList[k];
  1771. if ( flFractionOnLine >= badSection.first && flFractionOnLine <= badSection.second )
  1772. {
  1773. bIsInBadSection = true;
  1774. secondPass[nSecondPassCount] = pBlob;
  1775. ++nSecondPassCount;
  1776. break;
  1777. }
  1778. }
  1779. if ( bIsInBadSection )
  1780. continue;
  1781. pBlob->SetPosition( pBlob->GetTempEndPosition() );
  1782. pBlob->SetVelocity( pBlob->GetTempEndVelocity() );
  1783. pBlob->ResetGhostState();
  1784. }
  1785. } // end for this beam
  1786. AssertMsg( totalBlobsInBeams == blobsInBeam.Count(), "Blobs are in bad beam state\n");
  1787. // do collision
  1788. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  1789. for ( int i=0; i<nSecondPassCount; ++i )
  1790. {
  1791. CBasePaintBlob *pBlob = secondPass[i];
  1792. if ( pBlob->ShouldDeleteThis() )
  1793. continue;
  1794. float updateDeltaTime = gpGlobals->curtime - pBlob->GetLastUpdateTime();
  1795. const Vector& vecEndPos = pBlob->GetTempEndPosition();
  1796. Vector vecEndVelocity = pBlob->GetTempEndVelocity();
  1797. // Exit early if the blob isn't moving
  1798. if( pBlob->GetPosition() == vecEndPos )
  1799. {
  1800. continue;
  1801. }
  1802. pBlob->UpdateBlobCollision( updateDeltaTime, vecEndPos, vecEndVelocity );
  1803. if ( pBlob->ShouldDeleteThis() )
  1804. {
  1805. continue;
  1806. }
  1807. pBlob->UpdateBlobPostCollision( updateDeltaTime );
  1808. }
  1809. // update time for blobs that get in first pass
  1810. for ( int i=0; i<nFirstPassCount; ++i )
  1811. {
  1812. CBasePaintBlob *pBlob = firstPass[i];
  1813. pBlob->SetLastUpdateTime( gpGlobals->curtime );
  1814. }
  1815. }
  1816. void CBasePaintBlob::SetShouldPlaySound( bool shouldPlaySound )
  1817. {
  1818. m_bShouldPlaySound = shouldPlaySound;
  1819. }
  1820. bool CBasePaintBlob::ShouldPlaySound() const
  1821. {
  1822. return m_bShouldPlaySound && !m_bDrawOnly;
  1823. }
  1824. bool CBasePaintBlob::ShouldPlayEffect() const
  1825. {
  1826. return m_bShouldPlayEffect && !m_bDrawOnly;
  1827. }