1
0
forked from 0ad/0ad

Added a "collision patch" system that groups entities into a grid on top of the map to make aura checking and collision detection faster.

This was SVN commit r3253.
This commit is contained in:
Matei 2005-12-16 05:35:26 +00:00
parent 96df45e83d
commit 45255d3f37
6 changed files with 155 additions and 28 deletions

View File

@ -7,63 +7,61 @@
CBoundingObject* getContainingObject( const CVector2D& point )
{
std::vector<HEntity>* entities = g_EntityManager.getExtant();
std::vector<HEntity>::iterator it;
std::vector<CEntity*> entities;
g_EntityManager.GetInRange( point.x, point.y, COLLISION_RANGE, entities );
std::vector<CEntity*>::iterator it;
for( it = entities->begin(); it != entities->end(); it++ )
for( it = entities.begin(); it != entities.end(); it++ )
{
if( !(*it)->m_bounds ) continue;
if( (*it)->m_bounds->contains( point ) )
{
CBoundingObject* bounds = (*it)->m_bounds;
delete( entities );
return( bounds );
}
}
delete( entities );
return( NULL );
}
CEntity* GetCollisionObject( float x, float y )
{
CVector2D point( x, y );
std::vector<HEntity>* entities = g_EntityManager.getExtant();
std::vector<HEntity>::iterator it;
for( it = entities->begin(); it != entities->end(); it++ )
std::vector<CEntity*> entities;
g_EntityManager.GetInRange( x, y, COLLISION_RANGE, entities );
std::vector<CEntity*>::iterator it;
for( it = entities.begin(); it != entities.end(); it++ )
{
if( !(*it)->m_bounds ) continue;
if( (*it)->m_bounds->contains( point ) )
{
CEntity* e = (*it);
delete( entities );
return( e );
}
}
delete( entities );
return( NULL );
}
CBoundingObject* getCollisionObject( CBoundingObject* bounds )
{
std::vector<HEntity>* entities = g_EntityManager.getExtant();
std::vector<HEntity>::iterator it;
std::vector<CEntity*> entities;
g_EntityManager.GetInRange( bounds->m_pos.x, bounds->m_pos.y, COLLISION_RANGE, entities );
std::vector<CEntity*>::iterator it;
for( it = entities->begin(); it != entities->end(); it++ )
for( it = entities.begin(); it != entities.end(); it++ )
{
if( !(*it)->m_bounds ) continue;
if( (*it)->m_bounds == bounds ) continue;
if( bounds->intersects( (*it)->m_bounds ) )
{
CBoundingObject* obj = (*it)->m_bounds;
delete( entities );
return( obj );
}
}
delete( entities );
return( NULL );
}
@ -75,22 +73,21 @@ HEntity getCollisionObject( CEntity* entity )
if( !entity->m_bounds ) return HEntity();
#endif
std::vector<HEntity>* entities = g_EntityManager.getExtant();
std::vector<HEntity>::iterator it;
std::vector<CEntity*> entities;
g_EntityManager.GetInRange( entity->m_position.X, entity->m_position.Z, COLLISION_RANGE, entities );
std::vector<CEntity*>::iterator it;
for( it = entities->begin(); it != entities->end(); it++ )
for( it = entities.begin(); it != entities.end(); it++ )
{
if( !(*it)->m_bounds ) continue;
if( (*it)->m_bounds == entity->m_bounds ) continue;
if( entity->m_bounds->intersects( (*it)->m_bounds ) )
{
HEntity collisionObject = *it;
delete( entities );
HEntity collisionObject = HEntity((*it)->me);
return( collisionObject );
}
}
delete( entities );
return HEntity();
}

View File

@ -27,6 +27,9 @@ struct rayIntersectionResults
float distance;
};
// maximum radius at which we check for entities (if some entity is much bigger than this, that's bad)
#define COLLISION_RANGE 30
typedef std::vector<CEntity*> RayIntersects;
HEntity getCollisionObject( CEntity* entity );

View File

@ -67,7 +67,9 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation )
AddProperty( EventNames[t], &m_EventHandlers[t], false );
AddHandler( t, &m_EventHandlers[t] );
}
m_collisionPatch = NULL;
// Set our parent unit and build us an actor.
m_actor = NULL;
m_bounds = NULL;
@ -173,6 +175,8 @@ void CEntity::kill()
m_actor = NULL;
}
updateCollisionPatch();
me = HEntity(); // will deallocate the entity, assuming nobody else has a reference to it
}
@ -340,30 +344,39 @@ void CEntity::update( size_t timestep )
case CEntityOrder::ORDER_GOTO_COLLISION:
case CEntityOrder::ORDER_GOTO_SMOOTHED:
if( processGotoNoPathing( current, timestep ) ) break;
updateCollisionPatch();
return;
case CEntityOrder::ORDER_ATTACK_MELEE:
if( processAttackMeleeNoPathing( current, timestep ) ) break;
updateCollisionPatch();
return;
case CEntityOrder::ORDER_ATTACK_MELEE_NOPATHING:
if( processAttackMeleeNoPathing( current, timestep ) ) break;
updateCollisionPatch();
return;
case CEntityOrder::ORDER_GATHER:
if( processGather( current, timestep ) ) break;
updateCollisionPatch();
return;
case CEntityOrder::ORDER_GATHER_NOPATHING:
if( processGatherNoPathing( current, timestep ) ) break;
updateCollisionPatch();
return;
case CEntityOrder::ORDER_HEAL:
if( processHeal( current, timestep ) ) break;
updateCollisionPatch();
return;
case CEntityOrder::ORDER_HEAL_NOPATHING:
if( processHealNoPathing( current, timestep ) ) break;
updateCollisionPatch();
return;
case CEntityOrder::ORDER_GOTO:
if( processGoto( current, timestep ) ) break;
updateCollisionPatch();
return;
case CEntityOrder::ORDER_PATROL:
if( processPatrol( current, timestep ) ) break;
updateCollisionPatch();
return;
case CEntityOrder::ORDER_PATH_END_MARKER:
m_orderQueue.pop_front();
@ -399,6 +412,44 @@ void CEntity::update( size_t timestep )
}
}
void CEntity::updateCollisionPatch()
{
vector<CEntity*>* newPatch = g_EntityManager.getCollisionPatch( this );
if( newPatch != m_collisionPatch )
{
if( m_collisionPatch )
{
// remove ourselves from old patch
vector<CEntity*>& old = *m_collisionPatch;
if( old.size() == 1 )
{
// we were the only ones there, just pop us
old.pop_back();
}
else
{
// find our location and put in the last guy in the patch, then pop back
for( size_t i=0; i < old.size(); i++ )
{
if( old[i] == this )
{
old[i] = old.back();
old.pop_back();
break;
}
}
}
}
if( m_extant )
{
// add ourselves to new patch
newPatch->push_back( this );
m_collisionPatch = newPatch;
}
}
}
#if AURA_TEST
void CEntity::UpdateAuras( size_t timestep_millis )
{
@ -583,6 +634,7 @@ void CEntity::teleport()
{
m_position = m_graphics_position;
m_bounds->setPosition( m_position.X, m_position.Z );
updateCollisionPatch();
repath();
}
@ -1130,6 +1182,8 @@ bool CEntity::Kill( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(arg
{
m_extant = false;
}
updateCollisionPatch();
g_Selection.removeAll( me );

View File

@ -124,6 +124,9 @@ public:
// moved since it was last calculated, and the terrain hasn't been changed).
bool m_actor_transform_valid;
// Our current collision patch in CEntityManager
std::vector<CEntity*>* m_collisionPatch;
//-- Scripts
// Get script execution contexts - always run in the context of the entity that fired it.
@ -201,6 +204,9 @@ public:
// Process damage
void Damage( CDamageType& damage, CEntity* inflictor = NULL );
// Update collision patch (move ourselves to a new one if necessary)
void updateCollisionPatch();
float getAnchorLevel( float x, float z );
void snapToGround();

View File

@ -5,13 +5,16 @@
#include "BaseEntityCollection.h"
#include "ConfigDB.h"
#include "Profile.h"
#include "Terrain.h"
#include "Game.h"
int SELECTION_CIRCLE_POINTS;
int SELECTION_BOX_POINTS;
int SELECTION_SMOOTHNESS_UNIFIED = 9;
CEntityManager::CEntityManager()
: m_entities() // janwas: default-initialize entire array;
: m_collisionPatches(0)
, m_entities() // janwas: default-initialize entire array;
// CHandle ctor sets m_entity and m_refcount to 0
{
m_nextalloc = 0;
@ -30,12 +33,14 @@ CEntityManager::~CEntityManager()
m_extant = false;
for( int i = 0; i < MAX_HANDLES; i++ )
{
if( m_entities[i].m_refcount )
{
delete( m_entities[i].m_entity );
m_entities[i].m_entity = 0;
m_entities[i].m_refcount = 0;
}
}
// Delete entities that were killed, but not yet reaped by a call to updateAll,
// to avoid memory leak warnings upon exiting
@ -43,6 +48,9 @@ CEntityManager::~CEntityManager()
for( it = m_reaper.begin(); it < m_reaper.end(); it++ )
delete( *it );
m_reaper.clear();
delete[] m_collisionPatches;
m_collisionPatches = 0;
}
void CEntityManager::deleteAll()
@ -123,7 +131,36 @@ void CEntityManager::GetExtant( std::vector<CEntity*>& results )
void CEntityManager::GetInRange( float x, float z, float radius, std::vector<CEntity*>& results )
{
results.clear();
for( int i = 0; i < MAX_HANDLES; i++ )
int cx = (int) ( x / COLLISION_PATCH_SIZE );
int cz = (int) ( z / COLLISION_PATCH_SIZE );
int r = (int) ( radius / COLLISION_PATCH_SIZE + 1 );
int minX = MAX(cx-r, 0);
int minZ = MAX(cz-r, 0);
int maxX = MIN(cx+r, m_collisionPatchesPerSide-1);
int maxZ = MIN(cz+r, m_collisionPatchesPerSide-1);
for( int px = minX; px <= maxX; px++ )
{
for( int pz = minZ; pz <= maxZ; pz++ )
{
std::vector<CEntity*>& vec = m_collisionPatches[ px * m_collisionPatchesPerSide + pz ];
for( std::vector<CEntity*>::iterator it = vec.begin(); it != vec.end(); it++ )
{
CEntity* e = *it;
float dx = x - e->m_position.X;
float dz = z - e->m_position.Z;
if(dx*dx + dz*dz <= radius*radius)
{
results.push_back( e );
}
}
}
}
/*for( int i = 0; i < MAX_HANDLES; i++ )
{
if( m_entities[i].m_refcount && !m_entities[i].m_entity->m_destroyed && m_entities[i].m_entity->m_extant )
{
@ -134,7 +171,7 @@ void CEntityManager::GetInRange( float x, float z, float radius, std::vector<CEn
results.push_back( m_entities[i].m_entity );
}
}
}
}*/
}
/*
@ -148,15 +185,27 @@ void CEntityManager::dispatchAll( CMessage* msg )
void CEntityManager::InitializeAll()
{
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
int unitsPerSide = CELL_SIZE * ( terrain->GetVerticesPerSide() - 1 );
m_collisionPatchesPerSide = unitsPerSide / COLLISION_PATCH_SIZE + 1;
m_collisionPatches = new std::vector<CEntity*>[m_collisionPatchesPerSide * m_collisionPatchesPerSide];
for( int i = 0; i < MAX_HANDLES; i++ )
{
if( m_entities[i].m_refcount && !m_entities[i].m_entity->m_destroyed )
m_entities[i].m_entity->Initialize();
{
CEntity* e = m_entities[i].m_entity;
e->Initialize();
e->updateCollisionPatch();
}
}
}
void CEntityManager::TickAll()
{
for( int i = 0; i < MAX_HANDLES; i++ )
if( m_entities[i].m_refcount && !m_entities[i].m_entity->m_destroyed )
if( m_entities[i].m_refcount && !m_entities[i].m_entity->m_destroyed && m_entities[i].m_entity->m_extant )
m_entities[i].m_entity->Tick();
}
@ -183,7 +232,6 @@ void CEntityManager::updateAll( size_t timestep )
PROFILE_END( "tick all" );
*/
PROFILE_START( "update all" );
for( int i = 0; i < MAX_HANDLES; i++ )
if( m_entities[i].m_refcount && !m_entities[i].m_entity->m_destroyed )
@ -219,3 +267,15 @@ void CEntityManager::destroy( u16 handle )
}
bool CEntityManager::m_extant = false;
std::vector<CEntity*>* CEntityManager::getCollisionPatch( CEntity* e )
{
if( !e->m_extant )
{
return 0;
}
int ix = (int) ( e->m_position.X / COLLISION_PATCH_SIZE );
int iz = (int) ( e->m_position.Z / COLLISION_PATCH_SIZE );
return &m_collisionPatches[ ix * m_collisionPatchesPerSide + iz ];
}

View File

@ -27,6 +27,9 @@
#define MAX_HANDLES 4096
// collision patch size, in graphics units, not tiles (1 tile = 4 units)
#define COLLISION_PATCH_SIZE 8
#define g_EntityManager CEntityManager::GetSingleton()
class CEntityManager : public Singleton<CEntityManager>
@ -38,6 +41,8 @@ friend class CHandle;
int m_nextalloc;
static bool m_extant;
void destroy( u16 handle );
int m_collisionPatchesPerSide;
std::vector<CEntity*>* m_collisionPatches;
public:
CEntityManager();
@ -76,6 +81,8 @@ public:
}
void GetInRange( float x, float z, float radius, std::vector<CEntity*>& results );
std::vector<CEntity*>* getCollisionPatch( CEntity* e );
};
#endif