#include "Peds/Ped.h" #include "vehicleAi/FlyingVehicleAvoidance.h" #include "Vehicles/vehicle.h" #include "Vehicles/Planes.h" #include "grcore/debugdraw.h" #include ////////////////////////////////////////////////////////////////////////// // 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 CFlyingVehicleAvoidanceManager::m_Vehicles; atFixedArray 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(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& 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(); }