The-Simpsons-Hit-and-Run/game/code/presentation/gui/utility/hudmap.cpp

1915 lines
60 KiB
C++

//===========================================================================
// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
//
// Component: CHudMap
//
// Description: Implementation of the CHudMap class.
//
// Authors: Tony Chu
//
// Revisions Date Author Revision
// 2002/08/01 TChu Created for SRR2
//
//===========================================================================
//===========================================================================
// Includes
//===========================================================================
#include <presentation/gui/utility/hudmap.h>
#include <presentation/gui/utility/hudmapcam.h>
#include <presentation/gui/utility/specialfx.h>
#include <presentation/gui/guiscreen.h>
#include <ai/vehicle/vehicleai.h>
#include <interiors/interiormanager.h>
#include <memory/srrmemory.h>
#include <meta/zoneeventlocator.h>
#include <mission/gameplaymanager.h>
#include <render/intersectmanager/intersectmanager.h>
#include <roads/geometry.h>
#include <roads/road.h>
#include <roads/intersection.h>
#include <roads/roadmanager.h>
#include <worldsim/avatarmanager.h>
#include <worldsim/avatar.h>
#include <worldsim/hitnrunmanager.h>
#include <worldsim/redbrick/vehicle.h>
#include <p3d/utility.hpp>
#include <raddebug.hpp> // Foundation
#include <page.h>
#include <group.h>
#include <pure3dobject.h>
#include <sprite.h>
#include <polygon.h>
//===========================================================================
// Global Data, Local Data, Local Classes
//===========================================================================
#define SHOW_HUD_MAP
#define CIRCULAR_HUD_MAP
#ifdef CIRCULAR_HUD_MAP
#define SMARTER_ICON_LOCATIONS // only works for circular hud map
#endif
// This is for smooth camera height transitions, to prevent any sudden
// camera cuts.
//
#define SMOOTH_CAMERA_HEIGHT_TRANSITIONS
const int NUM_AMORTIZED_UPDATE_FRAMES = 5; // for DetermineOnRoadLocation()
#ifdef DEBUGWATCH
#include <raddebugwatch.hpp>
static const char* WATCHER_NAMESPACE = "GUI System - HUD Map";
float MIN_CAMERA_HEIGHT = 50.0f;
float MAX_CAMERA_HEIGHT = 150.0f;
float MAX_CAMERA_HEIGHT_TRANSITION = 10.0f;
float CHASE_FLASH_DISTANCE_THRESHOLD = 0.5f;
float CHASE_FADE_DISTANCE_THRESHOLD = 0.75f;
float RADAR_VISIBLE_RADIUS = 150.0f; // in metres
#else
const float MIN_CAMERA_HEIGHT = 50.0f;
const float MAX_CAMERA_HEIGHT = 150.0f;
const float MAX_CAMERA_HEIGHT_TRANSITION = 10.0f;
const float CHASE_FLASH_DISTANCE_THRESHOLD = 0.5f;
const float CHASE_FADE_DISTANCE_THRESHOLD = 0.75f;
const float RADAR_VISIBLE_RADIUS = 150.0f; // in metres
#endif
const float DEFAULT_CAMERA_HEIGHT = 150.0f; // in metres
const float MAX_RADAR_CONE_ALPHA = 0.35f;
enum eVisibilityMode
{
VISIBLE_IN_SUNDAY_DRIVE = 1,
VISIBLE_IN_MISSION = 2,
NUM_VISIBILITY_MODES
};
const tColour HIT_N_RUN_COLOUR_RED( 255, 0, 0 );
const tColour HIT_N_RUN_COLOUR_BLUE( 0, 0, 255 );
HudMapIcon CHudMap::s_registeredIcons[ MAX_NUM_REGISTERED_ICONS ];
int CHudMap::s_numRegisteredIcons = 0;
int CHudMap::s_fpIconID = -1;
//===========================================================================
// Public Member Functions
//===========================================================================
void
HudMapIcon::ApplyAICarIconColour()
{
rAssert( m_iconImage != NULL );
switch( m_type )
{
case ICON_AI_HIT_N_RUN:
{
m_iconImage->SetColour( HIT_N_RUN_COLOUR_RED );
break;
}
case ICON_AI_CHASE:
{
m_iconImage->SetColour( tColour( 255, 128, 0 ) );
break;
}
case ICON_AI_RACE:
{
m_iconImage->SetColour( tColour( 255, 255, 0 ) );
break;
}
case ICON_AI_EVADE:
{
m_iconImage->SetColour( tColour( 255, 255, 0 ) );
break;
}
case ICON_AI_TARGET:
{
m_iconImage->SetColour( tColour( 255, 0, 0 ) );
break;
}
default:
{
rAssertMsg( false, "Invalid AI car icon type!" );
break;
}
}
}
//===========================================================================
// CHudMap::CHudMap
//===========================================================================
// Description: Constructor.
//
// Constraints: None.
//
// Parameters: None.
//
// Return: N/A.
//
//===========================================================================
CHudMap::CHudMap( Scrooby::Page* pPage, int playerID, const char* p3dFile )
: m_playerID( playerID ),
m_p3dMap( NULL ),
m_p3dHole( NULL ),
m_originalPosX( 0 ),
m_originalPosY( 0 ),
m_originalWidth( 0 ),
m_originalHeight( 0 ),
m_isVisible( false ),
m_radar( NULL ),
m_radarCone( NULL ),
m_iconsGroup( NULL ),
m_hudMapCam( NULL ),
m_currentCameraHeight( DEFAULT_CAMERA_HEIGHT ),
m_fixedCameraHeight( 0.0f ),
m_currentAICarDistance( 0.0f ),
m_maxAICarDistance( 0.0f ),
m_elapsedTime( 0 ),
m_lastRoadSeg( NULL ),
m_lastOnRoadLocation( 0.0f, 0.0f, 0.0f ),
m_frameCount( -1 )
{
MEMTRACK_PUSH_GROUP( "CHudMap" );
memset( m_icons, 0, sizeof( m_icons ) );
m_hudMapCam = new HudMapCam( m_playerID );
rAssert( m_hudMapCam );
m_hudMapCam->AddRef();
m_currentCameraHeight = this->CalculateCameraHeight( RADAR_VISIBLE_RADIUS );
char name[ 16 ];
rAssert( pPage != NULL );
// get 3D HUD map
//
sprintf( name, "Map%d", playerID );
m_p3dMap = pPage->GetPure3dObject( name );
rAssert( m_p3dMap != NULL );
m_p3dMap->SetCamera( m_hudMapCam->GetCamera() );
// store original hud map position and size
//
m_p3dMap->GetOriginPosition( m_originalPosX, m_originalPosY );
m_p3dMap->GetBoundingBoxSize( m_originalWidth, m_originalHeight );
#ifdef SHOW_HUD_MAP
if( p3dFile != NULL )
{
// set pure3d file for hud map
//
m_p3dMap->Add3dModel( p3dFile );
}
// get 3D HUD hole
//
sprintf( name, "Hole%d", playerID );
m_p3dHole = pPage->GetPure3dObject( name );
rAssert( m_p3dHole != NULL );
m_p3dHole->SetCamera( m_hudMapCam->GetCamera() );
m_p3dHole->SetColourWrite( false );
if( CGuiScreen::IsWideScreenDisplay() )
{
m_p3dMap->SetWideScreenCorrectionEnabled( true );
m_p3dHole->SetWideScreenCorrectionEnabled( true );
}
#else
m_p3dMap->SetVisible( false );
#endif
/*
sprintf( name, "RadarLight%d", playerID );
m_radarLight = pPage->GetSprite( name );
rAssert( m_radarLight != NULL );
// hide radar light temporarily
//
m_radarLight->SetVisible( false );
*/
// get radar & radar cone
//
sprintf( name, "Radar%d", playerID );
m_radar = pPage->GetGroup( name );
rAssert( m_radar != NULL );
sprintf( name, "RadarCone%d", playerID );
m_radarCone = pPage->GetGroup( name );
rAssert( m_radarCone != NULL );
Scrooby::Polygon* radarConePolygon = m_radarCone->GetPolygon( name );
rAssert( radarConePolygon != NULL );
radarConePolygon->SetAlpha( MAX_RADAR_CONE_ALPHA );
// get icons group
//
sprintf( name, "Icons%d", playerID );
m_iconsGroup = pPage->GetGroup( name );
rAssert( m_iconsGroup != NULL );
#ifdef DEBUGWATCH
::radDbgWatchAddFloat( &MIN_CAMERA_HEIGHT,
"Mininum Camera Height",
WATCHER_NAMESPACE,
NULL, NULL, 30.0f, 90.0f );
::radDbgWatchAddFloat( &MAX_CAMERA_HEIGHT,
"Maximum Camera Height",
WATCHER_NAMESPACE,
NULL, NULL, 100.0f, 1000.0f );
::radDbgWatchAddFloat( &MAX_CAMERA_HEIGHT_TRANSITION,
"Maximum Camera Height Change",
WATCHER_NAMESPACE,
NULL, NULL, 1.0f, 50.0f );
::radDbgWatchAddFloat( &RADAR_VISIBLE_RADIUS,
"Radar Visible Radius",
WATCHER_NAMESPACE,
NULL, NULL, 0.0f, 1000.0f );
::radDbgWatchAddFloat( &CHASE_FLASH_DISTANCE_THRESHOLD,
"Chase Flash Distance (%)",
WATCHER_NAMESPACE );
::radDbgWatchAddFloat( &CHASE_FADE_DISTANCE_THRESHOLD,
"Chase Fade Distance (%)",
WATCHER_NAMESPACE );
#endif
MEMTRACK_POP_GROUP("CHudMap");
}
//===========================================================================
// CHudMap::~CHudMap
//===========================================================================
// Description: Destructor.
//
// Constraints: None.
//
// Parameters: None.
//
// Return: N/A.
//
//===========================================================================
CHudMap::~CHudMap()
{
#ifdef DEBUGWATCH
::radDbgWatchDelete( &MIN_CAMERA_HEIGHT );
::radDbgWatchDelete( &MAX_CAMERA_HEIGHT );
::radDbgWatchDelete( &MAX_CAMERA_HEIGHT_TRANSITION );
::radDbgWatchDelete( &RADAR_VISIBLE_RADIUS );
::radDbgWatchDelete( &CHASE_FLASH_DISTANCE_THRESHOLD );
::radDbgWatchDelete( &CHASE_FADE_DISTANCE_THRESHOLD );
#endif
if( m_hudMapCam != NULL )
{
m_hudMapCam->Release();
m_hudMapCam = NULL;
}
}
//===========================================================================
// CHudMap::SetCameraTarget
//===========================================================================
// Description:
//
// Constraints: None.
//
// Parameters: None.
//
// Return:
//
//===========================================================================
void
CHudMap::SetCameraTarget( ISuperCamTarget* target )
{
rAssert( m_hudMapCam );
m_hudMapCam->SetTarget( target );
}
void
CHudMap::EnableFixedCameraHeight( bool enable, float visibleRadius )
{
/*
if( !enable )
{
rWarningMsg( false, "Can't disable fixed camera height in new radar implementation!" );
// ignore request to disable fixed camera height
//
return;
}
*/
m_fixedCameraHeight = enable ?
visibleRadius / rmt::Tan( m_hudMapCam->GetFOV() / 2.0f ) :
0.0f;
}
//===========================================================================
// CHudMap::Update
//===========================================================================
// Description:
//
// Constraints: None.
//
// Parameters: None.
//
// Return:
//
//===========================================================================
void
CHudMap::Update( unsigned int elapsedTime )
{
if( GetInteriorManager()->IsInside() )
{
this->SetVisible( false );
}
if( !m_isVisible )
{
// no need to update if not visible
//
return;
}
/*
// update radar light (just rotate about radar center point)
//
static float RADAR_ROTATION_PERIOD = 2000.0f; // in msec
float rotation = (elapsedTime / RADAR_ROTATION_PERIOD) * 360.0f;
rAssert( m_radarLight != NULL );
m_radarLight->RotateAboutCenter( rotation );
*/
// get current hud map position and size
//
int mapX = 0;
int mapY = 0;
int mapWidth = 0;
int mapHeight = 0;
rAssert( m_p3dMap != NULL );
m_p3dMap->GetOriginPosition( mapX, mapY );
m_p3dMap->GetBoundingBoxSize( mapWidth, mapHeight );
float newCameraHeight = DEFAULT_CAMERA_HEIGHT;
if( m_fixedCameraHeight != 0.0f ) // fixed camera height enabled
{
newCameraHeight = m_fixedCameraHeight;
}
else
{
if( s_fpIconID != -1 )
{
HudMapIcon* focalPointIcon = &(s_registeredIcons[ s_fpIconID ]);
rAssert( focalPointIcon->m_iconImage );
if( focalPointIcon->m_dynamicLocator != NULL )
{
focalPointIcon->m_dynamicLocator->GetPosition( &(focalPointIcon->m_location) );
}
newCameraHeight = this->CalculateCameraHeight( s_registeredIcons[ 0 ].m_location,
focalPointIcon->m_location );
}
}
#ifdef SMOOTH_CAMERA_HEIGHT_TRANSITIONS
// for smoother camera height transitions
//
float cameraHeightChange = newCameraHeight - m_currentCameraHeight;
if( rmt::Abs( cameraHeightChange ) > MAX_CAMERA_HEIGHT_TRANSITION )
{
m_currentCameraHeight += rmt::Sign( cameraHeightChange ) * MAX_CAMERA_HEIGHT_TRANSITION;
// rTunePrintf( "Current camera height = %f m\n", m_currentCameraHeight );
}
else
#endif
{
m_currentCameraHeight = newCameraHeight;
}
// update hud map camera
//
rAssert( m_hudMapCam != NULL );
m_hudMapCam->SetHeight( m_currentCameraHeight );
m_hudMapCam->SetAspect( (float)mapWidth / (float)mapHeight );
m_hudMapCam->Update( elapsedTime );
rmt::Vector hudMapCenter;
m_hudMapCam->GetPosition( &hudMapCenter );
#ifdef CIRCULAR_HUD_MAP
// update hud hole
//
if( m_p3dHole != NULL )
{
static float awayFromCam = 4.5f;
m_p3dHole->SetDrawableTranslation( hudMapCenter.x,
hudMapCenter.y - awayFromCam,
hudMapCenter.z );
}
#endif
// update registered icons
//
int iconPosX = 0;
int iconPosY = 0;
int iconWidth = 0;
int iconHeight = 0;
const unsigned int PULSE_PERIOD = 500;
// bool iconBlinked = false;
// rWarningMsg( s_fpIconID != -1, "No active focal point icon on hud map!" );
for( unsigned int i = 0; i < MAX_NUM_REGISTERED_ICONS; i++ )
{
if( s_registeredIcons[ i ].m_iconImage != NULL )
{
// check if visible in current gameplay mode
//
if( GetGameplayManager()->IsSundayDrive() )
{
if( (s_registeredIcons[ i ].m_visibilityMask & VISIBLE_IN_SUNDAY_DRIVE) == 0 )
{
s_registeredIcons[ i ].m_iconImage->SetVisible( false );
continue;
}
}
else
{
if( (s_registeredIcons[ i ].m_visibilityMask & VISIBLE_IN_MISSION) == 0 )
{
s_registeredIcons[ i ].m_iconImage->SetVisible( false );
continue;
}
}
// hide icons that are not visible when player is in car
//
if( s_registeredIcons[ i ].m_type == HudMapIcon::ICON_PHONE_BOOTH ||
s_registeredIcons[ i ].m_type == HudMapIcon::ICON_PLAYER_CAR )
{
Avatar* player = GetAvatarManager()->GetAvatarForPlayer( m_playerID );
rAssert( player != NULL );
if( player->IsInCar() )
{
s_registeredIcons[ i ].m_iconImage->SetVisible( false );
continue;
}
}
// s_registeredIcons[ i ].m_iconImage->SetAlpha( 1.0f );
// center icon in hud map first
//
s_registeredIcons[ i ].m_iconImage->GetBoundingBoxSize( iconWidth, iconHeight );
iconPosX = (mapX + mapWidth / 2) - (iconWidth / 2);
iconPosY = (mapY + mapHeight / 2) - (iconHeight / 2);
s_registeredIcons[ i ].m_iconImage->SetPosition( iconPosX, iconPosY );
s_registeredIcons[ i ].m_iconImage->ResetTransformation();
// update current location for dynamic icons
if( s_registeredIcons[ i ].m_dynamicLocator != NULL )
{
s_registeredIcons[ i ].m_dynamicLocator->GetPosition( &s_registeredIcons[ i ].m_location );
// place icon object at sea level
s_registeredIcons[ i ].m_location.y = 0;
if( s_registeredIcons[ i ].IsAICarIcon() ||
s_registeredIcons[ i ].m_type == HudMapIcon::ICON_PLAYER ||
s_registeredIcons[ i ].m_type == HudMapIcon::ICON_PLAYER_CAR )
{
rmt::Vector heading( 0, 0, 0 );
s_registeredIcons[ i ].m_dynamicLocator->GetHeading( &heading );
this->UpdateIconHeading( s_registeredIcons[ i ].m_iconImage, &heading );
}
}
rmt::Vector iconLocInWorld = s_registeredIcons[ i ].m_location;
#ifdef SMARTER_ICON_LOCATIONS
if( static_cast< int >( i ) == s_fpIconID )
{
// TC: [TODO] need to determine this from the maximum camera height and the
// clipping boundary value
//
static float visibleRadius = 120.0f;
this->DetermineOnRoadLocation( hudMapCenter,
iconLocInWorld,
visibleRadius );
}
#endif
rmt::Vector iconLoc( 0, 0, 0 );
rAssert( m_p3dMap->GetCamera() );
m_p3dMap->GetCamera()->WorldToView( iconLocInWorld, &iconLoc );
iconLoc.z = 0.0f;
static float CLIPPING_BOUNDARY = 0.40f;
float iconScale = 1.0f;
#ifdef CIRCULAR_HUD_MAP
// apply view clipping (for circular view)
//
float iconLength = iconLoc.Magnitude();
if( iconLength > CLIPPING_BOUNDARY )
{
iconScale = CLIPPING_BOUNDARY / iconLength;
iconLoc.Scale( iconScale );
}
#else
// apply view clipping (for rectangular view)
//
// x-clipping
float xScale = 1.0f;
if( iconLoc.x > CLIPPING_BOUNDARY )
{
xScale = CLIPPING_BOUNDARY / iconLoc.x;
iconLoc.Scale( xScale, xScale, 1 );
}
else if( iconLoc.x < -CLIPPING_BOUNDARY )
{
xScale = -CLIPPING_BOUNDARY / iconLoc.x;
iconLoc.Scale( xScale, xScale, 1 );
}
// y-clipping
float yScale = 1.0f;
if( iconLoc.y > CLIPPING_BOUNDARY )
{
yScale = CLIPPING_BOUNDARY / iconLoc.y;
iconLoc.Scale( yScale, yScale, 1 );
}
else if( iconLoc.y < -CLIPPING_BOUNDARY )
{
yScale = -CLIPPING_BOUNDARY / iconLoc.y;
iconLoc.Scale( yScale, yScale, 1 );
}
iconScale = xScale * yScale;
#endif
bool isIconClipped = (iconScale < 1.0f);
rAssert( m_radarCone != NULL );
m_radarCone->SetVisible( s_fpIconID != -1 );
if( static_cast< int >( i ) == s_fpIconID ) // for focal point icon ...
{
// update radar cone direction
//
m_radarCone->ResetTransformation();
m_radarCone->RotateAboutCenter( rmt::RadianToDeg( this->CalculatRadarConeAngle( iconLoc ) ) );
if( !isIconClipped || s_registeredIcons[ i ].IsAICarIcon() )
{
m_radarCone->SetAlpha( iconScale );
}
else
{
const float MINIMUM_ICON_SCALE = 0.75f;
float alpha = (iconScale - MINIMUM_ICON_SCALE) / (1.0f - MINIMUM_ICON_SCALE);
m_radarCone->SetAlpha( alpha > 0.0f ? alpha : 0.0f );
}
/*
// apply alpha if focal point icon is clipped on boundary
//
if( iconScale < 1.0f )
{
if( iconScale < 0.3f )
{
s_registeredIcons[ i ].m_iconImage->SetAlpha( 0.3f );
}
else
{
s_registeredIcons[ i ].m_iconImage->SetAlpha( iconScale );
}
}
else
{
s_registeredIcons[ i ].m_iconImage->SetAlpha( 1.0f );
}
*/
#ifdef SMARTER_ICON_LOCATIONS
s_registeredIcons[ i ].m_iconImage->SetVisible( true );
#else
if( !s_registeredIcons[ i ].IsAICarIcon() )
{
// hide non-AI car icons if clipped on boundary
//
s_registeredIcons[ i ].m_iconImage->SetVisible( !isIconClipped );
}
#endif
// special cases
//
if( s_registeredIcons[ i ].m_type == HudMapIcon::ICON_MISSION )
{
static float SCALE_AMPLITUDE = 0.25f;
float scale = GuiSFX::Pulse( (float)m_elapsedTime,
(float)PULSE_PERIOD,
1.0f + SCALE_AMPLITUDE,
SCALE_AMPLITUDE );
/*
float scale = 1.0f + (float)m_elapsedTime / PULSE_PERIOD * SCALE_AMPLITUDE;
float alpha = 1.0f - (float)m_elapsedTime / PULSE_PERIOD * ALPHA_AMPLITUDE;
s_registeredIcons[ i ].m_iconImage->SetAlpha( alpha );
*/
s_registeredIcons[ i ].m_iconImage->ScaleAboutCenter( scale );
}
/*
else if( s_registeredIcons[ i ].m_type == HudMapIcon::ICON_AI_CAR )
{
// if focal point is AI car icon, fade out when near
// distance limit
//
if( m_maxAICarDistance > 0.0f )
{
float distanceRatio = m_currentAICarDistance / m_maxAICarDistance;
rAssert( distanceRatio <= 1.0f );
if( distanceRatio > CHASE_FLASH_DISTANCE_THRESHOLD )
{
// blink icon
//
iconBlinked = GuiSFX::Blink( s_registeredIcons[ i ].m_iconImage,
(float)m_elapsedTime,
(float)BLINKING_PERIOD );
if( distanceRatio > CHASE_FADE_DISTANCE_THRESHOLD )
{
float iconAlpha = (1.0f - distanceRatio) /
(1.0f - CHASE_FADE_DISTANCE_THRESHOLD);
s_registeredIcons[ i ].m_iconImage->SetAlpha( iconAlpha );
}
else
{
s_registeredIcons[ i ].m_iconImage->SetAlpha( 1.0f );
}
}
else
{
s_registeredIcons[ i ].m_iconImage->SetVisible( true );
}
}
}
*/
}
else // for non-focal point icons ...
{
if( !s_registeredIcons[ i ].IsAICarIcon() )
{
// hide non-AI car icons if clipped on boundary
//
s_registeredIcons[ i ].m_iconImage->SetVisible( !isIconClipped );
}
/*
// for phone booth icons
//
if( s_registeredIcons[ i ].m_type == HudMapIcon::ICON_PHONE_BOOTH )
{
// only show if within visible radius
//
rmt::Vector playerToPhone;
playerToPhone.Sub( s_registeredIcons[ i ].m_location,
s_registeredIcons[ 0 ].m_location );
float distanceRatio = playerToPhone.Magnitude() / PHONE_BOOTH_VISIBLE_RADIUS;
bool isWithinRadius = (distanceRatio < 1.0f);
// fade out nicely to prevent popping
//
const float FADE_OUT_THRESHOLD = 0.9f;
if( !isIconClipped && isWithinRadius )
{
if( distanceRatio > FADE_OUT_THRESHOLD )
{
float iconAlpha = (1.0f - distanceRatio) / (1.0f - FADE_OUT_THRESHOLD);
s_registeredIcons[ i ].m_iconImage->SetAlpha( iconAlpha );
}
else
{
s_registeredIcons[ i ].m_iconImage->SetAlpha( 1.0f );
}
}
else
{
s_registeredIcons[ i ].m_iconImage->SetAlpha( 0.0f );
}
}
*/
}
if( s_registeredIcons[ i ].m_type == HudMapIcon::ICON_AI_HIT_N_RUN )
{
if( GetHitnRunManager()->IsHitnRunActive() )
{
s_registeredIcons[ i ].m_iconImage->SetVisible( true );
// modulate icon colour
//
tColour iconColour;
GuiSFX::ModulateColour( &iconColour,
(float)m_elapsedTime,
(float)PULSE_PERIOD,
HIT_N_RUN_COLOUR_RED,
HIT_N_RUN_COLOUR_BLUE );
s_registeredIcons[ i ].m_iconImage->SetColour( iconColour );
}
else
{
// hide icon if H&R is not active
//
s_registeredIcons[ i ].m_iconImage->SetVisible( false );
}
}
// translate the icon (in screen space wrt. hud map center)
//
int iconX = static_cast<int>( iconLoc.x * mapWidth );
int iconY = static_cast<int>( iconLoc.y * mapHeight );
s_registeredIcons[ i ].m_iconImage->Translate( iconX, iconY );
}
}
/*
if( iconBlinked )
{
m_elapsedTime %= PULSE_PERIOD;
}
*/
m_elapsedTime = (m_elapsedTime + elapsedTime) % PULSE_PERIOD;
}
//===========================================================================
// CHudMap::AddIconToInventory
//===========================================================================
// Description:
//
// Constraints: None.
//
// Parameters: None.
//
// Return:
//
//===========================================================================
void
CHudMap::AddIconToInventory( HudMapIcon::eIconType type, Scrooby::Sprite* image )
{
if( image == NULL )
{
return;
}
unsigned int i = 0;
for( i = 0; i < MAX_NUM_ICONS_PER_TYPE; i++ )
{
// find first empty slot in inventory to add icon
//
if( m_icons[ type ][ i ] == NULL )
{
m_icons[ type ][ i ] = image;
// hide icon image
//
image->SetVisible( false );
break;
}
}
rAssertMsg( i < MAX_NUM_ICONS_PER_TYPE,
"ERROR: *** Exceeded maximum number of icons per type!\n" );
}
//===========================================================================
// CHudMap::RemoveIconFromInventory
//===========================================================================
// Description:
//
// Constraints: None.
//
// Parameters: None.
//
// Return:
//
//===========================================================================
Scrooby::Sprite*
CHudMap::RemoveIconFromInventory( HudMapIcon::eIconType type )
{
Scrooby::Sprite* iconImage = NULL;
// find an available icon of specified type in inventory
//
for( unsigned int i = 0; i < MAX_NUM_ICONS_PER_TYPE; i++ )
{
if( m_icons[ type ][ i ] != NULL )
{
iconImage = m_icons[ type ][ i ];
iconImage->SetVisible( true );
// free up slot in inventory
//
m_icons[ type ][ i ] = NULL;
break;
}
}
return iconImage;
}
//===========================================================================
// CHudMap::RegisterIcon
//===========================================================================
// Description:
//
// Constraints: None.
//
// Parameters: None.
//
// Return:
//
//===========================================================================
int
CHudMap::RegisterIcon( HudMapIcon::eIconType type,
rmt::Vector location,
IHudMapIconLocator* hudMapIconLocator,
bool newFocalPoint )
{
rAssert( s_numRegisteredIcons < static_cast<int>( MAX_NUM_REGISTERED_ICONS ) );
int iconID = -1;
Scrooby::Sprite* iconImage = this->RemoveIconFromInventory( type );
if( iconImage == NULL )
{
rAssertMsg( false, "WARNING: *** No more available icons of this type! Please tell Tony." );
return -1;
}
// convert locations in interiors to world-space location of the interior
//
tName interiorName = GetInteriorManager()->ClassifyPoint( location );
if( interiorName.GetUID() != static_cast< tUID >( 0 ) )
{
ZoneEventLocator* locator = p3d::find<ZoneEventLocator>( interiorName.GetUID() );
locator->GetPosition( &location );
if( hudMapIconLocator != NULL )
{
hudMapIconLocator = locator;
}
}
#ifdef RAD_DEBUG
// check if there's already a static icon of the same type
// registered at exactly the same location
//
for( unsigned int j = 0; j < MAX_NUM_REGISTERED_ICONS; j++ )
{
if( s_registeredIcons[ j ].m_iconImage != NULL &&
s_registeredIcons[ j ].m_type == type )
{
rAssertMsg( s_registeredIcons[ j ].m_dynamicLocator != NULL ||
s_registeredIcons[ j ].m_location != location,
"WARNING: *** Another icon of this type has already been registered at this location. Is this by design??" );
}
}
#endif // RAD_DEBUG
for( unsigned int i = 0; i < MAX_NUM_REGISTERED_ICONS; i++ )
{
if( i == 0 && type != HudMapIcon::ICON_PLAYER )
{
// icon ID = 0 is reserved for player icon
continue;
}
// find first empty slot to register icon
if( s_registeredIcons[ i ].m_iconImage == NULL )
{
iconID = i;
s_numRegisteredIcons++;
// place icon object at sea level
location.y = 0;
s_registeredIcons[ iconID ].m_iconImage = iconImage;
s_registeredIcons[ iconID ].m_type = type;
s_registeredIcons[ iconID ].m_location = location;
s_registeredIcons[ iconID ].m_dynamicLocator = hudMapIconLocator;
// set visible in sunday drive mode for these icon types
//
if( type == HudMapIcon::ICON_PLAYER ||
type == HudMapIcon::ICON_PLAYER_CAR ||
type == HudMapIcon::ICON_MISSION ||
type == HudMapIcon::ICON_BONUS_MISSION ||
type == HudMapIcon::ICON_STREET_RACE ||
type == HudMapIcon::ICON_WAGER_RACE ||
type == HudMapIcon::ICON_PURCHASE_CENTRE ||
type == HudMapIcon::ICON_PHONE_BOOTH ||
type == HudMapIcon::ICON_AI_HIT_N_RUN )
{
s_registeredIcons[ i ].m_visibilityMask |= VISIBLE_IN_SUNDAY_DRIVE;
}
// set visible in mission mode for these icon types
//
if( type == HudMapIcon::ICON_PLAYER ||
type == HudMapIcon::ICON_PLAYER_CAR ||
type == HudMapIcon::ICON_FLAG_CHECKERED ||
type == HudMapIcon::ICON_FLAG_WAYPOINT ||
type == HudMapIcon::ICON_COLLECTIBLE ||
type == HudMapIcon::ICON_MISSION ||
s_registeredIcons[ i ].IsAICarIcon() )
{
s_registeredIcons[ i ].m_visibilityMask |= VISIBLE_IN_MISSION;
}
// colour code AI car icons
//
if( s_registeredIcons[ i ].IsAICarIcon() )
{
s_registeredIcons[ i ].ApplyAICarIconColour();
}
break;
}
}
rAssertMsg( iconID != -1, "ERROR: *** There should have been an empty slot!\n" );
if( newFocalPoint )
{
// set as new focal point icon
//
s_fpIconID = iconID;
// whenever the target changes, we want to recompute its closest road segment
m_lastRoadSeg = NULL;
m_frameCount = -1; // reset frame count
}
return iconID;
}
//===========================================================================
// CHudMap::UnregisterIcon
//===========================================================================
// Description:
//
// Constraints: None.
//
// Parameters: None.
//
// Return:
//
//===========================================================================
void
CHudMap::UnregisterIcon( int iconID )
{
if( iconID != -1 )
{
rAssert( iconID < static_cast<int>( MAX_NUM_REGISTERED_ICONS ) );
if( s_registeredIcons[ iconID ].m_iconImage != NULL )
{
// return icon to inventory
//
this->AddIconToInventory( s_registeredIcons[ iconID ].m_type,
s_registeredIcons[ iconID ].m_iconImage );
s_registeredIcons[ iconID ].m_iconImage = NULL;
s_registeredIcons[ iconID ].m_visibilityMask = 0;
s_numRegisteredIcons--;
if( s_fpIconID == iconID )
{
s_fpIconID = -1;
}
}
}
else
{
rAssertMsg( false, "This should only happen if you didn't get a valid icon ID upon registration!" );
}
}
//=============================================================================
// CHudMap::ChangeIconType
//=============================================================================
// Description: Comment
//
// Parameters: ( HudMapIcon::eIconType type, int iconID )
//
// Return: void
//
//=============================================================================
int
CHudMap::ChangeIconType( int iconID, HudMapIcon::eIconType type )
{
int newIconID = -1;
if( iconID != -1 )
{
bool isFocalPointIcon = (iconID == s_fpIconID);
// save a copy of old icon
//
HudMapIcon* oldIcon = &s_registeredIcons[ iconID ];
// unregister it first
//
this->UnregisterIcon( iconID );
// re-registered icon of new type
//
newIconID = this->RegisterIcon( type, oldIcon->m_location, oldIcon->m_dynamicLocator, isFocalPointIcon );
}
return newIconID;
}
//===========================================================================
// CHudMap::SetFocalPointIcon
//===========================================================================
// Description:
//
// Constraints: None.
//
// Parameters: None.
//
// Return:
//
//===========================================================================
void
CHudMap::SetFocalPointIcon( int iconID )
{
if( iconID != -1 )
{
rAssert( iconID < static_cast< int >( MAX_NUM_REGISTERED_ICONS ) );
s_fpIconID = iconID;
// whenever the target changes, we want to recompute its closest road segment
m_lastRoadSeg = NULL;
m_frameCount = -1; // reset frame count
}
}
//===========================================================================
// CHudMap::FindIcon
//===========================================================================
// Description:
//
// Constraints: None.
//
// Parameters: None.
//
// Return:
//
//===========================================================================
HudMapIcon*
CHudMap::FindIcon( HudMapIcon::eIconType type ) const
{
const HudMapIcon* icon = NULL;
for( unsigned int i = 0; i < MAX_NUM_REGISTERED_ICONS; i++ )
{
if( s_registeredIcons[ i ].m_iconImage != NULL &&
s_registeredIcons[ i ].m_type == type )
{
// found it!
//
icon = &(s_registeredIcons[ i ]);
break;
}
}
return const_cast<HudMapIcon*>( icon );
}
//===========================================================================
// CHudMap::Translate
//===========================================================================
// Description:
//
// Constraints: None.
//
// Parameters: None.
//
// Return:
//
//===========================================================================
void CHudMap::Translate( int x, int y )
{
rAssert( m_p3dMap );
m_originalPosX += x;
m_originalPosY += y;
m_p3dMap->SetPosition( m_originalPosX, m_originalPosY );
if( m_p3dHole != NULL )
{
m_p3dHole->SetPosition( m_originalPosX, m_originalPosY );
}
this->Update( 0 );
}
//===========================================================================
// CHudMap::Reset
//===========================================================================
// Description:
//
// Constraints: None.
//
// Parameters: None.
//
// Return:
//
//===========================================================================
void CHudMap::Reset()
{
rAssert( m_p3dMap != NULL );
// restore hud map position and size
//
m_p3dMap->SetPosition( m_originalPosX, m_originalPosY );
m_p3dMap->SetBoundingBoxSize( m_originalWidth, m_originalHeight );
if( m_p3dHole != NULL )
{
m_p3dHole->SetPosition( m_originalPosX, m_originalPosY );
m_p3dHole->SetBoundingBoxSize( m_originalWidth, m_originalHeight );
}
// restore hud map camera
//
m_p3dMap->SetCamera( m_hudMapCam->GetCamera() );
m_elapsedTime = 0;
this->Update( 0 );
}
//===========================================================================
// CHudMap::SetVisible
//===========================================================================
// Description: Toggle visibility of 3D map.
//
// Constraints: None.
//
// Parameters: visibility
//
// Return:
//
//===========================================================================
void CHudMap::SetVisible( bool isVisible )
{
m_isVisible = isVisible;
rAssert( m_p3dMap != NULL );
m_p3dMap->SetVisible( isVisible );
rAssert( m_radar != NULL );
m_radar->SetVisible( isVisible );
rAssert( m_iconsGroup != NULL );
m_iconsGroup->SetVisible( isVisible );
}
void
CHudMap::RestoreAllRegisteredIcons()
{
if( s_numRegisteredIcons > 0 )
{
int numIconsRestored = 0;
for( unsigned int i = 0; i < MAX_NUM_REGISTERED_ICONS; i++ )
{
if( s_registeredIcons[ i ].m_iconImage != NULL )
{
s_registeredIcons[ i ].m_iconImage = this->RemoveIconFromInventory( s_registeredIcons[ i ].m_type );
rAssert( s_registeredIcons[ i ].m_iconImage != NULL );
if( s_registeredIcons[ i ].IsAICarIcon() )
{
s_registeredIcons[ i ].ApplyAICarIconColour();
}
numIconsRestored++;
}
}
rAssertMsg( numIconsRestored == s_numRegisteredIcons, "Not all registered icons were restored!" );
}
}
void
CHudMap::ClearAllRegisteredIcons()
{
memset( s_registeredIcons, 0, sizeof( s_registeredIcons ) );
s_numRegisteredIcons = 0;
s_fpIconID = -1;
}
//===========================================================================
// Private Member Functions
//===========================================================================
//===========================================================================
// CHudMap::CalculateDistanceBetweenPoints
//===========================================================================
// Description: Calculates the scalar distance between 2 points in 3D-space.
//
// Constraints: None.
//
// Parameters: None.
//
// Return:
//
//===========================================================================
float
CHudMap::CalculateDistanceBetweenPoints( rmt::Vector& a, rmt::Vector& b )
{
rmt::Vector displacement( 0.0f, 0.0f, 0.0f );
displacement.Sub( a, b );
return displacement.Magnitude();
}
//===========================================================================
// CHudMap::UpdateIconHeading
//===========================================================================
// Description:
//
// Constraints: None.
//
// Parameters: None.
//
// Return:
//
//===========================================================================
void
CHudMap::UpdateIconHeading( Scrooby::Sprite* iconImage, rmt::Vector* iconHeading )
{
rmt::Vector objectHeading( iconHeading->x, 0, iconHeading->z );
rmt::Vector camHeading;
tPointCamera* camera = (tPointCamera*)m_p3dMap->GetCamera();
rAssert( camera );
camera->GetVUp( &camHeading );
camHeading.y = 0.0f;
if( camHeading.MagnitudeSqr() > 0.0f && objectHeading.MagnitudeSqr() > 0.0f )
{
float ratio = camHeading.Dot( objectHeading ) /
(camHeading.Magnitude() * objectHeading.Magnitude());
float rotation = 0.0f;
if( ratio > 1.0f )
{
rotation = 0.0f;
}
else if( ratio < -1.0f )
{
rotation = rmt::PI;
}
else
{
rotation = rmt::ACos( ratio );
}
rAssert( !rmt::IsNan( rotation ) );
rmt::Vector normal = camHeading;
normal.CrossProduct( objectHeading );
if( normal.y < 0 )
{
rotation = -rotation;
}
rAssert( iconImage );
iconImage->RotateAboutCenter( rmt::RadianToDeg( rotation ) );
}
}
//===========================================================================
// CHudMap::CalculateCameraHeight
//===========================================================================
// Description:
//
// Constraints: None.
//
// Parameters: None.
//
// Return:
//
//===========================================================================
float
CHudMap::CalculateCameraHeight( rmt::Vector& player, rmt::Vector& mission ) const
{
// fix locations at sea level
//
player.y = 0;
mission.y = 0;
float distance = CalculateDistanceBetweenPoints( player, mission );
// calculate camera height based on distance
//
float cameraHeight = this->CalculateCameraHeight( distance * 1.5f );
if( cameraHeight < MIN_CAMERA_HEIGHT )
{
cameraHeight = MIN_CAMERA_HEIGHT;
}
else if( cameraHeight > MAX_CAMERA_HEIGHT )
{
cameraHeight = MAX_CAMERA_HEIGHT;
}
return cameraHeight;
}
//===========================================================================
// CHudMap::CalculateCameraHeight
//===========================================================================
// Description:
//
// Constraints: None.
//
// Parameters: None.
//
// Return:
//
//===========================================================================
float
CHudMap::CalculateCameraHeight( float visibleRadius ) const
{
float cameraHeight = visibleRadius;
if( rmt::Abs( m_hudMapCam->GetFOV() - rmt::PI_BY2 ) > 0.001f )
{
cameraHeight = visibleRadius / rmt::Tan( m_hudMapCam->GetFOV() / 2.0f );
}
return cameraHeight;
}
//===========================================================================
// CHudMap::CalculatRadarConeAngle
//===========================================================================
// Description:
//
// Constraints: None.
//
// Parameters: None.
//
// Return:
//
//===========================================================================
float
CHudMap::CalculatRadarConeAngle( rmt::Vector& iconLoc ) const
{
float radarConeAngle = 0.0f; // zero angle by default
if( iconLoc.y > 0.0f ) // icon is located in either Quadrant I or II
{
radarConeAngle = rmt::ATan( iconLoc.x / iconLoc.y );
}
else if( iconLoc.y < 0.0f ) // icon is located in either Quadrant III or IV
{
radarConeAngle = rmt::ATan( iconLoc.x / iconLoc.y ) + rmt::PI;
}
else // icon is located somewhere along the X-axis
{
if( iconLoc.x > 0.0f )
{
radarConeAngle = rmt::PI_BY2;
}
else if( iconLoc.x < 0.0f )
{
radarConeAngle = -rmt::PI_BY2;
}
}
return radarConeAngle;
}
//===========================================================================
// CHudMap::DetermineOnRoadLocation
//===========================================================================
// Description:
//
// Constraints: None.
//
// Parameters: None.
//
// Return:
//
//===========================================================================
// We return the target vector (where we want to direct the navigation cone)
void
CHudMap::DetermineOnRoadLocation( rmt::Vector& source, rmt::Vector& target, float visibleRadius )
{
// place source and target at sea level
//
source.y = 0;
target.y = 0;
float distance = CalculateDistanceBetweenPoints( source, target );
if( distance <= visibleRadius )
{
// no need to do any calculations if target is within 'visibleRadius' from source
//
return;
}
// check to see if we should skip this update and return cached information instead
//
m_frameCount = (m_frameCount + 1) % NUM_AMORTIZED_UPDATE_FRAMES;
if( m_frameCount != 0 )
{
target = m_lastOnRoadLocation;
return;
}
// get reference to road manager
//
RoadManager* roadManager = RoadManager::GetInstance();
rAssert( roadManager != NULL );
//////////////
// find path between source and target
//
bool isPathElementFound = false;
float searchRadius = 200.0f;
float dummy;
// Find source's closest path element
RoadSegment* sourceSeg = NULL;
float sourceSegT = 0.0f;
RoadManager::PathElement sourceElem;
float sourceRoadT = 0.0f;
isPathElementFound = VehicleAI::FindClosestPathElement( source,
sourceElem, sourceSeg, sourceSegT, sourceRoadT, false ); // don't want shortcuts on hudmap
if( !isPathElementFound )
{
GetIntersectManager()->FindClosestRoad( source, searchRadius, sourceSeg, dummy ); // ignore shortcuts
rAssertMsg( sourceSeg, "Should always find a nearby road seg! Increase search radius!" );
if( !sourceSeg )
{
return;
}
sourceElem.type = RoadManager::ET_NORMALROAD;
sourceElem.elem = sourceSeg->GetRoad();
sourceSegT = roadManager->DetermineSegmentT( source, sourceSeg );
sourceRoadT = roadManager->DetermineRoadT( sourceSeg, sourceSegT );
}
rAssert( sourceElem.elem );
// Find target's closest path element
RoadSegment* targetSeg = NULL;
float targetSegT = 0.0f;
RoadManager::PathElement targetElem;
float targetRoadT = 0.0f;
isPathElementFound = VehicleAI::FindClosestPathElement( target,
targetElem, targetSeg, targetSegT, targetRoadT, false ); // don't want shortcuts on hudmap
if( !isPathElementFound )
{
RoadSegment* closestRoadSeg = NULL;
if( m_lastRoadSeg )
{
closestRoadSeg = m_lastRoadSeg;
}
else
{
GetIntersectManager()->FindClosestRoad( target, searchRadius, closestRoadSeg, dummy );
rAssertMsg( closestRoadSeg, "Should always find a nearby road seg! Increase search radius!" );
if( !closestRoadSeg )
{
return;
}
m_lastRoadSeg = closestRoadSeg;
}
targetElem.type = RoadManager::ET_NORMALROAD;
targetElem.elem = closestRoadSeg->GetRoad();
targetSeg = closestRoadSeg;
targetSegT = roadManager->DetermineSegmentT( target, targetSeg );
targetRoadT = roadManager->DetermineRoadT( targetSeg, targetSegT );
}
else
{
// need to make this check cuz FindClosestPathElement could return an intersection
if( targetSeg )
{
m_lastRoadSeg = targetSeg;
}
}
rAssert( targetElem.elem );
HeapMgr()->PushHeap( GMA_TEMP );
SwapArray<RoadManager::PathElement> pathElements;
pathElements.Allocate( roadManager->GetMaxPathElements() );
RoadManager::PathElement tmpSrcElem = sourceElem;
float roadDistance = roadManager->FindPathElementsBetween( false,
tmpSrcElem, sourceRoadT, source,
targetElem, targetRoadT, target,
pathElements );
rAssert( pathElements.mUseSize > 0 );
// Temp stuff we use over and over again
RoadManager::PathElement* prevElem = &(pathElements[0]);
bool isRoadBackwards = false;
int numIntersects = 0;
rmt::Vector intPts[2];
rmt::Sphere visibleSphere( source, visibleRadius ); // this is the hud map circle
// find closest path element that intersects visible sphere (or circle, really)
//
bool isClosestPathElementFound = false;
if( pathElements.mUseSize == 1 )
{
// if only one element returned
if( pathElements[0].type == RoadManager::ET_INTERSECTION )
{
// if the only elem is an intersection, just point to target
numIntersects = IntersectLineSphere( source, target, visibleSphere, intPts );
rAssert( numIntersects == 1 );
target = intPts[0];
}
else // the only element is a road
{
Road* theRoad = (Road*) targetElem.elem;
// we know here that one element returned is either cuz:
// A) source and target lie on same road
// or
// B) source and target lie on opposite roads that
// describe the same physical road (target road is returned)
// case B
if( sourceElem != targetElem )
{
// iterate through target road's segments to find the closest seg
// to source, the t value of this segment will tell us whether to
// traverse the segments backwards or forwards...
//
rmt::Vector closestPos;
float closestDist;
int closestSegIndex;
RoadManager::FindClosestPointOnRoad( theRoad, source, closestPos, closestDist, closestSegIndex );
rAssert( 0 <= closestSegIndex && closestSegIndex < (int) theRoad->GetNumRoadSegments() );
RoadSegment* closestSeg = theRoad->GetRoadSegment( (unsigned int) closestSegIndex );
rAssert( closestSeg );
sourceElem.elem = theRoad;
sourceSeg = closestSeg;
sourceSegT = RoadManager::DetermineSegmentT( closestPos, closestSeg );
sourceRoadT = RoadManager::DetermineRoadT( sourceSeg, sourceSegT );
}
rAssert( sourceElem == targetElem );
// this should now work for either A or B
if( sourceRoadT > targetRoadT )
{
isRoadBackwards = true;
}
unsigned int startIndex = sourceSeg->GetSegmentIndex();
unsigned int endIndex = targetSeg->GetSegmentIndex();
bool foundIntersect = false;
if( !isRoadBackwards )
{
for( unsigned int i=startIndex; i<=endIndex; i++ )
{
RoadSegment* seg = theRoad->GetRoadSegment( i );
rmt::Vector vec0, vec1, vec2, vec3, start, end;
seg->GetCorner( 0, vec0 );
seg->GetCorner( 1, vec1 );
seg->GetCorner( 2, vec2 );
seg->GetCorner( 3, vec3 );
start = (vec0 + vec3) * 0.5f;
start.y = 0.0f;
end = (vec1 + vec2) * 0.5f;
end.y = 0.0f;
int numIntersects = IntersectLineSphere( start, end, visibleSphere, intPts );
rAssert( 0 <= numIntersects && numIntersects <= 1 );
if( numIntersects == 1 )
{
target = intPts[0];
isClosestPathElementFound = true;
break;
}
}
}
else
{
for( int i=(int)startIndex; i>=(int)endIndex; i-- )
{
RoadSegment* seg = theRoad->GetRoadSegment( (unsigned int)i );
rmt::Vector vec0, vec1, vec2, vec3, start, end;
seg->GetCorner( 0, vec0 );
seg->GetCorner( 1, vec1 );
seg->GetCorner( 2, vec2 );
seg->GetCorner( 3, vec3 );
start = (vec0 + vec3) * 0.5f;
start.y = 0.0f;
end = (vec1 + vec2) * 0.5f;
end.y = 0.0f;
int numIntersects = IntersectLineSphere( start, end, visibleSphere, intPts );
rAssert( 0 <= numIntersects && numIntersects <= 1 );
if( numIntersects == 1 )
{
target = intPts[0];
isClosestPathElementFound = true;
break;
}
}
}
}
}
else
{
// At this point, we're dealing with multiple path elements
//
Intersection* previousIntersection = NULL;
for( int i = 0; i < pathElements.mUseSize; i++ )
{
if( isClosestPathElementFound )
{
// we're done! we found the closest path element
//
break;
}
RoadManager::PathElement* currentPathElement = &( pathElements[ i ] );
rAssert( currentPathElement != NULL );
switch( currentPathElement->type )
{
case RoadManager::ET_NORMALROAD:
{
Road* road = static_cast<Road*>( currentPathElement->elem );
rAssert( road != NULL );
int numRoadSegments = static_cast<int>( road->GetNumRoadSegments() );
// determine which direction to iterate over all road segments
//
bool isRoadBackwards = false;
if( i == 0 ) // special case if first path element is a road
{
for( int j = 1; j < pathElements.mUseSize; j++ )
{
RoadManager::PathElement* pathElement = &( pathElements[ j ] );
rAssert( pathElement != NULL );
if( pathElement->type == RoadManager::ET_INTERSECTION )
{
isRoadBackwards = ( road->GetSourceIntersection() == static_cast<Intersection*>( pathElement->elem ) );
break;
}
}
}
else
{
isRoadBackwards = ( road->GetSourceIntersection() != previousIntersection );
}
int currentRoadSegmentIndex = isRoadBackwards ? numRoadSegments - 1 : 0;
while( currentRoadSegmentIndex >= 0 && currentRoadSegmentIndex < numRoadSegments )
{
RoadSegment* currentRoadSegment = road->GetRoadSegment( currentRoadSegmentIndex );
rmt::Vector vec0, vec1, vec2, vec3, start, end;
currentRoadSegment->GetCorner( 0, vec0 );
currentRoadSegment->GetCorner( 1, vec1 );
currentRoadSegment->GetCorner( 2, vec2 );
currentRoadSegment->GetCorner( 3, vec3 );
start = (vec0 + vec3) * 0.5f;
start.y = 0.0f;
end = (vec1 + vec2) * 0.5f;
end.y = 0.0f;
rmt::Vector intersectPoints[ 2 ];
int numIntersectPoints = IntersectLineSphere( start, end, visibleSphere, intersectPoints );
if( numIntersectPoints > 0 )
{
rmt::Vector closestIntersectPoint = intersectPoints[ 0 ];
// TC [TODO]: if more than one intersection points, need to determine which
// one is closest to source
//
if( numIntersectPoints > 1 )
{
rTuneWarningMsg( false, "Multiple intersection points not yet handled properly, but this should not happen here!" );
}
// check to make sure that this point is, in fact, closer to the target than the source
//
if( i == 0 ) // only need to check this for the first path element
{
float roadSegT = roadManager->DetermineSegmentT( closestIntersectPoint, const_cast<RoadSegment*>( currentRoadSegment ) );
float intersectRoadT = roadManager->DetermineRoadT( const_cast<RoadSegment*>( currentRoadSegment ), roadSegT );
if( ( isRoadBackwards && intersectRoadT < sourceRoadT) ||
(!isRoadBackwards && intersectRoadT > sourceRoadT) )
{
target = closestIntersectPoint;
isClosestPathElementFound = true;
break; // stop iterating over road segments
}
else
{
int dummy = 0;
}
}
else
{
target = closestIntersectPoint;
isClosestPathElementFound = true;
break; // stop iterating over road segments
}
}
currentRoadSegmentIndex += isRoadBackwards ? -1 : +1;
}
break;
}
case RoadManager::ET_INTERSECTION:
{
Intersection* intersection = static_cast<Intersection*>( currentPathElement->elem );
rAssert( intersection != NULL );
// keep track of previous intersection
//
previousIntersection = intersection;
rmt::Vector intersectionLocation;
intersection->GetLocation( intersectionLocation );
intersectionLocation.y = 0.0f;
float distance = CalculateDistanceBetweenPoints( source, intersectionLocation );
if( rmt::Abs( distance - visibleRadius ) <= intersection->GetRadius() )
{
// project target onto radar edge, on a point that is closest to the
// intersection center point
//
rmt::Vector sourceToIntersection;
sourceToIntersection.Sub( intersectionLocation, source );
sourceToIntersection.Scale( visibleRadius / distance );
target.Add( source, sourceToIntersection );
isClosestPathElementFound = true;
}
break;
}
default:
{
rAssertMsg( false, "Invalid path element type!" );
break;
}
}
}
}
HeapMgr()->PopHeap( GMA_TEMP );
target.y = 0.0f;
m_lastOnRoadLocation = target;
#ifndef RAD_RELEASE
if( !isClosestPathElementFound )
{
// TC: [INVESTIGATE] if closest path element is not found, there must be some discontinuity
// in the closest path found
//
rTuneWarningMsg( false, "Closest path element not found! Please go tell Tony." );
}
#endif
}