1
0
forked from 0ad/0ad

Pathfinding tweaks; coldet fixes.

This was SVN commit r379.
This commit is contained in:
MarkT 2004-06-03 02:20:48 +00:00
parent 4daa183dd3
commit d32be2011f
14 changed files with 898 additions and 747 deletions

View File

@ -160,7 +160,7 @@ void CMapReader::ApplyData(CFileUnpacker& unpacker)
CVector3D orient = -((CMatrix3D*)m_Objects[i].m_Transform)->GetIn();
CVector3D position = ((CMatrix3D*)m_Objects[i].m_Transform)->GetTranslation();
g_EntityManager.create( templateObject, position, atan2( orient.X, orient.Z ) );
g_EntityManager.create( templateObject, position, atan2( -orient.X, -orient.Z ) );
}
else
{

View File

@ -155,7 +155,7 @@ void calc_fps()
// update fps counter if update threshold is exceeded
const float avg_fps = fps_sum / H;
const float d_avg = avg_fps-fps;
const float max_diff = fminf(5.f, 0.05f*fps);
const float max_diff = __min( 5.f, 0.05f * fps ); //fminf(5.f, 0.05f*fps);
if((trend > 0 && (avg_fps > fps || d_avg < -4.f)) || // going up, or large drop
(trend < 0 && (avg_fps < fps || d_avg > 4.f)) || // going down, or large raise

View File

@ -1,3 +1,4 @@
///////////////////////////////////////////////////////////////////////////////
//
// Name: Renderer.cpp

View File

@ -46,7 +46,7 @@ CBoundingCircle::CBoundingCircle( float x, float y, CBoundingCircle* copy )
void CBoundingObject::setPosition( float x, float y )
{
m_pos.x = x; m_pos.y = y;
m_pos += m_offset;
m_pos -= m_offset;
}
void CBoundingCircle::setRadius( float radius )
@ -99,6 +99,23 @@ CBoundingBox::CBoundingBox( float x, float y, const CVector2D& u, CBoundingBox*
setOrientation( u );
}
CBoundingBox::CBoundingBox( float x, float y, float orientation, float width, float height )
{
m_type = BOUND_OABB;
setPosition( x, y );
setDimensions( width, height );
setOrientation( orientation );
}
CBoundingBox::CBoundingBox( float x, float y, float orientation, CBoundingBox* copy )
{
m_type = BOUND_OABB;
m_offset = copy->m_offset;
setPosition( x, y );
setDimensions( copy->getWidth(), copy->getHeight() );
setOrientation( orientation );
}
void CBoundingBox::setDimensions( float width, float height )
{
m_w = width / 2.0f;

View File

@ -56,15 +56,9 @@ public:
CVector2D m_v; // Unit vector along the direction of this box's width.
float m_h; // Half this box's height.
float m_w; // Half this box's width.
CBoundingBox( float x, float y, float orientation, float width, float height )
{
CBoundingBox( x, y, CVector2D( sin( orientation ), cos( orientation ) ), width, height );
}
CBoundingBox( float x, float y, float orientation, float width, float height );
CBoundingBox( float x, float y, const CVector2D& orientation, float width, float height );
CBoundingBox( float x, float y, float orientation, CBoundingBox* copy )
{
CBoundingBox( x, y, CVector2D( sin( orientation ), cos( orientation ) ), copy );
}
CBoundingBox( float x, float y, float orientation, CBoundingBox* copy );
CBoundingBox( float x, float y, const CVector2D& orientation, CBoundingBox* copy );
void setDimensions( float width, float height );
void setOrientation( float orientation );

View File

@ -54,7 +54,7 @@ HEntity getCollisionObject( CEntity* entity, float x, float y )
return( _e );
}
bool getRayIntersection( const CVector2D& source, const CVector2D& forward, const CVector2D& right, float length, float maxDistance, rayIntersectionResults* results )
bool getRayIntersection( const CVector2D& source, const CVector2D& forward, const CVector2D& right, float length, float maxDistance, CBoundingObject* destinationCollisionObject, rayIntersectionResults* results )
{
std::vector<HEntity>* entities = g_EntityManager.getActive();
std::vector<HEntity>::iterator it;
@ -69,13 +69,17 @@ bool getRayIntersection( const CVector2D& source, const CVector2D& forward, cons
for( it = entities->begin(); it != entities->end(); it++ )
{
assert( (*it)->m_bounds );
if( (*it)->m_bounds == destinationCollisionObject ) continue;
// HACK:
if( (*it)->m_bounds->m_type == CBoundingObject::BOUND_OABB ) continue;
if( (*it)->m_speed ) continue;
CBoundingObject* obj = (*it)->m_bounds;
delta = obj->m_pos - source;
closestApproach = delta.dot( right );
dist = delta.dot( forward );
float collisionRadius = maxDistance + obj->m_radius;
if( ( fabs( closestApproach ) < maxDistance + obj->m_radius ) && ( dist > -maxDistance ) && ( dist < length + maxDistance ) )
if( ( fabs( closestApproach ) < collisionRadius ) && ( dist > collisionRadius * 0.0f ) && ( dist < length - collisionRadius * 0.0f ) )
{
if( dist < results->distance )
{

View File

@ -30,6 +30,6 @@ struct rayIntersectionResults
HEntity getCollisionObject( CEntity* entity );
HEntity getCollisionObject( CEntity* entity, float x, float y );
CBoundingObject* getContainingObject( const CVector2D& point );
bool getRayIntersection( const CVector2D& source, const CVector2D& forward, const CVector2D& right, float length, float maxDistance, rayIntersectionResults* results );
bool getRayIntersection( const CVector2D& source, const CVector2D& forward, const CVector2D& right, float length, float maxDistance, CBoundingObject* destinationCollisionObject, rayIntersectionResults* results );
#endif

View File

@ -17,6 +17,10 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation )
m_base = base;
m_actor = new CUnit(m_base->m_actorObject,m_base->m_actorObject->m_Model->Clone());
// HACK: Debugging
// assert( m_base->m_name != CStr( "Waypoint" ) );
// Register the actor with the renderer.
g_UnitMan.AddUnit( m_actor );
@ -33,7 +37,7 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation )
if( m_base->m_bound_type == CBoundingObject::BOUND_CIRCLE )
{
m_bounds = new CBoundingCircle( m_position.X, m_position.Z, m_base->m_bound_circle );
m_bounds = new CBoundingCircle( m_position.X, m_position.Z, m_base->m_bound_circle );
}
else if( m_base->m_bound_type == CBoundingObject::BOUND_OABB )
{
@ -141,6 +145,7 @@ void CEntity::update( float timestep )
{
case CEntityOrder::ORDER_GOTO_NOPATHING:
case CEntityOrder::ORDER_GOTO_COLLISION:
case CEntityOrder::ORDER_GOTO_SMOOTHED:
if( processGotoNoPathing( current, timestep ) ) break;
return;
case CEntityOrder::ORDER_GOTO:
@ -200,21 +205,59 @@ void CEntity::pushOrder( CEntityOrder& order )
void CEntity::render()
{
// Rich! Help! ;)
// We can loose this later on, I just need a way to see collision boxes temporarily
// HACK: As in this entire function is a...
if( !m_orderQueue.empty() )
{
glShadeModel( GL_FLAT );
glBegin( GL_LINE_STRIP );
std::deque<CEntityOrder>::iterator it;
CBoundingObject* destinationCollisionObject;
float x0, y0, x, y;
glVertex3f( m_position.X, m_position.Y + 0.25f /* 20.0f */, m_position.Z );
x = m_orderQueue.front().m_data[0].location.x;
y = m_orderQueue.front().m_data[0].location.y;
for( it = m_orderQueue.begin(); it < m_orderQueue.end(); it++ )
{
float x = it->m_data[0].location.x;
float y = it->m_data[0].location.y;
if( it->m_type == CEntityOrder::ORDER_PATROL )
break;
x = it->m_data[0].location.x;
y = it->m_data[0].location.y;
}
destinationCollisionObject = getContainingObject( CVector2D( x, y ) );
glShadeModel( GL_FLAT );
glBegin( GL_LINE_STRIP );
glVertex3f( m_position.X, m_position.Y + 0.25f, m_position.Z );
x = m_position.X;
y = m_position.Z;
for( it = m_orderQueue.begin(); it < m_orderQueue.end(); it++ )
{
x0 = x; y0 = y;
x = it->m_data[0].location.x;
y = it->m_data[0].location.y;
rayIntersectionResults r;
CVector2D fwd( x - x0, y - y0 );
float l = fwd.length();
fwd = fwd.normalize();
CVector2D rgt = fwd.beta();
if( getRayIntersection( CVector2D( x0, y0 ), fwd, rgt, l, m_bounds->m_radius, destinationCollisionObject, &r ) )
{
glEnd();
glBegin( GL_LINES );
glColor3f( 1.0f, 0.0f, 0.0f );
glVertex3f( x0 + fwd.x * r.distance, getExactGroundLevel( x0 + fwd.x * r.distance, y0 + fwd.y * r.distance ) + 0.25f, y0 + fwd.y * r.distance );
glVertex3f( r.position.x, getExactGroundLevel( r.position.x, r.position.y ) + 0.25f, r.position.y );
glEnd();
glBegin( GL_LINE_STRIP );
glVertex3f( x0, getExactGroundLevel( x0, y0 ), y0 );
}
switch( it->m_type )
{
case CEntityOrder::ORDER_GOTO:
@ -222,11 +265,15 @@ void CEntity::render()
case CEntityOrder::ORDER_GOTO_COLLISION:
glColor3f( 1.0f, 0.5f, 0.5f ); break;
case CEntityOrder::ORDER_GOTO_NOPATHING:
case CEntityOrder::ORDER_GOTO_SMOOTHED:
glColor3f( 0.5f, 0.5f, 0.5f ); break;
case CEntityOrder::ORDER_PATROL:
glColor3f( 0.0f, 1.0f, 0.0f ); break;
default:
glColor3f( 1.0f, 1.0f, 1.0f ); break;
continue;
}
glVertex3f( x, getExactGroundLevel( x, y ) + 0.25f /* 20.0f */, y );
glVertex3f( x, getExactGroundLevel( x, y ) + 0.25f, y );
}
glEnd();
@ -237,7 +284,7 @@ void CEntity::render()
if( getCollisionObject( this ) ) glColor3f( 0.5f, 0.5f, 1.0f );
m_bounds->render( m_position.Y + 0.25f /* 20.0f */ );
m_bounds->render( getExactGroundLevel( m_position.X, m_position.Z ) + 0.25f ); //m_position.Y + 0.25f );
}

View File

@ -39,6 +39,7 @@ class HEntity
{
friend class CEntityManager;
u16 m_handle;
private:
void addRef();
void decRef();
HEntity( u16 index );

View File

@ -6,8 +6,11 @@
//
// Usage: All orders at this point use the location component of the union.
// Orders are: ORDER_GOTO_NOPATHING: Attempts to reach the given destination via a line-of-sight
// system. Do not create an order of this type directly; it is
// ORDER_GOTO_SMOOTED: system. Do not create an order of these types directly; it is
// used to return a path of line segments from the pathfinder.
// _SMOOTHED flags to the entity state-control that it's OK to
// smooth the corner between segments. _NOPATHING just does
// zero-radius turns.
// ORDER_GOTO_COLLISION: When the coldet system is trying to get us out of a collision,
// it generates these intermediate waypoints. We don't really have
// any reason to go to this specific point, so if a better way
@ -45,6 +48,7 @@ public:
enum
{
ORDER_GOTO_NOPATHING,
ORDER_GOTO_SMOOTHED,
ORDER_GOTO_COLLISION,
ORDER_GOTO,
ORDER_PATROL

View File

@ -14,13 +14,71 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, float timestep )
float len = delta.length();
if( len < 0.1f )
// Curve smoothing.
// Here there be trig.
if( current->m_type != CEntityOrder::ORDER_GOTO_SMOOTHED )
{
m_orderQueue.pop_front();
return( false );
// We can only really attempt to smooth paths the pathfinder
// has flagged for us. If the turning-radius calculations are
// applied to other types of waypoint, wierdness happens.
// Things like an entity trying to walk to a point inside
// his turning radius (which he can't do directly, so he'll
// orbit the point indefinately), or just massive deviations
// making the paths we calculate useless.
// It's also painful trying to watch two entities resolve their
// collision when they're both bound by turning constraints.
m_ahead = delta / len;
m_orientation = atan2( m_ahead.x, m_ahead.y );
}
else
{
m_targetorientation = atan2( delta.x, delta.y );
float deltatheta = m_targetorientation - m_orientation;
while( deltatheta > PI ) deltatheta -= 2 * PI;
while( deltatheta < -PI ) deltatheta += 2 * PI;
if( fabs( deltatheta ) > 0.01f )
{
float maxTurningSpeed = ( m_speed / m_turningRadius ) * timestep;
if( deltatheta > 0 )
{
m_orientation += MIN( deltatheta, maxTurningSpeed );
}
else
m_orientation += MAX( deltatheta, -maxTurningSpeed );
m_ahead.x = sin( m_orientation );
m_ahead.y = cos( m_orientation );
}
else
{
m_ahead = delta / len;
m_orientation = atan2( m_ahead.x, m_ahead.y );
}
}
m_ahead = delta / len;
if( len < 0.1f )
{
if( current->m_type == CEntityOrder::ORDER_GOTO_COLLISION )
{
// Repath.
CVector2D destination;
while( !m_orderQueue.empty() &&
( ( m_orderQueue.front().m_type == CEntityOrder::ORDER_GOTO_COLLISION )
|| ( m_orderQueue.front().m_type == CEntityOrder::ORDER_GOTO_NOPATHING )
|| ( m_orderQueue.front().m_type == CEntityOrder::ORDER_GOTO_SMOOTHED ) ) )
{
destination = m_orderQueue.front().m_data[0].location;
m_orderQueue.pop_front();
}
g_Pathfinder.requestPath( me, destination );
}
else
m_orderQueue.pop_front();
return( false );
}
if( m_bounds->m_type == CBoundingObject::BOUND_OABB )
((CBoundingBox*)m_bounds)->setOrientation( m_ahead );
@ -78,7 +136,7 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, float timestep )
avoidance.m_type = CEntityOrder::ORDER_GOTO_COLLISION;
CVector2D right;
right.x = m_ahead.y; right.y = -m_ahead.x;
CVector2D avoidancePosition = collide->m_bounds->m_pos + right * ( collide->m_bounds->m_radius * 2.5f );
CVector2D avoidancePosition = collide->m_bounds->m_pos + right * ( collide->m_bounds->m_radius + m_bounds->m_radius * 2.5f );
avoidance.m_data[0].location = avoidancePosition;
if( current->m_type == CEntityOrder::ORDER_GOTO_COLLISION )
m_orderQueue.pop_front();
@ -91,12 +149,27 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, float timestep )
{
// A circle.
// TODO: Implement this properly.
// Try turning right.
// Work out if our path goes to the left or to the right
// of this obstacle. Go that way.
// Weight a little to the right, too (helps unit-unit collisions)
CEntityOrder avoidance;
avoidance.m_type = CEntityOrder::ORDER_GOTO_COLLISION;
CVector2D right;
right.x = m_ahead.y; right.y = -m_ahead.x;
CVector2D avoidancePosition = collide->m_bounds->m_pos + right * ( collide->m_bounds->m_radius * 2.5f );
CVector2D avoidancePosition;
if( ( collide->m_bounds->m_pos - m_bounds->m_pos ).dot( right ) < 1 )
{
// Turn right.
avoidancePosition = collide->m_bounds->m_pos + right * ( collide->m_bounds->m_radius + m_bounds->m_radius * 2.5f );
}
else
{
// Turn left.
avoidancePosition = collide->m_bounds->m_pos - right * ( collide->m_bounds->m_radius + m_bounds->m_radius * 2.5f );
}
avoidance.m_data[0].location = avoidancePosition;
if( current->m_type == CEntityOrder::ORDER_GOTO_COLLISION )
m_orderQueue.pop_front();

View File

@ -1,7 +1,5 @@
#include "PathfindSparse.h"
#define NODESMOOTH_STEPS 4
sparsePathTree::sparsePathTree( const CVector2D& _from, const CVector2D& _to, HEntity _entity, CBoundingObject* _destinationCollisionObject )
{
from = _from; to = _to;
@ -13,6 +11,7 @@ sparsePathTree::sparsePathTree( const CVector2D& _from, const CVector2D& _to, HE
rightPre = NULL; rightPost = NULL;
type = SPF_OPEN_UNVISITED;
leftImpossible = false; rightImpossible = false;
nextSubtree = 0;
}
sparsePathTree::~sparsePathTree()
@ -31,46 +30,58 @@ bool sparsePathTree::slice()
CVector2D forward = to - from;
float len = forward.length();
forward /= len;
CVector2D right = CVector2D( forward.y, -forward.x );
// Hit nothing or hit destination; that's OK.
if( !getRayIntersection( from, forward, right, len, entity->m_bounds->m_radius * 1.1f, &r ) || ( r.boundingObject == destinationCollisionObject ) )
assert( len != 0.0f );
forward /= len;
CVector2D v_right = CVector2D( forward.y, -forward.x );
if( !getRayIntersection( from, forward, v_right, len, entity->m_bounds->m_radius * 1.1f, destinationCollisionObject, &r ) )
{
type = SPF_CLOSED_DIRECT;
return( true );
}
float turningRadius = ( entity->m_bounds->m_radius + r.boundingObject->m_radius ) * 1.1f;
float turningRadius = ( entity->m_bounds->m_radius + r.boundingObject->m_radius ) * 1.1f;
if( turningRadius < entity->m_turningRadius ) turningRadius = entity->m_turningRadius;
// Too close, an impossible turn
if( r.distance < turningRadius ||
r.distance > ( len - turningRadius ) )
if( r.distance < turningRadius )
{
type = SPF_IMPOSSIBLE;
return( true );
// Too close to make a proper turn; try dodging immediately a long way to the left or right.
left = from - v_right * r.boundingObject->m_radius * 2.5f;
right = from + v_right * r.boundingObject->m_radius * 2.5f;
}
else if( r.distance > ( len - turningRadius ) )
{
// Again, too close to avoid it properly. Try approaching the goal from the left or right.
left = to - v_right * r.boundingObject->m_radius * 2.5f;
right = to + v_right * r.boundingObject->m_radius * 2.5f;
}
else
{
// Dodge to the left or right of the obstacle.
// A distance of offsetDistance is sufficient to guarantee we'll make the turn.
CVector2D delta = r.position - from;
float length = delta.length();
float offsetDistance = ( turningRadius * length / sqrt( length * length - turningRadius * turningRadius ) );
CVector2D delta = r.position - from;
float length = delta.length();
float offsetDistance = ( turningRadius * length / sqrt( length * length - turningRadius * turningRadius ) );
left = r.position - v_right * offsetDistance;
right = r.position + v_right * offsetDistance;
}
favourLeft = false;
if( r.closestApproach < 0 )
favourLeft = true;
// First we path to the left...
left = r.position - right * offsetDistance;
leftPre = new sparsePathTree( from, left, entity, destinationCollisionObject );
leftPost = new sparsePathTree( left, to, entity, destinationCollisionObject );
// Then we path to the right...
right = r.position + right * offsetDistance;
rightPre = new sparsePathTree( from, right, entity, destinationCollisionObject );
rightPost = new sparsePathTree( right, to, entity, destinationCollisionObject );
@ -87,24 +98,18 @@ bool sparsePathTree::slice()
else /* type == SPF_OPEN_PROCESSING */
{
bool done = false;
if( !leftImpossible )
while( !done )
{
if( !done && ( leftPre->type & SPF_OPEN ) )
done |= leftPre->slice();
if( !done && ( leftPost->type & SPF_OPEN ) )
done |= leftPost->slice();
if( ( leftPre->type == SPF_IMPOSSIBLE ) || ( leftPost->type == SPF_IMPOSSIBLE ) )
leftImpossible = true;
}
if( !rightImpossible && !done )
{
if( !done && ( rightPre->type & SPF_OPEN ) )
done |= rightPre->slice();
if( !done && ( rightPost->type & SPF_OPEN ) )
done |= rightPost->slice();
if( ( rightPre->type == SPF_IMPOSSIBLE ) || ( rightPost->type == SPF_IMPOSSIBLE ) )
rightImpossible = true;
if( subtrees[nextSubtree]->type & SPF_OPEN )
if( subtrees[nextSubtree]->slice() )
done = true;
nextSubtree++;
nextSubtree %= 4;
}
if( ( leftPre->type == SPF_IMPOSSIBLE ) || ( leftPost->type == SPF_IMPOSSIBLE ) )
leftImpossible = true;
if( ( rightPre->type == SPF_IMPOSSIBLE ) || ( rightPost->type == SPF_IMPOSSIBLE ) )
rightImpossible = true;
if( leftImpossible && rightImpossible )
{
type = SPF_IMPOSSIBLE;
@ -149,14 +154,11 @@ void sparsePathTree::pushResults( std::vector<CVector2D>& nodelist )
void nodeSmooth( HEntity entity, std::vector<CVector2D>& nodelist )
{
// All your CPU are belong to us.
// But Jan wanted it ;)
std::vector<CVector2D>::iterator it;
CVector2D next = nodelist.front();
CEntityOrder node;
node.m_type = CEntityOrder::ORDER_GOTO_NOPATHING;
node.m_type = CEntityOrder::ORDER_GOTO_SMOOTHED;
node.m_data[0].location = next;
entity->m_orderQueue.push_front( node );
@ -173,30 +175,30 @@ void nodeSmooth( HEntity entity, std::vector<CVector2D>& nodelist )
CVector2D ubar = u.beta();
CVector2D vbar = v.beta();
float alpha = entity->m_turningRadius * ( ubar - vbar ).length() / ( u + v ).length();
u *= alpha;
v *= alpha;
for( int t = NODESMOOTH_STEPS; t >= 0; t-- )
{
float lambda = t / (float)NODESMOOTH_STEPS;
CVector2D arcpoint = current + v * lambda * lambda - u * ( 1 - lambda ) * ( 1 - lambda );
node.m_data[0].location = arcpoint;
entity->m_orderQueue.push_front( node );
}
node.m_data[0].location = current - u * alpha;
entity->m_orderQueue.push_front( node );
next = current;
}
// If we try to apply turning constraints to getting onto this path, there's a reasonable
// risk the entity will deviate so far from the first path segment that the path becomes
// unwalkable for it.
entity->m_orderQueue.front().m_type = CEntityOrder::ORDER_GOTO_NOPATHING;
}
void pathSparse( HEntity entity, CVector2D destination )
{
std::vector<CVector2D> pathnodes;
sparsePathTree sparseEngine( CVector2D( entity->m_position.X, entity->m_position.Z ), destination, entity, getContainingObject( destination ) );
CVector2D source( entity->m_position.X, entity->m_position.Z );
sparsePathTree sparseEngine( source, destination, entity, getContainingObject( destination ) );
while( sparseEngine.type & sparsePathTree::SPF_OPEN ) sparseEngine.slice();
assert( sparseEngine.type & sparsePathTree::SPF_SOLVED ); // Shouldn't be any impossible cases yet.
if( sparseEngine.type & sparsePathTree::SPF_SOLVED )
{
sparseEngine.pushResults( pathnodes );
pathnodes.push_back( source );
nodeSmooth( entity, pathnodes );
}
else

View File

@ -47,10 +47,18 @@ struct sparsePathTree
bool rightImpossible;
CVector2D right;
bool favourLeft;
sparsePathTree* leftPre;
sparsePathTree* leftPost;
sparsePathTree* rightPre;
sparsePathTree* rightPost;
union
{
struct
{
sparsePathTree* leftPre;
sparsePathTree* leftPost;
sparsePathTree* rightPre;
sparsePathTree* rightPost;
};
sparsePathTree* subtrees[4];
};
unsigned short nextSubtree;
sparsePathTree( const CVector2D& from, const CVector2D& to, HEntity entity, CBoundingObject* destinationCollisionObject );
~sparsePathTree();
bool slice();