|
|
//========= 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"
#define __DEBUG_PARTICLE_COLLISION_RETEST 1
#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; }
|