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.
388 lines
12 KiB
388 lines
12 KiB
//========= 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 |
|
if ( debugoverlay ) |
|
{ |
|
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 |
|
if ( debugoverlay ) |
|
{ |
|
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 |
|
if ( debugoverlay ) |
|
{ |
|
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 |
|
if ( debugoverlay ) |
|
{ |
|
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; |
|
}
|
|
|