aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/client/particle_collision.cpp
diff options
context:
space:
mode:
authorJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
committerJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
commit39ed87570bdb2f86969d4be821c94b722dc71179 (patch)
treeabc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/client/particle_collision.cpp
downloadsource-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz
source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/client/particle_collision.cpp')
-rw-r--r--mp/src/game/client/particle_collision.cpp376
1 files changed, 376 insertions, 0 deletions
diff --git a/mp/src/game/client/particle_collision.cpp b/mp/src/game/client/particle_collision.cpp
new file mode 100644
index 00000000..0f956f13
--- /dev/null
+++ b/mp/src/game/client/particle_collision.cpp
@@ -0,0 +1,376 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Local fast collision system for particles
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "particle_collision.h"
+#include "engine/ivdebugoverlay.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#ifdef _XBOX
+#define __DEBUG_PARTICLE_COLLISION_RETEST 0
+#else
+#define __DEBUG_PARTICLE_COLLISION_RETEST 1
+#endif // _XBOX
+
+#define __DEBUG_PARTICLE_COLLISION_OVERLAY 0
+#define __DEBUG_PARTICLE_COLLISION_OVERLAY_LIFETIME 0.1f
+
+#define NUM_DISCREET_STEPS 8.0f
+#define NUM_SIMULATION_SECONDS 2.0f
+
+#define COLLISION_EPSILON 0.01f
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseSimpleCollision::CBaseSimpleCollision( void )
+{
+ ClearActivePlanes();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &origin -
+// radius -
+//-----------------------------------------------------------------------------
+void CBaseSimpleCollision::Setup( const Vector &origin, float speed, float gravity )
+{
+ TestForPlane( origin, Vector( 1, 0, 0 ), speed, gravity );
+ TestForPlane( origin, Vector( -1, 0, 0 ), speed, gravity );
+ TestForPlane( origin, Vector( 0, 1, 0 ), speed, gravity );
+ TestForPlane( origin, Vector( 0, -1, 0 ), speed, gravity );
+ TestForPlane( origin, Vector( 0, 0, 1 ), speed, gravity );
+ TestForPlane( origin, Vector( 0, 0, -1 ), speed, gravity );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Trace line for super-simplified traces
+// Input : &start - start position
+// &end - end position
+// *pTrace - trace structure to fill
+// coarse - tests again with a real trace unless coarse is set
+//-----------------------------------------------------------------------------
+void CBaseSimpleCollision::TraceLine( const Vector &start, const Vector &end, trace_t *pTrace, bool coarse )
+{
+ //Iterate over all active planes
+ for ( int i = 0; i < m_nActivePlanes; i++ )
+ {
+ //Must be a valid plane
+ if ( m_collisionPlanes[i].m_Dist == -1.0f )
+ continue;
+
+ //Get our information about the relation to this plane
+ float dot1 = m_collisionPlanes[i].DistTo(start);
+ float dot2 = m_collisionPlanes[i].DistTo(end);
+
+ //Don't consider particles on the backside of planes
+ if ( dot1 < -COLLISION_EPSILON )
+ continue;
+
+ //Must be crossing the plane's boundary
+ if ( ( dot1 > COLLISION_EPSILON ) == ( dot2 > COLLISION_EPSILON ) )
+ continue;
+
+ //Find the intersection point
+ float t = dot1 / (dot1 - dot2);
+ Vector vIntersection = start + (end - start) * t;
+
+ //Fake the collision info
+ pTrace->endpos = vIntersection;
+ pTrace->fraction = t - COLLISION_EPSILON;
+ pTrace->plane.normal = m_collisionPlanes[i].m_Normal;
+ pTrace->plane.dist = m_collisionPlanes[i].m_Dist;
+
+ //If we need an exact trace, test again on a successful hit
+ if ( ( coarse == false ) && ( pTrace->fraction < 1.0f ) )
+ {
+ UTIL_TraceLine( start, end, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, pTrace );
+ }
+
+ #if __DEBUG_PARTICLE_COLLISION_OVERLAY
+ debugoverlay->AddBoxOverlay( vIntersection, Vector(-1,-1,-1), Vector(1,1,1), QAngle(0,0,0), 0, 255, 0, 16, __DEBUG_PARTICLE_COLLISION_OVERLAY_LIFETIME );
+ #endif //__DEBUG_PARTICLE_COLLISION_OVERLAY
+
+ //Done
+ return;
+ }
+
+ //Fell through, so clear all the fields
+ pTrace->plane.normal[0] = 0.0f;
+ pTrace->plane.normal[1] = 0.0f;
+ pTrace->plane.normal[2] = 0.0f;
+ pTrace->plane.dist = 0.0f;
+ pTrace->fraction = 1.0f;
+ pTrace->allsolid = false;
+ pTrace->startsolid = false;
+ pTrace->m_pEnt = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Tests the planes against all others for validity
+// Input : *plane - plane to test
+//-----------------------------------------------------------------------------
+void CBaseSimpleCollision::ConsiderPlane( cplane_t *plane )
+{
+ //Test against all other active planes
+ for ( int i = 0; i < m_nActivePlanes; i++ )
+ {
+ if ( m_collisionPlanes[i].m_Dist != -1.0f )
+ {
+ //Test for coplanar
+ if ( ( m_collisionPlanes[i].m_Normal == plane->normal ) && ( m_collisionPlanes[i].m_Dist == plane->dist ) )
+ return;
+ }
+ }
+
+ //Don't overrun
+ if ( m_nActivePlanes >= MAX_COLLISION_PLANES )
+ return;
+
+ //Take it
+ m_collisionPlanes[m_nActivePlanes].m_Dist = plane->dist;
+ m_collisionPlanes[m_nActivePlanes].m_Normal = plane->normal;
+ m_nActivePlanes++;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Runs a simulation of the average particle's movement, looking for collisions along the way
+// Input : &start - start of the simulation
+// &dir - direction of travel
+// speed - speed of the particle
+// gravity - gravity being used
+//-----------------------------------------------------------------------------
+void CBaseSimpleCollision::TestForPlane( const Vector &start, const Vector &dir, float speed, float gravity )
+{
+ trace_t tr;
+ Vector testStart, testEnd;
+
+ testStart = start;
+
+ //Setup our step increments
+ float dStepTime = (NUM_SIMULATION_SECONDS/NUM_DISCREET_STEPS);
+ Vector vStepIncr = dir * ( speed * dStepTime );
+ float flGravIncr = gravity*dStepTime;
+
+ //Simulate collsions in discreet steps
+ for ( int i = 1; i <= NUM_DISCREET_STEPS; i++ )
+ {
+ testEnd = testStart + vStepIncr;
+ testEnd[2] -= flGravIncr * (0.5f*(dStepTime*i)*(dStepTime*i) );
+
+ //Trace the line
+ UTIL_TraceLine( testStart, testEnd, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr );
+
+ //See if we found one
+ if ( tr.fraction != 1.0f )
+ {
+ #if __DEBUG_PARTICLE_COLLISION_OVERLAY
+ debugoverlay->AddLineOverlay( testStart, tr.endpos, 255, 0, 0, true, __DEBUG_PARTICLE_COLLISION_OVERLAY_LIFETIME );
+
+ QAngle angles;
+
+ VectorAngles( tr.plane.normal,angles );
+ angles[PITCH] += 90;
+
+ debugoverlay->AddBoxOverlay( tr.endpos, Vector(-64,-64,0), Vector(64,64,0), angles, 255, 0, 0, 16, __DEBUG_PARTICLE_COLLISION_OVERLAY_LIFETIME );
+ #endif //__DEBUG_PARTICLE_COLLISION_OVERLAY
+
+ //Test the plane against a set of criteria
+ ConsiderPlane( &tr.plane );
+
+ return;
+ }
+
+ //We missed
+ #if __DEBUG_PARTICLE_COLLISION_OVERLAY
+ debugoverlay->AddLineOverlay( testStart, tr.endpos, 0, 128.0f+(128.0f*((float)i/(float)NUM_DISCREET_STEPS)), 0, true, __DEBUG_PARTICLE_COLLISION_OVERLAY_LIFETIME );
+ #endif //__DEBUG_PARTICLE_COLLISION_OVERLAY
+
+ //Save that position for the next round
+ testStart = testEnd;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseSimpleCollision::ClearActivePlanes( void )
+{
+ for ( int i = 0; i < MAX_COLLISION_PLANES; i++ )
+ {
+ m_collisionPlanes[i].m_Dist = -1.0f;
+ m_collisionPlanes[i].m_Normal.Init();
+ }
+
+ m_nActivePlanes = 0;
+}
+
+//
+// CParticleCollision
+//
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CParticleCollision::CParticleCollision( void )
+{
+ m_flGravity = 800.0f;
+ m_flCollisionDampen = 0.5f;
+ m_flAngularCollisionDampen = 0.25f;
+
+ ClearActivePlanes();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Test for surrounding collision surfaces for quick collision testing for the particle system
+// Input : &origin - starting position
+// *dir - direction of movement (if NULL, will do a point emission test in four directions)
+// angularSpread - looseness of the spread
+// minSpeed - minimum speed
+// maxSpeed - maximum speed
+// gravity - particle gravity for the sytem
+// dampen - dampening amount on collisions
+//-----------------------------------------------------------------------------
+void CParticleCollision::Setup( const Vector &origin, const Vector *dir, float angularSpread, float minSpeed, float maxSpeed, float gravity, float dampen )
+{
+ //Take the information for this simulation
+ m_flGravity = gravity;
+ m_flCollisionDampen = dampen;
+ m_nActivePlanes = 0;
+
+ //We take a rough estimation of the spray
+ float speedAvg = (minSpeed+maxSpeed)*0.5f;
+
+ //Point or directed?
+ if ( dir == NULL )
+ {
+ //Test all around
+ TestForPlane( origin, Vector( 1, 0, 0 ), speedAvg, gravity );
+ TestForPlane( origin, Vector( -1, 0, 0 ), speedAvg, gravity );
+ TestForPlane( origin, Vector( 0, 1, 0 ), speedAvg, gravity );
+ TestForPlane( origin, Vector( 0, -1, 0 ), speedAvg, gravity );
+ TestForPlane( origin, Vector( 0, 0, 1 ), speedAvg, gravity );
+ TestForPlane( origin, Vector( 0, 0, -1 ), speedAvg, gravity );
+ }
+ else
+ {
+ Vector vSkewDir, vRight;
+ QAngle vAngles;
+
+ //FIXME: Quicker conversion?
+ //FIXME: We need to factor in the angular spread instead
+ VectorAngles( *dir, vAngles );
+ AngleVectors( vAngles, NULL, &vRight, NULL );
+
+ //Test straight
+ TestForPlane( origin, *dir, speedAvg, gravity );
+
+ vSkewDir = vRight;
+
+ //Test right
+ TestForPlane( origin, vSkewDir, speedAvg, gravity );
+
+ vSkewDir *= -1.0f;
+
+ //Test left
+ TestForPlane( origin, vSkewDir, speedAvg, gravity );
+
+ #if __DEBUG_PARTICLE_COLLISION_OVERLAY
+ DevMsg( 1, "CParticleCollision: Found %d active plane(s)\n", m_nActivePlanes );
+ #endif //__DEBUG_PARTICLE_COLLISION_OVERLAY
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Simulate movement, with collision
+// Input : &origin - position of the particle
+// &velocity - velocity of the particle
+// &rollDelta - roll delta of the particle
+// timeDelta - time step
+//-----------------------------------------------------------------------------
+bool CParticleCollision::MoveParticle( Vector &origin, Vector &velocity, float *rollDelta, float timeDelta, trace_t *pTrace )
+{
+ //Don't bother with non-moving particles
+ if ( velocity == vec3_origin )
+ return false;
+
+ //Factor in gravity
+ velocity[2] -= m_flGravity * timeDelta;
+
+ //Move
+ Vector testPosition = ( origin + ( velocity * timeDelta ) );
+
+ //Only collide if we have active planes
+ if ( m_nActivePlanes > 0 )
+ {
+ //Collide
+ TraceLine( origin, testPosition, pTrace );
+
+ //See if we hit something
+ if ( pTrace->fraction != 1.0f )
+ {
+ #if __DEBUG_PARTICLE_COLLISION_RETEST
+ //Retest the collision with a true trace line to avoid errant collisions
+ UTIL_TraceLine( origin, testPosition, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, pTrace );
+ #endif //__DEBUG_RETEST_COLLISION
+
+ //Did we hit anything?
+ if ( pTrace->fraction != 1.0f )
+ {
+ //See if we've settled
+ if ( ( pTrace->plane.normal[2] >= 0.5f ) && ( fabs( velocity[2] ) <= 48.0f ) )
+ {
+ //Leave the particle at the collision point
+ origin += velocity * ( (pTrace->fraction-COLLISION_EPSILON) * timeDelta );
+
+ //Stop the particle
+ velocity = vec3_origin;
+
+ if ( rollDelta != NULL )
+ {
+ *rollDelta = 0.0f;
+ }
+
+ return false;
+ }
+ else
+ {
+ //Move the particle to the collision point
+ origin += velocity * ( (pTrace->fraction-COLLISION_EPSILON) * timeDelta );
+
+ //Find the reflection vector
+ float proj = velocity.Dot( pTrace->plane.normal );
+ velocity += pTrace->plane.normal * (-proj*2.0f);
+
+ //Apply dampening
+ velocity *= random->RandomFloat( (m_flCollisionDampen-0.1f), (m_flCollisionDampen+0.1f) );
+
+ //Dampen the roll of the particles
+ if ( rollDelta != NULL )
+ {
+ (*rollDelta) *= -0.25f;
+ }
+
+ return true;
+ }
+ }
+ else
+ {
+ #if __DEBUG_PARTICLE_COLLISION_OVERLAY
+ //Display a false hit
+ debugoverlay->AddBoxOverlay( pTrace->endpos, Vector(-1,-1,-1), Vector(1,1,1), QAngle(0,0,0), 255, 0, 0, 16, __DEBUG_PARTICLE_COLLISION_OVERLAY_LIFETIME );
+ #endif //__DEBUG_PARTICLE_COLLISION_OVERLAY
+ }
+ }
+ }
+
+ //Simple move, no collision
+ origin = testPosition;
+
+ return false;
+}