Files
GTASource/game/vehicleAi/FlyingVehicleAvoidance.cpp

830 lines
29 KiB
C++
Raw Permalink Normal View History

2025-02-23 17:40:52 +08:00
#include "Peds/Ped.h"
#include "vehicleAi/FlyingVehicleAvoidance.h"
#include "Vehicles/vehicle.h"
#include "Vehicles/Planes.h"
#include "grcore/debugdraw.h"
#include <float.h>
//////////////////////////////////////////////////////////////////////////
// Helper class used to prevent flying vehicles from colliding with each other
// If you have a lot of vehicles in the air doing the same thing, you should be calling SlideDestinationTarget each frame to adjust the target position
// Generally, SteerAvoidCollisions should be called each frame which will adjust the target velocity based on various criteria
//////////////////////////////////////////////////////////////////////////
bool CFlyingVehicleAvoidanceManager::s_drawAvoid = false;
atFixedArray<RegdVeh, MAX_FLYING_VEHICLES_AVOIDANCE> CFlyingVehicleAvoidanceManager::m_Vehicles;
atFixedArray<Vector3, MAX_FLYING_VEHICLE_DESTINATIONS> CFlyingVehicleAvoidanceManager::m_Destinations;
//make tuneables
float CFlyingVehicleAvoidanceManager::s_fMinSpeedFlockAvoid = 4.0f;
float CFlyingVehicleAvoidanceManager::s_fLookAheadTime = 2.5f;
float CFlyingVehicleAvoidanceManager::s_fAvoidDistMultiplier = 10.0f;
float CFlyingVehicleAvoidanceManager::s_fSightConeAngle = 30.0f;
float CFlyingVehicleAvoidanceManager::s_fGetOutWayTime = 0.15f;
Vector3 RandomDirections[20] =
{
Vector3(0.0f, 0.0f, 1.0f),
Vector3(0.0f, 0.0f, 0.0f), //-1.0f),
Vector3(0.0f, 1.0f, 0.0f),
Vector3(0.0f, -1.0f, 0.0f),
Vector3(1.0f, 0.0f, 0.0f),
Vector3(-1.0f, 0.0f, 0.0f),
Vector3(0.0f, 0.707f, 0.707f),
Vector3(0.0f, 0.707f, 0.0f), //-0.707f),
Vector3(0.0f, -0.707f, 0.707f),
Vector3(0.0f, -0.707f, 0.0f), //-0.707f),
Vector3(0.0f, 0.707f, 0.0f), //-0.707f),
Vector3(0.707f, 0.0f, 0.707f),
Vector3(0.707f, 0.0f, 0.0f), //-0.707f),
Vector3(-0.707f, 0.0f, 0.707f),
Vector3(-0.707f, 0.0f, 0.0f), //-0.707f),
Vector3(0.707f, 0.0f, 0.0f), //-0.707f),
Vector3(0.707f, 0.707f, 0.0f ),
Vector3(0.707f, -0.707f, 0.0f ),
Vector3(-0.707f, 0.707f, 0.0f ),
Vector3(-0.707f, -0.707f, 0.0f ),
};
CFlyingVehicleAvoidanceManager::CFlyingVehicleAvoidanceManager():
m_vehicle(0)
,m_fAvoidanceScalar(0.0f)
,m_closeVehicles()
,m_fOutWayTimer(0.0f)
{
m_outTargetPosition.Zero();
m_outTargetVelocity.Zero();
m_currentTargetVelocity.Zero();
m_currentTargetPos.Zero();
m_holdTargetVelocity.Zero();
}
CFlyingVehicleAvoidanceManager::~CFlyingVehicleAvoidanceManager()
{
}
void CFlyingVehicleAvoidanceManager::SteerAvoidCollisions(Vector3& outTargetPosition, const Vector3& in_currentTargetVelocity, const Vector3& targetPos)
{
if(m_vehicle->InheritsFromPlane())
{
m_currentTargetPos = targetPos;
m_currentTargetVelocity = in_currentTargetVelocity;
m_outTargetPosition = m_currentTargetPos;
bool bPreventYaw;
UpdatePlaneAvoidance(bPreventYaw);
#if __BANK
if (s_drawAvoid)
{
if(m_outTargetPosition.Dist(outTargetPosition) > 0.0f)
{
grcDebugDraw::Sphere(m_outTargetPosition, 2.0f, Color_green, false, -1);
grcDebugDraw::Line(m_vehicle->GetTransform().GetPosition(), VECTOR3_TO_VEC3V(m_outTargetPosition), Color_orange2, -1);
}
}
#endif
outTargetPosition = m_outTargetPosition;
}
}
//main entry for flying vehicle avoidance
bool CFlyingVehicleAvoidanceManager::SteerAvoidCollisions(Vector3& outTargetVelocity, bool& preventYaw, const Vector3& in_currentTargetVelocity, const Vector3& targetPos)
{
TUNE_GROUP_BOOL(FLYING_AVOIDANCE, USE_OLD_AVOIDANCE, false);
if(USE_OLD_AVOIDANCE)
{
//keeping old system around for now
SteerDesiredVelocity_Old(outTargetVelocity, RegdVeh(m_vehicle), in_currentTargetVelocity, m_fAvoidanceScalar);
}
else
{
m_currentTargetVelocity = in_currentTargetVelocity;
m_outTargetVelocity = outTargetVelocity;
m_currentTargetPos = targetPos;
m_closeVehicles.clear();
if(m_vehicle->InheritsFromPlane())
{
UpdatePlaneAvoidance(preventYaw);
}
else if(m_vehicle->InheritsFromBlimp())
{
//we're a blimp....lets hope other flying vehicles avoid us!
}
else if(m_vehicle->InheritsFromHeli())
{
UpdateHeliAvoidance(preventYaw);
}
outTargetVelocity = m_outTargetVelocity;
}
return outTargetVelocity.Dist2(in_currentTargetVelocity) > FLOAT_EPSILON;
}
//helis can move in 3axis, so we'll be setting a desired velocity that we want them to go in depending on their local state
void CFlyingVehicleAvoidanceManager::UpdateHeliAvoidance(bool& preventYaw)
{
preventYaw = false;
if(m_fOutWayTimer > 0.0f)
{
m_fOutWayTimer -= fwTimer::GetTimeStep();
m_outTargetVelocity = m_holdTargetVelocity;
preventYaw = true;
return;
}
float boundRadius = m_vehicle->GetBoundRadius();
boundRadius = square(boundRadius * 4 * m_fAvoidanceScalar * 2);
//B* 2114685, include planes. Player could be flying one in which case we have to do the avoiding ourself
int closeCount = GetCloseVehicles(m_vehicle, boundRadius, m_closeVehicles, false);
if(closeCount > 0)
{
#if __BANK
if (s_drawAvoid)
{
Vector3 vMyVehiclePosition = VEC3V_TO_VECTOR3(m_vehicle->GetTransform().GetPosition());
char text[32];
sprintf(text, "Num Close: %d",closeCount);
grcDebugDraw::Text(vMyVehiclePosition, Color_orange2, 0, 10, text);
grcDebugDraw::Circle(vMyVehiclePosition, Sqrtf(boundRadius), Color_green, Vector3(1.f,0.f,0.f), Vector3(0.f,1.f,0.f), false, false, -1);
for(int i = 0; i < m_closeVehicles.size(); ++i)
{
grcDebugDraw::Line(VECTOR3_TO_VEC3V(vMyVehiclePosition), m_closeVehicles[i].otherVehicle->GetTransform().GetPosition(), Color_orange2, -1);
}
}
#endif
float velocityScaler = 1.0f;
bool weAggressor;
bool reactingToVehicle = false;
int collideIndex = CheckFutureCollisions( weAggressor);
if( collideIndex >= 0 )
{
//we're going to collide with a vehicle soon, better do something about it
reactingToVehicle = SteerAvoidVehicle(*m_closeVehicles[collideIndex].otherVehicle, weAggressor, preventYaw, velocityScaler);
}
if(!reactingToVehicle)
{
//check if we suddenly want to go opposite direction
bool hasSteeringChange = SteerLargeTargetChange();
if(!hasSteeringChange)
{
hasSteeringChange = SteerAvoidOthers(preventYaw );
if(!hasSteeringChange)
{
//few vehicles nearby; steer to avoid the closest ahead
SteerDesiredVelocity();
}
}
}
//adjust target speed by a scaler set when we are avoiding close vehicles
//so we try and move to a better location, but also slow down if we need to
m_outTargetVelocity *= velocityScaler;
}
}
//We have a vehicles that's very close to us, or in our way, in this case we really just want to slow down and let it be
bool CFlyingVehicleAvoidanceManager::SteerLargeTargetChange()
{
//This needs to be target position
float currentSpeed = m_vehicle->GetAiXYSpeed();
Vector3 ourDirection = VEC3V_TO_VECTOR3(m_vehicle->GetTransform().GetForward());
Vector3 toTarget = m_currentTargetPos - VEC3V_TO_VECTOR3(m_vehicle->GetTransform().GetPosition());
toTarget.Normalize();
float directionDot = ourDirection.Dot(toTarget);
if( directionDot < 0.0f && m_currentTargetVelocity.Mag2() > 100.0f && currentSpeed < s_fMinSpeedFlockAvoid)
{
//in this case - allow us to keep moving, but only slowly - the vehicle AI will turn us and then we can move again
m_outTargetVelocity = m_currentTargetVelocity;
m_outTargetVelocity.Normalize();
m_outTargetVelocity *= s_fMinSpeedFlockAvoid;
return true;
}
return false;
}
//determine if we're likely to hit another vehicle in the future
int CFlyingVehicleAvoidanceManager::CheckFutureCollisions(bool& usAggresor)
{
TUNE_GROUP_BOOL(FLYING_AVOIDANCE, FUTURECOLLISIONGETOUTWAY, true);
int foundIndex = -1;
Vector3 vDirection;
vDirection.Normalize(m_vehicle->GetVelocity());
Vector3 vMyVehiclePosition = VEC3V_TO_VECTOR3(m_vehicle->GetTransform().GetPosition());
float boundRadius = m_vehicle->GetBoundRadius();
for(int i = 0; i < m_closeVehicles.GetCount(); i++)
{
float velDiff = (m_vehicle->GetVelocity() - m_closeVehicles[i].otherVehicle->GetVelocity()).Mag2();
if(velDiff > s_fMinSpeedFlockAvoid*s_fMinSpeedFlockAvoid )
{
//we have a high speed difference - we might need to do some aggressive avoidance
Vector3 vOtherVehiclePosition = VEC3V_TO_VECTOR3(m_closeVehicles[i].otherVehicle->GetTransform().GetPosition());
Vector3 vOtherVehicleFuturePos = vOtherVehiclePosition + ( m_closeVehicles[i].otherVehicle->GetVelocity() * s_fLookAheadTime );
Vector3 vFuturePosition = vMyVehiclePosition + ( m_vehicle->GetVelocity() * s_fLookAheadTime );
#if __BANK
if (s_drawAvoid)
{
grcDebugDraw::Sphere(vOtherVehicleFuturePos, 0.1f, Color_red, false, -1);
grcDebugDraw::Sphere(vFuturePosition, 0.1f, Color_blue, false, -1);
}
#endif
if(m_vehicle->GetAiXYSpeed() > 2.0f)
{
#if __BANK
if (s_drawAvoid)
{
grcDebugDraw::Line(VECTOR3_TO_VEC3V(vMyVehiclePosition), VECTOR3_TO_VEC3V(vFuturePosition), Color_green, -1);
}
#endif
//check our movement against their future position
Vector3 toFuturePos = vFuturePosition - vMyVehiclePosition;
const float vTValue = geomTValues::FindTValueOpenSegToPoint(vMyVehiclePosition, toFuturePos, vOtherVehicleFuturePos);
if ( vTValue > 0.0f && vTValue < 1.0f)
{
Vector3 vClosestPoint = vMyVehiclePosition + (toFuturePos * vTValue);
Vector3 vCenterToClosestDir = vClosestPoint - vOtherVehicleFuturePos;
float distance2 = vCenterToClosestDir.Mag2();
if ( distance2 > FLT_EPSILON && distance2 < boundRadius*boundRadius)
{
#if __BANK
if (s_drawAvoid)
{
grcDebugDraw::Sphere(vClosestPoint, 0.1f, Color_green, false, -1);
}
#endif
//we are going to hit
usAggresor = true;
foundIndex = i;
break;
}
}
}
else if(FUTURECOLLISIONGETOUTWAY)
{
#if __BANK
if (s_drawAvoid)
{
grcDebugDraw::Line(VECTOR3_TO_VEC3V(vOtherVehiclePosition), VECTOR3_TO_VEC3V(vOtherVehicleFuturePos), Color_red, -1);
}
#endif
//check their future movement against our position
Vector3 toFuturePos = vOtherVehicleFuturePos - vOtherVehiclePosition;
const float vTValue = geomTValues::FindTValueOpenSegToPoint(vOtherVehiclePosition, toFuturePos, vFuturePosition);
if ( vTValue > 0.0f && vTValue < 1.0f)
{
Vector3 vClosestPoint = vOtherVehiclePosition + (toFuturePos * vTValue);
Vector3 vCenterToClosestDir = vClosestPoint - vFuturePosition;
float distance2 = vCenterToClosestDir.Mag2();
if ( distance2 > FLT_EPSILON && distance2 < boundRadius*boundRadius)
{
#if __BANK
if (s_drawAvoid)
{
grcDebugDraw::Sphere(vClosestPoint, 0.1f, Color_red, false, -1);
}
#endif
//we are going to hit
usAggresor = false;
foundIndex = i;
break;
}
}
}
// Vector3 futurePosDirection = vFuturePosition - vOtherVehicleFuturePos;
// float futureDistance2 = futurePosDirection.Mag2();
// futurePosDirection.Normalize();
// float futurePosDot = futurePosDirection.Dot(vDirection);
//
// bool bAheadAndVeryClose = futureDistance2 < boundRadius && (FUTURECOLLISIONGETOUTWAY || futurePosDot > 0.0f);
// float fClosestThreshold2 = square(boundRadius * 2);
// //check they are close, and ahead of us within travel cone
// if (bAheadAndVeryClose || (futureDistance2 < fClosestThreshold2 && (futurePosDot > 0.0f && futurePosDot <= cos(s_fSightConeAngle*DtoR))))
// {
// usAggresor = m_vehicle->GetAiXYSpeed() > 2.0f;
// foundIndex = i;
// break;
// }
}
}
return foundIndex;
}
//We have a vehicles that's very close to us, or in our way, in this case we really just want to slow down and let it be
bool CFlyingVehicleAvoidanceManager::SteerAvoidVehicle(const CVehicle& in_OtherVehicle, bool usAggressor, bool& preventYaw, float& velocityScaler)
{
if(usAggressor)
{
//we are the ones moving fast, we should slow down
//m_outTargetVelocity *= velocityScaler;
float boundRadius = m_vehicle->GetBoundRadius();
float toTargetDist = VEC3V_TO_VECTOR3(in_OtherVehicle.GetTransform().GetPosition() - m_vehicle->GetTransform().GetPosition()).Mag();
velocityScaler = RampValue(toTargetDist, boundRadius, boundRadius * 4 * m_fAvoidanceScalar, 0.0f, 1.0f );
//check case where both vehicles are facing each other and not moving
Vector3 theirDirection = VEC3V_TO_VECTOR3(m_vehicle->GetTransform().GetForward());
if(theirDirection.Dot(m_currentTargetVelocity) < 0.0f)
{
if( (m_vehicle->GetAiXYSpeed() * in_OtherVehicle.GetAiXYSpeed()) < 1.0f)
{
//move to the right
m_outTargetVelocity = VEC3V_TO_VECTOR3(m_vehicle->GetTransform().GetRight());
m_outTargetVelocity.z = 0.0f;
return true;
}
}
}
else
{
//they are coming at us fast - we need to get out the way!
//for now, just steer away from vehicle in shortest normal
Vector3 ourFowardVel;
ourFowardVel.Normalize(m_vehicle->GetVelocity());
Vector3 toTarget = VEC3V_TO_VECTOR3(in_OtherVehicle.GetTransform().GetPosition() - m_vehicle->GetTransform().GetPosition());
Vector3 toTargetRight(toTarget.y, -toTarget.x, 0.0f);
toTarget.Normalize();
m_outTargetVelocity = toTargetRight * 10.0f;
if(ourFowardVel.Dot(toTargetRight) < 0.0f)
{
m_outTargetVelocity *= -1.0f;
}
m_outTargetVelocity.z = 0.0f;
preventYaw = true;
m_fOutWayTimer = s_fGetOutWayTime;
m_holdTargetVelocity = m_outTargetVelocity;
return true;
//TODO - we could set our target position using FindTargetVelocityAvoidingPosition
//to move us to point away line at bounds radius
}
return false;
}
//steers away from all local vehicles, value returned is new target position which is average of avoidance of all nearby vehicles
//in most cases, we'll want to slow down the vehicle alot until the nearby vehicles have gone away
//vehicles that rely on forward thrust cannot use this (i.e planes)
bool CFlyingVehicleAvoidanceManager::SteerAvoidOthers(bool& preventYaw)
{
TUNE_GROUP_FLOAT(FLYING_AVOIDANCE, AVOIDOTHERBOUNDRADIUS, 2.5f, 0.0f, 10.0, 0.1f);
TUNE_GROUP_BOOL(FLYING_AVOIDANCE, AVOIDOTHERALSOSLOW, true);
preventYaw = false;
Vector3 toTarget = m_currentTargetPos - VEC3V_TO_VECTOR3(m_vehicle->GetTransform().GetPosition());
if( m_vehicle->GetAiXYSpeed() < s_fMinSpeedFlockAvoid && toTarget.Mag2() < 100.0f)
{
Vector3 vMyVehiclePosition = VEC3V_TO_VECTOR3(m_vehicle->GetTransform().GetPosition());
float boundRadius = m_vehicle->GetBoundRadius();
Vector3 desiredVelocity = Vector3(Vector3::ZeroType);
int groupCount = 0;
float closestDistance = FLT_MAX;
for(int i = 0; i < m_closeVehicles.GetCount(); i++)
{
//if(!AVOIDOTHERALSOSLOW || m_closeVehicles[i].otherVehicle->GetAiXYSpeed() < s_fMinSpeedFlockAvoid )
{
float currentDist2 = m_closeVehicles[i].distSqr;
if ( currentDist2 < square(boundRadius * AVOIDOTHERBOUNDRADIUS) )
{
closestDistance = Min(closestDistance, currentDist2);
Vector3 vOtherVehiclePosition = VEC3V_TO_VECTOR3(m_closeVehicles[i].otherVehicle->GetTransform().GetPosition());
Vector3 vMeToOtherVehicle = vOtherVehiclePosition - vMyVehiclePosition;
vMeToOtherVehicle.Normalize();
//float dotVelocity = m_currentTargetVelocity.Dot(vMeToOtherVehicle);
//if(dotVelocity > QUARTER_PI)
{
//close vehicles have more force
float distActual = Sqrtf(currentDist2) - boundRadius;
float scale = s_fAvoidDistMultiplier/(Max(1.0f, distActual));
vMeToOtherVehicle.Scale(Max(scale, 1.0f));
desiredVelocity -= vMeToOtherVehicle;
++groupCount;
#if __BANK
if (s_drawAvoid)
{
grcDebugDraw::Line(VECTOR3_TO_VEC3V(vMyVehiclePosition), VECTOR3_TO_VEC3V(vOtherVehiclePosition), Color_red, -1);
grcDebugDraw::Arrow(VECTOR3_TO_VEC3V(vMyVehiclePosition), VECTOR3_TO_VEC3V(vMyVehiclePosition + vMeToOtherVehicle), 2, Color_blue, -1);
}
#endif
}
}
}
}
if( groupCount > 0)
{
desiredVelocity /= (float)groupCount;
//lerp with target velocity based on distance to closest vehicle
// float targetRatio = RampValue(Sqrtf(closestDistance), boundRadius, boundRadius * 2, 1.0f, 0.0f);
// desiredVelocity = desiredVelocity * ( 1.0f - targetRatio) + m_currentTargetVelocity * targetRatio;
//we only want slow movement when avoiding others
//desiredVelocity.Normalize();
//scale out velocity by factor of the closest vehicle to us
//float scaler = RampValue(sqrt(closestDistance),boundRadius, square(boundRadius * AVOIDOTHERBOUNDRADIUS), 1.0f, 0.0f);
//desiredVelocity *= scaler;
m_outTargetVelocity = desiredVelocity;
#if __BANK
if (s_drawAvoid)
{
grcDebugDraw::Circle(vMyVehiclePosition, boundRadius * AVOIDOTHERBOUNDRADIUS, Color_blue, Vector3(1.f,0.f,0.f),Vector3(0.f,1.f,0.f), false, false, -1);
grcDebugDraw::Arrow(VECTOR3_TO_VEC3V(vMyVehiclePosition), VECTOR3_TO_VEC3V(vMyVehiclePosition + m_outTargetVelocity), 2, Color_green, -1);
}
#endif
//we aren't trying to go to a target here, but are avoiding a position
//so we don't want to turn to position, we just want to make the vehicle start moving in that direction
preventYaw = true; //targetRatio < 0.25f;
return true;
}
}
return false;
}
//sets a new target position that touches edge of bounding circle around closest entity ahead of us in direction of original target
//only handles avoiding single vehicle; used when we've no nearby vehicles, but want to avoid those ahead of us premptivly
bool CFlyingVehicleAvoidanceManager::SteerDesiredVelocity()
{
bool steeringAdjusted = false;
TUNE_GROUP_BOOL(FLYING_AVOIDANCE, STEER_USE_POSITIONS, true);
m_outTargetVelocity = m_currentTargetVelocity;
float fSpeed = m_currentTargetVelocity.Mag();
if ( fSpeed >= s_fMinSpeedFlockAvoid )
{
Vector3 vDirection = m_currentTargetVelocity * (1 / fSpeed);
Vector3 vMyVehiclePosition = VEC3V_TO_VECTOR3(m_vehicle->GetTransform().GetPosition());
float fClosestThreshold2 = FLT_MAX;
for(int i = 0; i < m_closeVehicles.GetCount(); i++)
{
if ( m_closeVehicles[i].distSqr < fClosestThreshold2 )
{
fClosestThreshold2 = m_closeVehicles[i].distSqr;
float boundRadius = 0.0f;
if(m_vehicle->GetVehicleType() == VEHICLE_TYPE_PLANE)
{
boundRadius = (m_vehicle->GetBoundRadius() + m_closeVehicles[i].otherVehicle->GetBoundRadius()) * 2.0f;
}
else
{
boundRadius = m_vehicle->GetBoundRadius() + (m_closeVehicles[i].otherVehicle->GetBoundRadius() * 0.5f);
}
if(STEER_USE_POSITIONS)
{
//steers away from vehicles in our current direction of travel
Vector3 vFuturePosition = vMyVehiclePosition + vDirection * sqrt(m_closeVehicles[i].distSqr);
Vector3 vOtherVehiclePosition = VEC3V_TO_VECTOR3(m_closeVehicles[i].otherVehicle->GetTransform().GetPosition());
bool bAdjusted = FindTargetVelocityAvoidingPosition(m_outTargetVelocity, vMyVehiclePosition, vFuturePosition, vOtherVehiclePosition, boundRadius);
if(bAdjusted)
{
//planes use a target position to steer to, not a velocity, push target position to edge of radius around avoided vehicle
if(m_vehicle->GetVehicleType() == VEHICLE_TYPE_PLANE)
{
float fDistToCurrentTarget = (vOtherVehiclePosition - vMyVehiclePosition).Mag();
m_outTargetPosition = vMyVehiclePosition + (m_outTargetVelocity * (fDistToCurrentTarget + 5.0f));
}
//push new direction away from us by our desired speed
m_outTargetVelocity *= fSpeed;
steeringAdjusted = true;
}
}
else
{
//steers away from vehicles in our desired direction of travel
Vector3 vFuturePosition = vMyVehiclePosition + ( m_vehicle->GetVelocity() * s_fLookAheadTime );
Vector3 vOtherVehiclePosition = VEC3V_TO_VECTOR3(m_closeVehicles[i].otherVehicle->GetTransform().GetPosition());
bool bAdjusted = FindTargetVelocityAvoidingPosition(m_outTargetVelocity, vFuturePosition, m_currentTargetVelocity, vOtherVehiclePosition, boundRadius);
if(bAdjusted)
{
m_outTargetVelocity *= fSpeed;
steeringAdjusted = true;
}
}
}
}
}
return steeringAdjusted;
}
//planes in VTOL can avoid the same as helicopters, otherwise they only have forward thrust, so can only avoid vehicles ahead
void CFlyingVehicleAvoidanceManager::UpdatePlaneAvoidance(bool& preventYaw)
{
//Assertf(false,"This code has not been tested at all. It's up to you to test it before using it!");
preventYaw = false;
float boundRadius = m_vehicle->GetBoundRadius();
boundRadius = square(boundRadius * 4 * m_fAvoidanceScalar);
m_closeVehicles.clear();
CPlane* pPlane = static_cast<CPlane*>(m_vehicle);
if (pPlane->GetVerticalFlightModeAvaliable() && pPlane->GetVerticalFlightModeRatio() >= 1.0f)
{
//in VTOL mode
GetCloseVehicles(m_vehicle, boundRadius, m_closeVehicles, false);
//check soon to happen collisions
bool weAggressor;
int collideIndex = CheckFutureCollisions(weAggressor);
if( collideIndex >= 0 )
{
float tmp;
SteerAvoidVehicle(*m_closeVehicles[collideIndex].otherVehicle, weAggressor, preventYaw, tmp);
}
else
{
//no dangerous collisions - just stay wary of nearby vehicles
SteerAvoidOthers(preventYaw);
}
}
else
{
//TODO: Planes will only avoid player planes at the moment as risky enabling full avoidance
GetCloseVehicles(m_vehicle, boundRadius, m_closeVehicles, false, true);
//non-VTOL, only has forward thrust, so have to adjust target position
//and we'll be going fast - so best just to avoid vehicles we're going to collide with as last resort
bool weAggressor;
int collideIndex = CheckFutureCollisions(weAggressor);
if( collideIndex >= 0 )
{
SteerDesiredVelocity();
}
}
}
//return normalized direction to a point on circle with radius of avoidRadius around avoidPoint
//that is tangential to us and closest to direction of endPosition - startPosition
bool CFlyingVehicleAvoidanceManager::FindTargetVelocityAvoidingPosition(Vector3& outDirection, const Vector3& startPosition,
const Vector3& endPosition, const Vector3& avoidPoint, float avoidRadius)
{
Vector3 toFuturePos = endPosition - startPosition;
const float vTValue = geomTValues::FindTValueOpenSegToPoint(startPosition, toFuturePos, avoidPoint);
if ( vTValue > 0.0f ) //point is ahead of us
{
//gets point on line from start to end closest to avoid point
Vector3 vClosestPoint = startPosition + (toFuturePos * vTValue);
//offset from avoidPoint to point on line
Vector3 vCenterToClosestDir = vClosestPoint - avoidPoint;
float distance2 = vCenterToClosestDir.Mag2();
if ( distance2 > FLT_EPSILON && distance2 < avoidRadius*avoidRadius) //check the closest point is within our checking radius
{
float distance = sqrt(distance2);
vCenterToClosestDir *= 1/distance;
//push point on line away from avoidpoint to distance of avoidRadius
Vector3 vTargetOffset = avoidPoint + vCenterToClosestDir * avoidRadius;
//get offset from us to that point
Vector3 vAwayFromTarget = vTargetOffset - startPosition;
#if __BANK
if (s_drawAvoid)
{
grcDebugDraw::Circle(avoidPoint, avoidRadius, Color_green, Vector3(1.f,0.f,0.f),Vector3(0.f,1.f,0.f), false, false, -1);
grcDebugDraw::Arrow(VECTOR3_TO_VEC3V(avoidPoint), VECTOR3_TO_VEC3V(vTargetOffset), 0.2f, Color_blue, -1);
grcDebugDraw::Arrow(VECTOR3_TO_VEC3V(startPosition), VECTOR3_TO_VEC3V(vTargetOffset), 0.2f, Color_red, -1);
}
#endif
//don't allow us to go down
vAwayFromTarget.z = Max(vAwayFromTarget.z, 0.0f);
//normalize final result
outDirection.Normalize(vAwayFromTarget);
return true;
}
}
return false;
}
int CFlyingVehicleAvoidanceManager::GetCloseVehicles(const CVehicle* currentVehicle, float radius, atVector<CloseVehicle>& out_closeVehicles, bool excludeJets, bool bOnlyPlayers)
{
int count = 0;
for(int i = 0; i < m_Vehicles.GetCount(); i++)
{
if (m_Vehicles[i] && m_Vehicles[i] != currentVehicle && m_Vehicles[i]->IsInAir())
{
if( !excludeJets || !m_Vehicles[i]->InheritsFromPlane())
{
if(!bOnlyPlayers || !m_Vehicles[i]->GetDriver() || m_Vehicles[i]->GetDriver()->IsPlayer())
{
Vector3 vOtherVehiclePosition = VEC3V_TO_VECTOR3(m_Vehicles[i]->GetTransform().GetPosition());
Vector3 vMyVehiclePosition = VEC3V_TO_VECTOR3(currentVehicle->GetTransform().GetPosition());
Vector3 vMeToOtherVehicle = vOtherVehiclePosition - vMyVehiclePosition;
float fDistance2 = vMeToOtherVehicle.Mag2();
if ( fDistance2 < radius )
{
++count;
out_closeVehicles.push_back(CloseVehicle(m_Vehicles[i], fDistance2));
}
}
}
}
}
return count;
}
int CFlyingVehicleAvoidanceManager::CountCloseVehicles(const RegdVeh& currentVehicle, float radius)
{
int count = 0;
for(int i = 0; i < m_Vehicles.GetCount(); i++)
{
if (m_Vehicles[i] && m_Vehicles[i] != currentVehicle && m_Vehicles[i]->IsInAir())
{
Vector3 vOtherVehiclePosition = VEC3V_TO_VECTOR3(m_Vehicles[i]->GetTransform().GetPosition());
Vector3 vMyVehiclePosition = VEC3V_TO_VECTOR3(currentVehicle->GetTransform().GetPosition());
Vector3 vMeToOtherVehicle = vOtherVehiclePosition - vMyVehiclePosition;
float fDistance2 = vMeToOtherVehicle.Mag2();
if ( fDistance2 < radius )
{
++count;
}
}
}
return count;
}
void CFlyingVehicleAvoidanceManager::SteerDesiredVelocity_Old(Vector3& o_Velocity, const RegdVeh& in_Vehicle, const Vector3& in_Velocity, float in_fAvoidanceScalar)
{
o_Velocity = in_Velocity;
float fSpeed = in_Velocity.Mag();
if ( fSpeed >= SMALL_FLOAT )
{
Vector3 vDirection = in_Velocity * (1 / fSpeed);
Vector3 vMyVehiclePosition = VEC3V_TO_VECTOR3(in_Vehicle->GetTransform().GetPosition());
float boundRadius = in_Vehicle->GetBoundRadius();
float fClosestThreshold2 = square(boundRadius * 4 * in_fAvoidanceScalar);
for(int i = 0; i < m_Vehicles.GetCount(); i++)
{
if (m_Vehicles[i] && m_Vehicles[i] != in_Vehicle )
{
Vector3 vOtherVehiclePosition = VEC3V_TO_VECTOR3(m_Vehicles[i]->GetTransform().GetPosition());
Vector3 vMeToOtherVehicle = vOtherVehiclePosition - vMyVehiclePosition;
float fDistance2 = vMeToOtherVehicle.Mag2();
if ( fDistance2 < fClosestThreshold2 )
{
fClosestThreshold2 = fDistance2;
Vector3 vFuturePosition = vMyVehiclePosition + vDirection * fDistance2;
Vector3 vToFuturePosition = vFuturePosition - vMyVehiclePosition;
const float vTValue = geomTValues::FindTValueOpenSegToPoint(vMyVehiclePosition, vToFuturePosition, vOtherVehiclePosition);
if ( vTValue > 0.0f )
{
Vector3 vClosestPoint = vMyVehiclePosition + (vToFuturePosition * vTValue);
Vector3 vCenterToClosestDir = vClosestPoint - vOtherVehiclePosition;
float distance2 = vCenterToClosestDir.Mag2();
if ( distance2 > FLT_EPSILON && distance2 < boundRadius*boundRadius )
{
float distance = sqrt(distance2);
vCenterToClosestDir *= 1/distance;
Vector3 vTargetOffset = vOtherVehiclePosition + vCenterToClosestDir * boundRadius;
Vector3 vAwayFromTarget = vTargetOffset - vMyVehiclePosition;
vAwayFromTarget.z = Max(vAwayFromTarget.z, 0.0f);
vDirection.Normalize(vAwayFromTarget);
}
}
}
}
}
o_Velocity = vDirection* fSpeed;
}
}
//handles multiple vehicles that have the same target destination by pushing out destinations from the desired position
void CFlyingVehicleAvoidanceManager::SlideDestinationTarget(Vector3& o_Target, const RegdVeh& m_vehicle, const Vector3& in_Target, float in_fAvoidanceScalar, bool addTargetToList)
{
static float s_fTargetAvoidanceBoundScalar = 4;
static float s_fTargetAvoidanceStrengthScalar = 3;
o_Target = in_Target;
Vector3 vRepulsion = Vector3(0.0f, 0.0f, 0.0f);
float boundRadius = m_vehicle->GetBoundRadius();
float count = 0;
for(int i = 0; i < m_Destinations.GetCount(); i++)
{
Vector3 vDestinationToDestination = in_Target - m_Destinations[i];
vDestinationToDestination.z = Max(vDestinationToDestination.z * 1.5f, 0.0f); // don't allow negative z and add additional + z to compensate
if ( vDestinationToDestination.Mag2() <= FLT_EPSILON )
{
//exact same destination as us, so offset in random direction
vDestinationToDestination = RandomDirections[i];
}
//push destination away from other destinations
float fDistanceToMe2 = vDestinationToDestination.Mag2();
if ( fDistanceToMe2 < square(boundRadius*s_fTargetAvoidanceBoundScalar))
{
float fDistanceToMe = sqrt(Max(fDistanceToMe2, 0.01f));
float fBaseStrength = boundRadius * s_fTargetAvoidanceStrengthScalar * in_fAvoidanceScalar;
float fStrength = fBaseStrength / fDistanceToMe;
vRepulsion += vDestinationToDestination * fStrength;
count += 1.0f;
}
}
if ( count > 0)
{
vRepulsion *= 1.0f / count;
o_Target += vRepulsion;
}
//this is cleared every update
if(addTargetToList && m_Destinations.GetAvailable())
{
m_Destinations.Push(in_Target);
}
}
void CFlyingVehicleAvoidanceManager::RemoveVehicle(const RegdVeh& vehicle)
{
int index = m_Vehicles.Find(vehicle);
if(index != -1)
{
m_Vehicles.DeleteFast(index);
}
}
void CFlyingVehicleAvoidanceManager::AddVehicle(const RegdVeh& vehicle)
{
if(m_Vehicles.GetAvailable() && m_Vehicles.Find(vehicle) == -1)
{
m_Vehicles.Push(vehicle);
}
// Assertf(m_Vehicles.GetAvailable(), "Run out of space in flying vehicle avoidance. You might want to consider increasing number of vehicles");
#if __DEV
if(!m_Vehicles.GetAvailable())
{
Warningf("Run out of space in flying vehicle avoidance. You might want to consider increasing number of vehicles");
}
#endif
}
void CFlyingVehicleAvoidanceManager::Update()
{
for(int i = 0; i < m_Vehicles.GetCount(); )
{
if (!m_Vehicles[i] )
{
m_Vehicles.DeleteFast(i);
}
else
{
i++;
}
}
m_Destinations.clear();
}