The-Simpsons-Hit-and-Run/game/code/ai/actor/intersectionlist.cpp

722 lines
21 KiB
C++

//===========================================================================
// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
//
// Component: intersectionlist
//
// Description: Holds a list of pointers to sim collision bounding boxes
//
// Authors: Michael Riegger
//
//===========================================================================
//---------------------------------------------------------------------------
// Includes
//===========================================================================
#include <ai\actor\intersectionlist.h>
#include <simcollision\proximitydetection.hpp>
#include <simcollision\collisionvolume.hpp>
#include <simcollision\collisionobject.hpp>
#include <simcommon\simstate.hpp>
#include <simcommon\simstatearticulated.hpp>
#include <float.h>
#include <render\DSG\DynaPhysDSG.h>
#include <render\DSG\fenceentitydsg.h>
#include <render\DSG\AnimCollisionEntityDSG.h>
#include <render\intersectmanager\intersectmanager.h>
//===========================================================================
// Local Constants, Typedefs, and Macros
//===========================================================================
const unsigned int SIZE_INTERSECTION_LIST_ARRAYS = 50;
const unsigned int SIZE_FENCE_PIECE_ARRAY = 100;
const float INTERSECTION_TEST_RAY_THICKNESS = 1.0f;
//===========================================================================
// Global Data, Local Data, Local Classes
//===========================================================================
//===========================================================================
// Member Functions
//===========================================================================
//===========================================================================
// IntersectionList::IntersectionList
//===========================================================================
// Description:
// IntersectionList ctor - presizes internal arrays
//
// Constraints:
//
// Parameters:
//
// Return:
//
//===========================================================================
IntersectionList::IntersectionList()
{
m_StaticCollisionList.reserve( SIZE_INTERSECTION_LIST_ARRAYS );
m_DynamicCollisionList.reserve( SIZE_INTERSECTION_LIST_ARRAYS );
m_AnimPhysCollisionList.reserve( SIZE_INTERSECTION_LIST_ARRAYS );
// Allocate the fence list array
m_FenceList.reserve( SIZE_FENCE_PIECE_ARRAY );
}
//===========================================================================
// IntersectionList::~IntersectionList
//===========================================================================
// Description:
// IntersectionList dtor - removes all data from the list
//
// Constraints:
//
// Parameters:
//
// Return:
//
//===========================================================================
IntersectionList::~IntersectionList()
{
Clear();
}
void
IntersectionList::Clear()
{
ClearStatics();
ClearDynamics();
ClearAnimPhys();
ClearFencePieces();
}
void
IntersectionList::ClearDynamics()
{
DynaCollListIt it;
for ( it = m_DynamicCollisionList.begin() ; it != m_DynamicCollisionList.end() ; it++ )
{
it->first->Release();
if ( it->second )
it->second->Release();
}
m_DynamicCollisionList.clear();
}
void IntersectionList::ClearAnimPhys()
{
for ( unsigned int i = 0 ; i < m_AnimPhysCollisionList.size() ; i++ )
{
m_AnimPhysCollisionList[i]->Release();
}
m_AnimPhysCollisionList.resize(0);
}
void
IntersectionList::ClearStatics()
{
for ( unsigned int i = 0 ; i < m_StaticCollisionList.size() ; i++ )
{
m_StaticCollisionList[i]->Release();
}
m_StaticCollisionList.resize(0);
}
void
IntersectionList::ClearFencePieces()
{
m_FenceList.resize(0);
}
bool
IntersectionList::TestIntersectionStatics( const rmt::Vector& sourceOfRay, const rmt::Vector& endOfRay, rmt::Vector* intersection )
{
rAssert( m_StaticCollisionList.capacity() == SIZE_INTERSECTION_LIST_ARRAYS );
bool foundIntersection = false;
// Lets iterate through the boxes and see if any hit
rmt::Vector sray;
sray.Sub(endOfRay, sourceOfRay);
rmt::Vector ray = sray;
float rayLen = ray.NormalizeSafe();
float closestDistance = FLT_MAX;
sim::RayIntersectionInfo info;
info.SetMethod( sim::RayIntersectionVolume );
info.sRayThickness = INTERSECTION_TEST_RAY_THICKNESS;
for ( unsigned int i = 0 ; i < m_StaticCollisionList.size() ; i++ )
{
sim::CollisionVolume* vol = m_StaticCollisionList[i]->GetCollisionVolume();
if ( vol )
{
info.mCollisionVolume = vol;
if ( sim::RayIntersectVolume( sourceOfRay, ray, sray, rayLen, info ) )
{
if( info.mDistFromSource < closestDistance )
{
// This volume is the closest yet
// make not of it and keep going
foundIntersection = true;
closestDistance = info.mDistFromSource;
}
}
}
}
// Test fence pieces
LineSegment2D rayLineSeg;
rayLineSeg.p1.x = sourceOfRay.x;
rayLineSeg.p1.y = sourceOfRay.z;
rayLineSeg.p2.x = endOfRay.x;
rayLineSeg.p2.y = endOfRay.z;
for ( unsigned int i = 0 ; i < m_FenceList.size() ; i++ )
{
float t;
if ( LineSegmentIntersection( rayLineSeg, m_FenceList[i], &t ) )
{
LineSegment2D f = m_FenceList[i];
// Get the distance based upon t
rmt::Vector fenceIntersection = sourceOfRay + t * ( endOfRay - sourceOfRay );
float dist = ( sourceOfRay - fenceIntersection ).Magnitude();
if ( dist < closestDistance )
{
closestDistance = dist;
foundIntersection = true;
}
}
}
if ( foundIntersection )
{
// The intersection point is going to be
// sourceOfRay + ray * closestDistance
closestDistance += INTERSECTION_TEST_RAY_THICKNESS;
*intersection = sourceOfRay + ray * closestDistance;
}
return foundIntersection;
}
bool
IntersectionList::TestIntersectionDynamics( const rmt::Vector& sourceOfRay,
const rmt::Vector& endOfRay,
rmt::Vector* intersection,
DynaPhysDSG** objectHit )
{
rAssert( m_DynamicCollisionList.capacity() == SIZE_INTERSECTION_LIST_ARRAYS );
bool foundIntersection;
// Lets iterate through the boxes and see if any hit
rmt::Vector sray;
sray.Sub(endOfRay, sourceOfRay);
rmt::Vector ray = sray;
float rayLen = ray.NormalizeSafe();
float closestDistance = FLT_MAX;
sim::RayIntersectionInfo info;
info.SetMethod( sim::RayIntersectionVolume );
info.sRayThickness = INTERSECTION_TEST_RAY_THICKNESS;
DynaCollListIt it;
for ( it = m_DynamicCollisionList.begin() ; it != m_DynamicCollisionList.end() ; it++ )
{
info.mCollisionVolume = it->first->GetCollisionVolume();
if ( info.mCollisionVolume )
{
if ( sim::RayIntersectVolume( sourceOfRay, ray, sray, rayLen, info ) )
{
if( info.mDistFromSource < closestDistance )
{
// This volume is the closest yet
// make not of it and keep going
closestDistance = info.mDistFromSource;
if ( objectHit )
*objectHit = it->second;
}
}
}
}
if ( closestDistance != FLT_MAX )
{
// The intersection point is going to be
// sourceOfRay + ray * closestDistance
closestDistance += INTERSECTION_TEST_RAY_THICKNESS;
*intersection = sourceOfRay + ray * closestDistance;
foundIntersection = true;
}
else
{
foundIntersection = false;
}
return foundIntersection;
}
bool
IntersectionList::TestIntersectionAnimPhys( const rmt::Vector& sourceOfRay,
const rmt::Vector& endOfRay,
rmt::Vector* intersection )
{
rAssert( m_AnimPhysCollisionList.capacity() == SIZE_INTERSECTION_LIST_ARRAYS );
bool foundIntersection = false;
// Lets iterate through the boxes and see if any hit
rmt::Vector sray;
sray.Sub(endOfRay, sourceOfRay);
rmt::Vector ray = sray;
float rayLen = ray.NormalizeSafe();
float closestDistance = FLT_MAX;
sim::RayIntersectionInfo info;
info.SetMethod( sim::RayIntersectionVolume );
info.sRayThickness = INTERSECTION_TEST_RAY_THICKNESS;
for ( unsigned int i = 0 ; i < m_AnimPhysCollisionList.size() ; i++ )
{
sim::CollisionVolume* vol = m_AnimPhysCollisionList[i]->GetCollisionVolume();
if ( vol )
{
info.mCollisionVolume = vol;
if ( sim::RayIntersectVolume( sourceOfRay, ray, sray, rayLen, info ) )
{
if( info.mDistFromSource < closestDistance )
{
// This volume is the closest yet
// make not of it and keep going
foundIntersection = true;
closestDistance = info.mDistFromSource;
}
}
}
}
// Test fence pieces
LineSegment2D rayLineSeg;
rayLineSeg.p1.x = sourceOfRay.x;
rayLineSeg.p1.y = sourceOfRay.z;
rayLineSeg.p2.x = endOfRay.x;
rayLineSeg.p2.y = endOfRay.z;
for ( unsigned int i = 0 ; i < m_FenceList.size() ; i++ )
{
float t;
if ( LineSegmentIntersection( rayLineSeg, m_FenceList[i], &t ) )
{
LineSegment2D f = m_FenceList[i];
// Get the distance based upon t
rmt::Vector fenceIntersection = sourceOfRay + t * ( endOfRay - sourceOfRay );
float dist = ( sourceOfRay - fenceIntersection ).Magnitude();
if ( dist < closestDistance )
{
closestDistance = dist;
foundIntersection = true;
}
}
}
if ( foundIntersection )
{
// The intersection point is going to be
// sourceOfRay + ray * closestDistance
closestDistance += INTERSECTION_TEST_RAY_THICKNESS;
*intersection = sourceOfRay + ray * closestDistance;
}
return foundIntersection;
}
bool
IntersectionList::TestIntersection( const rmt::Vector& sourceOfRay, const rmt::Vector& endOfRay, rmt::Vector* intersection, float rayWidthForFences )
{
rAssert( m_StaticCollisionList.capacity() == SIZE_INTERSECTION_LIST_ARRAYS );
rAssert( m_DynamicCollisionList.capacity() == SIZE_INTERSECTION_LIST_ARRAYS );
bool foundIntersection;
// Lets iterate through the boxes and see if any hit
rmt::Vector sray;
sray.Sub(endOfRay, sourceOfRay);
rmt::Vector ray = sray;
float rayLen = ray.NormalizeSafe();
float closestDistance = FLT_MAX;
sim::RayIntersectionInfo info;
info.SetMethod( sim::RayIntersectionVolume );
info.sRayThickness = INTERSECTION_TEST_RAY_THICKNESS;
DynaCollListIt it;
for ( it = m_DynamicCollisionList.begin() ; it != m_DynamicCollisionList.end() ; it++ )
{
info.mCollisionVolume = it->first->GetCollisionVolume();
if ( info.mCollisionVolume )
{
if ( sim::RayIntersectVolume( sourceOfRay, ray, sray, rayLen, info ) )
{
if( info.mDistFromSource < closestDistance )
{
// This volume is the closest yet
// make note of it and keep going
closestDistance = info.mDistFromSource;
}
}
}
}
for ( unsigned int i = 0 ; i < m_StaticCollisionList.size() ; i++ )
{
info.mCollisionVolume = m_StaticCollisionList[i]->GetCollisionVolume();
if ( info.mCollisionVolume )
{
if ( sim::RayIntersectVolume( sourceOfRay, ray, sray, rayLen, info ) )
{
if( info.mDistFromSource < closestDistance )
{
// This volume is the closest yet
// make note of it and keep going
closestDistance = info.mDistFromSource;
}
}
}
}
LineSegment2D rayLineSeg;
rayLineSeg.p1.x = sourceOfRay.x;
rayLineSeg.p1.y = sourceOfRay.z;
rayLineSeg.p2.x = endOfRay.x;
rayLineSeg.p2.y = endOfRay.z;
for ( unsigned int i = 0 ; i < m_FenceList.size() ; i++ )
{
float t;
if ( LineSegmentIntersection( rayLineSeg, m_FenceList[i], &t ) )
{
// Get the distance based upon t
rmt::Vector fenceIntersection = sourceOfRay + t * ( endOfRay - sourceOfRay );
float dist = ( sourceOfRay - fenceIntersection ).Magnitude();
if ( dist < closestDistance )
{
closestDistance = dist - rayWidthForFences;
foundIntersection = true;
}
}
}
if ( closestDistance != FLT_MAX )
{
// The intersection point is going to be
// sourceOfRay + ray * closestDistance
closestDistance += INTERSECTION_TEST_RAY_THICKNESS;
*intersection = sourceOfRay + ray * closestDistance;
foundIntersection = true;
}
else
{
foundIntersection = false;
}
return foundIntersection;
}
bool
IntersectionList::LineOfSight( const rmt::Vector& sourceOfRay, const rmt::Vector& endOfRay, float rayWidth, bool staticsOnly )
{
rmt::Vector sray;
sray.Sub(endOfRay, sourceOfRay);
rmt::Vector ray = sray;
float rayLen = ray.NormalizeSafe();
unsigned int i;
sim::RayIntersectionInfo info;
info.SetMethod( sim::RayIntersectionVolume );
info.sRayThickness = rayWidth;
// Test the statics
for ( i = 0 ; i < m_StaticCollisionList.size() ; i++ )
{
info.mCollisionVolume = m_StaticCollisionList[i]->GetCollisionVolume();
if ( info.mCollisionVolume )
{
if ( sim::RayIntersectVolume( sourceOfRay, ray, sray, rayLen, info ) )
{
// Hit
return false;
}
}
}
if ( !staticsOnly )
{
// Test the dynamics
DynaCollListIt it;
for ( it = m_DynamicCollisionList.begin() ; it != m_DynamicCollisionList.end() ; it++ )
{
info.mCollisionVolume = it->first->GetCollisionVolume();
if ( info.mCollisionVolume )
{
if ( sim::RayIntersectVolume( sourceOfRay, ray, sray, rayLen, info ) )
{
// Hit
return false;
}
}
}
for ( i = 0 ; i < m_AnimPhysCollisionList.size() ; i++ )
{
info.mCollisionVolume = m_AnimPhysCollisionList[i]->GetCollisionVolume();
if ( info.mCollisionVolume )
{
if ( sim::RayIntersectVolume( sourceOfRay, ray, sray, rayLen, info ) )
{
// Hit
return false;
}
}
}
}
LineSegment2D rayLineSeg;
rayLineSeg.p1.x = sourceOfRay.x;
rayLineSeg.p1.y = sourceOfRay.z;
rayLineSeg.p2.x = endOfRay.x;
rayLineSeg.p2.y = endOfRay.z;
// Test the fence pieces
for ( unsigned int i = 0 ; i < m_FenceList.size() ; i++ )
{
float t;
if ( LineSegmentIntersection( rayLineSeg, m_FenceList[i], &t ) )
{
return false;
}
}
return true;
}
void
IntersectionList::AddStatic( sim::SimState* simstate )
{
if ( m_StaticCollisionList.size() >= SIZE_INTERSECTION_LIST_ARRAYS )
return;
if ( simstate )
{
sim::CollisionObject* collisionObject = simstate->GetCollisionObject();
if ( collisionObject )
{
collisionObject->AddRef();
m_StaticCollisionList.push_back( collisionObject );
}
}
}
void
IntersectionList::AddDynamic( sim::SimState* simstate, DynaPhysDSG* object )
{
if ( m_DynamicCollisionList.size() >= SIZE_INTERSECTION_LIST_ARRAYS )
return;
if ( simstate )
{
sim::CollisionObject* collisionObject = simstate->GetCollisionObject();
if ( collisionObject )
{
if(m_DynamicCollisionList.find(collisionObject) == m_DynamicCollisionList.end())
{
collisionObject->AddRef();
if ( object )
object->AddRef();
m_DynamicCollisionList.insert( collisionObject, object );
}
else
{
// rAssert(0);
}
}
}
}
void
IntersectionList::AddAnimPhys( sim::SimStateArticulated* simstate )
{
if ( m_AnimPhysCollisionList.size() >= SIZE_INTERSECTION_LIST_ARRAYS )
return;
if ( simstate )
{
sim::CollisionObject* collisionObject = simstate->GetCollisionObject();
if ( collisionObject )
{
collisionObject->AddRef();
m_AnimPhysCollisionList.push_back( collisionObject );
}
}
}
void
IntersectionList::AddFencePiece( FenceEntityDSG* fencePiece )
{
static int maxcount = 0;
// Prevent any dynamic allocations because of the push_back contained in this function
if ( m_FenceList.size() >= SIZE_FENCE_PIECE_ARRAY )
{
return;
}
LineSegment2D lineSegment;
lineSegment.p1.x = fencePiece->mStartPoint.x;
lineSegment.p1.y = fencePiece->mStartPoint.z;
lineSegment.p2.x = fencePiece->mEndPoint.x;
lineSegment.p2.y = fencePiece->mEndPoint.z;
m_FenceList.push_back( lineSegment );
}
// Add all objects in the given sphere to the list
int
IntersectionList::FillIntersectionListStatics( const rmt::Vector& position, float radius )
{
int numAdded = FillIntersectionListFence( position, radius );
ClearStatics();
// Iterate through the statics and grab them all
ReserveArray< StaticPhysDSG* > spList(200);
GetIntersectManager()->FindStaticPhysElems( const_cast< rmt::Vector& >( position ), radius, spList );
for ( int i = 0 ; i < spList.mUseSize ; i++ )
{
StaticPhysDSG* object = spList[i];
sim::SimState* simState = object->GetSimState();
AddStatic( simState );
}
numAdded += spList.mUseSize;
return numAdded;
}
int
IntersectionList::FillIntersectionListFence( const rmt::Vector& position, float radius )
{
ClearFencePieces();
ReserveArray< FenceEntityDSG* > feList(200);
// Perhaps intersectmanager should be changed to accept a const rmt::Vector& instead?
GetIntersectManager()->FindFenceElems( const_cast< rmt::Vector& > (position), radius, feList );
for ( int i = 0 ; i < feList.mUseSize ; i++ )
{
FenceEntityDSG* object = feList[i];
AddFencePiece( object );
}
return feList.mUseSize;
}
int IntersectionList::FillIntersectionListDynamics( const rmt::Vector& position, float radius, bool addDynaPhys, DynaPhysDSG* source )
{
ClearDynamics();
ReserveArray< DynaPhysDSG* > dpList(200);
// Perhaps intersectmanager should be changed to accept a const rmt::Vector& instead?
GetIntersectManager()->FindDynaPhysElems( const_cast< rmt::Vector& > (position), radius, dpList );
for ( int i = 0 ; i < dpList.mUseSize ; i++ )
{
DynaPhysDSG* object = dpList[i];
if ( object != source )
{
sim::SimState* simState = object->GetSimState();
// [Dusit: June 16th, 2003]
// Don't seem to be using object anywhere, and there seemed to be an addref/release
// mismatch coming from this system on traffic vehicles (supercam uses this on the
// traffic)... So this is a quick hack to solve that little problem.
AddDynamic( simState, (addDynaPhys? object : NULL) );
}
}
return dpList.mUseSize;
}
// Add all objects in the given sphere to the list
int
IntersectionList::FillIntersectionListAnimPhys( const rmt::Vector& position, float radius )
{
int numAdded = FillIntersectionListFence( position, radius );
ClearAnimPhys();
// Iterate through the statics and grab them all
ReserveArray< AnimCollisionEntityDSG* > spList(200);
GetIntersectManager()->FindAnimPhysElems( const_cast< rmt::Vector& >( position ), radius, spList );
for ( int i = 0 ; i < spList.mUseSize ; i++ )
{
AnimCollisionEntityDSG* object = spList[i];
sim::SimStateArticulated* simState = object->GetSimState();
AddAnimPhys( simState );
}
numAdded += spList.mUseSize;
return numAdded;
}
bool
IntersectionList::LineSegmentIntersection( const LineSegment2D& line1, const LineSegment2D& line2, float* t )
{
float x1 = line1.p1.x;
float x2 = line1.p2.x;
float x3 = line2.p1.x;
float x4 = line2.p2.x;
float y1 = line1.p1.y;
float y2 = line1.p2.y;
float y3 = line2.p1.y;
float y4 = line2.p2.y;
float a_denom = (y4-y3)*(x2-x1)-(x4-x3)*(y2-y1);
if ( rmt::Fabs( a_denom ) < 0.001f )
return false;
float ua = ( (x4 - x3) * ( y1 - y3 ) - ( y4 - y3 ) * ( x1 - x3 ) ) / a_denom ;
if ( ua < 0 || ua > 1.0f )
return false;
float b_denom = (y4-y3)*(x2-x1)-(x4-x3)*(y2-y1);
if ( rmt::Fabs( b_denom ) < 0.001f )
return false;
float ub = ( (x2 - x1) * ( y1 - y3 ) - ( y2 - y1 ) * ( x1 - x3 ) ) / b_denom;
if ( ub < 0 || ub > 1.0f )
return false;
*t = ua;
return true;
}