1
0
forked from 0ad/0ad
0ad/source/simulation/BoundingObjects.cpp

280 lines
6.5 KiB
C++

#include "precompiled.h"
#include "BoundingObjects.h"
#include "lib/ogl.h"
#include "maths/MathUtil.h"
bool CBoundingObject::intersects( CBoundingObject* obj )
{
CVector2D delta = m_pos - obj->m_pos;
if( !delta.within( m_radius + obj->m_radius ) )
return( false );
if( obj->m_type > m_type ) // More complex types get the burden of processing.
{
return( obj->_intersects( this, delta ) );
}
else
return( _intersects( obj, delta ) );
}
bool CBoundingObject::contains( const CVector2D& point )
{
CVector2D delta = m_pos - point;
if( !delta.within( m_radius ) )
return( false );
return( _contains( point, delta ) );
}
void CBoundingObject::setPosition( float x, float y )
{
m_pos.x = x; m_pos.y = y;
}
void CBoundingObject::setHeight( float height )
{
m_height = height;
}
CBoundingCircle::CBoundingCircle( float x, float y, float radius, float height )
{
m_type = BOUND_CIRCLE;
setPosition( x, y );
setRadius( radius );
setHeight( height );
}
CBoundingCircle::CBoundingCircle( float x, float y, CBoundingCircle* copy )
{
m_type = BOUND_CIRCLE;
setPosition( x, y );
setRadius( copy->m_radius );
setHeight( copy->m_height );
}
void CBoundingCircle::setRadius( float radius )
{
m_radius = radius;
}
bool CBoundingCircle::_intersects( CBoundingObject* obj, const CVector2D& UNUSED(delta) )
{
debug_assert( obj->m_type == BOUND_CIRCLE );
// Easy enough. The only time this gets called is a circle-circle collision,
// but we know the circles collide (they passed the trivial rejection step)
return( true );
}
bool CBoundingCircle::_contains( const CVector2D& UNUSED(point), const CVector2D& UNUSED(delta) )
{
return( true );
}
void CBoundingCircle::render( float height )
{
glBegin( GL_LINE_LOOP );
for( int i = 0; i < 10; i++ )
{
float ang = i * 2 * PI / 10.0f;
float x = m_pos.x + m_radius * sin( ang );
float y = m_pos.y + m_radius * cos( ang );
glVertex3f( x, height, y );
}
glEnd();
}
CBoundingBox::CBoundingBox( float x, float y, const CVector2D& u, float width, float depth, float height )
{
m_type = BOUND_OABB;
setPosition( x, y );
setDimensions( width, depth );
setHeight( height );
setOrientation( u );
}
CBoundingBox::CBoundingBox( float x, float y, const CVector2D& u, CBoundingBox* copy )
{
m_type = BOUND_OABB;
setPosition( x, y );
setDimensions( copy->getWidth(), copy->getDepth() );
setHeight( copy->m_height );
setOrientation( u );
}
CBoundingBox::CBoundingBox( float x, float y, float orientation, float width, float depth, float height )
{
m_type = BOUND_OABB;
setPosition( x, y );
setDimensions( width, depth );
setHeight( height );
setOrientation( orientation );
}
CBoundingBox::CBoundingBox( float x, float y, float orientation, CBoundingBox* copy )
{
m_type = BOUND_OABB;
setPosition( x, y );
setDimensions( copy->getWidth(), copy->getDepth() );
setHeight( copy->m_height );
setOrientation( orientation );
}
void CBoundingBox::setDimensions( float width, float depth )
{
m_w = width / 2.0f;
m_d = depth / 2.0f;
m_radius = sqrt( ( m_w * m_w ) + ( m_d * m_d ) );
}
void CBoundingBox::setOrientation( float orientation )
{
m_u.x = sin( orientation );
m_u.y = cos( orientation );
m_v.x = m_u.y;
m_v.y = -m_u.x;
}
void CBoundingBox::setOrientation( const CVector2D& u )
{
m_u = u;
m_v.x = m_u.y;
m_v.y = -m_u.x;
}
bool CBoundingBox::_intersects( CBoundingObject* obj, const CVector2D& delta )
{
if( obj->m_type == BOUND_CIRCLE )
{
// Imperfect but quick...
CBoundingCircle* c = (CBoundingCircle*)obj;
float deltad = fabs( delta.dot( m_u ) );
if( deltad > ( m_d + c->m_radius ) ) return( false );
float deltaw = fabs( delta.dot( m_v ) );
if( deltaw > ( m_w + c->m_radius ) ) return( false );
return( true );
}
else
{
// Another OABB:
// Seperable axis theorem. (Optimizations: Algorithmic done, low-level stuff not.)
// Debugging this can often be quite entertaining.
CBoundingBox* b = (CBoundingBox*)obj;
// SAT in a nutshell: If two boxes are disjoint, there's an axis where their projections don't overlap
// That axis (in 2D) will run parallel to a side of one of the boxes
// This code computes projections of boxes onto each axis in turn - hopefully quickly
// then does simple max/min checks to determine overlap
// uv, vu, uu, vv are dot-products of our u- and v- axes of each box.
// prj1, prj2 are two coordinates locating the projections of two corners of a box onto an axis.
// dm is the distance between the boxes, projected onto the axis.
// Lots of nice symmetries used to help speed things up
// Note that a trivial-rejection test has already taken place by this point.
float uv, vu, uu, vv, prj1, prj2, dm;
uv = m_u.dot( b->m_v );
vu = m_v.dot( b->m_u );
uu = m_u.dot( b->m_u );
vv = m_v.dot( b->m_v );
// Project box 2 onto v-axis of box 1
prj1 = fabs( vu * b->m_d + vv * b->m_w );
prj2 = fabs( vu * b->m_d - vv * b->m_w );
dm = delta.dot( m_v );
if( prj1 > prj2 )
{
if( ( dm - prj1 ) > m_w ) return( false );
}
else
if( ( dm - prj2 ) > m_w ) return( false );
// Project box 2 onto u-axis of box 1
prj1 = fabs( uu * b->m_d + uv * b->m_w );
prj2 = fabs( uu * b->m_d - uv * b->m_w );
dm = delta.dot( m_u );
if( prj1 > prj2 )
{
if( ( dm - prj1 ) > m_d ) return( false );
}
else
if( ( dm - prj2 ) > m_d ) return( false );
// Project box 1 onto v-axis of box 2
prj1 = fabs( uv * m_d + vv * m_w );
prj2 = fabs( uv * m_d - vv * m_w );
dm = delta.dot( b->m_v );
if( prj1 > prj2 )
{
if( ( dm - prj1 ) > b->m_w ) return( false );
}
else
if( ( dm - prj2 ) > b->m_w ) return( false );
// Project box 1 onto u-axis of box 2
prj1 = fabs( uu * m_d + vu * m_w );
prj2 = fabs( uu * m_d - vu * m_w );
dm = delta.dot( b->m_u );
if( prj1 > prj2 )
{
if( ( dm - prj1 ) > b->m_d ) return( false );
}
else
if( ( dm - prj2 ) > b->m_d ) return( false );
return( true );
}
}
bool CBoundingBox::_contains( const CVector2D& UNUSED(point), const CVector2D& delta )
{
float deltad = fabs( delta.dot( m_u ) );
if( deltad > m_d ) return( false );
float deltaw = fabs( delta.dot( m_v ) );
if( deltaw > m_w ) return( false );
return( true );
}
void CBoundingBox::render( float height )
{
glBegin( GL_LINE_LOOP );
CVector2D p;
p = m_pos + m_u * m_d + m_v * m_w;
glVertex3f( p.x, height, p.y );
p = m_pos + m_u * m_d - m_v * m_w;
glVertex3f( p.x, height, p.y );
p = m_pos - m_u * m_d - m_v * m_w;
glVertex3f( p.x, height, p.y );
p = m_pos - m_u * m_d + m_v * m_w;
glVertex3f( p.x, height, p.y );
glEnd();
}