Files
GTASource/game/vehicleAi/VehicleAILodManager.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

1904 lines
77 KiB
C++

#include "VehicleAILodManager.h"
// Game headers
#include "Camera/CamInterface.h"
#include "Camera/Viewports/ViewportManager.h"
#include "Camera/scripted/ScriptDirector.h"
#include "Control/replay/replay.h"
#include "debug/VectorMap.h"
#include "game/dispatch/Incidents.h"
#include "Peds/PedPopulation.h"
#include "Renderer/Occlusion.h"
#include "scene/lod/LodScale.h"
#include "Scene/World/GameWorld.h"
#include "Scene/EntityIterator.h"
#include "streaming/streaming.h"
#include "VehicleAI/VehicleAiLod.h"
#include "VehicleAI/VehicleIntelligence.h"
#include "VehicleAI/Task/TaskVehicleMissionBase.h"
#include "Vehicles/Vehicle.h"
#include "Vehicles/VehiclePopulation.h"
#include "Vehicles/virtualroad.h"
#include "ai/debug/system/AIDebugLogManager.h"
// Rage headers
#if __BANK
#include "Camera/CamInterface.h"
#include "Camera/Debug/DebugDirector.h"
#include "Camera/Gameplay/GameplayDirector.h"
#endif
#include "GrCore/DebugDraw.h"
AI_OPTIMISATIONS()
AI_VEHICLE_OPTIMISATIONS()
VEHICLE_OPTIMISATIONS()
PARAM(notimeslicevehicles, "Disable timesliced vehicle updates.");
PARAM(parkedsuperdummy, "Disallow parked vehicles to become super-dummy");
bank_bool CVehicleAILodManager::ms_bVehicleAILoddingActive = true;
bank_float CVehicleAILodManager::ms_fRealDistance = 15.0f;
bank_float CVehicleAILodManager::ms_fDummyLodDistance = 40.0f;
bank_float CVehicleAILodManager::ms_fSuperDummyLodDistance = 75.0f;
bank_float CVehicleAILodManager::ms_fSuperDummyLodDistanceParked = 40.0f;
bank_float CVehicleAILodManager::ms_fLodFarFromPopCenterDistance = 135.0f;
bank_float CVehicleAILodManager::ms_fOffscreenLodDistScale = 3.0f;
bank_bool CVehicleAILodManager::ms_bConsiderOccludedAsOffscreen = true;
bank_float CVehicleAILodManager::ms_fHysteresisDistance = 5.0f;
float CVehicleAILodManager::ms_fLodRangeScale = 1.0f;
int CVehicleAILodManager::ms_iTimeBetweenConversionAttempts = 2000;
int CVehicleAILodManager::ms_iTimeBetweenConversionToRealAttempts = 500;
bank_bool CVehicleAILodManager::ms_bNLodActive = true;
bank_s32 CVehicleAILodManager::ms_iNLodRealMax = 10;
bank_s32 CVehicleAILodManager::ms_iNLodDummyMax = 20;
bank_s32 CVehicleAILodManager::ms_iNLodNonTimeslicedMax = 14;
bank_s32 CVehicleAILodManager::ms_iNLodRealDriversAndPassengersMax = 24; // WAS 12 (PS3/XBox360)
bank_bool CVehicleAILodManager::ms_bUseDummyLod = true;
bank_bool CVehicleAILodManager::ms_bUseSuperDummyLod = true;
bool CVehicleAILodManager::ms_bUseSuperDummyLodForParked = true;
bank_bool CVehicleAILodManager::ms_bAllSuperDummys = false;
bank_bool CVehicleAILodManager::ms_bDeTimesliceTransmissionAndSleep = true;
bank_bool CVehicleAILodManager::ms_bDeTimesliceWheelCollisions = true;
bank_bool CVehicleAILodManager::ms_bConvertExteriorVehsToDummyWhenInInterior = true;
bank_bool CVehicleAILodManager::ms_bCollideWithOthersInSuperDummyMode = true;
bank_bool CVehicleAILodManager::ms_bCollideWithOthersInSuperDummyInactiveMode = true;
bank_bool CVehicleAILodManager::ms_bFreezeParkedSuperDummyWhenCollisionsNotLoaded = true;
bank_bool CVehicleAILodManager::ms_bUseRoadCamber = true;
bank_bool CVehicleAILodManager::ms_bAllowTrailersToConvertFromReal = true;
bank_bool CVehicleAILodManager::ms_bAllowAltRouteInfoForRearWheels = true;
bank_bool CVehicleAILodManager::ms_bDisablePhysicsInSuperDummyMode = true;
bool CVehicleAILodManager::ms_bDisableConstraintsForSuperDummyInactive = true;
u32 CVehicleAILodManager::ms_preferredNumRealVehiclesAroundPlayer = 5;
bool CVehicleAILodManager::ms_usePretendOccupantsSystem = true;
float CVehicleAILodManager::ms_makePedsIntoPretendOccupantsRangeOnScreen = 110.0f; // WAS 55.0f (PS3/XBox360)
float CVehicleAILodManager::ms_makePedsFromPretendOccupantsRangeOnScreen = 90.0f; // WAS 45.0f (PS3/XBox360)
float CVehicleAILodManager::ms_makePedsIntoPretendOccupantsRangeOffScreen = 20.0f;
float CVehicleAILodManager::ms_makePedsFromPretendOccupantsRangeOffScreen = 5.0f;
bank_float CVehicleAILodManager::ms_fSuperDummySteerSpeed = PI*0.75f;
bank_float CVehicleAILodManager::ms_fSuperDummyCloneHeadingToVelBlendRatio = 0.2f;
bool CVehicleAILodManager::ms_bUseTimeslicing = false;
u16 CVehicleAILodManager::ms_TimesliceMaxUpdatesPerFrame = CONFIGURED_FROM_FILE;
bank_u16 CVehicleAILodManager::ms_TimesliceMinUpdatePeriodInFrames = 4;
bank_float CVehicleAILodManager::ms_TimesliceAllowOnScreenDistSmall = 75.0f; // lowered to match superdummy LOD distance
bank_float CVehicleAILodManager::ms_TimesliceAllowOnScreenDistLarge = 150.0f;
u16 CVehicleAILodManager::ms_TimesliceCountdownUntilWrap = 0;
u16 CVehicleAILodManager::ms_TimesliceNumInLowLodCounting = 0;
u16 CVehicleAILodManager::ms_TimesliceNumInLowLodPrevious = 0;
s16 CVehicleAILodManager::ms_TimesliceLastIndex = 0;
s16 CVehicleAILodManager::ms_TimesliceFirstVehIndex = -1;
s16 CVehicleAILodManager::ms_TimesliceLastVehIndex = -1;
u32 CVehicleAILodManager::ms_dummyPathReconsiderLengthMs = 500;
float CVehicleAILodManager::ms_dummyMaxDistFromRoadsEdgeMult = 1.5f;
float CVehicleAILodManager::ms_dummyMaxDistFromRoadFromAboveMult = 0.0f;
float CVehicleAILodManager::ms_dummyMaxDistFromRoadFromBelowMult = 3.0f;
bool CVehicleAILodManager::ms_convertUseFixupCollisionWithWheelSurface = false;
bool CVehicleAILodManager::ms_convertEnforceDummyMustBeNearPaths = true;
bank_bool CVehicleAILodManager::ms_bAllowSkipUpdatesInTimeslicedLod = true;
bank_bool CVehicleAILodManager::ms_bSkipTimeslicedUpdatesWhileParked = true;
bank_bool CVehicleAILodManager::ms_bSkipTimeslicedUpdatesWhileStopped = true;
bank_u32 CVehicleAILodManager::ms_iMaxTimeslicedUpdatesToSkipWhileStopped = 1;
#if __BANK
bool CVehicleAILodManager::ms_bDisplaySkippedTimeslicedUpdates = false;
#endif // __BANK
bank_bool CVehicleAILodManager::ms_bDisableSuperDummyForSlowBikes = true;
bank_float CVehicleAILodManager::ms_fDisableSuperDummyForSlowBikesSpeed = 2.0f;
bank_s32 CVehicleAILodManager::ms_iDummyConstraintModePrivate = CVehicleAILodManager::DCM_PhysicalDistanceAndRotation;
bank_s32 CVehicleAILodManager::ms_iSuperDummyConstraintModePrivate = CVehicleAILodManager::DCM_PhysicalDistance;
bank_float CVehicleAILodManager::ms_fConstrainToRoadsExtraDistance = 1.0f;
bank_float CVehicleAILodManager::ms_fConstrainToRoadsExtraDistancePreJunction= 27.0f;
bank_float CVehicleAILodManager::ms_fConstrainToRoadsExtraDistanceAtJunction= 27.0f;
bank_float CVehicleAILodManager::ms_fPercentOfJunctionLengthToAddToConstraint = 0.4f;
#if __BANK
bool CVehicleAILodManager::ms_bForceDummyCars = false;
float CVehicleAILodManager::ms_fForceLodDistance = -1.0f;
bool CVehicleAILodManager::ms_bForceAllTimesliced = false;
bool CVehicleAILodManager::ms_displayDummyVehicleMarkers = false;
bool CVehicleAILodManager::ms_bDisplayDummyWheelHits = false;
bool CVehicleAILodManager::ms_bDisplayDummyPath = false;
bool CVehicleAILodManager::ms_bDisplayDummyPathDetailed = false;
bool CVehicleAILodManager::ms_bDisplayDummyPathConstraint = false;
bool CVehicleAILodManager::ms_bDisplayVirtualJunctions = false;
bool CVehicleAILodManager::ms_bRaiseDummyRoad = false;
bool CVehicleAILodManager::ms_bRaiseDummyJunctions = false;
float CVehicleAILodManager::ms_fRaiseDummyRoadHeight = 1.0f;
bool CVehicleAILodManager::ms_bUseDummyWheelTransition = true;
bool CVehicleAILodManager::ms_bDebug = false;
bool CVehicleAILodManager::ms_bVMLODDebug = false;
bool CVehicleAILodManager::ms_bDebugExtraInfo = false;
bool CVehicleAILodManager::ms_bDebugPedsInfo = false;
bool CVehicleAILodManager::ms_bDebugExtraWheelInfo = false;
bool CVehicleAILodManager::ms_bDebugScoring = false;
ENABLE_FRAG_OPTIMIZATION_ONLY(bool CVehicleAILodManager::ms_bDebugFragCacheInfo = false;)
float CVehicleAILodManager::ms_fDebugDrawRange = 300.0f;
bool CVehicleAILodManager::ms_displayDummyVehicleNonConvertReason = false;
bool CVehicleAILodManager::ms_bDisplayFullUpdates = false;
bool CVehicleAILodManager::ms_bValidateDummyConstraints = false;
float CVehicleAILodManager::ms_fValidateDummyConstraintsBuffer = 5.0f;
#endif
#if __DEV
bool CVehicleAILodManager::ms_focusVehBreakOnCheckAttemptPopConversion = false;
bool CVehicleAILodManager::ms_focusVehBreakOnAttemptedPopConversion = false;
bool CVehicleAILodManager::ms_focusVehBreakOnPopulationConversion = false;
#endif // __DEV
atArray<CVehicleAILodManager::CVehicleAiPriority> CVehicleAILodManager::ms_PrioritizedVehicleArray; // array of vehicle priorities, sorted by distance
Vec3V CVehicleAILodManager::ms_vCachedVehiclePriorityCenter; // cached population center, to determine if we need to recalculate vehicle priority based on distance moved
#define VEHICLE_PRIORITY_RECALCULATE_DISTANCE_SQUARED 100.0f // Distance popcenter has to move to trigger an immediate recalculate
u8 CVehicleAILodManager::ms_iVehiclePriorityCountdown = 0; // Current countdown timer
u8 CVehicleAILodManager::ms_iVehiclePriorityCountdownMax = 3; // Max counter timer = number of frames to spread vehicle lod update across
u32 CVehicleAILodManager::ms_iVehicleCount = 0;
float CVehicleAILodManager::ms_ExpectedWorstTimeslicedTimestep = 1.0f;
atQueue<float, CVehicleAILodManager::kFrameTimeMemorySize> CVehicleAILodManager::ms_FrameTimeMemory;
int CVehicleAILodManager::ms_iNLodRealRemaining = 0;
int CVehicleAILodManager::ms_iNLodDummyRemaining = 0;
s32 CVehicleAILodManager::ms_iNLodNonTimeslicedRemaining = 0;
int CVehicleAILodManager::ms_iNLodRealDriversAndPassengersRemaining = 0;
s32 CVehicleAILodManager::ms_iMostDistantRealPedsPrioritizedIndex = 0;
s32 CVehicleAILodManager::ms_iClosestPretendPedsPrioritizedIndex = 0;
s32 CVehicleAILodManager::ms_iNumVehiclesConvertedToRealPedsThisFrame = 0;
CVehicleAILodManager::EDummyConstraintMode CVehicleAILodManager::GetDummyConstraintMode()
{
return static_cast<CVehicleAILodManager::EDummyConstraintMode>(ms_iDummyConstraintModePrivate);
}
CVehicleAILodManager::EDummyConstraintMode CVehicleAILodManager::GetSuperDummyConstraintMode()
{
return static_cast<CVehicleAILodManager::EDummyConstraintMode>(ms_iSuperDummyConstraintModePrivate);
}
// Hack ahoy!
// Eventually all LOD state changing will happen via the CVehicleAILodManager::Update() function,
// at which point SetLodFlag() & GetLodFlag() function can be removed.
// But for now we still need a way for the vehicles themselves to manipulate their LOD flags from
// within CVehicle::TryToMakeIntoDummy()
void CVehicleAILodManager::SetLodFlag(CVehicle * pVehicle, const u32 iFlag)
{
pVehicle->GetVehicleAiLod().SetLodFlag(iFlag);
}
void CVehicleAILodManager::ClearLodFlag(CVehicle * pVehicle, const u32 iFlag)
{
pVehicle->GetVehicleAiLod().ClearLodFlag(iFlag);
}
bool CVehicleAILodManager::ShouldDoFullUpdate(CVehicle& veh)
{
// Don't update vehicles in the reuse pool
if(veh.GetIsInReusePool())
{
return false;
}
// First, if we are not in low LOD timeslicing mode, always return true.
if(!veh.GetVehicleAiLod().IsLodFlagSet(CVehicleAILod::AL_LodTimeslicing))
{
return true;
}
// Check if we are forced to update.
if(veh.m_nVehicleFlags.bLodForceUpdateThisTimeslice || veh.m_nVehicleFlags.bLodForceUpdateUntilNextAiUpdate)
{
return true;
}
if(veh.ShouldSkipUpdatesInTimeslicedLod())
{
return false;
}
return veh.GetVehicleAiLod().m_TimeslicedUpdateThisFrame;
}
int CVehicleAILodManager::ComputeRealDriversAndPassengersRemaining()
{
return Max(0, ComputeRealDriversAndPassengersMax() - CPedPopulation::ms_nNumInVehAmbient - CPedPopulation::GetNumScheduledPedsInVehicles());
}
int CVehicleAILodManager::ComputeRealDriversAndPassengersMax()
{
if(CWantedIncident::IsIncidentNearby(CWantedIncident::GetMaxDistanceForNearbyIncidents()))
{
static dev_s32 iNonLawInVehiclesPadCount = 5;
return Max(ms_iNLodRealDriversAndPassengersMax, (CPedPopulation::ms_nNumInVehCop + CPedPopulation::ms_nNumInVehSwat + iNonLawInVehiclesPadCount));
}
static dev_s32 iNonExemptPadCount = 5;
return Max(ms_iNLodRealDriversAndPassengersMax, ComputeRealDriversAndPassengersExceptions() + iNonExemptPadCount);
}
int CVehicleAILodManager::ComputeRealDriversAndPassengersExceptions()
{
return (CPedPopulation::ms_nNumInVehAmbientDead + CPedPopulation::ms_nNumInVehAmbientNoPretendModel);
}
void CVehicleAILodManager::DisableTimeslicingImmediately(CVehicle& veh)
{
CVehicleAILod& lod = veh.GetVehicleAiLod();
// If already in timesliced mode, immediately kick out of it.
if(lod.IsLodFlagSet(CVehicleAILod::AL_LodTimeslicing))
{
lod.ClearLodFlag(CVehicleAILod::AL_LodTimeslicing);
veh.SwitchAwayFromTimeslicedLod();
}
// Make sure we don't go back to timeslicing for at least a frame or so.
lod.SetBlockedLodFlag(CVehicleAILod::AL_LodTimeslicing);
// This shouldn't really be necessary, except possibly if some code explicitly
// unblocks timeslicing again.
veh.m_nVehicleFlags.bLodForceUpdateThisTimeslice = true;
}
void CVehicleAILodManager::ResetOnReuseVehicle(CVehicle& veh)
{
CVehicleAILod& lod = veh.GetVehicleAiLod();
// TODO: Perhaps we should do more to reset the LOD state here? For now, it seems
// safer to just turn off the timeslicing LOD, so we don't bypass any transition
// functions that may need to execute.
if(lod.IsLodFlagSet(CVehicleAILod::AL_LodTimeslicing))
{
veh.GetVehicleAiLod().ClearLodFlag(CVehicleAILod::AL_LodTimeslicing);
veh.SwitchAwayFromTimeslicedLod();
}
}
void CVehicleAILodManager::UpdateLodRangeScale()
{
//-----------------------------------------------------------------------------------------------
// Scale all real/dummy conversion ranges according to depth of the pop control into interiors
switch(CPedPopulation::GetPopulationControlPointInfo().m_locationInfo)
{
default:
Assert(false);
case LOCATION_EXTERIOR:
ms_fLodRangeScale = 1.0f;
break;
case LOCATION_SHALLOW_INTERIOR:
ms_fLodRangeScale = CVehiclePopulation::GetRangeScaleShallowInterior();
break;
case LOCATION_DEEP_INTERIOR:
ms_fLodRangeScale = CVehiclePopulation::GetRangeScaleDeepInterior();
break;
}
ms_fLodRangeScale = ms_fLodRangeScale * CVehiclePopulation::GetAllZoneScaler() * g_LodScale.GetGlobalScale();
}
eVehicleDummyMode CVehicleAILodManager::CalcDesiredLodForPoint(Vec3V_In vPos)
{
eVehicleDummyMode desiredMode = VDM_REAL;
const Vec3V vPopCenter = VECTOR3_TO_VEC3V(CPedPopulation::GetPopulationControlPointInfo().m_conversionCentre);
float fDistSq = DistSquared(vPos,vPopCenter).Getf();
const float fDummyLodDist = ms_fDummyLodDistance * ms_fLodRangeScale;
const float fSuperDummyLodDist = ms_fSuperDummyLodDistance * ms_fLodRangeScale;
if(fDistSq > square(fSuperDummyLodDist))
{
desiredMode = VDM_SUPERDUMMY;
}
else if(fDistSq > square(fDummyLodDist))
{
desiredMode = VDM_DUMMY;
}
#if __BANK
desiredMode = DevOverrideLod(desiredMode);
#endif
return desiredMode;
}
#if __BANK
eVehicleDummyMode CVehicleAILodManager::DevOverrideLod(eVehicleDummyMode lodIn)
{
eVehicleDummyMode lodOut = lodIn;
// Allow us to turn off the super-dummy system
if(!ms_bUseSuperDummyLod && lodIn == VDM_SUPERDUMMY)
{
lodOut = VDM_DUMMY;
}
// Force all real cars to dummy
if(ms_bForceDummyCars && lodIn == VDM_REAL)
{
lodOut = VDM_DUMMY;
}
// Allow us to turn off the dummy system
if(!ms_bUseDummyLod)
{
lodOut = VDM_REAL;
}
if(ms_bAllSuperDummys)
{
lodOut = VDM_SUPERDUMMY;
}
return lodOut;
}
#endif
void CVehicleAILodManager::InitValuesFromConfig()
{
ms_TimesliceMaxUpdatesPerFrame = static_cast<u16>(GET_POPULATION_VALUE(VehicleTimesliceMaxUpdatesPerFrame));
}
void CVehicleAILodManager::Init(unsigned initMode)
{
if(initMode == INIT_SESSION)
{
ms_bUseTimeslicing = !PARAM_notimeslicevehicles.Get();
if(PARAM_parkedsuperdummy.Get())
{
ms_bUseSuperDummyLodForParked = true;
}
InitValuesFromConfig();
}
}
// Initialize the prioritized vehicle array to the size of the vehicle pool
void CVehicleAILodManager::InitializeVehiclePriorityArray()
{
if(ms_PrioritizedVehicleArray.GetCount() == 0)
ms_PrioritizedVehicleArray.ResizeGrow((u32) CVehicle::GetPool()->GetSize());
}
void CVehicleAILodManager::Update()
{
#if GTA_REPLAY
if (CReplayMgr::IsEditModeActive())
return;
#endif
PF_AUTO_PUSH_TIMEBAR("VehicleAILodMgr Update");
#if __BANK
CVehicleAiLodTester::ValidateState();
CVehicleAiLodTester::ApplyChanges();
#endif
if (!CStreaming::IsPlayerPositioned())
{
return;
}
// Use local player position as default population control center
Vector3 popCtrlCentre = CGameWorld::FindLocalPlayerCoors();
// Check if script camera is in use
if( camInterface::GetScriptDirector().IsRendering() )
{
// use camera position as population control center
popCtrlCentre = camInterface::GetScriptDirector().GetFrame().GetPosition();
}
#if __BANK
if (ms_bDisplayVirtualJunctions)
{
CVirtualRoad::DebugDrawJunctions(RCC_VEC3V(popCtrlCentre));
}
#endif //__BANK
if(fwTimer::IsGamePaused())
return;
ms_iNumVehiclesConvertedToRealPedsThisFrame = 0;
UpdateExpectedWorstTimeslicedTimestep();
// Update LOD ranges
UpdateLodRangeScale();
const float fDummyLodDistance = ms_fDummyLodDistance*ms_fLodRangeScale;
const float fSuperDummyLodDistance = ms_fSuperDummyLodDistance*ms_fLodRangeScale;
const float fSuperDummyLodDistanceParked = ms_fSuperDummyLodDistanceParked*ms_fLodRangeScale;
// Check that the pop center hasn't moved far enough to invalidate our last check
// or if countdown has elapsed
if(!ms_iVehiclePriorityCountdown || (DistSquared(ms_vCachedVehiclePriorityCenter, VECTOR3_TO_VEC3V(CPedPopulation::GetPopulationControlPointInfo().m_conversionCentre)).Getf() > VEHICLE_PRIORITY_RECALCULATE_DISTANCE_SQUARED))
{
ms_iVehicleCount = FindVehiclesByPriority();
ms_vCachedVehiclePriorityCenter = VECTOR3_TO_VEC3V(CPedPopulation::GetPopulationControlPointInfo().m_conversionCentre);
ms_iVehiclePriorityCountdown = ms_iVehiclePriorityCountdownMax;
ms_iNLodRealRemaining = ms_iNLodRealMax;
ms_iNLodDummyRemaining = ms_iNLodDummyMax;
ms_iNLodNonTimeslicedRemaining = ms_iNLodNonTimeslicedMax;
ms_iNLodRealDriversAndPassengersRemaining = ComputeRealDriversAndPassengersRemaining();
// We are starting over from the beginning of the pool, reset the count of the number of vehicles in timeslicing LOD.
ms_TimesliceNumInLowLodCounting = 0;
// Keep track of most distant prioritized vehicle array index with real ped passengers.
// We will convert this vehicle to pretend occupants if we exceed the limit.
ms_iMostDistantRealPedsPrioritizedIndex = -1;
// Keep track of the closest prioritized vehicle array index with pretend occupants.
// This will serve as a marker when traversing the list from back to front.
ms_iClosestPretendPedsPrioritizedIndex = -1;
}
// batch the vehicles, closest to furthest
int batchCount = static_cast<int>(ceil(ms_iVehicleCount/(ms_iVehiclePriorityCountdownMax*1.f)));
int batchstart = batchCount * (ms_iVehiclePriorityCountdownMax- ms_iVehiclePriorityCountdown);
int batchend = Min<u32>(batchstart + batchCount, ms_iVehicleCount); // don't try to read outside of the valid entries of the array
// countdown to recalculate
ms_iVehiclePriorityCountdown--;
// Count the number of timesliced vehicles in this batch.
int timesliceNumInLowLodCounting = ms_TimesliceNumInLowLodCounting;
// Handle Pretend peds by batch as well
const int kMaxVehiclesConvertedToRealPedsPerFrame = 1;
int nNumVehiclesConvertedToRealPeds = 0;
//----------------------------------------------------------------------------------------------------
// Loop over vehicles by priority
for(int i = batchstart; i<batchend; i++)
{
CVehicle * pVehicle = ms_PrioritizedVehicleArray[i].m_pVehicle;
// check that the vehicle is still valid
if(!CVehicle::GetPool()->IsValidPtr(pVehicle))
continue;
if(pVehicle->GetIsInReusePool())
continue; // skip vehicles that are on hold for re-use
const float fDistanceSq = ms_PrioritizedVehicleArray[i].m_fEffectiveDistSq;
if(!CVehicleAILodManager::ms_bVehicleAILoddingActive)
{
pVehicle->GetVehicleAiLod().SetLodFlags(CVehicleAILod::AL_DefaultLodFlags);
}
else if(pVehicle->InheritsFromTrailer()
|| CVehicle::IsEntityAttachedToTrailer(pVehicle)
|| (pVehicle->GetDummyAttachmentParent() && pVehicle->GetDummyAttachmentParent()->InheritsFromTrailer())
|| pVehicle->GetIsBeingTowed())
{
// Attached trailers never process their own dummy state - they will convert with the cab
// ... as do all vehicles attached to trailers.
// ... as do all vehicles being towed by towtruck or cargobob
if(pVehicle->GetVehicleAiLod().IsLodFlagSet(CVehicleAILod::AL_LodTimeslicing))
{
pVehicle->GetVehicleAiLod().ClearLodFlag(CVehicleAILod::AL_LodTimeslicing);
pVehicle->SwitchAwayFromTimeslicedLod();
}
}
else
{
//-----------------------------------------------------
// Calc desired LOD
const eVehicleDummyMode currentMode = pVehicle->GetVehicleAiLod().GetDummyMode();
eVehicleDummyMode desiredMode = currentMode;
if( currentMode == VDM_REAL )
{
float fSuperDummyDist = fSuperDummyLodDistance;
// Use the parked super-dummy threshold if this vehicle is parked.
if(ms_bUseSuperDummyLodForParked && pVehicle->ConsiderParkedForLodPurposes())
{
fSuperDummyDist = fSuperDummyLodDistanceParked;
}
const float fDummyDist = fDummyLodDistance+ms_fHysteresisDistance;
if( (fDistanceSq > square(fSuperDummyDist)) && pVehicle->GetCanMakeIntoDummy(VDM_SUPERDUMMY) )
{
desiredMode = VDM_SUPERDUMMY;
}
else if( (fDistanceSq > square(fDummyDist)) && pVehicle->GetCanMakeIntoDummy(VDM_DUMMY) )
{
desiredMode = VDM_DUMMY;
}
}
else if( currentMode == VDM_DUMMY )
{
const float fRealDist = fDummyLodDistance-ms_fHysteresisDistance;
const float fSuperDummyDist = fSuperDummyLodDistance+ms_fHysteresisDistance;
// can the vehicle still be a dummy?
if (pVehicle->IsDummy() && !pVehicle->GetCanMakeIntoDummy(VDM_DUMMY))
{
//BANK_ONLY(aiDisplayf("Vehicle (%s) at (%0.2f,%0.2f) was dummy and failed GetCanMakeIntoDummy() because '%s'. Task is '%s'.",pVehicle->GetModelName(),pVehicle->GetVehiclePosition().GetXf(),pVehicle->GetVehiclePosition().GetYf(),pVehicle->GetNonConversionReason(),(pVehicle->GetIntelligence() && pVehicle->GetIntelligence()->GetActiveTask()) ? pVehicle->GetIntelligence()->GetActiveTask()->GetTaskName():"no task");)
desiredMode = VDM_REAL;
}
if( fDistanceSq < square(fRealDist) )
{
desiredMode = VDM_REAL;
}
else if( (fDistanceSq > square(fSuperDummyDist)) && pVehicle->GetCanMakeIntoDummy(VDM_SUPERDUMMY) )
{
desiredMode = VDM_SUPERDUMMY;
}
}
else if( currentMode == VDM_SUPERDUMMY )
{
const float fDummyDist = fSuperDummyLodDistance-ms_fHysteresisDistance;
float fRealDist = fDummyLodDistance;
// If this is a parked super-dummy vehicle, don't let the distance at which we turn it to
// real be any less than the distance at which we would have turned it to super-dummy
// (with some hysteresis).
if(ms_bUseSuperDummyLodForParked && pVehicle->ConsiderParkedForLodPurposes())
{
fRealDist = Min(fRealDist, fSuperDummyLodDistanceParked - ms_fHysteresisDistance);
}
// can the vehicle still be a super dummy?
if (pVehicle->IsDummy() && !pVehicle->GetCanMakeIntoDummy(VDM_SUPERDUMMY))
{
//BANK_ONLY(aiDisplayf("Vehicle (%s) at (%0.2f,%0.2f) was super-dummy and failed GetCanMakeIntoDummy() because '%s'. Task is '%s'.",pVehicle->GetModelName(),pVehicle->GetVehiclePosition().GetXf(),pVehicle->GetVehiclePosition().GetYf(),pVehicle->GetNonConversionReason(),(pVehicle->GetIntelligence() && pVehicle->GetIntelligence()->GetActiveTask()) ? pVehicle->GetIntelligence()->GetActiveTask()->GetTaskName():"no task");)
if (pVehicle->GetCanMakeIntoDummy(VDM_DUMMY))
{
desiredMode = VDM_DUMMY;
}
else
{
desiredMode = VDM_REAL;
}
}
if( fDistanceSq < square(fRealDist) )
{
desiredMode = VDM_REAL;
}
else if( (fDistanceSq < square(fDummyDist)) && pVehicle->GetCanMakeIntoDummy(VDM_DUMMY) )
{
desiredMode = VDM_DUMMY;
}
}
// Clip LOD as required to satisfy N-lodding
if(ms_bNLodActive)
{
if(desiredMode == VDM_REAL && ms_iNLodRealRemaining <= 0 && desiredMode != currentMode)
{
desiredMode = VDM_DUMMY;
}
if(desiredMode == VDM_DUMMY && ms_iNLodDummyRemaining <= 0 && desiredMode != currentMode)
{
desiredMode = VDM_SUPERDUMMY;
}
}
// Allow vehicle to override desired mode
desiredMode = pVehicle->ProcessOverriddenDummyMode(desiredMode);
//-------------------------------------
// Process LOD changes
eVehicleDummyMode resultantMode = currentMode;
if(desiredMode != currentMode)
{
switch(desiredMode)
{
case VDM_REAL:
{
const bool iTimerPassedOrWrapped = (fwTimer::GetTimeInMilliseconds() >= (pVehicle->m_realConvertAttemptTimeMs + CVehicleAILodManager::ms_iTimeBetweenConversionToRealAttempts))
|| (fwTimer::GetTimeInMilliseconds() < pVehicle->m_realConvertAttemptTimeMs)
|| pVehicle->IsParkedSuperDummy(); // bypass the timer check for parked supper dummy
if (iTimerPassedOrWrapped)
{
const bool bSkipClearanceTest = pVehicle->ConsiderParkedForLodPurposes()
|| pVehicle->HasShrunkDummyConstraintsAsMuchAsPossible()
|| pVehicle->InheritsFromBike()
|| pVehicle->GetIsAircraft()
|| pVehicle->InheritsFromBoat()
|| !pVehicle->HasDummyConstraint()
|| pVehicle->IsParkedSuperDummy();
if(pVehicle->TryToMakeFromDummy(bSkipClearanceTest))
{
resultantMode = desiredMode;
}
#if __BANK
else
{
CAILogManager::GetLog().Log("DUMMY CONVERSION: %s Failed to convert to real: %s\n",
AILogging::GetEntityLocalityNameAndPointer(pVehicle).c_str(), pVehicle->GetNonConversionReason());
}
#endif
}
break;
}
case VDM_DUMMY:
case VDM_SUPERDUMMY:
{
const bool iTimerPassedOrWrapped = (fwTimer::GetTimeInMilliseconds() >= (pVehicle->m_dummyConvertAttemptTimeMs + CVehicleAILodManager::ms_iTimeBetweenConversionAttempts))
|| (fwTimer::GetTimeInMilliseconds() < pVehicle->m_dummyConvertAttemptTimeMs);
if(iTimerPassedOrWrapped)
{
if(pVehicle->TryToMakeIntoDummy(desiredMode))
{
resultantMode = desiredMode;
}
#if __BANK
else
{
CAILogManager::GetLog().Log("DUMMY CONVERSION: %s Failed to convert to dummy: %s\n",
AILogging::GetEntityLocalityNameAndPointer(pVehicle).c_str(), pVehicle->GetNonConversionReason());
}
#endif
}
break;
}
default:
{
Assertf(false,"Desired LOD not handled.");
}
}
}
// Check if this vehicle should be in timesliced mode, which requires that it's
// a superdummy, tasks have allowed it, and visibility checks have passed.
bool useTimeslicingLod = false;
if(ms_bUseTimeslicing)
{
if(pVehicle->m_nVehicleFlags.bLodShouldUseTimeslicing)
{
useTimeslicingLod = true;
}
else if((resultantMode == VDM_SUPERDUMMY && pVehicle->GetIsStatic())
|| pVehicle->InheritsFromHeli() // Helicopters are allowed to do timeslicing when in real mode, since they lack superdummy support.
)
{
if(pVehicle->m_nVehicleFlags.bLodCanUseTimeslicingAfterVisibilityChecks)
{
useTimeslicingLod = true;
}
else if(ms_iNLodNonTimeslicedRemaining == 0 && pVehicle->m_nVehicleFlags.bLodCanUseTimeslicingIfTooManyUpdates)
{
// In this case, we wouldn't normally be far enough away on screen to use timeslicing,
// but too many other vehicles are already not timesliced.
useTimeslicingLod = true;
}
}
#if __BANK
useTimeslicingLod |= ms_bForceAllTimesliced;
#endif
}
pVehicle->m_nVehicleFlags.bLodForceUpdateThisTimeslice = false;
// Update the AL_LodTimeslicing flag and otherwise notify the vehicle in case the LOD level changed.
bool wasInTimeslicingLod = pVehicle->GetVehicleAiLod().IsLodFlagSet(CVehicleAILod::AL_LodTimeslicing);
if(useTimeslicingLod)
{
timesliceNumInLowLodCounting++;
if(!wasInTimeslicingLod)
{
pVehicle->GetVehicleAiLod().SetLodFlag(CVehicleAILod::AL_LodTimeslicing);
pVehicle->SwitchToTimeslicedLod();
}
}
else
{
// Count down how many we have seen that are not timesliced.
ms_iNLodNonTimeslicedRemaining = Max(ms_iNLodNonTimeslicedRemaining - 1, 0);
if(wasInTimeslicingLod)
{
pVehicle->GetVehicleAiLod().ClearLodFlag(CVehicleAILod::AL_LodTimeslicing);
pVehicle->SwitchAwayFromTimeslicedLod();
}
}
//-------------------------------------
// Update N-LOD counts
switch(resultantMode)
{
case VDM_REAL:
ms_iNLodRealRemaining--;
break;
case VDM_DUMMY:
ms_iNLodDummyRemaining--;
break;
default:
break;
}
#if __ASSERT
if(pVehicle->IsSuperDummy() && pVehicle->InheritsFromPlane() && pVehicle->GetStatus() == STATUS_WRECKED && pVehicle->IsInAir())
{
eVehicleDummyMode eOverriddenMode = pVehicle->ProcessOverriddenDummyMode(desiredMode);
bool unused;
eVehicleDummyMode eOverriddenMode2 = pVehicle->GetOverriddenDummyMode(desiredMode, unused);
bool bAlwayBeReal = pVehicle->ShouldAlwaysTryToBeReal();
Assertf(false,
"Wrecked plane switches to supper dummy in the middle of air, might stuck there for ever. vehicle 0x%p, fixedUntilCollisionFlag %x, bShouldFixIfNoCollision %x, AllowFreezeIfNoCollision %x, ShouldFixIfNoCollisionLoadedAroundPosition %x, Collision loaded %x, current mode %d, desired mode %d, overridden mode %d, overridden mode2 %d real LOD remaining %d, netClone %d, becomeSuperDummy %d, alwaysBeReal %d, nonConversion %s",
pVehicle, pVehicle->GetIsFixedUntilCollisionFlagSet(),
pVehicle->m_nVehicleFlags.bShouldFixIfNoCollision, pVehicle->m_nPhysicalFlags.bAllowFreezeIfNoCollision,
pVehicle->ShouldFixIfNoCollisionLoadedAroundPosition(), pVehicle->IsCollisionLoadedAroundPosition(),
currentMode, desiredMode, eOverriddenMode, eOverriddenMode2,
ms_iNLodRealRemaining, pVehicle->IsNetworkClone(), pVehicle->m_nVehicleFlags.bMayBecomeParkedSuperDummy,
bAlwayBeReal, pVehicle->GetNonConversionReason());
}
if(pVehicle->IsParkedSuperDummy() && !pVehicle->GetIsStatic() && !pVehicle->IsNetworkClone())
{
eVehicleDummyMode eOverriddenMode = pVehicle->ProcessOverriddenDummyMode(desiredMode);
bool unused;
eVehicleDummyMode eOverriddenMode2 = pVehicle->GetOverriddenDummyMode(desiredMode, unused);
bool bAlwayBeReal = pVehicle->ShouldAlwaysTryToBeReal();
Assertf(pVehicle->ShouldFixIfNoCollisionLoadedAroundPosition() && !pVehicle->IsCollisionLoadedAroundPosition(),
"Parked supper dummy should be switching to real once being activated with ground collision loaded. vehicle 0x%p, fixedUntilCollisionFlag %x, bShouldFixIfNoCollision %x, AllowFreezeIfNoCollision %x, ShouldFixIfNoCollisionLoadedAroundPosition %x, Collision loaded %x, current mode %d, desired mode %d, overridden mode %d, overridden mode2 %d real LOD remaining %d, netClone %d, becomeSuperDummy %d, alwaysBeReal %d, nonConversion %s",
pVehicle, pVehicle->GetIsFixedUntilCollisionFlagSet(),
pVehicle->m_nVehicleFlags.bShouldFixIfNoCollision, pVehicle->m_nPhysicalFlags.bAllowFreezeIfNoCollision,
pVehicle->ShouldFixIfNoCollisionLoadedAroundPosition(), pVehicle->IsCollisionLoadedAroundPosition(),
currentMode, desiredMode, eOverriddenMode, eOverriddenMode2,
ms_iNLodRealRemaining, pVehicle->IsNetworkClone(), pVehicle->m_nVehicleFlags.bMayBecomeParkedSuperDummy,
bAlwayBeReal, pVehicle->GetNonConversionReason());
}
#endif
}
// By this time we have established our actual LOD, so clear this "want to be" flag.
pVehicle->m_nVehicleFlags.bCreatingAsInactive = false;
//--------------------------------------------------------------------------------
// Track occurrences of interest
bool bVehicleNotConvertedToRealDueToMAX = false;
int prevNumVehiclesConvertedToRealPeds = nNumVehiclesConvertedToRealPeds;
// Convert this vehicle if appropriate from pretend occupants to real peds or vice versa.
ManageVehiclePretendOccupants( pVehicle, popCtrlCentre, kMaxVehiclesConvertedToRealPedsPerFrame, ms_iNLodRealDriversAndPassengersRemaining,
nNumVehiclesConvertedToRealPeds, bVehicleNotConvertedToRealDueToMAX );
// Did this vehicle get converted to real peds, or is it already using real peds?
if( nNumVehiclesConvertedToRealPeds > prevNumVehiclesConvertedToRealPeds || (!pVehicle->IsUsingPretendOccupants() && pVehicle->CanSetUpWithPretendOccupants()) )
{
// Track the most distant vehicle with real peds that could be converted
// NOTE: we have no distance comparisons here because list is sorted smallest to largest already
ms_iMostDistantRealPedsPrioritizedIndex = i;
}
// Have we not set closest index yet and was this vehicle denied real passengers due to the limit on real peds?
if( ms_iClosestPretendPedsPrioritizedIndex == -1 && bVehicleNotConvertedToRealDueToMAX )
{
// Track the closest vehicle that should have real ped passengers
// NOTE: no distance comparisons because list is sorted smallest to largest
ms_iClosestPretendPedsPrioritizedIndex = i;
}
// Maintain an accurate count
ms_iNLodRealDriversAndPassengersRemaining = ComputeRealDriversAndPassengersRemaining();
}
// If the sweep over the prioritized vehicle list has just finished
// and we have reached or exceeded the ideal real ambient ped passengers limit
if( (u32)batchend == ms_iVehicleCount && CPedPopulation::ms_nNumInVehAmbient >= ComputeRealDriversAndPassengersMax() && ms_iMostDistantRealPedsPrioritizedIndex > 0 )
{
// Traverse down from farthest real peds vehicle to closest trying to convert to pretend occupants
bool bConvertedRealToPretend = false;
for(int listIndex = ms_iMostDistantRealPedsPrioritizedIndex; listIndex > ms_iClosestPretendPedsPrioritizedIndex && ms_iClosestPretendPedsPrioritizedIndex >= 0 && !bConvertedRealToPretend; listIndex--)
{
if( AssertVerify(listIndex >= 0) && AssertVerify(listIndex <= ms_PrioritizedVehicleArray.GetCount()) )
{
// Try to convert the most distant tracked vehicle from real peds to pretend occupants
CVehicle* pVehicle = ms_PrioritizedVehicleArray[listIndex].m_pVehicle;
// check that the vehicle is still valid
if(!CVehicle::GetPool()->IsValidPtr(pVehicle))
continue;
if(pVehicle->GetIsInReusePool())
continue; // skip vehicles that are on hold for re-use
if( pVehicle && !pVehicle->IsUsingPretendOccupants() )
{
bConvertedRealToPretend = pVehicle->TryToMakePedsIntoPretendOccupants();
}
}
}
}
// Update the count of timesliced vehicles we have found so far.
Assert(timesliceNumInLowLodCounting <= 0xffff);
ms_TimesliceNumInLowLodCounting = (u16)timesliceNumInLowLodCounting;
// If this is the last batch, update ms_TimesliceNumInLowLodPrevious as we have
// now gone through all the vehicles.
if(!ms_iVehiclePriorityCountdown)
{
ms_TimesliceNumInLowLodPrevious = (u16)timesliceNumInLowLodCounting;
}
// If we have any vehicles in timesliced LOD, determine the next update window.
// Note that we don't always have an exact count, just a number from the last iteration
// through all the vehicles.
if(ms_TimesliceNumInLowLodPrevious)
{
UpdateTimeslicing(ms_TimesliceNumInLowLodPrevious);
}
}
int CVehicleAILodManager::FindVehiclesByPriority()
{
const Vec3V vPopCenter(VECTOR3_TO_VEC3V(CPedPopulation::GetPopulationControlPointInfo().m_conversionCentre));
const CViewport * pGameViewport = gVpMan.GetGameViewport();
const grcViewport * pGameGrcViewport = pGameViewport ? &pGameViewport->GetGrcViewport() : NULL;
const ScalarV fOffscreenLodDistScaleSq(square(ms_fOffscreenLodDistScale));
const ScalarV fLodFarFromPopCenterDistSq(square(CVehicleAILodManager::ms_fLodFarFromPopCenterDistance));
const int iVehPoolSize = (int) CVehicle::GetPool()->GetSize();
const bool bNetworkGame = NetworkInterface::IsGameInProgress();
static const ScalarV scOne(V_ONE);
static const ScalarV fShortVehicleHalfLength( V_TWO );
static const ScalarV fLongVehicleHalfLength( V_FOUR );
// Set up a squared distance threshold for timesliced vehicles on screen.
const ScalarV timesliceAllowOnScreenDistSmallV(ms_TimesliceAllowOnScreenDistSmall);
const ScalarV timesliceAllowOnScreenDistLargeV(ms_TimesliceAllowOnScreenDistLarge);
const ScalarV timeSliceLengthRangeInverse = scOne / (fLongVehicleHalfLength - fShortVehicleHalfLength);
int iNumVehicles = 0;
for(int i=0; i<iVehPoolSize; i++)
{
if(CVehicle * pVehicle = CVehicle::GetPool()->GetSlot(i))
{
// Calc an effective distance for use in LOD selection
const Vec3V vVehPos(pVehicle->GetVehiclePosition());
const ScalarV fVehRadius(pVehicle->GetBoundRadius());
const Vec4V vVehSphereAndRadius(vVehPos,fVehRadius);
ScalarV fDistSq = DistSquared(vPopCenter,vVehPos);
const ScalarV fLengthScale = (fVehRadius - fShortVehicleHalfLength) * timeSliceLengthRangeInverse;
const ScalarV fDesiredOnScreenDist = (timesliceAllowOnScreenDistLargeV * fLengthScale)
- (timesliceAllowOnScreenDistSmallV * fLengthScale) + timesliceAllowOnScreenDistSmallV;
const ScalarV timesliceAllowOnScreenDistV = Clamp(fDesiredOnScreenDist, timesliceAllowOnScreenDistSmallV, timesliceAllowOnScreenDistLargeV);
const ScalarV timesliceAllowOnScreenDistSqV = Scale(timesliceAllowOnScreenDistV, timesliceAllowOnScreenDistV);
// If we have too many vehicles getting updates, we use this threshold instead. This is the hard limit, half of
// the regular limit (V_QUARTER == 0.5*0.5).
const ScalarV timesliceAllowOnScreenTooManyUpdatesDistSqV = Scale(timesliceAllowOnScreenDistSqV, ScalarV(V_QUARTER));
bool bVehicleIsOnscreenToLocal = pVehicle->GetIsVisibleInSomeViewportThisFrame();
if(bVehicleIsOnscreenToLocal && pGameGrcViewport && !pGameGrcViewport->IsSphereVisible(vVehSphereAndRadius))
{
bVehicleIsOnscreenToLocal = false;
}
bool bVehicleOffscreenCloseAndVisibleRemotely = false;
bool bWithinRealRange = IsLessThanAll(fDistSq, ScalarV(ms_fDummyLodDistance*ms_fDummyLodDistance)) != 0;
//in network games we also want to take into account remote players
if(bNetworkGame && !pVehicle->IsNetworkClone() && !bVehicleIsOnscreenToLocal && bWithinRealRange)
{
//this flag only set if the vehicle is offscreen locally within dummy range and visible remotely within dummy range
float fRadius = pVehicle->GetBoundRadius();
bVehicleOffscreenCloseAndVisibleRemotely = NetworkInterface::IsVisibleToAnyRemotePlayer(VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition()), fRadius, ms_fDummyLodDistance);
}
bool bVehicleOnscreenToAnyPlayer = bVehicleOffscreenCloseAndVisibleRemotely || bVehicleIsOnscreenToLocal;
//this flag is local only
pVehicle->m_nVehicleFlags.bVehicleIsOnscreen = bVehicleIsOnscreenToLocal;
// If the vehicle has a desire to use timeslicing, check if it fulfills the visibility conditions.
// If bLodShouldUseTimeslicing is set, we don't want to respect visibility anyway, so no need
// to check distance, etc.
pVehicle->m_nVehicleFlags.bLodCanUseTimeslicingAfterVisibilityChecks = pVehicle->m_nVehicleFlags.bLodCanUseTimeslicingIfTooManyUpdates = pVehicle->m_nVehicleFlags.bLodCanUseTimeslicing;
if(ms_bUseTimeslicing && pVehicle->m_nVehicleFlags.bLodCanUseTimeslicing && !pVehicle->m_nVehicleFlags.bLodShouldUseTimeslicing)
{
// Start off by making use of the earlier viewport/occlusion check.
bool bVehicleIsVisibleForTimeslicing = bVehicleOnscreenToAnyPlayer;
bool bVehicleIsVisibleForTimeslicingTooManyUpdates = bVehicleOnscreenToAnyPlayer;
if(bVehicleIsVisibleForTimeslicing)
{
// We are potentially visible, but check if we are far enough away to use timeslicing anyway.
if(IsGreaterThanAll(fDistSq, timesliceAllowOnScreenDistSqV))
{
// Yeah, we are not too visible for timeslicing.
bVehicleIsVisibleForTimeslicingTooManyUpdates = bVehicleIsVisibleForTimeslicing = false;
}
else if(IsGreaterThanAll(fDistSq, timesliceAllowOnScreenTooManyUpdatesDistSqV))
{
// In a pinch, this is far enough away to allow timeslicing when on screen.
bVehicleIsVisibleForTimeslicingTooManyUpdates = false;
}
}
// Clear bLodCanUseTimeslicing again if we were visible. This would generally get
// set back to true during the next task update.
pVehicle->m_nVehicleFlags.bLodCanUseTimeslicingAfterVisibilityChecks = !bVehicleIsVisibleForTimeslicing;
pVehicle->m_nVehicleFlags.bLodCanUseTimeslicingIfTooManyUpdates = !bVehicleIsVisibleForTimeslicingTooManyUpdates;
}
//don't muck with distance if vehicle is within 15 meters of us or within real distance to remote players
if(!bVehicleOnscreenToAnyPlayer && IsGreaterThanAll(fDistSq, ScalarV(ms_fRealDistance*ms_fRealDistance)))
{
fDistSq *= fOffscreenLodDistScaleSq;
}
#if __BANK
if(ms_fForceLodDistance >= 0.0f)
{
fDistSq = ScalarV(square(ms_fForceLodDistance));
}
ms_PrioritizedVehicleArray[iNumVehicles].bVisibleLocally = bVehicleIsOnscreenToLocal;
ms_PrioritizedVehicleArray[iNumVehicles].bWithinLocalRealDist = bWithinRealRange;
ms_PrioritizedVehicleArray[iNumVehicles].bVisibleRemotely = bVehicleOffscreenCloseAndVisibleRemotely;
#endif
pVehicle->m_nVehicleFlags.bLodFarFromPopCenter = IsGreaterThanAll(fDistSq,fLodFarFromPopCenterDistSq) ? true : false;
StoreScalar32FromScalarV(ms_PrioritizedVehicleArray[iNumVehicles].m_fEffectiveDistSq,fDistSq);
ms_PrioritizedVehicleArray[iNumVehicles].m_pVehicle = pVehicle;
iNumVehicles++;
}
}
std::sort(ms_PrioritizedVehicleArray.GetElements(), ms_PrioritizedVehicleArray.GetElements() + iNumVehicles, CVehicleAiPriority::LessThan);
return iNumVehicles;
}
void CVehicleAILodManager::UpdateExpectedWorstTimeslicedTimestep()
{
// MAGIC! In practice, these values seem to give us an overestimate in
// at least ~98% of the cases.
static bank_u32 s_NumExtraVehicles = 10;
static bank_float s_ExtraTimeFactor = 1.5f;
// Push in the current time step into the memory.
float currentTimestep = fwTimer::GetTimeStep();
if(ms_FrameTimeMemory.IsFull())
{
ms_FrameTimeMemory.Drop();
}
ms_FrameTimeMemory.Push(currentTimestep);
// Find the worst frame time in recent memory.
const int numInMemory = ms_FrameTimeMemory.GetCount();
float longestFrameTime = 0.0f;
for(int i = 0; i < numInMemory; i++)
{
longestFrameTime = Max(longestFrameTime, ms_FrameTimeMemory[i]);
}
// Get the number of timesliced vehicles, plus some to account for that we may be getting more.
const int numTimesliced = ms_TimesliceNumInLowLodPrevious + s_NumExtraVehicles;
// Compute the time between updates, by dividing the number of timesliced vehicles by the number
// we update per frame (rounded up).
int framesBetweenUpdates = (numTimesliced + ms_TimesliceMaxUpdatesPerFrame - 1)/ms_TimesliceMaxUpdatesPerFrame;
// Take into account that we have a minimum limit to the update time.
framesBetweenUpdates = Max(framesBetweenUpdates, (int)ms_TimesliceMinUpdatePeriodInFrames);
// Compute the time between updates from the frame time and the update period, and apply
// a fudge factor since the frame rate may be varying, or the update order could possibly change
// a bit, etc.
const float timeBetweenUpdates = longestFrameTime*(float)framesBetweenUpdates*s_ExtraTimeFactor;
ms_ExpectedWorstTimeslicedTimestep = timeBetweenUpdates;
}
void CVehicleAILodManager::ManageVehiclePretendOccupants(CVehicle* pVehicle, const Vector3& popCtrlCentre, const int kMaxVehiclesConvertedToRealPedsPerFrame, const int kMaxRealPeds, int& inout_NumVehiclesConvertedToRealPeds, bool& out_bVehicleNotConvertedToRealDueToMAX)
{
#if GTA_REPLAY
if(CReplayMgr::IsEditModeActive())
return;
#endif // GTA_REPLAY
// Compute distance to population control center
float fDistToPopCenterSq = (popCtrlCentre - VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition())).Mag2();
// Initialize effective distance using population control center by default
float fEffectiveDistSq = fDistToPopCenterSq;
// If a multiplayer game is running
bool bRemotePlayerIsCloser = false;
if(NetworkInterface::IsGameInProgress())
{
// Get the vehicle multiplayer object
CNetObjEntity *netObjEntity = static_cast<CNetObjEntity *>(pVehicle->GetNetworkObject());
if(netObjEntity)
{
// Compute the distance squared to nearest multiplayer player
float netPlayerDistSq = netObjEntity->GetDistanceToNearestPlayerSquared();
if( netPlayerDistSq < fDistToPopCenterSq )
{
// use closest multiplayer player distance if closer
fEffectiveDistSq = netPlayerDistSq;
bRemotePlayerIsCloser = true;
}
}
}
// Handle on-screen / off-screen with different ranges
// The goal is to not visibly notice passengers appearing/disappearing, and not have missing drivers
// --
// Initialize with off screen ranges unless replay is recording
bool bIsOnScreen = pVehicle->m_nVehicleFlags.bVehicleIsOnscreen;
#if GTA_REPLAY
bIsOnScreen |= CReplayMgr::IsRecording();
#endif
float fEffectivePedToPretendRange = ms_makePedsIntoPretendOccupantsRangeOffScreen;
float fEffectivePretendToPedRange = ms_makePedsFromPretendOccupantsRangeOffScreen;
if(bIsOnScreen || bRemotePlayerIsCloser)
{
// Get pretend to real range multiplier from vehicle model info
const float fVehiclePretendOccupantsScale = pVehicle->GetVehicleModelInfo()->GetPretendOccupantsScale();
// Check if the vehicle should get an additional visibility scale factor
float driverVisibilityScale = 1.0f;
// first check if the vehicle driver can be seen far away
const bool bDriverCanBeSeenFarAway = !pVehicle->CarHasRoof() || (pVehicle->GetVehicleType()==VEHICLE_TYPE_BOAT) || (pVehicle->GetVehicleType() == VEHICLE_TYPE_TRAIN);
if( bDriverCanBeSeenFarAway )
{
driverVisibilityScale = 2.0f;
}
else
{
// check if the vehicle is fleeing
const bool bVehicleFleeing = pVehicle->GetIntelligence()->GetActiveTask() && pVehicle->GetIntelligence()->GetActiveTask()->GetTaskType() == CTaskTypes::TASK_VEHICLE_FLEE;
if( bVehicleFleeing )
{
driverVisibilityScale = 2.0f;
}
}
// Compute scaled visible ranges as the effective ranges
fEffectivePedToPretendRange = ms_makePedsIntoPretendOccupantsRangeOnScreen * ms_fLodRangeScale * driverVisibilityScale * fVehiclePretendOccupantsScale;
fEffectivePretendToPedRange = ms_makePedsFromPretendOccupantsRangeOnScreen * ms_fLodRangeScale * driverVisibilityScale * fVehiclePretendOccupantsScale;
}
// Check if we should try to convert real to pretend occupants
if( !pVehicle->IsUsingPretendOccupants() )
{
// if vehicle is far enough away
if( fEffectiveDistSq > (fEffectivePedToPretendRange * fEffectivePedToPretendRange) )
{
bool bProceed = true;
if (NetworkInterface::IsGameInProgress() && NetworkInterface::IsVisibleOrCloseToAnyPlayer(VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition()), pVehicle->GetBoundRadius(), 100.f, 10.f))
{
bProceed = false;
}
if (bProceed)
{
// NOTE: special cases will be handled inside TryToMakePedsIntoPretendOccupants so we don't cull real peds that should persist.
pVehicle->TryToMakePedsIntoPretendOccupants();
}
}
}
// Check if we should try to convert pretend to real
else // pVehicle->IsUsingPretendOccupants()
{
bool bProceed = false;
// if vehicle is close enough
if( fEffectiveDistSq < (fEffectivePretendToPedRange * fEffectivePretendToPedRange) )
{
bProceed = true;
}
else
{
//Special consideration for MP - when other players come near a remotely owned vehicle with fake peds
if (NetworkInterface::IsGameInProgress() && NetworkInterface::IsVisibleOrCloseToAnyPlayer(VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition()), pVehicle->GetBoundRadius(), 100.f, 10.f))
{
bProceed = true;
}
}
if (bProceed)
{
// if number of vehicles converted this frame has not reached the limit
if(inout_NumVehiclesConvertedToRealPeds < kMaxVehiclesConvertedToRealPedsPerFrame)
{
// if number of real peds has not reached the limit
if( kMaxRealPeds > 0 )
{
const bool bOnlyAddDriver = false;
if(pVehicle->TryToMakePedsFromPretendOccupants(bOnlyAddDriver, &kMaxRealPeds))
{
inout_NumVehiclesConvertedToRealPeds++;
ms_iNumVehiclesConvertedToRealPedsThisFrame++;
}
}
else // kMaxRealPeds <= 0
{
out_bVehicleNotConvertedToRealDueToMAX = true;
}
}
}
}
}
void CVehicleAILodManager::UpdateTimeslicing(int numInLowTimesliceLOD)
{
// Unset the m_TimeslicedUpdateThisFrame flag on the vehicles we updated on the previous frame.
SetTimeslicedUpdateFlagForVehiclesInRange(ms_TimesliceFirstVehIndex, ms_TimesliceLastVehIndex, false);
// First, we get the index we stored off on the previous frame,
// and keep a copy of it.
int vehIndex = ms_TimesliceLastIndex;
// Get the pool and its size, and set up some other variables we will use.
CVehicle::Pool *pPool = CVehicle::GetPool();
const int numSlots = (int) pPool->GetSize();
int numToUpdate = 0;
int numConsidered = 0;
const int numToUpdateBasedOnCount = (numInLowTimesliceLOD + ms_TimesliceMinUpdatePeriodInFrames - 1)/ms_TimesliceMinUpdatePeriodInFrames;
const int maxToUpdate = Clamp(numToUpdateBasedOnCount, (int)1, (int)ms_TimesliceMaxUpdatesPerFrame);
int firstIndex = -1;
int lastIndex = -1;
bool wrapped = false;
// Loop over vehicles until we've found the desired amount, or wrapped around.
while(1)
{
// Advance to the next slot, wrapping around if we reached the end of the pool.
vehIndex++;
if(vehIndex >= numSlots)
{
wrapped = true;
vehIndex = 0;
}
// Get the vehicle if it exists, and check if it should be timesliced. If not, we just move on to the next.
CVehicle* pVeh = pPool->GetSlot(vehIndex);
if(pVeh && pVeh->GetVehicleAiLod().IsLodFlagSet(CVehicleAILod::AL_LodTimeslicing))
{
// Store the pointer to the first we found on this frame.
if(firstIndex < 0)
{
firstIndex = vehIndex;
}
// Store the pointer to the last we have found so far.
lastIndex = vehIndex;
pVeh->UpdateFlagsForTimeslicedLod();
if(!pVeh->ShouldSkipUpdatesInTimeslicedLod())
{
// Count them, and stop if we have found enough.
numToUpdate++;
if(numToUpdate >= maxToUpdate)
{
break;
}
}
}
// Keep track of how many slots we have checked, and break if all slots have been
// checked at least once, so we are back where we started.
if(++numConsidered >= numSlots)
{
break;
}
}
if(ms_TimesliceCountdownUntilWrap > 0)
{
ms_TimesliceCountdownUntilWrap--;
}
if(wrapped)
{
if(ms_TimesliceCountdownUntilWrap > 0)
{
ms_TimesliceFirstVehIndex = ms_TimesliceLastVehIndex = -1;
return;
}
ms_TimesliceCountdownUntilWrap = ms_TimesliceMinUpdatePeriodInFrames;
}
// Store the index we are at.
Assert(vehIndex >= 0 && vehIndex < 0x8000);
ms_TimesliceLastIndex = (s16)vehIndex;
// Store the indices of the range of peds to update. We don't use these directly from ShouldDoFullUpdate(),
// but they are useful for clearing the m_TimeslicedUpdateThisFrame flag on the next frame.
Assert(firstIndex >= -0x8000 && firstIndex < 0x8000);
Assert(lastIndex >= -0x8000 && lastIndex < 0x8000);
ms_TimesliceFirstVehIndex = (s16)firstIndex;
ms_TimesliceLastVehIndex = (s16)lastIndex;
// Set the m_TimeslicedUpdateThisFrame to true within the range we want to update.
SetTimeslicedUpdateFlagForVehiclesInRange(firstIndex, lastIndex, true);
}
void CVehicleAILodManager::SetTimeslicedUpdateFlagForVehiclesInRange(int poolStartIndex, int poolEndIndex, bool value)
{
CVehicle::Pool *pPool = CVehicle::GetPool();
const int numSlots = (int) pPool->GetSize();
if(poolStartIndex >= 0)
{
Assert(poolEndIndex >= 0);
int index = poolStartIndex;
while(1)
{
Assert(index >= 0 && index < numSlots);
CVehicle* pVehicle = pPool->GetSlot(index);
if(pVehicle)
{
// If setting the value to true, make sure it's not already true - the way
// we do this now, that would be an indication of something not working properly.
Assert(!value || !pVehicle->GetVehicleAiLod().m_TimeslicedUpdateThisFrame);
if(value)
{
if(pVehicle->ShouldSkipUpdatesInTimeslicedLod())
{
// We're definitely getting a timesliced update to skip this frame -- increment skip counter
pVehicle->IncrementNumTimeslicedUpdatesSkipped();
}
else
{
pVehicle->ResetNumTimeslicedUpdatesSkipped();
}
}
pVehicle->GetVehicleAiLod().m_TimeslicedUpdateThisFrame = value;
}
// Check if we reached the end. Note that this is inclusive.
if(index == poolEndIndex)
{
break;
}
// Move to the next slot, wrapping around if necessary.
if(++index == numSlots)
{
index = 0;
}
}
}
else
{
// We expect both of the indices to be valid, or both to be invalid if there is no range.
Assert(poolEndIndex < 0);
}
}
#if __BANK
void CVehicleAILodManager::Debug()
{
if(!ms_bVMLODDebug && !ms_bDebug && !ms_bDebugExtraInfo && !ms_bDebugPedsInfo && !ms_bDisplayFullUpdates && !ms_bDebugScoring)
return;
camDebugDirector& debugDirector = camInterface::GetDebugDirector();
Vector3 vOrigin = debugDirector.IsFreeCamActive() ? debugDirector.GetFreeCamFrame().GetPosition() : CPlayerInfo::ms_cachedMainPlayerPos;
char tmp[256];
CVehicle::Pool * pVehiclePool = CVehicle::GetPool();
s32 iPoolSize = (s32) pVehiclePool->GetSize();
while(iPoolSize--)
{
CVehicle * pVehicle = pVehiclePool->GetSlot(iPoolSize);
if(!pVehicle || pVehicle->GetIsInReusePool())
continue;
//not yet added to world
if(!pVehicle->GetIsRetainedByInteriorProxy() && pVehicle->GetOwnerEntityContainer() == NULL)
continue;
if(ms_bVMLODDebug && !pVehicle->GetIsAircraft() && pVehicle->InheritsFromAutomobile())
{
Color32 col = pVehicle->IsDummy() ? (pVehicle->IsSuperDummy() ? Color_red : Color_yellow) : Color_green;
static float fVehicleScale = 1.0f;
CVectorMap::DrawVehicleLodInfo(pVehicle, col, fVehicleScale);
}
// If ms_bDisplayFullUpdates is set, draw a sphere around vehicles that update on this frame. Note that
// we do this before checking the range, since we are generally interested in seeing this happen in the distance too.
if(ms_bDisplayFullUpdates)
{
if(ShouldDoFullUpdate(*pVehicle))
{
grcDebugDraw::Sphere(pVehicle->GetTransform().GetPosition(), 2.5f, Color_white, false);
}
if(!ms_bDebug && !ms_bDebugExtraInfo && !ms_bDebugPedsInfo)
{
// No other debug drawing was enabled.
continue;
}
}
const Vector3 vPos = VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition());
const float fRange = (vOrigin - vPos).Mag();
if(fRange > ms_fDebugDrawRange)
continue;
const float fFullAlphaFraction = 0.5f;
const float fAlpha = 1.0f - Max(0.0f,fRange-ms_fDebugDrawRange*fFullAlphaFraction) / ((1.0f-fFullAlphaFraction) * ms_fDebugDrawRange);
Color32 col;
if(pVehicle)
{
if(pVehicle->GetVehicleAiLod().IsLodFlagSet(CVehicleAILod::AL_LodSuperDummy))
{
Assert(!pVehicle->GetVehicleAiLod().IsLodFlagSet(CVehicleAILod::AL_LodDummy));
if(ms_bDebugExtraInfo)
{
sprintf(tmp, "SuperDummy (%p)",pVehicle);
}
else
{
sprintf(tmp, "SuperDummy");
}
if(pVehicle->GetIsStatic())
{
if(pVehicle->GetVehicleAiLod().IsLodFlagSet(CVehicleAILod::AL_LodTimeslicing))
{
col = Color_violet;
}
else
{
col = Color_purple;
}
}
else
{
col = Color_red;
}
}
else if(pVehicle->GetVehicleAiLod().IsLodFlagSet(CVehicleAILod::AL_LodDummy))
{
if(ms_bDebugExtraInfo)
{
sprintf(tmp, "Dummy (%p)",pVehicle);
}
else
{
sprintf(tmp, "Dummy");
}
col = Color_orange;
}
else // Real
{
if(ms_bDebugExtraInfo)
{
sprintf(tmp, "Real (%p)",pVehicle);
}
else
{
sprintf(tmp, "Real");
}
if(!pVehicle->GetCollider())
{
col = Color_white;
}
else if(pVehicle->GetCollider()->IsArticulated())
{
col = Color_cyan;
}
else
{
col = Color_green;
}
}
col.SetAlpha((int)(fAlpha * 255.0f));
int yOffset = -grcDebugDraw::GetScreenSpaceTextHeight()*4;
if( ms_bDebug || ms_bDebugExtraInfo )
{
grcDebugDraw::Text(vPos, col, 0, yOffset, tmp, false);
yOffset += grcDebugDraw::GetScreenSpaceTextHeight();
if(ms_bDebugExtraInfo)
{
sprintf(tmp, "Collider : %p",pVehicle->GetCollider());
}
else
{
sprintf(tmp, "Collider : %s",pVehicle->GetCollider() ? "true" : "false");
}
grcDebugDraw::Text(vPos, col, 0, yOffset, tmp, false);
yOffset += grcDebugDraw::GetScreenSpaceTextHeight();
if(ms_bDebugExtraInfo && !pVehicle->HasCollisionLoadedAroundVehicle())
{
sprintf(tmp, "No collision");
grcDebugDraw::Text(vPos, Color_red, 0, yOffset, tmp, false);
yOffset += grcDebugDraw::GetScreenSpaceTextHeight();
}
}
if(ms_bDebugExtraInfo)
{
// Extra information about attachments (trying to display in a concise form)
const fwEntity * pParentPhys = pVehicle->GetAttachParent();
const fwEntity * pParentDummy = pVehicle->GetDummyAttachmentParent();
const fwEntity * pParent = pParentPhys ? pParentPhys : pParentDummy;
const char * sParentType = pParentPhys ? "phys" : (pParentDummy ? "dummy" : "none");
if(pParentPhys && pParentDummy)
{
Assertf(pParentPhys==pParentDummy,"Vehicle with physical and dummy parents that are different.");
col = Color_yellow;
sParentType = "both";
}
bool bChildrenVehiclePhys = false;
const fwEntity * pChildPhys = pVehicle->GetChildAttachment();
while(pChildPhys && !bChildrenVehiclePhys)
{
if(static_cast<const CPhysical*>(pChildPhys)->GetIsTypeVehicle())
{
bChildrenVehiclePhys = true;
}
pChildPhys = pChildPhys->GetSiblingAttachment();
}
bool bChildrenDummy = pVehicle->HasDummyAttachmentChildren();
const char * sChildrenType = bChildrenVehiclePhys ? "phys" : (bChildrenDummy ? "dummy" : "none");
if(bChildrenVehiclePhys && bChildrenDummy)
{
col = Color_yellow;
sChildrenType = "both";
}
if(pParent)
{
sprintf(tmp, "Parent: %s(%p), Children: %s",sParentType,pParent,sChildrenType);
}
else
{
sprintf(tmp, "Parent: %s, Children: %s",sParentType,sChildrenType);
}
if(NetworkInterface::IsGameInProgress())
{
if(pVehicle->IsNetworkClone())
{
col = Color_white;
}
}
grcDebugDraw::Text(vPos, col, 0, yOffset, tmp, false);
yOffset += grcDebugDraw::GetScreenSpaceTextHeight();
static bool bDisplaySpeeds = false;
if(bDisplaySpeeds)
{
float fSpeedCollider = pVehicle->CPhysical::GetVelocity().Mag();
float fSpeedSuperDummy = pVehicle->GetSuperDummyVelocity().Mag();
sprintf(tmp,"Collider speed = %0.2f, SD speed = %0.2f",fSpeedCollider,fSpeedSuperDummy);
grcDebugDraw::Text(vPos, col, 0, yOffset, tmp, false);
yOffset = grcDebugDraw::GetScreenSpaceTextHeight();
}
}
if(ms_bDebugPedsInfo)
{
int numCPedOccupants = pVehicle->GetNumberOfPassenger();
if( pVehicle->GetDriver() )
{
numCPedOccupants += 1;
static dev_float fVertOffsetForHead = 0.60f;
static dev_float fHeadIndicatorRadius = 0.55f;
grcDebugDraw::Sphere(VEC3V_TO_VECTOR3(pVehicle->GetDriver()->GetTransform().GetPosition()) + Vector3(0.0f,0.0f,fVertOffsetForHead), fHeadIndicatorRadius, Color_white, false);
}
if( numCPedOccupants > 0 )
{
formatf(tmp, "Real Peds : %d", numCPedOccupants);
grcDebugDraw::Text(vPos, Color_white, 0, yOffset, tmp, false);
yOffset += grcDebugDraw::GetScreenSpaceTextHeight();
if( !pVehicle->GetVehicleModelInfo()->GetAllowPretendOccupants() )
{
formatf(tmp, "!AllowPretendOccupants");
grcDebugDraw::Text(vPos, Color_white, 0, yOffset, tmp, false);
yOffset += grcDebugDraw::GetScreenSpaceTextHeight();
}
}
else // no occupants
{
if( pVehicle->m_nVehicleFlags.bUsingPretendOccupants )
{
formatf(tmp, "PretendOccupants");
grcDebugDraw::Text(vPos, Color_blue, 0, yOffset, tmp, false);
yOffset += grcDebugDraw::GetScreenSpaceTextHeight();
if( pVehicle->m_nVehicleFlags.bFailedToResetPretendOccupants )
{
switch(pVehicle->m_RealPedFailReason)
{
case CVehicle::RPFR_SUCCEEDED:
formatf(tmp, "SUCCEEDED");// this shouldn't happen in practice
break;
case CVehicle::RPFR_IS_NET_CLONE:
formatf(tmp, "IS_NET_CLONE");
break;
case CVehicle::RPFR_IS_NOT_NET_OBJECT:
formatf(tmp, "IS_NOT_NET_OBJECT");
break;
case CVehicle::RPFR_NET_OBJ_REGISTER_FAILED:
formatf(tmp, "NET_OBJ_REGISTER_FAILED");
break;
case CVehicle::RPFR_PED_POOL_FULL:
formatf(tmp, "PED_POOL_FULL");
break;
case CVehicle::RPFR_NO_FALLBACK_PED:
formatf(tmp, "NO_FALLBACK_PED");
break;
case CVehicle::RPFR_ADD_POLICE_FAILED:
formatf(tmp, "ADD_POLICE_FAILED");
break;
case CVehicle::RPFR_NET_REQUEST_DRIVER_MODEL:
formatf(tmp, "NET_REQUEST_DRIVER_MODEL");
break;
case CVehicle::RPFR_NET_SET_UP_DRIVER:
formatf(tmp, "NET_SET_UP_DRIVER");
break;
case CVehicle::RPFR_FILTERED_SETUP_DRIVER:
formatf(tmp, "FILTERED_SETUP_DRIVER");
break;
case CVehicle::RPFR_UNFILTERED_SETUP_DRIVER:
formatf(tmp, "UNFILTERED_SETUP_DRIVER");
break;
case CVehicle::RPFR_ADD_FOR_TRAIN:
formatf(tmp, "ADD_FOR_TRAIN");
break;
case CVehicle::RPFR_ADD_FOR_VEH:
formatf(tmp, "ADD_FOR_VEH");
break;
default:
formatf(tmp, "Unknown Real Ped Failure");
}
grcDebugDraw::Text(vPos, Color_red, 0, yOffset, tmp, false);
yOffset += grcDebugDraw::GetScreenSpaceTextHeight();
}
}
else // !pVehicle->m_nVehicleFlags.bUsingPretendOccupants
{
formatf(tmp, "NoPretendOccupants");
grcDebugDraw::Text(vPos, Color_black, 0, yOffset, tmp, false);
yOffset += grcDebugDraw::GetScreenSpaceTextHeight();
}
if( pVehicle->m_nVehicleFlags.bDisablePretendOccupants )
{
Color32 speedColor = Color_black;
if(pVehicle->GetVelocity().Mag() > 0.2f)
{
Color_red;
}
formatf(tmp, "DisablePretendOccupants");
grcDebugDraw::Text(vPos, speedColor, 0, yOffset, tmp, false);
yOffset += grcDebugDraw::GetScreenSpaceTextHeight();
}
}
}
if(ms_bDebugExtraWheelInfo)
{
// Show wheel compression and hit info for the first 6 wheels (sprintf below assumes 6)
const int kMaxWheelsToDisplay = 6;
float fCompression[kMaxWheelsToDisplay];
bool bHit[kMaxWheelsToDisplay];
CWheel const * const * ppWheels = pVehicle->GetWheels();
for(int i=0; i<kMaxWheelsToDisplay; i++)
{
fCompression[i] = -1.0f;
bHit[i] = false;
if(i<pVehicle->GetNumWheels())
{
fCompression[i] = ppWheels[i]->GetCompression();
bHit[i] = ppWheels[i]->GetDynamicFlags().IsFlagSet(WF_HIT);
}
}
sprintf(tmp,"Compressions = %0.3f(%d), %0.3f(%d), %0.3f(%d), %0.3f(%d), %0.3f(%d), %0.3f(%d)",
fCompression[0],bHit[0],fCompression[1],bHit[1],fCompression[2],bHit[2],fCompression[3],bHit[3],fCompression[4],bHit[4],fCompression[5],bHit[5]);
grcDebugDraw::Text(vPos, col, 0, yOffset, tmp, false);
yOffset = grcDebugDraw::GetScreenSpaceTextHeight();
}
}
}
if(ms_bVMLODDebug)
{
CVectorMap::DrawCircle(CGameWorld::FindLocalPlayerCoors(), 2.0f, Color_purple, true);
const float fFOV = camInterface::GetGameplayDirector().GetFrame().GetFov() * CVectorMap::m_fLastTanFOV;
const float fFOVStart = DtoR*(-fFOV-90.0f);
const float fFOVEnd = DtoR*(fFOV-90.0f);
const Vector3 vConversionCentre = CPedPopulation::GetPopulationControlPointInfo().m_conversionCentre;
CVectorMap::DrawArc(vConversionCentre, ms_fDummyLodDistance, fFOVStart, fFOVEnd, Color_yellow, false);
CVectorMap::DrawArc(vConversionCentre, ms_fSuperDummyLodDistance, fFOVStart, fFOVEnd, Color_red, false);
CVectorMap::DrawArc(vConversionCentre, rage::Max(ms_fRealDistance, ms_fDummyLodDistance / ms_fOffscreenLodDistScale), fFOVEnd, fFOVStart, Color_yellow, false);
CVectorMap::DrawArc(vConversionCentre, ms_fSuperDummyLodDistance / ms_fOffscreenLodDistScale,fFOVEnd, fFOVStart, Color_red, false);
}
if(ms_bDebugScoring)
{
char tmp[256];
Vec2V pos(0.01f, 0.01f);
float yOffset = 0.01f;
sprintf(tmp, "%s","Index, Addr, Network, DistSqr, Vis Local, <Local Real Dist, Vis Remote (Color = Current Mode)");
grcDebugDraw::Text(pos, DD_ePCS_NormalisedZeroToOne, Color_blue, tmp);
float fNextLineBreakDistance = ms_fDummyLodDistance*ms_fDummyLodDistance;
for(int i = 0; i < ms_iVehicleCount; ++i)
{
CVehicleAiPriority& pPriority = ms_PrioritizedVehicleArray[i];
if(pPriority.m_fEffectiveDistSq > fNextLineBreakDistance)
{
pos.SetYf(yOffset += 0.01f);
fNextLineBreakDistance = fNextLineBreakDistance == ms_fDummyLodDistance*ms_fDummyLodDistance ? ms_fSuperDummyLodDistance*ms_fSuperDummyLodDistance : FLT_MAX;
}
if(CVehicle::GetPool()->IsValidPtr(pPriority.m_pVehicle) && !pPriority.m_pVehicle->GetIsAircraft() && pPriority.m_pVehicle->InheritsFromAutomobile())
{
if(!pPriority.m_pVehicle->GetIsRetainedByInteriorProxy() && pPriority.m_pVehicle->GetOwnerEntityContainer() == NULL)
continue;
sprintf(tmp, "%d 0x%p (%s)= %.0f %s %s %s", i, pPriority.m_pVehicle,pPriority.m_pVehicle->IsNetworkClone() ? "C" : "L",
pPriority.m_fEffectiveDistSq, pPriority.bVisibleLocally?"T":"F", pPriority.bWithinLocalRealDist?"T":"F", pPriority.bVisibleRemotely?"T":"F");
Color32 col = pPriority.m_pVehicle->IsDummy() ? (pPriority.m_pVehicle->IsSuperDummy() ? Color_red : Color_yellow) : Color_green;
pos.SetYf(yOffset += 0.01f);
grcDebugDraw::Text(pos, DD_ePCS_NormalisedZeroToOne, col, tmp);
}
}
}
}
#endif
// #if __DEV
// void PrintLoadedJunctionsWrapper()
// {
// CVirtualRoad::ms_VirtualJunctions.PrintSizeofAllLoadedJunctions();
// }
// #endif //__DEV
#if __BANK
void CVehicleAILodManager::InitWidgets()
{
bkBank* pBank = BANKMGR.FindBank("Vehicle AI and Nodes");
if(pBank)
{
pBank->PushGroup("Vehicle lodding");
pBank->AddToggle("Debug", &ms_bDebug);
pBank->AddToggle("Debug, extra info", &ms_bDebugExtraInfo);
pBank->AddToggle("Debug, peds info", &ms_bDebugPedsInfo);
pBank->AddToggle("Debug, extra wheel info", &ms_bDebugExtraWheelInfo);
pBank->AddToggle("Debug, scoring", &ms_bDebugScoring);
pBank->AddToggle("Debug, VM Lod", &ms_bVMLODDebug, datCallback(UpdateDebugLogDisplay));
ENABLE_FRAG_OPTIMIZATION_ONLY(pBank->AddToggle("Debug, frag cache info", &ms_bDebugFragCacheInfo);)
pBank->AddSlider("Debug, draw range", &ms_fDebugDrawRange, 0.0f, 20000.0f, 1.0f);
pBank->AddToggle("Lodding Active", &ms_bVehicleAILoddingActive);
pBank->AddSlider("Lod - Dummy Distance", &ms_fDummyLodDistance, 0.0f, 9999.0f, 1.0f);
pBank->AddSlider("Lod - Super-Dummy Distance", &ms_fSuperDummyLodDistance, 0.0f, 9999.0f, 1.0f);
pBank->AddSlider("Lod - Super-Dummy Distance (Parked)", &ms_fSuperDummyLodDistanceParked, 0.0f, 9999.0f, 1.0f);
pBank->AddSlider("Lod - Offscreen distance scale", &ms_fOffscreenLodDistScale, 1.0f, 10.0f, 0.1f);
pBank->AddToggle("Lod - Occluded counts as offscreen", &ms_bConsiderOccludedAsOffscreen);
pBank->AddSlider("Lod - Far from pop center dist", &ms_fLodFarFromPopCenterDistance, 0.0f, 9999.0f, 1.0f);
pBank->AddToggle("N-Lod, Active", &ms_bNLodActive);
pBank->AddSlider("N-Lod, Real max", &ms_iNLodRealMax, 0, 1000, 1);
pBank->AddSlider("N-Lod, Dummy max", &ms_iNLodDummyMax, 0, 1000, 1);
pBank->AddSlider("N-Lod, Non-timesliced max", &ms_iNLodNonTimeslicedMax, 0, 1000, 1);
pBank->AddSlider("N-Lod, Real occupants max", &ms_iNLodRealDriversAndPassengersMax, 0, 1000, 1);
pBank->AddToggle("Display Dummy Vehicle Non Convert Reason",&ms_displayDummyVehicleNonConvertReason);
pBank->AddToggle("Validate dummy constraints", &ms_bValidateDummyConstraints);
pBank->AddSlider("Validate dummy constraints, buffer", &ms_fValidateDummyConstraintsBuffer, 0.0f, 100.0f, 1.0f);
pBank->AddToggle("Timeslicing, Active", &ms_bUseTimeslicing);
pBank->AddSlider("Timeslicing, Num to Update", &ms_TimesliceMaxUpdatesPerFrame, 1, 100, 1);
pBank->AddSlider("Timeslicing, Min Update Period", &ms_TimesliceMinUpdatePeriodInFrames, 0, 100, 1);
pBank->AddToggle("Timeslicing, Display Full Updates", &ms_bDisplayFullUpdates);
pBank->AddSlider("Timeslicing, Allow On Screen Min Distance (Small)", &ms_TimesliceAllowOnScreenDistSmall, 0.0f, 10000.0f, 5.0f);
pBank->AddSlider("Timeslicing, Allow On Screen Min Distance (Large)", &ms_TimesliceAllowOnScreenDistLarge, 0.0f, 10000.0f, 5.0f);
pBank->AddToggle("Timeslicing, Allow skip updates in timesliced lod", &ms_bAllowSkipUpdatesInTimeslicedLod);
pBank->AddToggle("Timeslicing, Skip timesliced updates while parked", &ms_bSkipTimeslicedUpdatesWhileParked);
pBank->AddToggle("Timeslicing, Skip timesliced updates while stopped", &ms_bSkipTimeslicedUpdatesWhileStopped);
pBank->AddSlider("Timeslicing, Maximum timesliced updates to skip while stopped", &ms_iMaxTimeslicedUpdatesToSkipWhileStopped, 0, 20, 1);
pBank->AddToggle("Timeslicing, Display skipped timesliced updates", &ms_bDisplaySkippedTimeslicedUpdates);
pBank->AddSlider("Lod Timeslicing", &ms_iVehiclePriorityCountdownMax, 1, 10, 1);
pBank->AddSeparator();
#if __DEV
pBank->AddToggle("Focus Vehicle Break On Check Should Attempt Conversion", &ms_focusVehBreakOnCheckAttemptPopConversion);
pBank->AddToggle("Focus Vehicle Break On Attempted Conversion", &ms_focusVehBreakOnAttemptedPopConversion);
pBank->AddToggle("Focus Vehicle Break On Conversion", &ms_focusVehBreakOnPopulationConversion);
#endif // __DEV
pBank->AddToggle("Display Dummy Wheel Hits", &ms_bDisplayDummyWheelHits);
pBank->AddToggle("Display Dummy Path", &ms_bDisplayDummyPath);
pBank->AddToggle("Display Dummy Path (detailed)", &ms_bDisplayDummyPathDetailed);
pBank->AddToggle("Display Dummy Path Constraint", &ms_bDisplayDummyPathConstraint);
pBank->AddToggle("Display Virtual Junction Heightmaps", &ms_bDisplayVirtualJunctions);
pBank->AddToggle("Raise dummy road", &ms_bRaiseDummyRoad);
pBank->AddToggle("Raise dummy junctions", &ms_bRaiseDummyJunctions);
pBank->AddSlider("Raise dummy road, height", &ms_fRaiseDummyRoadHeight, -10.0f, 10.0f, 0.01f);
pBank->AddToggle("Use dummy wheel transition", &ms_bUseDummyWheelTransition);
pBank->AddToggle("Use The Dummy Vehicle System", &ms_bUseDummyLod);
pBank->AddToggle("Enable use of Super-Dummy LOD", &ms_bUseSuperDummyLod);
pBank->AddToggle("Enable use of Super-Dummy LOD for parked vehicles", &ms_bUseSuperDummyLodForParked);
pBank->AddToggle("Enable use of Virtual Junction Heightmap Sampling", &CVirtualRoad::ms_bEnableVirtualJunctionHeightmaps);
pBank->AddToggle("Enable constraint slack when changing paths", &CVirtualRoad::ms_bEnableConstraintSlackWhenChangingPaths);
// #if __DEV
// pBank->AddButton("Print Size of All Loaded Junctions", datCallback(PrintLoadedJunctionsWrapper));
// #endif //__DEV
pBank->AddSlider("Force Lod Distance", &ms_fForceLodDistance, -1.0f, 1000.0f, 1.0f);
pBank->AddToggle("Force all Super-dummys", &ms_bAllSuperDummys);
pBank->AddToggle("Force all timesliced", &ms_bForceAllTimesliced);
pBank->AddToggle("De-timeslice transmission and sleep", &ms_bDeTimesliceTransmissionAndSleep);
pBank->AddToggle("De-timeslice wheel collisions", &ms_bDeTimesliceWheelCollisions);
pBank->AddSlider("Super-dummy steer speed", &ms_fSuperDummySteerSpeed, 0.0f, PI*4.0f, 0.05f);
pBank->AddSlider("Super-dummy clone heading to vel blend ratio", &ms_fSuperDummyCloneHeadingToVelBlendRatio, 0.0f, 1.0f, 0.01f);
pBank->PushGroup("Pretend Occupants", false);
pBank->AddToggle("Use The Pretend Occupants System", &ms_usePretendOccupantsSystem);
pBank->AddSlider("Occupant Removal Distance (OnScreen)", &ms_makePedsIntoPretendOccupantsRangeOnScreen, 0.0f, 1000.0f, 1.0f);
pBank->AddSlider("Occupant Restore Distance (OnScreen)", &ms_makePedsFromPretendOccupantsRangeOnScreen, 0.0f, 1000.0f, 1.0f);
pBank->AddSlider("Occupant Removal Distance (OFFScreen)", &ms_makePedsIntoPretendOccupantsRangeOffScreen, 0.0f, 1000.0f, 1.0f);
pBank->AddSlider("Occupant Restore Distance (OFFScreen)", &ms_makePedsFromPretendOccupantsRangeOffScreen, 0.0f, 1000.0f, 1.0f);
pBank->PopGroup();
const char * pConstraintModes[] =
{
"None",
"Physical distance only",
"Physical distance & rotation",
"Non-physical distance"
};
pBank->AddCombo("Dummy constraint method:", &ms_iDummyConstraintModePrivate, 3, pConstraintModes);
pBank->AddCombo("SuperDummy constraint method:", &ms_iSuperDummyConstraintModePrivate, 4, pConstraintModes);
pBank->AddSlider("Distance constraint extra leeway", &ms_fConstrainToRoadsExtraDistance, 0.0f, 10.0f, 0.1f);
pBank->AddSlider("Distance constraint extra leeway (junction node)", &ms_fConstrainToRoadsExtraDistanceAtJunction, 0.0f, 50.0f, 0.1f);
pBank->AddSlider("Distance constraint extra leeway (pre-junction node)", &ms_fConstrainToRoadsExtraDistancePreJunction, 0.0f, 50.0f, 0.1f);
pBank->AddSlider("Portion of Junction Length for constraint buffer dist", &ms_fPercentOfJunctionLengthToAddToConstraint, 0.0f, 1.0f, 0.01f);
pBank->AddSlider("Dummy Path reconsider Time", &ms_dummyPathReconsiderLengthMs, 0, 2000, 10);
pBank->AddSlider("Dummy Max Dist From Roads Edge Mult", &ms_dummyMaxDistFromRoadsEdgeMult, 0.0f, 10.0f, 0.1f);
pBank->AddSlider("Dummy Max Dist From Road From Above Mult", &ms_dummyMaxDistFromRoadFromAboveMult, 0.0f, 10.0f, 0.1f);
pBank->AddSlider("Dummy Max Dist From Road From Below Mult", &ms_dummyMaxDistFromRoadFromBelowMult, 0.0f, 10.0f, 0.1f);
pBank->AddToggle("Use Fixup Collision With Wheel Surface", &ms_convertUseFixupCollisionWithWheelSurface);
pBank->AddToggle("Enforce ConvertToDummy Must Be Near Paths", &ms_convertEnforceDummyMustBeNearPaths);
pBank->AddToggle("Convert All Exterior Vehicles To Dummy When In Interior", &ms_bConvertExteriorVehsToDummyWhenInInterior);
pBank->AddToggle("Display Dummy Vehicle Markers", &ms_displayDummyVehicleMarkers);
pBank->AddToggle("Collide with others in super-dummy mode", &ms_bCollideWithOthersInSuperDummyMode);
pBank->AddToggle("Collide with others in super-dummy inactive mode",&ms_bCollideWithOthersInSuperDummyInactiveMode);
pBank->AddToggle("Freeze Parked SuperDummy When Collisions Not Loaded", &ms_bFreezeParkedSuperDummyWhenCollisionsNotLoaded);
pBank->AddToggle("Disable physics in super-dummy mode", &ms_bDisablePhysicsInSuperDummyMode);
pBank->AddToggle("Use road camber in super-dummy mode", &ms_bUseRoadCamber);
pBank->AddToggle("Allow trailers to convert from real", &ms_bAllowTrailersToConvertFromReal);
pBank->AddToggle("Allow alt RouteInfo for rear wheels", &ms_bAllowAltRouteInfoForRearWheels);
pBank->AddToggle("Disable SuperDummy for slow bikes", &ms_bDisableSuperDummyForSlowBikes);
pBank->AddSlider("Disable SuperDummy for slow bikes, speed", &ms_fDisableSuperDummyForSlowBikesSpeed, 0.0f, 100.0f, 0.1f);
CVehicleAiLodTester::InitWidgets(*pBank);
pBank->PopGroup();
}
}
void CVehicleAILodManager::UpdateDebugLogDisplay()
{
CVectorMap::m_bDisplayLocalPlayerCamera = ms_bVMLODDebug;
}
#endif
///////////////////////////////////////////////////////////////////////////////
// CVehicleAiLodTester
#if __BANK
bool CVehicleAiLodTester::ms_bTestForAboveRoad = false;
float CVehicleAiLodTester::ms_fTestForAboveRoadThreshold = 1.0f;
bool CVehicleAiLodTester::ms_bTestForBelowRoad = false;
float CVehicleAiLodTester::ms_fTestForBelowRoadThreshold = 1.0f;
bool CVehicleAiLodTester::ms_bTeleportVehicles = false;
Vector3 CVehicleAiLodTester::ms_vTeleportOffset(0.0f,0.0f,5.0f);
Vector3 CVehicleAiLodTester::ms_vTeleportEulers(0.0f,0.0f,0.0f);
void CVehicleAiLodTester::InitWidgets(bkBank & bank)
{
// Validations and modifications
bank.PushGroup("Vehicle validation and modification");
bank.AddToggle("Test for above road", &ms_bTestForAboveRoad);
bank.AddSlider("Test for above road, threshold", &ms_fTestForAboveRoadThreshold, 0.0f, 100.0f, 0.1f);
bank.AddToggle("Test for below road", &ms_bTestForBelowRoad);
bank.AddSlider("Test for below road, threshold", &ms_fTestForBelowRoadThreshold, 0.0f, 100.0f, 0.1f);
bank.AddToggle("Teleport vehicles", &ms_bTeleportVehicles);
bank.AddSlider("Teleport vehicles, offset", &ms_vTeleportOffset, -100.0f, 100.0f, 0.1f);
bank.AddSlider("Teleport vehicles, Eulers", &ms_vTeleportEulers, -10.0f, 10.0f, 0.1f);
bank.PopGroup();
}
void CVehicleAiLodTester::ValidateState()
{
// Player vehicle shouldn't be dummy
CVehicle * pPlayerVehicle = FindPlayerVehicle();
if(pPlayerVehicle)
{
Assertf(pPlayerVehicle->GetVehicleAiLod().GetDummyMode()==VDM_REAL, "Player's vehicle is in dummy mode %i - it should always be VDM_REAL!", pPlayerVehicle->GetVehicleAiLod().GetDummyMode());
}
// TODO: Check for floating or sunk vehicles
}
void CVehicleAiLodTester::ApplyChanges()
{
CVehicle::Pool *pool = CVehicle::GetPool();
if(ms_bTeleportVehicles)
{
for(int i=0; i<pool->GetSize(); i++)
{
if(CVehicle * pVeh = pool->GetSlot(i))
{
Mat34V oldMatrix(pVeh->GetMatrix());
const Vec3V vEulers = Mat34VToEulersXYZ(oldMatrix);
const Vec3V vNewEulers = vEulers + RCC_VEC3V(ms_vTeleportEulers);
const Vec3V vNewPos = oldMatrix.d() + RCC_VEC3V(ms_vTeleportOffset);
Mat34V newMatrix;
Mat34VFromEulersXYZ(newMatrix,vNewEulers,vNewPos);
pVeh->SetMatrix(RCC_MATRIX34(newMatrix),true,true,true);
}
}
ms_bTeleportVehicles = false;
}
}
#endif // __BANK