/*===========================================================================
Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
Component: Road
Description:
Authors: Travis Brown-John
Revisions Date Author Revision
2001/02/02 Tbrown-John Created
===========================================================================*/
#include <roads/road.h>
#include <roads/roadsegment.h>
#include <roads/lane.h>
#include <roads/geometry.h>
#include <raddebug.hpp>
#ifndef TOOLS
#include <memory/srrmemory.h>
#else
#define MEMTRACK_PUSH_GROUP(x)
#define MEMTRACK_POP_GROUP()
#endif
/*
==============================================================================
Road::Road
==============================================================================
Description: Comment
Parameters: ( void )
Return: na
=============================================================================
*/
Road::Road( void )
:
mpSourceIntersection( 0 ),
mpDestinationIntersection( 0 ),
mLaneList( 0 ),
mnLanes( 0 ),
mppRoadSegmentArray( 0 ),
mnMaxRoadSegments( 0 ),
mnRoadSegments( 0 ),
mSpeed( 0 ),
mDensity( 0 ),
mDifficulty( 0 ),
mIsShortCut( false )
{
}
/*
==============================================================================
Road::~Road
==============================================================================
Description: Comment
Parameters: ( void )
Return: na
=============================================================================
*/
Road::~Road( void )
{
if ( mLaneList )
{
delete [] mLaneList;
mLaneList = 0;
}
if ( mppRoadSegmentArray )
{
delete[] mppRoadSegmentArray;
mppRoadSegmentArray = NULL;
}
}
/*
==============================================================================
Road::AllocateSegments
==============================================================================
Description: Comment
Parameters: ( unsigned int numSegments )
Return: void
=============================================================================
*/
void Road::AllocateSegments( unsigned int numSegments )
{
MEMTRACK_PUSH_GROUP( "Road" );
#ifndef TOOLS
#ifdef RAD_GAMECUBE
HeapMgr()->PushHeap( GMA_GC_VMM );
#else
HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
#endif
#endif
rAssert( numSegments > 0 );
rAssert( 0 == mppRoadSegmentArray );
rAssert( 0 == mnRoadSegments );
mnMaxRoadSegments = numSegments;
mppRoadSegmentArray = new RoadSegment*[ mnMaxRoadSegments ];
unsigned int i = 0;
for ( i = 0; i < mnMaxRoadSegments; i++ )
{
mppRoadSegmentArray[ i ] = 0;
}
#ifndef TOOLS
#ifdef RAD_GAMECUBE
HeapMgr()->PopHeap( GMA_GC_VMM );
#else
HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
#endif
#endif
MEMTRACK_POP_GROUP( "Road" );
}
/*
==============================================================================
Road::AddRoadSegment
==============================================================================
Description: Add a road segment.
Parameters: ( RoadSegment* pRoadSegment )
Return: void
=============================================================================
*/
void Road::AddRoadSegment( RoadSegment* pRoadSegment )
{
mppRoadSegmentArray[ mnRoadSegments ] = pRoadSegment;
pRoadSegment->SetSegmentIndex( mnRoadSegments );
mnRoadSegments++;
unsigned int numVertices = 4;
rmt::Vector vertex;
unsigned int i = 0;
for ( i = 0; i < numVertices; i++ )
{
pRoadSegment->GetCorner( i, vertex );
if ( 1 == mnRoadSegments && 0 == i )
{
// This is the first time.
// Initialize to some value.
//
mBox.low = mBox.high = vertex;
}
else
{
if ( mBox.low.x > vertex.x )
{
mBox.low.x = vertex.x;
}
if ( mBox.low.y > vertex.y )
{
mBox.low.y = vertex.y;
}
if ( mBox.low.z > vertex.z )
{
mBox.low.z = vertex.z;
}
if ( mBox.high.x < vertex.x )
{
mBox.high.x = vertex.x;
}
if ( mBox.high.y < vertex.y )
{
mBox.high.y = vertex.y;
}
if ( mBox.high.z < vertex.z )
{
mBox.high.z = vertex.z;
}
}
}
// now we should have a bounding box.
//
// compute the bounding sphere.
//
// first the centre.
//
rmt::Vector vectorBetween;
vectorBetween.Sub( mBox.high, mBox.low );
vectorBetween.Scale( 0.5f );
mSphere.centre.Add( mBox.low, vectorBetween );
// Then the radius
mSphere.radius = vectorBetween.Magnitude( );
}
/*
==============================================================================
Road::CreateLanes
==============================================================================
Description: Comment
Parameters: ( void )
Return: void
=============================================================================
*/
void Road::CreateLanes( void )
{
MEMTRACK_PUSH_GROUP( "Road" );
// Allocate the lanes.
//
//TODO: REMOVE!
#ifndef TOOLS
#ifdef RAD_GAMECUBE
HeapMgr()->PushHeap( GMA_GC_VMM );
#else
HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
#endif
#endif
mLaneList = new Lane[ mnLanes ];
// Allocate the memory for the lane points.
//
unsigned int totalDensity = GetDensity();
unsigned int laneDensity = totalDensity / mnLanes;
unsigned int i = 0;
for ( i = 0; i < mnLanes; i++ )
{
// Do this first.
//
// if on last lane, just add whatever's left of totalDensity
unsigned int density = 0;
if( i == mnLanes - 1 )
{
density = totalDensity;
}
else
{
density = laneDensity;
}
mLaneList[ i ].Create( (int)density, this );
totalDensity -= laneDensity;
mLaneList[ i ].SetSpeedLimit( (float)GetSpeed() * KPH_2_MPS ); //convert from kph to mps
#if defined(RAD_DEBUG) || defined(RAD_TUNE)
// For each lane, allocate enough points to store all the roadSegment data plus one.
mLaneList[ i ].AllocatePoints( this->GetNumRoadSegments( ) + 1 );
#endif
}
// Iterate through the segments.
//
for ( i = 0; i < mnRoadSegments; i++ )
{
RoadSegment* pSeg = mppRoadSegmentArray[ i ];
unsigned int j = 0;
for ( j = 0; j < mnLanes; j++ )
{
// For each lane in each segment, add a point to the lane.
//
rmt::Vector start, facing;
pSeg->GetLaneLocation( 0.0f, j, start, facing );
#if defined(RAD_TUNE) || defined(RAD_DEBUG)
mLaneList[ j ].SetPoint( i, start );
#endif
}
}
// This is attached to the out intersection.
//
RoadSegment* pSeg = mppRoadSegmentArray[ mnRoadSegments - 1 ];
// Now add the last point.
//
for ( i = 0; i < mnLanes; i++ )
{
rmt::Vector end, facing;
pSeg->GetLaneLocation( 1.0f, i, end, facing );
#if defined(RAD_TUNE) || defined(RAD_DEBUG)
mLaneList[ i ].SetPoint( mnRoadSegments, end );
#endif
}
//TODO: REMOVE!
#ifndef TOOLS
#ifdef RAD_GAMECUBE
HeapMgr()->PopHeap( GMA_GC_VMM );
#else
HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
#endif
#endif
MEMTRACK_POP_GROUP( "Road" );
}
/*
==============================================================================
Road::GetLane
==============================================================================
Description: Comment
Parameters: ( unsigned int LaneId )
Return: Lane*
=============================================================================
*/
Lane* Road::GetLane( unsigned int LaneId ) const
{
if ( LaneId < mnLanes )
{
return &mLaneList[ LaneId ];
}
else
{
return 0;
}
}
RoadSegment* Road::GetRoadSegment( unsigned int index ) const
{
rAssert( index < mnRoadSegments );
return mppRoadSegmentArray[ index ];
}
//
//
/*
==============================================================================
Road::GetDestinationIntersectionJoinPoint
==============================================================================
Description: Return the point where the road attaches to the incoming intersection.
Parameters: ( rmt::Vector& out )
Return: void
=============================================================================
*/
void Road::GetDestinationIntersectionJoinPoint( rmt::Vector& out )
{
RoadSegment* pRoadSegment = this->GetRoadSegment( 0 );
pRoadSegment->GetCorner( 0, out );
}
/*
==============================================================================
Road::GetSourceIntersectionJoinPoint
==============================================================================
Description: Return the point where the road attaches to the outgoing intersection.
Parameters: ( rmt::Vector& out )
Return: void
=============================================================================
*/
void Road::GetSourceIntersectionJoinPoint( rmt::Vector& out )
{
RoadSegment* pRoadSegment = this->GetRoadSegment( this->GetNumRoadSegments( ) - 1 );
pRoadSegment->GetCorner( 1, out );
}
/*
==============================================================================
Road::GetRoadSegmentAtPoint
==============================================================================
Description: returns a road segment, if the point is inside a segment on this road.
Parameters: ( const rmt::Vector& point, RoadSegment& outRoadSegment, int hint )
Return: int - index of road segment this point is in.
=============================================================================
*/
int Road::GetRoadSegmentAtPoint( const rmt::Vector& point, RoadSegment** ppOutRoadSegment, float& in, float& lateral, int hint ) const
{
rmt::Vector vectorBetween;
// Do the 2D Math Explicitly.
// My profile test show it is significantly faster.
//
vectorBetween.x = point.x - mSphere.centre.x;
vectorBetween.y = 0.0f;
vectorBetween.z = point.z - mSphere.centre.z;
vectorBetween.x *= vectorBetween.x;
vectorBetween.z *= vectorBetween.z;
vectorBetween.x += vectorBetween.z;
if ( vectorBetween.x <= rmt::Sqr( mSphere.radius ) )
{
// Now test against AABB.
//
if ( point.x <= mBox.high.x
&& point.x >= mBox.low.x
&& point.z <= mBox.high.z
&& point.z >= mBox.low.z )
{
int i = hint;
// Check the hint.
//
if ( !IsPointInRoadSegment( i, point, in, lateral ) )
{
// Check the one ahead.
//
if ( !IsPointInRoadSegment( ++i, point, in, lateral ) )
{
// Check the one behind.
//
if ( !IsPointInRoadSegment( i -= 2, point, in, lateral ) )
{
// Scan the entire list.
//
for ( i = 0; i < static_cast< int >( GetNumRoadSegments( ) ); i++ )
{
// Skip the ones we've looked at.
//
if ( i != hint && i != hint + 1 && i != hint - 1 )
{
// Stop when we find one.
//
if ( IsPointInRoadSegment( i, point, in, lateral ) )
{
break;
}
}
}
}
}
}
if ( i < static_cast< int >( GetNumRoadSegments( ) ) )
{
// We found one.
//
*ppOutRoadSegment = GetRoadSegment( i );
return i;
}
}
}
*ppOutRoadSegment = 0;
return -1;
}
/*
==============================================================================
Road::IsPointInRoadSegment
==============================================================================
Description: Tests the distance into and the lateral distance in a segment
if both lateral and in distance are between 0.0f - 1.0f
we return true.
Quick and Dirty performance number:
XBox 02/28/2002
26.0 ms per 10000 calls point inside BBox
1.0 ms per 10000 calls test BBox only.
0.0025 / 10000
XBox 0.0025 ms in Debug for one call to IsPointInRoadSegment
Parameters: ( int index )
Return: bool - true if point is in road segment.
=============================================================================
*/
bool Road::IsPointInRoadSegment( int index, const rmt::Vector& point, float& in, float& lateral ) const
{
if ( index >= 0 && index < static_cast<int>( GetNumRoadSegments() ) )
{
RoadSegment* pRoadSegment = GetRoadSegment( index );
rmt::Vector vectorBetween;
rmt::Sphere sphere;
pRoadSegment->GetBoundingSphere( &sphere );
// Do the 2D Math Explicitly.
// My profile test show it is significantly faster.
//
vectorBetween.x = point.x - sphere.centre.x;
vectorBetween.y = 0.0f;
vectorBetween.z = point.z - sphere.centre.z;
vectorBetween.x *= vectorBetween.x;
vectorBetween.z *= vectorBetween.z;
vectorBetween.x += vectorBetween.z;
if ( vectorBetween.x <= rmt::Sqr( sphere.radius ) )
{
in = pRoadSegment->CalculateUnitDistIntoRoadSegment( point.x, point.z );
if ( in >= 0.0f && in <= 1.0f )
{
lateral = pRoadSegment->CalculateUnitHeightInRoadSegment( point.x, point.z );
if ( lateral >= 0.0f && lateral <= 1.0f )
{
return true;
}
}
}
}
return false;
}
//=============================================================================
// RoadManager::FindRoadSegmentAhead
//=============================================================================
// Description: Comment
//
// Parameters: ( const Road* pRoad,
// float& dist,
// const float maxDist,
// RoadSegment** segment )
//
// Return: bool
//
//=============================================================================
bool Road::FindSegmentAhead( float& dist,
const float maxDist,
const int currentIndex,
RoadSegment** segment ) const
{
rmt::Vector backleft;
rmt::Vector frontleft;
rmt::Vector frontright;
rmt::Vector backright;
rmt::Vector leftdir;
rmt::Vector rightdir;
rmt::Vector direction;
rmt::Vector destination;
rmt::Vector currentPos;
RoadSegment* pCurrentSegment = GetRoadSegment( currentIndex );
//
// Temporarily use pCurrentSegment to get data about where
// we're starting
//
pCurrentSegment->GetCorner( 0, backleft );
pCurrentSegment->GetCorner( 1, frontleft );
pCurrentSegment->GetCorner( 2, frontright );
pCurrentSegment->GetCorner( 3, backright );
//
// Find the front midpoint of the segment
//
currentPos.Add( frontleft, frontright );
currentPos.Scale( 0.5f, 0.5f, 0.5f );
//
// Find the average direction of the segment
//
leftdir.Sub( frontleft, backleft );
leftdir.Normalize();
rightdir.Sub( frontright, backright );
rightdir.Normalize();
//
// Take the average and scale by 0.1
//
direction.Add( leftdir, rightdir );
direction.Scale( 0.05f, 0.05f, 0.05f );
//
// This should put us within the next segment
//
currentPos.Add( direction );
RoadSegment* pNextSegment = NULL;
pCurrentSegment = NULL;
//float in, lateral = 0.0f;
int segmentIndex = currentIndex;
float currentDist = dist;
while( currentDist < maxDist )
{
//
// This search is not optimal
//
//segmentIndex = pRoad->GetRoadSegmentAtPoint( currentPos,
// &pNextSegment, in, lateral, segmentIndex + 1 );
//
// Much faster when the segments are sorted. Yay!
//
segmentIndex += 1;
if( segmentIndex >= static_cast<int>( GetNumRoadSegments() ) )
{
//
// Get as close as we can to the desired distance.
// Returns NULL if we didn't find anything at all.
//
(*segment) = pCurrentSegment;
dist = currentDist;
return false;
}
pNextSegment = GetRoadSegment( segmentIndex );
rAssert( pNextSegment != NULL );
//
// Find the midpoint of the front edge
//
pNextSegment->GetCorner( 1, frontleft );
pNextSegment->GetCorner( 2, frontright );
destination.Add( frontleft, frontright );
destination.Scale( 0.5f, 0.5f, 0.5f );
//
// Gives a vector along the centre line assuming currentPos
// is on the front edge midpoint of the previous segment
//
direction.Sub( destination, currentPos );
float segLength = direction.Magnitude();
currentDist += segLength;
//
// Normalize the direction and scale it by 0.1
//
// not necessary for the fast method
//float invLen = 0.1f / segLength;
//direction.Scale( invLen, invLen, invLen );
//currentPos.Add( destination, direction );
pCurrentSegment = pNextSegment;
}
//
// Got to the distance the person wanted
//
(*segment) = pCurrentSegment;
dist = currentDist;
return true;
}
//=============================================================================
// RoadManager::FindSegmentBehind
//=============================================================================
// Description: Comment
//
// Parameters: ( const Road* pRoad,
// float& dist,
// const float maxDist,
// RoadSegment** segment )
//
// Return: bool
//
//=============================================================================
bool Road::FindSegmentBehind( float& dist,
const float maxDist,
const int currentIndex,
RoadSegment** segment ) const
{
rmt::Vector backleft;
rmt::Vector frontleft;
rmt::Vector frontright;
rmt::Vector backright;
rmt::Vector leftdir;
rmt::Vector rightdir;
rmt::Vector direction;
rmt::Vector destination;
rmt::Vector currentPos;
RoadSegment* pCurrentSegment = GetRoadSegment( currentIndex );
//
// Temporarily use pCurrentSegment to get data about where
// we're starting
//
pCurrentSegment->GetCorner( 0, backleft );
pCurrentSegment->GetCorner( 1, frontleft );
pCurrentSegment->GetCorner( 2, frontright );
pCurrentSegment->GetCorner( 3, backright );
//
// Find the rear midpoint of the segment
//
currentPos.Add( backleft, backright );
currentPos.Scale( 0.5f, 0.5f, 0.5f );
//
// Find the average direction of the segment
//
leftdir.Sub( backleft, frontleft );
leftdir.Normalize();
rightdir.Sub( backright, frontright );
rightdir.Normalize();
//
// Take the average and scale by 0.1
//
direction.Add( leftdir, rightdir );
direction.Scale( 0.05f, 0.05f, 0.05f );
//
// This should put us within the next segment
//
currentPos.Add( direction );
RoadSegment* pNextSegment = NULL;
//float in, lateral = 0.0f;
int segmentIndex = currentIndex;
float currentDist = dist;
while( currentDist < maxDist )
{
pCurrentSegment = pNextSegment;
//
// This search is not optimal
//
//segmentIndex = pRoad->GetRoadSegmentAtPoint( currentPos,
// &pNextSegment, in, lateral, segmentIndex + 1 );
//
// Much faster when the segments are sorted. Yay!
//
segmentIndex -= 1;
if( segmentIndex < 0 )
{
//
// Get as close as we can to the desired distance.
// Returns NULL if we didn't find anything at all.
//
(*segment) = pCurrentSegment;
dist = currentDist;
return false;
}
pNextSegment = GetRoadSegment( segmentIndex );
rAssert( pNextSegment != NULL );
//
// Find the midpoint of the front edge
//
pNextSegment->GetCorner( 0, backleft );
pNextSegment->GetCorner( 3, backright );
destination.Add( backleft, backright );
destination.Scale( 0.5f, 0.5f, 0.5f );
//
// Gives a vector along the centre line assuming currentPos
// is on the front edge midpoint of the previous segment
//
direction.Sub( destination, currentPos );
float segLength = direction.Magnitude();
currentDist += segLength;
//
// Normalize the direction and scale it by 0.1
//
// not necessary for the fast method
//float invLen = 0.1f / segLength;
//direction.Scale( invLen, invLen, invLen );
//currentPos.Add( destination, direction );
}
//
// Got to the distance the person wanted
//
(*segment) = pCurrentSegment;
dist = currentDist;
return true;
}
//=============================================================================
// Road::SetDensity
//=============================================================================
// Description: Comment
//
// Parameters: ( unsigned int density )
//
// Return: void
//
//=============================================================================
void Road::SetDensity( unsigned int density )
{
mDensity = density;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void Road::FindRoadSegmentAtDist( float iDist, RoadSegment** oppRoadSegment )
{
if(iDist>GetRoadLength())
{
*oppRoadSegment = NULL;
return;
}
RoadSegment* pRoadSegment = NULL;
float distAccum = 0.0f;
for(int i=mnRoadSegments-1; i>-1 && distAccum<iDist; i--)
{
pRoadSegment = mppRoadSegmentArray[i];
distAccum += pRoadSegment->GetSegmentLength();
}
*oppRoadSegment = pRoadSegment;
}