From 45255d3f37e29f5d8a4035895d548b5f62214f96 Mon Sep 17 00:00:00 2001 From: Matei Date: Fri, 16 Dec 2005 05:35:26 +0000 Subject: [PATCH] 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. --- source/simulation/Collision.cpp | 39 ++++++++-------- source/simulation/Collision.h | 3 ++ source/simulation/Entity.cpp | 56 +++++++++++++++++++++- source/simulation/Entity.h | 6 +++ source/simulation/EntityManager.cpp | 72 ++++++++++++++++++++++++++--- source/simulation/EntityManager.h | 7 +++ 6 files changed, 155 insertions(+), 28 deletions(-) diff --git a/source/simulation/Collision.cpp b/source/simulation/Collision.cpp index ecd268bf22..6fad45af46 100755 --- a/source/simulation/Collision.cpp +++ b/source/simulation/Collision.cpp @@ -7,63 +7,61 @@ CBoundingObject* getContainingObject( const CVector2D& point ) { - std::vector* entities = g_EntityManager.getExtant(); - std::vector::iterator it; + std::vector entities; + g_EntityManager.GetInRange( point.x, point.y, COLLISION_RANGE, entities ); + std::vector::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* entities = g_EntityManager.getExtant(); - std::vector::iterator it; - for( it = entities->begin(); it != entities->end(); it++ ) + std::vector entities; + g_EntityManager.GetInRange( x, y, COLLISION_RANGE, entities ); + std::vector::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* entities = g_EntityManager.getExtant(); - std::vector::iterator it; + std::vector entities; + g_EntityManager.GetInRange( bounds->m_pos.x, bounds->m_pos.y, COLLISION_RANGE, entities ); + std::vector::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* entities = g_EntityManager.getExtant(); - std::vector::iterator it; + std::vector entities; + g_EntityManager.GetInRange( entity->m_position.X, entity->m_position.Z, COLLISION_RANGE, entities ); + std::vector::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(); } diff --git a/source/simulation/Collision.h b/source/simulation/Collision.h index 6675928dc0..06510a634d 100755 --- a/source/simulation/Collision.h +++ b/source/simulation/Collision.h @@ -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 RayIntersects; HEntity getCollisionObject( CEntity* entity ); diff --git a/source/simulation/Entity.cpp b/source/simulation/Entity.cpp index db07284d33..944c023e7b 100755 --- a/source/simulation/Entity.cpp +++ b/source/simulation/Entity.cpp @@ -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* newPatch = g_EntityManager.getCollisionPatch( this ); + if( newPatch != m_collisionPatch ) + { + if( m_collisionPatch ) + { + // remove ourselves from old patch + vector& 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 ); diff --git a/source/simulation/Entity.h b/source/simulation/Entity.h index 7a83682d1e..00deef0d4c 100755 --- a/source/simulation/Entity.h +++ b/source/simulation/Entity.h @@ -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* 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(); diff --git a/source/simulation/EntityManager.cpp b/source/simulation/EntityManager.cpp index a1fa9ad5b4..32243d8053 100755 --- a/source/simulation/EntityManager.cpp +++ b/source/simulation/EntityManager.cpp @@ -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& results ) void CEntityManager::GetInRange( float x, float z, float radius, std::vector& 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& vec = m_collisionPatches[ px * m_collisionPatchesPerSide + pz ]; + for( std::vector::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::vectorGetWorld()->GetTerrain(); + int unitsPerSide = CELL_SIZE * ( terrain->GetVerticesPerSide() - 1 ); + m_collisionPatchesPerSide = unitsPerSide / COLLISION_PATCH_SIZE + 1; + + m_collisionPatches = new std::vector[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* 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 ]; +} diff --git a/source/simulation/EntityManager.h b/source/simulation/EntityManager.h index 5cb50f9e85..754f50cc97 100755 --- a/source/simulation/EntityManager.h +++ b/source/simulation/EntityManager.h @@ -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 @@ -38,6 +41,8 @@ friend class CHandle; int m_nextalloc; static bool m_extant; void destroy( u16 handle ); + int m_collisionPatchesPerSide; + std::vector* m_collisionPatches; public: CEntityManager(); @@ -76,6 +81,8 @@ public: } void GetInRange( float x, float z, float radius, std::vector& results ); + + std::vector* getCollisionPatch( CEntity* e ); }; #endif