1024 lines
25 KiB
C++
1024 lines
25 KiB
C++
//=============================================================================
|
|
// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
|
|
//
|
|
// File: geometry.cpp
|
|
//
|
|
// Description: Some linear algebra/geometry stuff mostly used in traffic
|
|
// Also contains some useful structures.
|
|
//
|
|
// History: 09/09/2002 + Created -- Dusit Eakkachaichanvet
|
|
//
|
|
//=============================================================================
|
|
|
|
|
|
|
|
#include <roads/geometry.h>
|
|
#include <raddebug.hpp>
|
|
|
|
/*
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// OLD OLD OLD STUFF
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool fequals(float a, float b)
|
|
{
|
|
return (rmt::Fabs(a-b)<=MYEPSILON)? true: false;
|
|
}
|
|
bool fequals(float a, float b, float epsilon)
|
|
{
|
|
return (rmt::Fabs(a-b)<=epsilon)? true: false;
|
|
}
|
|
bool isVerticalLine( Line line )
|
|
{
|
|
if( fequals(line.x1,line.x2) )
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
Line getLine( float x1, float y1, float x2, float y2, bool isInfinite )
|
|
{
|
|
Line line;
|
|
line.x1 = x1;
|
|
line.y1 = y1;
|
|
line.x2 = x2;
|
|
line.y2 = y2;
|
|
line.isVertical = isVerticalLine( line );
|
|
if( !line.isVertical )
|
|
{
|
|
line.slope = (line.y2 - line.y1)/(line.x2 - line.x1);
|
|
line.b = line.y2 - line.slope * line.x2;
|
|
}
|
|
line.isInfinite = isInfinite;
|
|
line.isFinishLine = false;
|
|
return line;
|
|
}
|
|
bool isPointOnLine( Line line, Point p )
|
|
{
|
|
// test first if point lies in line equation
|
|
if( !line.isVertical )
|
|
{
|
|
if( fequals(p.y, line.slope * p.x + line.b) )
|
|
{
|
|
// Ok, so we've verified that p lies on the line.
|
|
// now if the line is infinite, we're done
|
|
if( line.isInfinite )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// else test if point lies on line itself we always make sure
|
|
// x1 < x2 and y1 < y2, swapping values as needed.
|
|
if( line.x2 < line.x1 )
|
|
{
|
|
float temp = line.x2;
|
|
line.x2 = line.x1;
|
|
line.x1 = temp;
|
|
}
|
|
if( line.y2 < line.y1 )
|
|
{
|
|
float temp = line.y2;
|
|
line.y2 = line.y1;
|
|
line.y1 = temp;
|
|
}
|
|
|
|
// now check if p.x lies between acceptable values of x
|
|
// and if p.y lies between acceptable values of y
|
|
if( (line.x1 - MYEPSILON < p.x && p.x < line.x2 + MYEPSILON ) &&
|
|
(line.y1 - MYEPSILON < p.y && p.y < line.y2 + MYEPSILON ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ // if vertical line
|
|
if( fequals(p.x,line.x1) )
|
|
{
|
|
if( line.isInfinite )
|
|
{
|
|
return true;
|
|
}
|
|
if( line.y2 < line.y1 )
|
|
{
|
|
float temp = line.y2;
|
|
line.y2 = line.y1;
|
|
line.y1 = temp;
|
|
}
|
|
|
|
if( line.y1 - MYEPSILON < p.y && p.y < line.y2 + MYEPSILON )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//==============================================================================
|
|
// Helper function: IntersectLines2D
|
|
//===============================================================================
|
|
// Description: Intersects two co-planar lines
|
|
//
|
|
// Parameters: (rmt::Vector) (rmt::Vector) (rmt::Vector) (rmt::Vector) (rmt::Vector&)
|
|
//
|
|
// Return: bool
|
|
//
|
|
//==============================================================================
|
|
bool IntersectLines2D( rmt::Vector p1,
|
|
rmt::Vector dir1,
|
|
rmt::Vector p2,
|
|
rmt::Vector dir2,
|
|
rmt::Vector& p )
|
|
{
|
|
// These will temporarily contain our intersection point coords
|
|
// Remember that Point and Line work on x-y plane, not x-z plane
|
|
// so let y = z
|
|
//
|
|
Point pTemp;
|
|
|
|
rmt::Vector q1 = p1 + dir1;
|
|
rmt::Vector q2 = p2 + dir2;
|
|
|
|
Line line1 = getLine( p1.x, p1.z, q1.x, q1.z, true );
|
|
Line line2 = getLine( p2.x, p2.z, q2.x, q2.z, true );
|
|
|
|
|
|
// treat vertical cases separately
|
|
if( line1.isVertical && line2.isVertical )
|
|
{
|
|
return false;
|
|
}
|
|
else if( line1.isVertical && !line2.isVertical )
|
|
{
|
|
// line1 is vertical and line2 is not, so pluck line1's
|
|
// x-value into line2's equation to find y coord of
|
|
// the potential intersection point.
|
|
pTemp.x = line1.x1;
|
|
pTemp.y = line2.slope * pTemp.x + line2.b;
|
|
if( isPointOnLine(line1,pTemp) && isPointOnLine(line2,pTemp) )
|
|
{
|
|
p.x = pTemp.x;
|
|
p.y = p1.y;
|
|
p.z = pTemp.y;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
//else if( !isVerticalLine(line1) && isVerticalLine(line2) )
|
|
else if( !line1.isVertical && line2.isVertical )
|
|
{
|
|
// same as case above, but switch line1, line2
|
|
pTemp.x = line2.x1;
|
|
pTemp.y = line1.slope * pTemp.x + line1.b;
|
|
if( isPointOnLine(line1,pTemp) && isPointOnLine(line2,pTemp) )
|
|
{
|
|
p.x = pTemp.x;
|
|
p.y = p1.y;
|
|
p.z = pTemp.y;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{ //both non vertical
|
|
|
|
if( line1.slope == line2.slope )
|
|
return false;
|
|
|
|
// find intersection point
|
|
pTemp.x = (line2.b-line1.b)/(line1.slope-line2.slope);
|
|
pTemp.y = line1.slope* pTemp.x + line1.b;
|
|
|
|
// make sure this point lies within the bounds of both lines
|
|
if( isPointOnLine(line1,pTemp) && isPointOnLine(line2,pTemp) )
|
|
{
|
|
p.x = pTemp.x;
|
|
p.y = p1.y;
|
|
p.z = pTemp.y;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return false; // just in case
|
|
}
|
|
*/
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// CUBIC BEZIER SHEEYATSU
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool CubicBezier::sIsInitialized = false;
|
|
float CubicBezier::B0[CubicBezier::MAX_CURVE_POINTS] = {0.0f};
|
|
float CubicBezier::B1[CubicBezier::MAX_CURVE_POINTS] = {0.0f};
|
|
float CubicBezier::B2[CubicBezier::MAX_CURVE_POINTS] = {0.0f};
|
|
float CubicBezier::B3[CubicBezier::MAX_CURVE_POINTS] = {0.0f};
|
|
|
|
|
|
void CubicBezier::InitOnceLUTs()
|
|
{
|
|
if( sIsInitialized )
|
|
{
|
|
return;
|
|
}
|
|
|
|
float bias = 1.0f / (float)(MAX_CURVE_POINTS - 1);
|
|
|
|
int i = MAX_CURVE_POINTS - 1;
|
|
float t = 0.0f, t2, t3;
|
|
for( i = MAX_CURVE_POINTS-1; i>=0; i-- )
|
|
{
|
|
t2 = t*t;
|
|
t3 = t2*t;
|
|
|
|
// Fill the LUTs
|
|
B0[i] = t3;
|
|
B1[i] = 3.0f * t2 * (1.0f-t);
|
|
B2[i] = 3.0f * t * (1.0f-t)*(1.0f-t);
|
|
B3[i] = (1.0f-t)*(1.0f-t)*(1.0f-t);
|
|
|
|
t += bias;
|
|
}
|
|
|
|
rAssert( i == -1);
|
|
|
|
sIsInitialized = true;
|
|
}
|
|
|
|
|
|
|
|
CubicBezier::CubicBezier()
|
|
{
|
|
if( !CubicBezier::sIsInitialized )
|
|
{
|
|
CubicBezier::InitOnceLUTs();
|
|
}
|
|
|
|
mCurveIsCreated = false;
|
|
mCurveIsCreated2D = false;
|
|
mNumControlPointsAdded = 0;
|
|
|
|
rmt::Vector dummy( 0.0f, 0.0f, 0.0f );
|
|
int i;
|
|
for( i=0; i<CubicBezier::MAX_CONTROL_POINTS; i++ )
|
|
{
|
|
AddControlPoint( dummy );
|
|
}
|
|
}
|
|
|
|
CubicBezier::~CubicBezier()
|
|
{
|
|
}
|
|
|
|
void CubicBezier::GetCubicBezierCurve(rmt::Vector*& pts, int& nPts)
|
|
{
|
|
if( !mCurveIsCreated )
|
|
{
|
|
CreateCubicBezierCurve();
|
|
}
|
|
pts = mCurve;
|
|
nPts = MAX_CURVE_POINTS;
|
|
}
|
|
|
|
void CubicBezier::GetCubicBezierCurve2D(rmt::Vector*& pts, int& nPts)
|
|
{
|
|
if( !mCurveIsCreated2D )
|
|
{
|
|
CreateCubicBezierCurve2D();
|
|
}
|
|
pts = mCurve2D;
|
|
nPts = MAX_CURVE_POINTS;
|
|
}
|
|
|
|
void CubicBezier::SetControlPoint( const rmt::Vector &cp, const int index )
|
|
{
|
|
rAssert( 0 <= index && index < mNumControlPointsAdded );
|
|
|
|
mCurveIsCreated = false;
|
|
mCurveIsCreated2D = false;
|
|
mControlPoints[index] = cp;
|
|
}
|
|
|
|
void CubicBezier::AddControlPoint( const rmt::Vector &cp )
|
|
{
|
|
rAssert( 0 <= mNumControlPointsAdded && mNumControlPointsAdded < MAX_CONTROL_POINTS );
|
|
|
|
mCurveIsCreated = false;
|
|
mCurveIsCreated2D = false;
|
|
mControlPoints[mNumControlPointsAdded] = cp;
|
|
mNumControlPointsAdded++;
|
|
}
|
|
|
|
|
|
void CubicBezier::CreateCubicBezierCurve()
|
|
{
|
|
rAssert( mNumControlPointsAdded == MAX_CONTROL_POINTS );
|
|
|
|
if( mCurveIsCreated )
|
|
{
|
|
return;
|
|
}
|
|
|
|
int i;
|
|
for( i=0; i<MAX_CURVE_POINTS; i++ )
|
|
{
|
|
mCurve[i] = mControlPoints[0] * B0[i] +
|
|
mControlPoints[1] * B1[i] +
|
|
mControlPoints[2] * B2[i] +
|
|
mControlPoints[3] * B3[i];
|
|
}
|
|
mCurveIsCreated = true;
|
|
|
|
}
|
|
|
|
void CubicBezier::CreateCubicBezierCurve2D()
|
|
{
|
|
rAssert( mNumControlPointsAdded == MAX_CONTROL_POINTS );
|
|
|
|
if( mCurveIsCreated2D )
|
|
{
|
|
return;
|
|
}
|
|
|
|
float ep = 0.1f;
|
|
|
|
// **** ASSERT INFORMATION ***
|
|
// If you encounter this assert, it means that the road segments in a nearby
|
|
// intersection don't have same values of y where they hit the intersection
|
|
// (intersection isn't horizontal). This is due to BAD ROAD DATA ON WORLD BUILDER SIDE.
|
|
//
|
|
// Because we're using CubicBezier in 2D now, the intersection must be FLAT!
|
|
// Or cars will "hop" when they make the turn.
|
|
//
|
|
// Please notify Sheik to check the road data around nearby intersections.
|
|
rAssert( rmt::Epsilon( mControlPoints[0].y, mControlPoints[1].y, ep ) &&
|
|
rmt::Epsilon( mControlPoints[0].y, mControlPoints[2].y, ep ) &&
|
|
rmt::Epsilon( mControlPoints[0].y, mControlPoints[3].y, ep ) );
|
|
|
|
int i;
|
|
for( i=0; i<MAX_CURVE_POINTS; i++ )
|
|
{
|
|
mCurve2D[i].x = mControlPoints[0].x * B0[i] +
|
|
mControlPoints[1].x * B1[i] +
|
|
mControlPoints[2].x * B2[i] +
|
|
mControlPoints[3].x * B3[i];
|
|
|
|
mCurve2D[i].y = mControlPoints[0].y;
|
|
|
|
mCurve2D[i].z = mControlPoints[0].z * B0[i] +
|
|
mControlPoints[1].z * B1[i] +
|
|
mControlPoints[2].z * B2[i] +
|
|
mControlPoints[3].z * B3[i];
|
|
}
|
|
mCurveIsCreated2D = true;
|
|
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// DListArray Class Definition
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
DListArray::DListArray()
|
|
{
|
|
this->Clear();
|
|
}
|
|
|
|
|
|
void DListArray::Clear()
|
|
{
|
|
int i=0;
|
|
for( i ; i<(MAX_ELEMS-1) ; ++i )
|
|
{
|
|
mElems[i].data = NULL;
|
|
mElems[i].next = i+1;
|
|
mElems[i].prev = -1;
|
|
}
|
|
mElems[i].data = NULL;
|
|
mElems[i].next = -1;
|
|
mElems[i].prev = -1;
|
|
|
|
mHead = -1;
|
|
mTail = -1;
|
|
mFree = 0;
|
|
|
|
mnElems = 0;
|
|
}
|
|
|
|
// returns the index value of the newly added element
|
|
// or -1 on error
|
|
int DListArray::AddLast( void* data )
|
|
{
|
|
assert( data != NULL );
|
|
|
|
if( mFree == -1 )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
mElems[mFree].data = data;
|
|
mElems[mFree].prev = mTail;
|
|
|
|
if( mnElems > 0 )
|
|
{
|
|
mElems[mTail].next = mFree;
|
|
}
|
|
else
|
|
{
|
|
mHead = mFree;
|
|
}
|
|
|
|
mTail = mFree;
|
|
mFree = mElems[mFree].next;
|
|
mElems[mTail].next = -1;
|
|
|
|
mnElems++;
|
|
return mTail;
|
|
}
|
|
|
|
// returns the index value of the newly added element
|
|
// or -1 on error
|
|
int DListArray::AddFirst( void* data )
|
|
{
|
|
assert( data != NULL );
|
|
|
|
if( mFree == -1 )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
int newIndex = mFree;
|
|
mFree = mElems[mFree].next;
|
|
|
|
mElems[newIndex].data = data;
|
|
mElems[newIndex].next = mHead;
|
|
|
|
if( mnElems > 0 )
|
|
{
|
|
mElems[mHead].prev = newIndex;
|
|
}
|
|
else
|
|
{
|
|
mTail = newIndex;
|
|
}
|
|
|
|
mHead = newIndex;
|
|
|
|
mnElems++;
|
|
return mHead;
|
|
}
|
|
|
|
|
|
// returns index value of the newly inserted element
|
|
// or -1 on error
|
|
int DListArray::InsertAfter( void* data, int i )
|
|
{
|
|
assert( mnElems >= 1 );
|
|
assert( data != NULL );
|
|
assert( 0 <= i && i < MAX_ELEMS );
|
|
assert( mElems[i].data != NULL );
|
|
|
|
if( mFree == -1 )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
int newIndex = mFree;
|
|
mFree = mElems[mFree].next;
|
|
|
|
mElems[newIndex].data = data;
|
|
mElems[newIndex].prev = i;
|
|
mElems[newIndex].next = mElems[i].next;
|
|
|
|
if( mElems[i].next != -1 )
|
|
{
|
|
mElems[ mElems[i].next ].prev = newIndex;
|
|
}
|
|
else
|
|
{
|
|
mTail = newIndex;
|
|
}
|
|
|
|
mElems[i].next = newIndex;
|
|
|
|
mnElems++;
|
|
return newIndex;
|
|
}
|
|
|
|
//
|
|
bool DListArray::Remove( void* data )
|
|
{
|
|
assert( data != NULL );
|
|
|
|
int i = 0;
|
|
bool res = false;
|
|
for( i ; i<mnElems ; i++ )
|
|
{
|
|
if( mElems[i].data == data )
|
|
{
|
|
res = true;
|
|
break;
|
|
}
|
|
}
|
|
if( res )
|
|
{
|
|
res = Remove(i);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
bool DListArray::Remove( int i )
|
|
{
|
|
assert( 0 <= i && i < MAX_ELEMS );
|
|
assert( mElems[i].data != NULL );
|
|
|
|
if( mnElems <= 0 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( mElems[i].next != -1 )
|
|
{
|
|
mElems[ mElems[i].next ].prev = mElems[i].prev;
|
|
}
|
|
else
|
|
{
|
|
mTail = mElems[i].prev;
|
|
}
|
|
|
|
if( mElems[i].prev != -1 )
|
|
{
|
|
mElems[ mElems[i].prev ].next = mElems[i].next;
|
|
}
|
|
else
|
|
{
|
|
mHead = mElems[i].next;
|
|
}
|
|
|
|
mElems[i].next = mFree;
|
|
mElems[i].prev = -1;
|
|
mElems[i].data = NULL;
|
|
|
|
mFree = i;
|
|
|
|
mnElems--;
|
|
return true;
|
|
}
|
|
|
|
int DListArray::Find( void* data )
|
|
{
|
|
rAssert( data != NULL );
|
|
|
|
int i = 0;
|
|
for( i ; i<mnElems ; i++ )
|
|
{
|
|
if( mElems[i].data == data )
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// MISC
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
// for Ray p1 to p2 and sphere 2
|
|
// return number of intersection points and the intersection points
|
|
// q1 and q2
|
|
int IntersectLineSphere( const rmt::Vector& p1,
|
|
const rmt::Vector& p2,
|
|
const rmt::Sphere& s,
|
|
rmt::Vector* intPts )
|
|
{
|
|
|
|
float X1 = (float)p1.x;
|
|
float X2 = (float)p2.x;
|
|
float X3 = (float)s.centre.x;
|
|
float Y1 = (float)p1.y;
|
|
float Y2 = (float)p2.y;
|
|
float Y3 = (float)s.centre.y;
|
|
float Z1 = (float)p1.z;
|
|
float Z2 = (float)p2.z;
|
|
float Z3 = (float)s.centre.z;
|
|
float Sr = (float)s.radius;
|
|
|
|
float A, B, C;
|
|
A = (X2 - X1)*(X2 - X1) + (Y2 - Y1)*(Y2 - Y1) + (Z2 - Z1)*(Z2 - Z1);
|
|
B = (X2 - X1)*(X1 - X3) + (Y2 - Y1)*(Y1 - Y3) + (Z2 - Z1)*(Z1 - Z3);
|
|
C = (X1 - X3)*(X1 - X3) + (Y1 - Y3)*(Y1 - Y3) + (Z1 - Z3)*(Z1 - Z3) - Sr*Sr;
|
|
//B = 2 * ((X2 - X1)*(X1 - X3) + (Y2 - Y1)*(Y1 - Y3) + (Z2 - Z1)*(Z1 - Z3));
|
|
//C = X3*X3 + Y3*Y3 + Z3*Z3 + X1*X1 + Y1*Y1 + Z1*Z1 - 2*(X3*X1 + Y3*Y1 + Z3*Z1) - Sr*Sr;
|
|
|
|
float discriminant = B*B - A*C;
|
|
//float discriminant = B*B - 4*A*C;
|
|
float t[2];
|
|
|
|
|
|
if( discriminant < 0.0f )
|
|
{
|
|
return 0;
|
|
}
|
|
else if( rmt::Epsilon(discriminant, 0.0f, 0.001f) )
|
|
{
|
|
//t[0] = (-1*B)/2*A;
|
|
t[0] = (-1*B)/A;
|
|
|
|
if( 0.0f <= t[0] && t[0] <= 1.0f )
|
|
{
|
|
intPts[0].Set(
|
|
(float)(X1+t[0]*(X2-X1)),
|
|
(float)(Y1+t[0]*(Y2-Y1)),
|
|
(float)(Z1+t[0]*(Z2-Z1)));
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
float sqrtDiscriminant = rmt::Sqrt(discriminant); // *** SQUARE ROOT! ***
|
|
// t[0] = (-1*B - sqrtDiscriminant) / 2*A;
|
|
// t[1] = (-1*B + sqrtDiscriminant) / 2*A;
|
|
t[0] = (-1*B - sqrtDiscriminant) / A;
|
|
t[1] = (-1*B + sqrtDiscriminant) / A;
|
|
|
|
rmt::Vector testVec;
|
|
int i=0, j=0;
|
|
for( i; i<2; i++)
|
|
{
|
|
if( 0.0f <= t[i] && t[i] <= 1.0f )
|
|
{
|
|
intPts[j].Set(
|
|
(float)(X1+t[i]*(X2-X1)),
|
|
(float)(Y1+t[i]*(Y2-Y1)),
|
|
(float)(Z1+t[i]*(Z2-Z1)));
|
|
j++;
|
|
}
|
|
}
|
|
return j;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
bool TestIntersectLineSphere( const rmt::Vector& lOrig,
|
|
const rmt::Vector& lDir,
|
|
const rmt::Sphere& s )
|
|
{
|
|
rmt::Vector closestPtOnLine;
|
|
FindClosestPointOnLine( lOrig, lOrig+lDir, s.centre, closestPtOnLine );
|
|
|
|
float distSqr = (s.centre - closestPtOnLine).MagnitudeSqr();
|
|
bool res = distSqr <= (s.radius * s.radius);
|
|
return res;
|
|
}
|
|
|
|
|
|
|
|
// Test using (normalized) myHeading DOT vectorFromMyHeadingToTarget
|
|
bool WillCollide( const rmt::Vector& myPos,
|
|
const rmt::Vector& myHeading, // Must be normalized
|
|
const rmt::Vector& mySide, // Must be normalized
|
|
float myRadius,
|
|
float myLookAheadDist,
|
|
const rmt::Vector& targetPos,
|
|
bool& targetOnMyRightSide )
|
|
{
|
|
rmt::Vector toTarget = targetPos - myPos;
|
|
float myLookAheadDistSqr = myLookAheadDist * myLookAheadDist;
|
|
|
|
// if target lies within my look-ahead distance
|
|
if( toTarget.MagnitudeSqr() < myLookAheadDistSqr )
|
|
{
|
|
float frontOrBehindTest = myHeading.Dot( toTarget );
|
|
if( frontOrBehindTest > 0.0f )
|
|
{
|
|
// target is in front, which is a concern...
|
|
float lateralDist = mySide.Dot( toTarget );
|
|
if( lateralDist > 0.0f )
|
|
{
|
|
targetOnMyRightSide = true;
|
|
}
|
|
else
|
|
{
|
|
targetOnMyRightSide = false;
|
|
}
|
|
lateralDist = rmt::Fabs( lateralDist );
|
|
|
|
// if target is in our path
|
|
if( lateralDist <= myRadius )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
rmt::Vector UpdateVUP( const rmt::Vector& position, const rmt::Vector& target )
|
|
{
|
|
const float epsilon = 0.01f;
|
|
//Set the vUP by projecting the heading into the ZX plane and creating a
|
|
//crossproduct of a right angle to the projected heading along the X axis
|
|
//and the heading.
|
|
rmt::Vector X, Y, Z;
|
|
Z.Sub(target, position);
|
|
X.Set(Z.z, 0, -Z.x);
|
|
|
|
if ( rmt::Epsilon( X.x, 0, epsilon ) &&
|
|
rmt::Epsilon( X.y, 0, epsilon ) &&
|
|
rmt::Epsilon( X.z, 0, epsilon ) )
|
|
{
|
|
//Then the camera is looking straight down.
|
|
Y.Set( 0, 0, 1.0f ); //Up along the Z...
|
|
}
|
|
else
|
|
{
|
|
Y.CrossProduct(Z, X);
|
|
Y.Normalize(); // *** SQUARE ROOT! ***
|
|
}
|
|
|
|
return Y;
|
|
}
|
|
|
|
// Given points P1 and P2, and two points that define a line, A and B
|
|
// P1 and P2 are on the same side of the line, if the Normals for BAxP1A
|
|
// and BAxP2A are pointing on the same side of the plane (i.e.
|
|
// N1-dot-N2 >= 0)
|
|
|
|
bool PointsOnSameSideOfLine( const rmt::Vector& P1,
|
|
const rmt::Vector& P2,
|
|
const rmt::Vector& A,
|
|
const rmt::Vector& B )
|
|
{
|
|
rmt::Vector BA, P1A, P2A, crossP1, crossP2;
|
|
|
|
BA.Sub( B, A );
|
|
P1A.Sub( P1, A );
|
|
P2A.Sub( P2, A );
|
|
|
|
crossP1.CrossProduct( BA, P1A );
|
|
crossP2.CrossProduct( BA, P2A );
|
|
if( crossP1.Dot(crossP2) >= 0 )
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
// Given triangle with vertices v1, v2, v3 and a point p
|
|
// p is inside triangle if it is on the same side of line v1v2 as v3
|
|
// and on the same side of line v2v3 as v1,
|
|
// and on the same side of line v1v3 as v2
|
|
//
|
|
// NOTE: Because we use Which-Side-of-Line solution, the point
|
|
// that is "within" a triangle is not necessarily on the
|
|
// same plane as the triangle (it could still be above or
|
|
// below the actual triangle, as long as it lies within the infinite
|
|
// planes formed by the 3 sides of the triangle.
|
|
//
|
|
bool PointLiesInTriangle ( const rmt::Vector& p,
|
|
const rmt::Vector& v1,
|
|
const rmt::Vector& v2,
|
|
const rmt::Vector& v3 )
|
|
{
|
|
if( PointsOnSameSideOfLine( p, v1, v2, v3 ) &&
|
|
PointsOnSameSideOfLine( p, v2, v1, v3 ) &&
|
|
PointsOnSameSideOfLine( p, v3, v1, v2 ) )
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
rmt::Vector GetProjectionVector( const rmt::Vector& source, const rmt::Vector& target )
|
|
{
|
|
return target * ( target.Dot(source) / target.Dot(target) );
|
|
}
|
|
|
|
// In Lefthand coordinate system, turn vector to
|
|
// the left (counter-clockwise) 90 degrees about Y axis & return new vector
|
|
rmt::Vector Get90DegreeLeftTurn( const rmt::Vector& orig )
|
|
{
|
|
rmt::Vector newVec;
|
|
newVec.Set( -1 * orig.z, orig.y, orig.x );
|
|
return newVec;
|
|
}
|
|
|
|
// In Lefthand coordinate system, turn vector to
|
|
// the right (clockwise) 90 degrees about Y axis & return new vector
|
|
rmt::Vector Get90DegreeRightTurn( const rmt::Vector& orig )
|
|
{
|
|
rmt::Vector newVec;
|
|
newVec.Set( orig.z, orig.y, -1 * orig.x );
|
|
return newVec;
|
|
}
|
|
|
|
|
|
float GetRotationAboutY( float x, float z )
|
|
{
|
|
float angle = 0.0f;
|
|
if( rmt::Epsilon(x, 0.0f) && rmt::Epsilon(z, 0.0f) )
|
|
{
|
|
angle = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
// generate angle from x-z vector
|
|
// - assumes DEFAULT_FACING_VECTOR is (0,0,-1)
|
|
// - assumes UP VECTOR is (0,1,0)
|
|
angle = rmt::ATan2(x, z);
|
|
if( rmt::IsNan(angle) )
|
|
{
|
|
angle = 0.0f;
|
|
}
|
|
|
|
/*
|
|
angle = rmt::ATan2(-x, -z);
|
|
|
|
// wrap to [0, 2*pi)
|
|
if (angle < 0.0f)
|
|
{
|
|
angle += rmt::PI_2;
|
|
}
|
|
*/
|
|
}
|
|
return angle;
|
|
}
|
|
|
|
|
|
bool PointToLineProjection2D( const rmt::Vector& inPt,
|
|
const rmt::Vector& linePt1,
|
|
const rmt::Vector& linePt2,
|
|
rmt::Vector& outPt )
|
|
{
|
|
// The beauty of calling this 2D function is that we break it down into components
|
|
// so we don't do unnecessary float ops for the y values
|
|
float x1,x2,x3,z1,z2,z3;
|
|
x1 = linePt1.x;
|
|
x2 = linePt2.x;
|
|
x3 = inPt.x;
|
|
z1 = linePt1.z;
|
|
z2 = linePt2.z;
|
|
z3 = inPt.z;
|
|
|
|
// make sure we were given a line, not a point dammit!
|
|
rAssertMsg( !( rmt::Epsilon( x1,x2,0.0005f ) && rmt::Epsilon( z1,z2,0.0005f )),
|
|
"PointToLineProjection2D: The two points of the \"line\" are too close together.\n" );
|
|
|
|
// THEORY
|
|
// ======
|
|
// Let outPt be the point resulting from projecting inPt onto
|
|
// linesegment [linePt2 - linePt1]:
|
|
//
|
|
// 1) outPt = linePt1 + u * [linePt2 - linePt1];
|
|
//
|
|
// Since the projection is at Right Angle, we can say that the lines
|
|
// [linePt2 - linePt1] and [inPt - outPt] have zero-length dotproduct:
|
|
//
|
|
// 2) [inPt - outPt] dot [linePt2 - linePt1] = 0
|
|
//
|
|
// Substituting 1) into 2) gives:
|
|
//
|
|
// 3) [ inPt - [linePt1+u*[linePt2 - linePt1]] ] dot [linePt2 - linePt1] = 0
|
|
//
|
|
// Solving for u gives:
|
|
//
|
|
float x2MINUSx1 = x2-x1;
|
|
float z2MINUSz1 = z2-z1;
|
|
float u = ( (x3-x1)*x2MINUSx1 + (z3-z1)*z2MINUSz1 ) /
|
|
( (x2MINUSx1*x2MINUSx1)+(z2MINUSz1*z2MINUSz1) );
|
|
|
|
// Populate the return point
|
|
outPt.Set( x1 + u*x2MINUSx1, inPt.y, z1 + u*z2MINUSz1 );
|
|
|
|
// If u is not on the line segment, outPt will not be in bounds
|
|
if( u < 0.0f || u > 1.0f )
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool PointOnLeftSideOfLine( const rmt::Vector& p,
|
|
const rmt::Vector& start,
|
|
const rmt::Vector& end )
|
|
{
|
|
rmt::Vector start2p = p - start;
|
|
rmt::Vector start2end = end - start;
|
|
rmt::Vector leftVec = Get90DegreeLeftTurn( start2end );
|
|
|
|
float dp = leftVec.Dot( start2p );
|
|
if( dp > 0.0f ) // +ve means on the left
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool PointOnRightSideOfLine( const rmt::Vector& p,
|
|
const rmt::Vector& start,
|
|
const rmt::Vector& end )
|
|
{
|
|
rmt::Vector start2p = p - start;
|
|
rmt::Vector start2end = end - start;
|
|
rmt::Vector rightVec = Get90DegreeRightTurn( start2end );
|
|
|
|
float dp = rightVec.Dot( start2p );
|
|
if( dp > 0.0f ) // +ve means on the right
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
float FindClosestPointOnLine( const rmt::Vector& start,
|
|
const rmt::Vector& end,
|
|
const rmt::Vector& p,
|
|
rmt::Vector& closestPt )
|
|
{
|
|
|
|
rmt::Vector start2p = p - start;
|
|
rmt::Vector lDir = end - start;
|
|
|
|
float lDirMagSqr = lDir.Dot(lDir);
|
|
|
|
// if end and start are basically the same point,
|
|
// then lDir is zero. So the closest point returned
|
|
// is that point
|
|
//
|
|
if( rmt::Epsilon( lDirMagSqr, 0.0f, 0.001f ) )
|
|
{
|
|
closestPt = end;
|
|
return 0.0f;
|
|
}
|
|
|
|
float scale = lDir.Dot( start2p ) / lDirMagSqr;
|
|
if( scale > 1.0f )
|
|
{
|
|
closestPt = end;
|
|
}
|
|
else if( scale < 0.0f )
|
|
{
|
|
closestPt = start;
|
|
}
|
|
else
|
|
{
|
|
closestPt = start + lDir * scale;
|
|
}
|
|
return scale;
|
|
}
|
|
|
|
float GetLineSegmentT
|
|
(
|
|
const rmt::Vector& segStart,
|
|
const rmt::Vector& segEnd,
|
|
const rmt::Vector& pt
|
|
)
|
|
{
|
|
float e = 0.0001f;
|
|
|
|
#if defined( RAD_DEBUG ) || defined( RAD_TUNE )
|
|
// make sure this point is on the line
|
|
rmt::Vector closestPt;
|
|
FindClosestPointOnLine( segStart, segEnd, pt, closestPt );
|
|
rAssert( closestPt.Equals( pt, e ) );
|
|
#endif
|
|
|
|
float segT = 0.0f;
|
|
if( rmt::Epsilon( segEnd.x, segStart.x, e ) )
|
|
{
|
|
if( rmt::Epsilon( segEnd.y, segStart.y, e ) )
|
|
{
|
|
if( rmt::Epsilon( segEnd.z, segStart.z, e ) )
|
|
{
|
|
segT = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
segT = (pt.z - segStart.z) / (segEnd.z - segStart.z);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
segT = (pt.y - segStart.y) / (segEnd.y - segStart.y);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
segT = (pt.x - segStart.x) / (segEnd.x - segStart.x);
|
|
}
|
|
rAssert( 0.0f <= segT && segT <= 1.0f );
|
|
return segT;
|
|
} |