sqwarmed/sdk_src/game/client/particle_collision.cpp

377 lines
12 KiB
C++

//========= Copyright © 1996-2005, 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;
}