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

6326 lines
223 KiB
C++

//
// streaming/populationstreaming.cpp
//
// Copyright (C) 1999-2013 Rockstar Games. All Rights Reserved.
//
#include "streaming/streaming_channel.h"
STREAMING_OPTIMISATIONS()
#include "populationstreaming.h"
#include "bank/bank.h"
#include "grcore/debugdraw.h"
#include "vfx/ptfx/ptfxmanager.h"
#include "ai/ambient/AmbientModelSetManager.h"
#include "camera/caminterface.h"
#include "core/game.h"
#include "core/gamesessionstatemachine.h"
#include "cutscene/CutSceneManagerNew.h"
#include "debug/debugglobals.h"
#include "game/clock.h"
#include "game/Dispatch/DispatchServices.h"
#include "game/modelindices.h"
#include "replaycoordinator/ReplayCoordinator.h"
#include "game/zones.h"
#include "fwmaths/random.h"
#include "fwscene/stores/drawablestore.h"
#include "fwscene/stores/txdstore.h"
#include "fwscene/stores/fragmentstore.h"
#include "modelinfo/pedmodelinfo.h"
#include "modelinfo/vehiclemodelinfo.h"
#include "modelinfo/VehicleModelInfoVariation.h"
#include "network/debug/networkdebug.h"
#include "network/networkinterface.h"
#include "objects/objectpopulation.h"
#include "peds/ped.h"
#include "peds/PedFactory.h"
#include "peds/pedpopulation.h"
#include "peds/popcycle.h"
#include "peds/popzones.h"
#include "peds/rendering/PedVariationStream.h"
#include "performance/clearinghouse.h"
#include "scene/world/gameworld.h"
#include "scene/texlod.h"
#include "script/script_cars_and_peds.h"
#include "streaming/defragmentation.h"
#include "streaming/streaming.h"
#include "streaming/streamingvisualize.h"
#include "system/memory.h"
#include "Task/Crimes/RandomEventManager.h"
#include "Task/Scenario/ScenarioClustering.h"
#include "vehicleai/pathfind.h"
#include "vehicles/cargen.h"
#include "vehicles/VehicleFactory.h"
#include "vehicles/vehiclepopulation.h"
AI_OPTIMISATIONS()
#define MERGED_BUDGET (ONE_STREAMING_HEAP || RSG_DURANGO || RSG_ORBIS)
CPopulationStreaming gPopStreaming;
const u32 DEFAULT_MEM_FOR_VEHICLES_LEVEL = MAX_MEM_LEVELS - 1;
static u32 s_MemForVehiclesLevel = DEFAULT_MEM_FOR_VEHICLES_LEVEL;
const u32 DEFAULT_MEM_FOR_PEDS_LEVEL = MAX_MEM_LEVELS - 1;
static u32 s_MemForPedsLevel = DEFAULT_MEM_FOR_PEDS_LEVEL;
#define BUFFER_MEM_FOR_PED_MODELS (1500000)
#define BUFFER_MEM_FOR_VEHICLE_MODELS (1500000)
// SINGLEPLAYER
static u32 s_MemForPeds_SP[] = { (0 + BUFFER_MEM_FOR_PED_MODELS),
(27000000 + BUFFER_MEM_FOR_PED_MODELS),
(36000000 + BUFFER_MEM_FOR_PED_MODELS),
(46800000 + BUFFER_MEM_FOR_PED_MODELS) };
static u32 s_MemForVehicles_SP[] = { (0 + BUFFER_MEM_FOR_VEHICLE_MODELS),
(16000000 + BUFFER_MEM_FOR_VEHICLE_MODELS),
(28000000 + BUFFER_MEM_FOR_VEHICLE_MODELS),
(35000000 + BUFFER_MEM_FOR_VEHICLE_MODELS) };
// MULTIPLAYER
static u32 s_MemForPeds_MP[] = { (0 + BUFFER_MEM_FOR_PED_MODELS),
(27000000 + BUFFER_MEM_FOR_PED_MODELS),
(36000000 + BUFFER_MEM_FOR_PED_MODELS),
(46800000 + BUFFER_MEM_FOR_PED_MODELS) };
static u32 s_MemForVehicles_MP[] = { (0 + BUFFER_MEM_FOR_VEHICLE_MODELS),
(16000000 + BUFFER_MEM_FOR_VEHICLE_MODELS),
(28000000 + BUFFER_MEM_FOR_VEHICLE_MODELS),
(35000000 + BUFFER_MEM_FOR_VEHICLE_MODELS) };
#if !MERGED_BUDGET
#define BUFFER_MEM_FOR_PED_MODELS_VRAM (1500000)
#define BUFFER_MEM_FOR_VEHICLE_MODELS_VRAM (1500000)
static u32 s_MemForPedsVram[] = { (0 + BUFFER_MEM_FOR_PED_MODELS),
(27000000 + BUFFER_MEM_FOR_PED_MODELS),
(36000000 + BUFFER_MEM_FOR_PED_MODELS),
(46800000 + BUFFER_MEM_FOR_PED_MODELS) };
static u32 s_MemForVehiclesVram[] = { (0 + BUFFER_MEM_FOR_VEHICLE_MODELS),
(16000000 + BUFFER_MEM_FOR_VEHICLE_MODELS),
(28000000 + BUFFER_MEM_FOR_VEHICLE_MODELS),
(35000000 + BUFFER_MEM_FOR_VEHICLE_MODELS) };
#endif // !MERGED_BUDGET
#define NUM_BOATS_TO_BE_STREAMED (2) // The number of boats we would like
#define NUMBER_STREAMED_CARS_NETWORK (8)
#define NUMBER_STREAMED_PEDS_NETWORK (6)
#define NUMBER_ZONE_SPECIFIC_CARS_NETWORK (4)
#define SCENARIO_PEDS_DONT_GET_REMOVED_DURATION (15000) // Scenario peds will not be removed for this long to give a scenario a chance to use it.
#define INVALID_GROUP_INDEX (~0u)
static const unsigned TARGET_NUM_AMBIENT_PEDS = 4;
static const unsigned TARGET_NUM_GANG_PEDS = 2;
CompileTimeAssert((TARGET_NUM_AMBIENT_PEDS + TARGET_NUM_GANG_PEDS) == NUMBER_STREAMED_PEDS_NETWORK);
static const unsigned MAX_NETWORK_MODELS_OF_TYPE_PENDING_STREAMING_REMOVAL = 2;
CompileTimeAssert(MAX_NETWORK_MODEL_INTERVALS % 4 == 0); // this needs to be divisible by 4 because we use a quarter range for working with wrapping
CompileTimeAssert(MAX_NETWORK_MODEL_INTERVALS > 0);
static const u32 s_vehicleAgeForRemoval = 10000; // ms, how old vehicles need to be to be considered for removal because of age
static const u32 s_intervalForVehicleRemoval = 10000; // try to remove a new vehicle every 30s
static u32 s_lastVehicleAgeRemoval = 0;
static const u32 s_pedAgeForRemoval = 30000;
static const u32 s_intervalForPedRemoval = 10000;
static u32 s_lastPedAgeRemoval = 0;
static const u32 s_scenarioPedMaxTimeDisallowedAfterEnteringPopZone = 3000; // Time in ms during which we deny new scenario ped model requests after a population zone switch.
static const int s_scenarioPedMinAppropriateToSkipDisallow = 3; // Allow new scenario ped models to be streamed in if we have at least this many appropriate peds.
// static bool s_isInCutscene = false;
static bool s_isNetGameInProgress = false;
static s32 s_numMalePedsRequested = 0;
static s32 s_numFemalePedsRequested = 0;
s32 CPopulationStreaming::sm_TotalMemUsedByPeds = 0;
s32 CPopulationStreaming::sm_TotalMemUsedByVehicles = 0;
#if (RSG_PC || RSG_DURANGO || RSG_ORBIS)
float CPedPopulation::ms_pedMemoryBudgetMultiplierNG = 1.0f;
#endif // (RSG_PC || RSG_DURANGO || RSG_ORBIS)
XPARAM(nopeds);
XPARAM(nocars);
enum ePedStreamRestriction
{
PSR_MALE = BIT(0),
PSR_DRIVER = BIT(1),
PSR_BIKER = BIT(2),
PSR_FEMALE = BIT(3)
};
enum eVehicleStreamRestriction
{
VSR_NO_BIKE = BIT(0),
VSR_COMMON_VEH = BIT(1),
VSR_BOAT = BIT(2)
};
struct sStreamData
{
sStreamData() : mi(fwModelId::MI_INVALID) {}
u32 mi;
u32 flags;
u32 lastTimeUsed;
s16 groupIndex;
s16 randVal;
};
int StreamDataCompare(const void* _a, const void* _b)
{
sStreamData* a = (sStreamData*)_a;
sStreamData* b = (sStreamData*)_b;
if (a->groupIndex == b->groupIndex)
{
if (a->lastTimeUsed == b->lastTimeUsed)
{
return (b->randVal > a->randVal) ? 1 : -1;
}
return (a->lastTimeUsed > b->lastTimeUsed) ? 1 : -1;
}
return (a->groupIndex > b->groupIndex) ? 1 : -1;
}
// Network model variations. The vehicle and ped lists used are often
// stored alphabetically, and network time is used to decide where to
// index the list, which would lead to the same models always being streamed (in alphabetical order)
// The network model variations allow different combinations of models to be streamed while remaining
// consistent across machines. The host of a session will pick a model variation set to use at start up
// and send this to players as they join the session
const unsigned NUM_SHUFFLED_INDICES = 10;
struct NetworkModelVariation
{
u32 m_NetworkTimeOffset;
u32 m_ShuffledIndices[NUM_SHUFFLED_INDICES];
};
NetworkModelVariation s_NetworkModelVariations[NUM_NETWORK_MODEL_VARIATIONS] =
{
{0, {45, 33, 63, 30, 18, 1, 23, 40, 16, 22} },
{5, {51, 39, 42, 24, 8, 19, 48, 55, 41, 28} },
{10, {29, 57, 34, 31, 7, 60, 35, 21, 37, 6 } },
{15, {61, 13, 15, 68, 59, 52, 46, 54, 65, 56} },
{20, {9, 38, 32, 25, 49, 20, 5, 50, 26, 10} },
{25, {36, 11, 17, 62, 69, 47, 64, 14, 2, 58} },
{30, {0, 4, 12, 53, 43, 27, 67, 3, 66, 44} },
};
CPopulationStreaming::CPopulationStreaming() :
m_bBoatsNeeded(false)
, m_MaxScenarioPedModelsLoadedPerSlot(MAX_NUMBER_STREAMED_SCENARIO_PEDS_PER_SLOT)
, m_MaxScenarioPedModelsLoadedOverride(-1)
, m_aNumScenarioPedModelsLoaded()
, m_TotalMemoryUsedByPedModelsMain(0)
, m_TotalMemoryUsedByPedModelsVram(0)
, m_TotalMemoryUsedByVehicleModelsMain(0)
, m_TotalMemoryUsedByVehicleModelsVram(0)
, m_MemOverBudgetMainPeds(0)
, m_MemOverBudgetVramPeds(0)
, m_MemOverBudgetMainVehs(0)
, m_MemOverBudgetVramVehs(0)
, m_TotalStreamedPedMemoryMain(0)
, m_TotalStreamedPedMemoryVram(0)
, m_TotalStreamedVehicleMemoryMain(0)
, m_TotalStreamedVehicleMemoryVram(0)
, m_TotalWeaponMemoryMain(0)
, m_TotalWeaponMemoryVram(0)
, m_bReducePedModelBudget(false)
, m_bReduceVehicleModelBudget(false)
, m_LastVehicleOnHighwaySpawn(0)
, m_ScenarioPedStreamingDisabledUntilTimeMS(0)
#if __BANK
, m_vehicleOverrideIndex(fwModelId::MI_INVALID)
#endif
{
}
void CPopulationStreaming::Init(unsigned initMode)
{
if(initMode == INIT_CORE)
{
m_bBoatsNeeded = false;
m_TotalMemoryUsedByPedModelsMain = 0;
m_TotalMemoryUsedByPedModelsVram = 0;
m_TotalMemoryUsedByVehicleModelsMain = 0;
m_TotalMemoryUsedByVehicleModelsVram = 0;
m_MemOverBudgetMainPeds = 0;
m_MemOverBudgetVramPeds = 0;
m_MemOverBudgetMainVehs = 0;
m_MemOverBudgetVramVehs = 0;
m_TotalStreamedPedMemoryMain = 0;
m_TotalStreamedPedMemoryVram = 0;
m_TotalStreamedVehicleMemoryMain = 0;
m_TotalStreamedVehicleMemoryVram = 0;
m_TotalWeaponMemoryMain = 0;
m_TotalWeaponMemoryVram = 0;
m_bReducePedModelBudget = false;
m_bReduceVehicleModelBudget = false;
m_NetworkModelVariationID = 0;
m_LastTimeUsedForStreamingNetPeds = 0;
m_LastTimeUsedForStreamingNetVehicles = 0;
m_LastZoneUsedForStreamingNetModels = -1;
m_LocalAllowedPedModelStartOffset = 0;
m_LocalAllowedVehicleModelStartOffset = 0;
m_ScenarioPedStreamingDisabledUntilTimeMS = 0;
for(unsigned index = 0; index < MAX_STREAMED_IN_MODELS; index++)
{
m_StreamedInModelData[index].m_ModelIndex = fwModelId::MI_INVALID;
m_StreamedInModelData[index].m_TimeStreamedIn = 0;
m_StreamedInModelData[index].m_TimeNoLongerRequired = 0;
m_StreamedInModelData[index].m_IsWeapon = false;
}
// Set the number of scenario models streamed in to be zero.
for(unsigned i=0; i < m_aNumScenarioPedModelsLoaded.GetMaxCount(); i++)
{
m_aNumScenarioPedModelsLoaded[i] = 0;
}
}
else if(initMode == INIT_AFTER_MAP_LOADED)
{
for(int i=0; i<CVehicleModelInfo::GetResidentObjects().GetCount(); i++)
AddResidentObject(CVehicleModelInfo::GetResidentObjects()[i]);
for(int i=0; i<CPedModelInfo::GetResidentObjects().GetCount(); i++)
AddResidentObject(CPedModelInfo::GetResidentObjects()[i]);
strLocalIndex moduleIndex = ptfxManager::GetAssetStore().FindSlot("core");
if (moduleIndex.IsValid())
{
strIndex streamingIndex = strStreamingEngine::GetInfo().GetModuleMgr().GetModule(ptfxManager::GetAssetStore().GetStreamingModuleId())->GetStreamingIndex(moduleIndex);
if(GetResidentObjectList().Find(streamingIndex) == -1)
{
AddResidentObject(streamingIndex);
}
}
}
else if(initMode == INIT_SESSION)
{
for(unsigned index = 0; index < MAX_STREAMED_IN_MODELS; index++)
{
m_StreamedInModelData[index].m_ModelIndex = fwModelId::MI_INVALID;
m_StreamedInModelData[index].m_TimeStreamedIn = 0;
m_StreamedInModelData[index].m_TimeNoLongerRequired = 0;
m_StreamedInModelData[index].m_IsWeapon = false;
}
m_AppropriateLoadedCars.Clear();
m_InAppropriateLoadedCars.Clear();
m_SpecialLoadedCars.Clear();
m_LoadedBoats.Clear();
m_VehicleModelsRequiredForNetwork.Clear();
m_DiscardedCars.Clear();
m_bBoatsNeeded = false;
m_AppropriateLoadedPeds.Clear();
m_InAppropriateLoadedPeds.Clear();
m_SpecialLoadedPeds.Clear();
m_PedModelsRequiredForNetwork.Clear();
m_DiscardedPeds.Clear();
m_vehicleComponents.Reset();
m_pedComponents.Reset();
m_weaponComponents.Reset();
// Reset the number of scenario models streamed in.
for(unsigned i=0; i < m_aNumScenarioPedModelsLoaded.GetMaxCount(); i++)
{
m_aNumScenarioPedModelsLoaded[i] = 0;
}
streamDebugf1("Clearing all counted as scenario ped flags.");
CModelInfo::ClearAllCountedAsScenarioPedFlags();
// Get the memory usage of the ped shared texture dictionaries
m_TotalMemoryUsedByPedModelsMain = 0;
m_TotalMemoryUsedByPedModelsVram = 0;
for(int i =0; i < CPedModelInfo::GetResidentObjects().GetCount(); i++)
{
strIndex index = CPedModelInfo::GetResidentObjects()[i];
u32 virtualSize=0, physicalSize=0;
strStreamingEngine::GetInfo().GetObjectAndDependenciesSizes(index, virtualSize, physicalSize);
AddTotalMemoryUsed(virtualSize, physicalSize, MTT_PED);
}
// Get the memory usage of the vehicle shared texture dictionaries
m_TotalMemoryUsedByVehicleModelsMain = 0;
m_TotalMemoryUsedByVehicleModelsVram = 0;
m_MemOverBudgetMainPeds = 0;
m_MemOverBudgetVramPeds = 0;
m_MemOverBudgetMainVehs = 0;
m_MemOverBudgetVramVehs = 0;
for(int i =0; i < CVehicleModelInfo::GetResidentObjects().GetCount(); i++)
{
strIndex index = CVehicleModelInfo::GetResidentObjects()[i];
u32 virtualSize=0, physicalSize=0;
strStreamingEngine::GetInfo().GetObjectAndDependenciesSizes(index, virtualSize, physicalSize);
AddTotalMemoryUsed(virtualSize, physicalSize, MTT_VEHICLE);
}
m_NetworkModelVariationID = 0;
m_LastTimeUsedForStreamingNetPeds = 0;
m_LastTimeUsedForStreamingNetVehicles = 0;
m_LastZoneUsedForStreamingNetModels = -1;
m_LocalAllowedPedModelStartOffset = 0;
m_LocalAllowedVehicleModelStartOffset = 0;
m_ScenarioPedStreamingDisabledUntilTimeMS = 0;
m_TotalStreamedPedMemoryMain = 0;
m_TotalStreamedPedMemoryVram = 0;
m_TotalStreamedVehicleMemoryMain = 0;
m_TotalStreamedVehicleMemoryVram = 0;
if (CFileMgr::IsGameInstalled())
{
// Set these so at least we have valid values (although they may change very quickly)
MI_CAR_POLICE_CURRENT.Set(MI_CAR_POLICE.GetModelIndex(), MI_CAR_POLICE.GetName());
MI_CAR_TAXI_CURRENT_1.Set(MI_CAR_CLASSIC_TAXI.GetModelIndex(), MI_CAR_CLASSIC_TAXI.GetName());
}
m_bReducePedModelBudget = false;
m_bReduceVehicleModelBudget = false;
m_numQueuedVehicleModels = 0;
s_lastPedAgeRemoval = 0;
s_lastVehicleAgeRemoval = 0;
m_LastVehicleOnHighwaySpawn = 0;
}
OnTunablesLoad();
}
void CPopulationStreaming::OnTunablesLoad()
{
// Load the Peds and Vehicles limits from tunables
s_MemForPeds_SP[1] = ::Tunables::GetInstance().TryAccess( CD_GLOBAL_HASH, ATSTRINGHASH("MEM_FOR_PEDS_SP_LEVEL_1", 0x1fd16740), 27000000) + BUFFER_MEM_FOR_PED_MODELS;
s_MemForPeds_SP[2] = ::Tunables::GetInstance().TryAccess( CD_GLOBAL_HASH, ATSTRINGHASH("MEM_FOR_PEDS_SP_LEVEL_2", 0x0a993cd0), 36000000) + BUFFER_MEM_FOR_PED_MODELS;
s_MemForPeds_SP[3] = ::Tunables::GetInstance().TryAccess( CD_GLOBAL_HASH, ATSTRINGHASH("MEM_FOR_PEDS_SP_LEVEL_3", 0xf45f105c), 46800000) + BUFFER_MEM_FOR_PED_MODELS;
s_MemForPeds_MP[1] = ::Tunables::GetInstance().TryAccess( CD_GLOBAL_HASH, ATSTRINGHASH("MEM_FOR_PEDS_MP_LEVEL_1", 0x37e7ed48), 27000000) + BUFFER_MEM_FOR_PED_MODELS;
s_MemForPeds_MP[2] = ::Tunables::GetInstance().TryAccess( CD_GLOBAL_HASH, ATSTRINGHASH("MEM_FOR_PEDS_MP_LEVEL_2", 0xb343e402), 36000000) + BUFFER_MEM_FOR_PED_MODELS;
s_MemForPeds_MP[3] = ::Tunables::GetInstance().TryAccess( CD_GLOBAL_HASH, ATSTRINGHASH("MEM_FOR_PEDS_MP_LEVEL_3", 0xada0d8bc), 46800000) + BUFFER_MEM_FOR_PED_MODELS;
s_MemForVehicles_SP[1] = ::Tunables::GetInstance().TryAccess( CD_GLOBAL_HASH, ATSTRINGHASH("MEM_FOR_VEHICLES_SP_LEVEL_1", 0x18cbc1d8), 16000000) + BUFFER_MEM_FOR_VEHICLE_MODELS;
s_MemForVehicles_SP[2] = ::Tunables::GetInstance().TryAccess( CD_GLOBAL_HASH, ATSTRINGHASH("MEM_FOR_VEHICLES_SP_LEVEL_2", 0x05a49b8a), 28000000) + BUFFER_MEM_FOR_VEHICLE_MODELS;
s_MemForVehicles_SP[3] = ::Tunables::GetInstance().TryAccess( CD_GLOBAL_HASH, ATSTRINGHASH("MEM_FOR_VEHICLES_SP_LEVEL_3", 0xf75fff01), 35000000) + BUFFER_MEM_FOR_VEHICLE_MODELS;
s_MemForVehicles_MP[1] = ::Tunables::GetInstance().TryAccess( CD_GLOBAL_HASH, ATSTRINGHASH("MEM_FOR_VEHICLES_MP_LEVEL_1", 0x4e67690f), 16000000) + BUFFER_MEM_FOR_VEHICLE_MODELS;
s_MemForVehicles_MP[2] = ::Tunables::GetInstance().TryAccess( CD_GLOBAL_HASH, ATSTRINGHASH("MEM_FOR_VEHICLES_MP_LEVEL_2", 0x5cb285a9), 28000000) + BUFFER_MEM_FOR_VEHICLE_MODELS;
s_MemForVehicles_MP[3] = ::Tunables::GetInstance().TryAccess( CD_GLOBAL_HASH, ATSTRINGHASH("MEM_FOR_VEHICLES_MP_LEVEL_3", 0x69f02024), 35000000) + BUFFER_MEM_FOR_VEHICLE_MODELS;
#if !MERGED_BUDGET
s_MemForPedsVram[1] = s_MemForPeds_SP[1];
s_MemForPedsVram[2] = s_MemForPeds_SP[2];
s_MemForPedsVram[3] = s_MemForPeds_SP[3];
s_MemForVehiclesVram[1] = s_MemForVehicles_SP[1];
s_MemForVehiclesVram[2] = s_MemForVehicles_SP[2];
s_MemForVehiclesVram[3] = s_MemForVehicles_SP[3];
#endif // !MERGED_BUDGET
}
#if __PS3 || __XENON
#define EFIGS_FONT_SIZE 272
#define JAPANESE_FONT_SIZE 1024
#define KOREAN_FONT_SIZE 448
#define CHINESE_FONT_SIZE 2048
s32 CPopulationStreaming::GetMemForPedsAdjustment(eMemoryType
#if !MERGED_BUDGET
eType
#endif // MERGED_BUDGET
, u32 uLevel) const
{
s32 reduce = 0;
#if !MERGED_BUDGET
if (eType == MEMTYPE_RESOURCE_VIRTUAL)
#endif
{
switch (CPauseMenu::GetMenuPreference(PREF_CURRENT_LANGUAGE))
{
case LANGUAGE_JAPANESE:
reduce = (JAPANESE_FONT_SIZE - EFIGS_FONT_SIZE) / 2;
break;
case LANGUAGE_CHINESE:
case LANGUAGE_CHINESE_SIMPLIFIED:
reduce = (CHINESE_FONT_SIZE - EFIGS_FONT_SIZE) / 2;
break;
case LANGUAGE_KOREAN:
reduce = (KOREAN_FONT_SIZE - EFIGS_FONT_SIZE) / 2;
break;
default:
reduce = 0;
break;
}
}
return reduce * (uLevel / 4);
}
s32 CPopulationStreaming::GetMemForVehiclesAdjustment(eMemoryType
#if !MERGED_BUDGET
eType
#endif // MERGED_BUDGET
, u32 uLevel) const
{
s32 reduce = 0;
#if !MERGED_BUDGET
if (eType == MEMTYPE_RESOURCE_VIRTUAL)
#endif
{
s32 reduce = 0;
switch (CPauseMenu::GetMenuPreference(PREF_CURRENT_LANGUAGE))
{
case LANGUAGE_JAPANESE:
reduce = (JAPANESE_FONT_SIZE - EFIGS_FONT_SIZE) / 2;
break;
case LANGUAGE_CHINESE:
case LANGUAGE_CHINESE_SIMPLIFIED:
reduce = (CHINESE_FONT_SIZE - EFIGS_FONT_SIZE) / 2;
break;
case LANGUAGE_KOREAN:
reduce = (KOREAN_FONT_SIZE - EFIGS_FONT_SIZE) / 2;
break;
default:
reduce = 0;
break;
}
}
return reduce * (uLevel / 4);
}
#endif // __PS3 || __XENON
s32 CPopulationStreaming::GetCurrentMemForVehicles() const
{
return GetMemForVehicles(MEMTYPE_GAME_VIRTUAL, s_MemForVehiclesLevel) + GetMemForVehicles(MEMTYPE_GAME_PHYSICAL, s_MemForVehiclesLevel);
}
s32 CPopulationStreaming::GetMemForVehicles(eMemoryType
#if (!MERGED_BUDGET) || __XENON || __PS3
eType
#endif // (!MERGED_BUDGET) || __XENON || __PS3
, u32 uLevel) const
{
Assert(uLevel < MAX_MEM_LEVELS);
s32 memForVehicles = (s32)((
#if (!MERGED_BUDGET) || __XENON || __PS3
(eType != MEMTYPE_GAME_VIRTUAL) ? s_MemForVehiclesVram[uLevel] * (NetworkInterface::IsGameInProgress() ? 1.0f : CVehiclePopulation::ms_vehicleMemoryBudgetMultiplierNG) :
#endif
(NetworkInterface::IsGameInProgress() ? s_MemForVehicles_MP[uLevel] :
s_MemForVehicles_SP[uLevel] * CVehiclePopulation::ms_vehicleMemoryBudgetMultiplierNG )));
#if __XENON || __PS3
return memForVehicles - GetMemForVehiclesAdjustment(eType, uLevel);
#else
return memForVehicles;
#endif // __XENON || __PS3
}
s32 CPopulationStreaming::GetCurrentMemForPeds() const
{
return GetMemForPeds(MEMTYPE_GAME_VIRTUAL, s_MemForPedsLevel) + GetMemForPeds(MEMTYPE_GAME_PHYSICAL, s_MemForPedsLevel);
}
s32 CPopulationStreaming::GetMemForPeds(eMemoryType
#if (!MERGED_BUDGET) || __XENON || __PS3
eType
#endif // (!MERGED_BUDGET) || __XENON || __PS3
, u32 uLevel) const
{
s32 result = 0;
Assert(uLevel < MAX_MEM_LEVELS);
result = (s32)((
#if (!MERGED_BUDGET) || __XENON || __PS3
(eType != MEMTYPE_GAME_VIRTUAL) ? s_MemForPedsVram[uLevel] * (NetworkInterface::IsGameInProgress() ? 1.0f : CPedPopulation::ms_pedMemoryBudgetMultiplierNG) :
#endif
(NetworkInterface::IsGameInProgress() ? s_MemForPeds_MP[uLevel] :
s_MemForPeds_SP[uLevel] * CPedPopulation::ms_pedMemoryBudgetMultiplierNG)));
#if __XENON || __PS3
return result - GetMemForPedsAdjustment(eType, uLevel);
#else
return result;
#endif // __XENON || __PS3
}
void CPopulationStreaming::RemoveResidentPedObjects()
{
// Get the memory usage of the shared ped texture dictionaries
for(int i =0; i < CPedModelInfo::GetResidentObjects().GetCount(); i++)
{
strIndex index = CPedModelInfo::GetResidentObjects()[i];
u32 virtualSize=0, physicalSize=0;
strStreamingEngine::GetInfo().GetObjectAndDependenciesSizes(index, virtualSize, physicalSize);
AddTotalMemoryUsed(-(s32)virtualSize, -(s32)physicalSize, MTT_PED);
}
}
void CPopulationStreaming::Shutdown(unsigned shutdownMode)
{
if(shutdownMode == SHUTDOWN_SESSION)
{
SCENARIOCLUSTERING.ResetModelRefCounts();
RemoveStreamedPedModelsOnShutdown();
RemoveStreamedCarModelsOnShutdown();
// Get the memory usage of the shared vehicle texture dictionaries
for(int i =0; i < CVehicleModelInfo::GetResidentObjects().GetCount(); i++)
{
strIndex index = CVehicleModelInfo::GetResidentObjects()[i];
u32 virtualSize=0, physicalSize=0;
strStreamingEngine::GetInfo().GetObjectAndDependenciesSizes(index, virtualSize, physicalSize);
AddTotalMemoryUsed(-(s32)virtualSize, -(s32)physicalSize, MTT_VEHICLE);
}
RemoveResidentPedObjects();
#if DEBUG_POPULATION_MEMORY
for (s32 i = 0; i < MAX_STREAMED_IN_MODELS; ++i)
{
if (m_StreamedInModelData[i].m_ModelIndex != fwModelId::MI_INVALID && CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(m_StreamedInModelData[i].m_ModelIndex))))
{
CBaseModelInfo* bmi = CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(m_StreamedInModelData[i].m_ModelIndex)));
if (bmi->GetModelType() == MI_TYPE_PED)
{
CPedModelInfo* pmi = (CPedModelInfo*)bmi;
Warningf("CPopulationStreaming::Shutdown: Non cleaned up model data for model '%s' found. M%dK V%dK HD M%dK V%dK", bmi->GetModelName(), m_StreamedInModelData[i].m_mainUsed>>10, m_StreamedInModelData[i].m_vramUsed>>10, m_StreamedInModelData[i].m_HDAssetSizeMain>>10, m_StreamedInModelData[i].m_HDAssetSizeVram>>10);
if (pmi)
Warningf("CPopulationStreaming::Shutdown: Ped %s has %d refs. %s", pmi->GetModelName(), pmi->GetNumPedModelRefs(), pmi->m_isRequestedAsDriver ? "(requested as driver)" : "");
}
else if(bmi->GetModelType() == MI_TYPE_VEHICLE)
{
CVehicleModelInfo* vmi = (CVehicleModelInfo*)bmi;
Warningf("CPopulationStreaming::Shutdown: Non cleaned up model data for model '%s' found. M%dK V%dK HD M%dK V%dK", bmi->GetModelName(), m_StreamedInModelData[i].m_mainUsed>>10, m_StreamedInModelData[i].m_vramUsed>>10, m_StreamedInModelData[i].m_HDAssetSizeMain>>10, m_StreamedInModelData[i].m_HDAssetSizeVram>>10);
if (vmi)
Warningf("CPopulationStreaming::Shutdown: Veh %s has %d(%d) refs (parked).", vmi->GetModelName(), vmi->GetNumVehicleModelRefs(), vmi->GetNumVehicleModelRefsParked());
}
/*else if (bmi->GetModelType() == MI_TYPE_WEAPON)
{
Warningf("CPopulationStreaming::Shutdown: Non cleaned up model data for weapon '%s' found. M%dK V%dK HD M%dK V%dK", bmi->GetModelName(), m_StreamedInModelData[i].m_mainUsed>>10, m_StreamedInModelData[i].m_vramUsed>>10, m_StreamedInModelData[i].m_HDAssetSizeMain>>10, m_StreamedInModelData[i].m_HDAssetSizeVram>>10);
}*/
}
}
for (s32 i = 0; i < m_pedComponents.GetCount(); ++i)
{
if (m_pedComponents[i].refCount != 0)
{
Warningf("CPopulationStreaming::Shutdown: Found ped component with ref count %d, streaming index: %d!", m_pedComponents[i].refCount, m_pedComponents[i].streamIdx.Get());
}
}
for (s32 i = 0; i < m_vehicleComponents.GetCount(); ++i)
{
if (m_vehicleComponents[i].refCount != 0)
{
Warningf("CPopulationStreaming::Shutdown: Found vehicle component with ref count %d, streaming index: %d!", m_vehicleComponents[i].refCount, m_vehicleComponents[i].streamIdx.Get());
}
}
// can't do this for weapons, they aren't streamed out at this point
//for (s32 i = 0; i < m_weaponComponents.GetCount(); ++i)
//{
// if (m_weaponComponents[i].refCount != 0)
// {
// Warningf("CPopulationStreaming::Shutdown: Found weapon component with ref count %d, streaming index: %d!", m_weaponComponents[i].refCount, m_weaponComponents[i].streamIdx.Get());
// }
//}
#endif // DEBUG_POPULATION_MEMORY
Assertf(m_TotalMemoryUsedByPedModelsMain == 0, "Main ped memory not what expected: %d", m_TotalMemoryUsedByPedModelsMain);
Assertf(m_TotalMemoryUsedByPedModelsVram == 0, "Physical ped memory not what expected: %d", m_TotalMemoryUsedByPedModelsVram);
Assertf(m_TotalMemoryUsedByVehicleModelsMain == m_TotalWeaponMemoryMain, "Main car memory not what expected: %d - %d", m_TotalMemoryUsedByVehicleModelsMain, m_TotalWeaponMemoryMain);
Assertf(m_TotalMemoryUsedByVehicleModelsVram == m_TotalWeaponMemoryVram, "Physical car memory not what expected: %d - %d", m_TotalMemoryUsedByVehicleModelsVram, m_TotalWeaponMemoryVram);
Assertf(m_TotalStreamedPedMemoryMain == 0, "Main streamed ped memory not what expected: %d", m_TotalStreamedPedMemoryMain);
Assertf(m_TotalStreamedPedMemoryVram == 0, "Physics streamed ped memory not what expected: %d", m_TotalStreamedPedMemoryVram);
Assertf(m_TotalStreamedVehicleMemoryMain == 0, "Main streamed car memory not what expected: %d", m_TotalStreamedVehicleMemoryMain);
Assertf(m_TotalStreamedVehicleMemoryVram == 0, "Physical streamed car memory not what expected: %d", m_TotalStreamedVehicleMemoryVram);
// can't have these asserts, weapon modelinfos aren't streamed out at this point
//Assertf(m_TotalWeaponMemoryMain == 0, "Main weapon memory not what expected: %d", m_TotalWeaponMemoryMain);
//Assertf(m_TotalWeaponMemoryVram == 0, "Physical weapons memory not what expected: %d", m_TotalWeaponMemoryVram);
for (int i = 0; i < m_residentObjectsToRemove.GetCount(); ++i)
{
strIndex index = m_residentObjectsToRemove[i];
m_residentObjects.Delete(m_residentObjects.Find(index));
}
m_residentObjectsToRemove.Reset();
}
else if(shutdownMode == SHUTDOWN_WITH_MAP_LOADED)
{
m_residentObjects.ResetCount();
}
}
void CPopulationStreaming::Update()
{
#if !__FINAL
if(CStreaming::IsStreamingPaused())
return;
#endif
EmergencyRemoveVehicleStructures();
if (!CStreaming::IsPlayerPositioned())
return;
if (!CFileMgr::IsGameInstalled())
return;
if (CGameSessionStateMachine::GetGameSessionState() == CGameSessionStateMachine::START_NEW_GAME)
return;
// if(strStreamingEngine::GetIsLoadingPriorityObjects())
// return;
if (!s_isNetGameInProgress && NetworkInterface::IsGameInProgress())
{
// flag any discarded vehicles we have as not used anymore, since we don't deal with them in MP games
// they might end up sticking around in memory for a while
s32 numDiscardedCars = m_DiscardedCars.CountMembers();
for (s32 i = 0; i < numDiscardedCars; ++i)
{
fwModelId modelId((strLocalIndex(m_DiscardedCars.GetMember(i))));
if (!modelId.IsValid())
continue;
CModelInfo::ClearAssetRequiredFlag(modelId, STRFLAG_DONTDELETE);
MarkModelNoLongerRequired(modelId.GetModelIndex());
}
ReclassifyLoadedVehiclesForNetwork();
}
s_isNetGameInProgress = NetworkInterface::IsGameInProgress();
#if 0
// Don't load new models when the cutscenemanager is running
if(CutSceneManager::GetInstance())
{
if (CutSceneManager::GetInstance()->IsRunning())
{
s_isInCutscene = true;
}
else
{
if (s_isInCutscene)
{
s_isInCutscene = false;
if (m_AppropriateLoadedCars.CountMembers() < 3 && s_MemForVehiclesLevel > 0)
{
StreamOneNewCar();
StreamOneNewCar();
StreamOneNewCar();
}
}
StreamModelsIn();
}
}
else
#endif
{
#if GTA_REPLAY
if(!CReplayCoordinator::ShouldDisablePopStreamer())
#endif // GTA_REPLAY
StreamModelsIn();
}
StreamModelsOut();
RemoveUnusedModels();
UpdateHDCosts();
perfClearingHouse::SetValue(perfClearingHouse::PED_STREAMING_OVERAGE, m_MemOverBudgetMainPeds + m_MemOverBudgetVramPeds);
perfClearingHouse::SetValue(perfClearingHouse::VEHICLE_STREAMING_OVERAGE, m_MemOverBudgetMainVehs + m_MemOverBudgetVramVehs);
// If we recently switched to a different population zone, we may have disallowed
// new scenario ped model requests. The code below makes sure we break out of that
// mode if we already have enough appropriate peds - hopefully that will prevent any
// scenario ped streaming interruption if we are just switching between population zones
// where roughly the same ped models are appropriate.
if(fwTimer::GetTimeInMilliseconds() < m_ScenarioPedStreamingDisabledUntilTimeMS)
{
streamDebugf2("Streaming of new scenario peds disabled after pop zone switch, %d ms remaining.",
(int)(m_ScenarioPedStreamingDisabledUntilTimeMS - fwTimer::GetTimeInMilliseconds()));
const int numAppropriate = m_AppropriateLoadedPeds.CountUsableOnes();
if(numAppropriate >= s_scenarioPedMinAppropriateToSkipDisallow)
{
streamDebugf1("Streaming of new scenario peds re-enabled after pop zone switch, %d appropriate peds loaded.", numAppropriate);
m_ScenarioPedStreamingDisabledUntilTimeMS = 0;
}
}
sm_TotalMemUsedByPeds = GetTotalMemUsedByPeds();
sm_TotalMemUsedByVehicles = GetTotalMemUsedByVehicles();
}
void CPopulationStreaming::SetNetworkModelVariationID(u32 networkModelVariationID)
{
if(Verifyf(networkModelVariationID < NUM_NETWORK_MODEL_VARIATIONS, "Trying to set an invalid network model variation!"))
{
m_NetworkModelVariationID = networkModelVariationID;
m_LastTimeUsedForStreamingNetPeds = GetCurrentNetworkTimeInterval();
m_LastTimeUsedForStreamingNetVehicles = GetCurrentNetworkTimeInterval();
}
}
void CPopulationStreaming::SetReduceAmbientPedModelBudget(bool value)
{
m_bReducePedModelBudget = value;
}
void CPopulationStreaming::SetReduceAmbientVehicleModelBudget(bool value)
{
m_bReduceVehicleModelBudget = value;
}
void CPopulationStreaming::HandlePopCycleInfoChange(bool enteredNewZone)
{
ReclassifyLoadedPeds();
TellStreamingAboutDeletablePeds();
ReclassifyLoadedCars();
TellStreamingAboutDeletableVehicles();
if(enteredNewZone)
{
// Prevent scenario peds from streaming in for a while, so we get a chance to catch
// up with generic models for the new population zone.
DisallowScenarioPedStreamingForDuration(s_scenarioPedMaxTimeDisallowedAfterEnteringPopZone);
}
}
void CPopulationStreaming::StreamOneNewCar()
{
#if __BANK
if (PARAM_nocars.Get())
return;
#endif // __BANK
if (CVehicleStructure::m_pInfoPool->GetNoOfFreeSpaces() <= 0)
{
// this should only happen when we leak vehicles or in the future when we afford 70+ vehicle models streamed in at the same time
Assertf(false, "CPopulationStreaming::StreamOneNewCar: Trying to stream new car while we have no more space in the CVehicleStruct pool!");
return;
}
strLocalIndex modelIndex = strLocalIndex(fwModelId::MI_INVALID);
if(NetworkInterface::IsGameInProgress())
{
modelIndex = ChooseNewVehicleModelForMP();
}
else
{
modelIndex = ChooseNewVehicleModelForSP();
}
if(CModelInfo::IsValidModelInfo(modelIndex.Get()))
{
STRVIS_SET_CONTEXT(strStreamingVisualize::POPSTREAMER);
fwModelId modelId(modelIndex);
CModelInfo::RequestAssets(modelId, STRFLAG_FORCE_LOAD | STRFLAG_DONTDELETE);
RequestVehicleDriver(modelIndex.Get());
if (m_DiscardedCars.IsMemberInGroup(modelIndex.Get()))
ReinstateDiscardedCar(modelIndex.Get());
// see if we should request a trailer too
u32 curTime = fwTimer::GetTimeInMilliseconds();
if ((curTime - m_LastVehicleOnHighwaySpawn) < 5000)
{
CVehicleModelInfo* vmi = (CVehicleModelInfo *)CModelInfo::GetBaseModelInfo(modelId);
if (vmi)
{
u32 trailersLoaded = 0;
CVehicleModelInfo* trailerVmi = NULL;
const u32 trailerCount = vmi->GetTrailerCount();
for (s32 i = 0; i < trailerCount; ++i)
{
if(CModelInfo::HaveAssetsLoaded(fwModelId(vmi->GetTrailer(i, trailerVmi))))
{
trailersLoaded++;
break;
}
}
if (trailerCount && trailersLoaded == 0)
{
fwModelId trailerId = vmi->GetTrailer(fwRandom::GetRandomNumberInRange(0, trailerCount), trailerVmi);
CModelInfo::RequestAssets(trailerId, STRFLAG_FORCE_LOAD | STRFLAG_DONTDELETE);
m_numQueuedVehicleModels++;
}
}
}
m_numQueuedVehicleModels++;
STRVIS_SET_CONTEXT(strStreamingVisualize::NONE);
}
}
u32 CPopulationStreaming::GetNumCommonVehicles()
{
u32 numComonVehs = 0;
s32 numVehs = m_AppropriateLoadedCars.CountMembers();
u32 maxAmbientVehs = (u32)(CVehiclePopulation::GetDesiredNumberAmbientVehicles() + 0.5f);
for (s32 i = 0; i < numVehs; ++i)
{
strLocalIndex mi = strLocalIndex(m_AppropriateLoadedCars.GetMember((u32)i));
if (CPopCycle::GetPopGroups().IsVehIndexMember(CPopCycle::GetCommonVehicleGroup(), mi.Get()))
{
CVehicleModelInfo* vmi = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(mi));
if (vmi->GetVehicleMaxNumber() >= maxAmbientVehs)
{
numComonVehs++;
}
}
}
return numComonVehs;
}
u32 CPopulationStreaming::ChooseNewVehicleModelForMP()
{
#if __BANK
if (m_vehicleOverrideIndex != fwModelId::MI_INVALID)
return m_vehicleOverrideIndex;
#endif
u32 chosenModelIndex = fwModelId::MI_INVALID;
unsigned numVehicleModelsRequired = m_VehicleModelsRequiredForNetwork.CountMembers();
for(unsigned index = 0; index < numVehicleModelsRequired && !CModelInfo::IsValidModelInfo(chosenModelIndex); index++)
{
strLocalIndex modelIndex = strLocalIndex(m_VehicleModelsRequiredForNetwork.GetMember(index));
fwModelId modelId(modelIndex);
if(modelId.IsValid())
{
if(CModelInfo::HaveAssetsLoaded(modelId))
{
if(!IsModelDataStreamedIn(modelIndex.Get()))
{
chosenModelIndex = modelIndex.Get();
}
}
else
{
chosenModelIndex = modelIndex.Get();
}
}
}
return chosenModelIndex;
}
u32 CPopulationStreaming::ChooseNewVehicleModelForSP()
{
u32 modelIndex = fwModelId::MI_INVALID;
#if __BANK
if (m_vehicleOverrideIndex != fwModelId::MI_INVALID)
return m_vehicleOverrideIndex;
#endif
u32 strRestrictions = m_bBoatsNeeded ? VSR_BOAT : 0;
// make sure we always have a common vehicle in the appropriate list
// a common vehicle is a vehicle in the VEH_MID group with no maxNumber restriction
if (GetNumCommonVehicles() <= 1)
strRestrictions |= VSR_COMMON_VEH;
// see if we have any peds that can ride bikes streamed in
if (!m_AppropriateLoadedPeds.IsBikeDriverInPedGroup())
strRestrictions |= VSR_NO_BIKE;
CLoadedModelGroup appropriateVehs;
appropriateVehs.Merge(&m_AppropriateLoadedCars, &m_LoadedBoats);
// if we're in the "default" zone wait, or we end up streaming in vehicles we don't need
static const u32 defaultHash = atStringHash("DEFAULT");
if (!CPopCycle::GetCurrentActiveZone() || CPopCycle::GetCurrentActiveZone()->m_id.GetHash() != defaultHash)
{
s32 *pGroupArray;
int NumAvailableGroups = CPopCycle::PickMostWantingGroupOfModels(appropriateVehs, false, &pGroupArray);
modelIndex = ChooseCarModelToLoad(pGroupArray, NumAvailableGroups, strRestrictions);
}
return modelIndex;
}
void CPopulationStreaming::StreamOneNewPed(bool bNeedDriver)
{
#if __BANK
if (PARAM_nopeds.Get())
return;
#endif // __BANK
strLocalIndex modelIndex = strLocalIndex(fwModelId::MI_INVALID);
if(NetworkInterface::IsGameInProgress())
{
modelIndex = ChooseNewPedModelForMP(bNeedDriver);
}
else
{
modelIndex = ChooseNewPedModelForSP(bNeedDriver);
}
if(CModelInfo::IsValidModelInfo(modelIndex.Get()))
{
fwModelId modelId(modelIndex);
if(CModelInfo::HaveAssetsLoaded(modelId))
{
if(!IsModelDataStreamedIn(modelIndex.Get()))
{
PedHasBeenStreamedIn(modelIndex.Get());
CModelInfo::SetAssetRequiredFlag(modelId, STRFLAG_DONTDELETE);
}
else
{
ReinstateDiscardedPed(modelIndex.Get());
}
}
else
{
CModelInfo::RequestAssets(modelId, STRFLAG_FORCE_LOAD | STRFLAG_DONTDELETE);
CPedModelInfo* pModelInfo = static_cast<CPedModelInfo *>(CModelInfo::GetBaseModelInfo(modelId));
if (pModelInfo->GetPersonalitySettings().GetIsMale())
s_numMalePedsRequested++;
else
s_numFemalePedsRequested++;
}
}
}
u32 CPopulationStreaming::ChooseNewPedModelForSP(bool bNeedDriver)
{
u32 modelIndex = fwModelId::MI_INVALID;
u32 strRestrictions = bNeedDriver ? PSR_DRIVER : 0;
if (!m_AppropriateLoadedPeds.IsMaleInPedGroup() && s_numMalePedsRequested == 0)
strRestrictions |= PSR_MALE;
else if (!m_AppropriateLoadedPeds.IsFemaleInPedGroup() && s_numFemalePedsRequested == 0)
strRestrictions |= PSR_FEMALE;
if (!m_AppropriateLoadedPeds.IsBikeDriverInPedGroup() && CPopCycle::VehicleBikeGroupActive())
strRestrictions |= PSR_BIKER;
// if current zone index is 0 we're in the "default" zone before the popzone data has been loaded.
// wait, or we end up streaming in vehicles we don't need
if (CPopCycle::GetCurrentZoneIndex() > 0)
{
s32 *pGroupArray;
int NumAvailableGroups = CPopCycle::PickMostWantingGroupOfModels(m_AppropriateLoadedPeds, true, &pGroupArray);
modelIndex = ChoosePedModelToLoad(pGroupArray, NumAvailableGroups, strRestrictions);
}
return modelIndex;
}
u32 CPopulationStreaming::ChooseNewPedModelForMP(bool bNeedDriver)
{
u32 chosenModelIndex = fwModelId::MI_INVALID;
u32 chosenNonDriver = fwModelId::MI_INVALID;
unsigned numPedModelsRequired = m_PedModelsRequiredForNetwork.CountMembers();
for(unsigned index = 0; index < numPedModelsRequired && !CModelInfo::IsValidModelInfo(chosenModelIndex); index++)
{
strLocalIndex modelIndex = strLocalIndex(m_PedModelsRequiredForNetwork.GetMember(index));
fwModelId modelId(modelIndex);
if(modelId.IsValid())
{
if(CModelInfo::HaveAssetsLoaded(modelId))
{
if(!IsModelDataStreamedIn(modelIndex.Get()))
{
if (CanPedModelDrive(modelIndex.Get()) || !bNeedDriver)
chosenModelIndex = modelIndex.Get();
else if (chosenNonDriver != fwModelId::MI_INVALID)
chosenNonDriver = modelIndex.Get();
}
}
else
{
chosenModelIndex = modelIndex.Get();
}
}
}
if (chosenModelIndex == fwModelId::MI_INVALID)
return chosenNonDriver;
return chosenModelIndex;
}
void CPopulationStreaming::StreamModelsIn()
{
bool streamNewModels = !(fwTimer::GetSystemFrameCount() & 63) || (!CStreaming::GetNumberObjectsRequested() && IsThereAnyMemoryLeft(MTT_VEHICLE));
if (streamNewModels || (CPopCycle::GetPreferredGroupForVehs() != -1 && m_numQueuedVehicleModels == 0))
{
STRVIS_AUTO_CONTEXT(strStreamingVisualize::POPSTREAMER);
bool bNeedDriver = true;
CLoadedModelGroup fallbackPedDrivers;
GetFallbackPedGroup(fallbackPedDrivers, true);
if (fallbackPedDrivers.CountMembers() > 0)
{
bNeedDriver = false;
}
if (streamNewModels)
{
if (IsThereAnyMemoryLeft(MTT_PED) || (m_AppropriateLoadedPeds.CountMembers() < MIN_NUMBER_STREAMED_PEDS && s_MemForPedsLevel == MAX_MEM_LEVELS - 1) || bNeedDriver)
{
NOTFINAL_ONLY(if(gbAllowAmbientPedGeneration || gbAllowVehGenerationOrRemoval))
{
StreamOneNewPed(bNeedDriver);
}
}
}
if (IsThereAnyMemoryLeft(MTT_VEHICLE) || (m_AppropriateLoadedCars.CountMembers() < MIN_NUMBER_STREAMED_CARS && s_MemForVehiclesLevel == MAX_MEM_LEVELS - 1))
{
NOTFINAL_ONLY(if(gbAllowVehGenerationOrRemoval))
{
StreamOneNewCar();
}
}
if(!(fwTimer::GetSystemFrameCount() & 127))
{
bool bOldBoatsNeeded = m_bBoatsNeeded;
// we don't support random boats in network games, if the players are split across the map,
// it's wasteful to stream in a vehicle model that not every player can use
bool bAllowRandomBoats = NetworkInterface::IsGameInProgress() ? CVehiclePopulation::ms_bAllowRandomBoatsMP : CVehiclePopulation::ms_bAllowRandomBoats;
if (bAllowRandomBoats)
{
m_bBoatsNeeded = ThePaths.IsWaterNodeNearby(camInterface::GetPos(), 80.0f);
}
else
{
m_bBoatsNeeded = false;
}
if (bOldBoatsNeeded != m_bBoatsNeeded)
{
TellStreamingAboutDeletableVehicles();
// If boats are needed now (and not before), make sure that any model already
// in m_LoadedBoats gets a driver requested.
if(m_bBoatsNeeded)
{
// Note: potentially we could restrict this to the members of the VEH_BOATS set.
// Or, maybe only VEH_BOATS members should go into the m_LoadedBoats set to begin with?
const int numLoadedBoats = m_LoadedBoats.CountMembers();
for(int i = 0; i < numLoadedBoats; i++)
{
const strLocalIndex modelIndex = strLocalIndex(m_LoadedBoats.GetMember(i));
fwModelId modelId(modelIndex);
if(modelId.IsValid())
{
RequestVehicleDriver(modelIndex.Get());
}
}
}
}
}
// update which ped models are required by the network game
if(streamNewModels && NetworkInterface::IsGameInProgress())
{
UpdateModelsRequiredForNetwork();
ReclassifyLoadedPedsForNetwork();
ReclassifyLoadedVehiclesForNetwork();
}
}
}
void CPopulationStreaming::StreamModelsOut()
{
bool forceStreamModelOut = (CPopCycle::GetPreferredGroupForVehs() != -1) && !NetworkInterface::IsGameInProgress();
if ((fwTimer::GetSystemFrameCount() & 31) && !forceStreamModelOut)
{
return;
}
// reclassify any network models based on whether they are current required
// by the network game
if(NetworkInterface::IsGameInProgress())
{
ReclassifyLoadedVehiclesForNetwork();
ReclassifyLoadedPedsForNetwork();
}
u32 removedPeds = 0;
// Go through the inappropriate peds and have them removed.
s32 numMembers = m_InAppropriateLoadedPeds.CountMembers();
for (int i = 0; i < numMembers; i++)
{
strLocalIndex ModelIndex = strLocalIndex(m_InAppropriateLoadedPeds.GetMember(i));
fwModelId modelId(ModelIndex);
if (!modelId.IsValid())
break;
CPedModelInfo *pPedModelInfo = (CPedModelInfo *)CModelInfo::GetBaseModelInfo(modelId);
if (pPedModelInfo->m_isRequestedAsDriver)
continue;
if ( (!pPedModelInfo->GetCountedAsScenarioPed()) ||
(fwTimer::GetTimeInMilliseconds() > GetTimeModelStreamedIn(ModelIndex.Get()) + SCENARIO_PEDS_DONT_GET_REMOVED_DURATION &&
!(pPedModelInfo->GetNumPedModelRefs() > 0)) )
{
Assert(CModelInfo::HaveAssetsLoaded(modelId));
CModelInfo::ClearAssetRequiredFlag(modelId, STRFLAG_DONTDELETE);
MarkModelNoLongerRequired(ModelIndex.Get());
if (!NetworkInterface::IsGameInProgress())
{
AddDiscardedPed(ModelIndex.Get());
--i;
}
++removedPeds;
}
}
// Scenario peds can also end up in the special peds (if they aren't in pedgrp.dat)
// Also go through the special peds and remove the scenario peds that have been there for a while.
numMembers = m_SpecialLoadedPeds.CountMembers();
for (int i = 0; i < numMembers; i++)
{
strLocalIndex ModelIndex = strLocalIndex(m_SpecialLoadedPeds.GetMember(i));
fwModelId modelId(ModelIndex);
if (!modelId.IsValid())
break;
CPedModelInfo *pPedModelInfo = (CPedModelInfo *)CModelInfo::GetBaseModelInfo(modelId);
bool canDeleteScenarioPed = pPedModelInfo->GetCountedAsScenarioPed() && !pPedModelInfo->m_isRequestedAsDriver && fwTimer::GetTimeInMilliseconds() > (GetTimeModelStreamedIn(ModelIndex.Get()) + SCENARIO_PEDS_DONT_GET_REMOVED_DURATION);
bool canDeleteOtherPed = !pPedModelInfo->GetCountedAsScenarioPed() && !pPedModelInfo->m_isRequestedAsDriver;
if ((canDeleteScenarioPed || canDeleteOtherPed) && pPedModelInfo->GetNumPedModelRefs() <= 0)
{
Assert(CModelInfo::HaveAssetsLoaded(modelId));
CModelInfo::ClearAssetRequiredFlag(modelId, STRFLAG_DONTDELETE);
MarkModelNoLongerRequired(ModelIndex.Get());
// add to discarded list if this ped is appropriate for this zone. scenario/special peds may or may not be useable in
// ambient pop. if they are, they need to end up in the discarded list here or else they won't be rotated out
if (IsPedModelNeededCurrently(ModelIndex.Get()))
{
AddDiscardedPed(ModelIndex.Get());
--i;
}
++removedPeds;
}
}
// try to remove old models so we get a nice rotation going
if (removedPeds == 0 && m_DiscardedPeds.CountMembers() <= 1 && m_AppropriateLoadedPeds.CountMembers() > 1)
{
strLocalIndex modelToDelete = FindOldestModelToStreamOut(true, true);
if (modelToDelete != fwModelId::MI_INVALID)
{
fwModelId modelId(modelToDelete);
Assert(CModelInfo::HaveAssetsLoaded(modelId));
CPedModelInfo* pPedModelInfo = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(modelId);
if (!pPedModelInfo->m_isRequestedAsDriver)
{
CModelInfo::ClearAssetRequiredFlag(modelId, STRFLAG_DONTDELETE);
MarkModelNoLongerRequired(modelToDelete.Get());
if (!NetworkInterface::IsGameInProgress())
AddDiscardedPed(modelToDelete.Get());
}
}
}
if(NetworkInterface::IsGameInProgress())
{
CLoadedModelGroup modelsReadyForRemoval;
CLoadedModelGroup modelsReadyForRemovalWithNoRefs;
unsigned modelsPendingRemoval = 0;
// In network games we stream out any cars that not in the current required list
int numMembers = m_InAppropriateLoadedCars.CountMembers();
for(int index = 0; index < numMembers; index++)
{
u32 modelIndex = m_InAppropriateLoadedCars.GetMember(index);
fwModelId modelIdToBeRemoved((strLocalIndex(modelIndex)));
if(modelIdToBeRemoved.IsValid())
{
CVehicleModelInfo *pModelInfo = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(modelIdToBeRemoved);
bool missionRequired = (CModelInfo::GetAssetStreamFlags(modelIdToBeRemoved) & STRFLAG_MISSION_REQUIRED) != 0;
missionRequired |= pModelInfo->HasAnyMissionInstances();
if(!missionRequired)
{
if(!CModelInfo::GetAssetRequiredFlag(modelIdToBeRemoved))
{
modelsPendingRemoval++;
}
else
{
// don't try to remove the player important vehicle, it won't be removed since the player is in it
if (CVehiclePopulation::ms_pInterestingVehicle)
if (CVehiclePopulation::ms_pInterestingVehicle->GetModelIndex() == modelIndex)
continue;
if (pModelInfo->GetNumRefs() <= 0)
modelsReadyForRemovalWithNoRefs.AddMember(modelIndex);
else
modelsReadyForRemoval.AddMember(modelIndex);
}
}
}
}
if(modelsPendingRemoval < MAX_NETWORK_MODELS_OF_TYPE_PENDING_STREAMING_REMOVAL)
{
// first try ejecting models with no refs, higher chance of them getting removed quickly
for(int index = 0; index < modelsReadyForRemovalWithNoRefs.CountMembers() && (modelsPendingRemoval < MAX_NETWORK_MODELS_OF_TYPE_PENDING_STREAMING_REMOVAL); index++)
{
strLocalIndex modelIndex = strLocalIndex(modelsReadyForRemovalWithNoRefs.GetMember(index));
fwModelId modelIdToBeRemoved(modelIndex);
if(modelIdToBeRemoved.IsValid())
{
Assert(CModelInfo::HaveAssetsLoaded(modelIdToBeRemoved));
CModelInfo::ClearAssetRequiredFlag(modelIdToBeRemoved, STRFLAG_DONTDELETE);
MarkModelNoLongerRequired(modelIndex.Get());
modelsPendingRemoval++;
}
}
for(int index = 0; index < modelsReadyForRemoval.CountMembers() && (modelsPendingRemoval < MAX_NETWORK_MODELS_OF_TYPE_PENDING_STREAMING_REMOVAL); index++)
{
strLocalIndex modelIndex = strLocalIndex(modelsReadyForRemoval.GetMember(index));
fwModelId modelIdToBeRemoved(modelIndex);
if(modelIdToBeRemoved.IsValid())
{
Assert(CModelInfo::HaveAssetsLoaded(modelIdToBeRemoved));
CModelInfo::ClearAssetRequiredFlag(modelIdToBeRemoved, STRFLAG_DONTDELETE);
MarkModelNoLongerRequired(modelIndex.Get());
modelsPendingRemoval++;
}
}
}
}
else
{
// TODO - Some code has been removed from here that was limiting the number of vehicles that
// can be streamed out concurrently - unless we were at the airport. This need checking
// to see whether it is still necessary. The existing code didn't work though, so I have removed it
// END TODO
bool canStreamCommonOut = GetNumCommonVehicles() > 1;
CLoadedModelGroup candidatesForRemoval;
GetCandidateVehiclesToStreamOutForSP(candidatesForRemoval, canStreamCommonOut);
// Pick the car that has been used most times so far. (More chance that the player has seen it)
// Take the vehicles frequency into account so that rare vehicle also will get streamed out.
s32 MinTimesUsed = -1;
strLocalIndex ModelIndexToBeRemoved = strLocalIndex(fwModelId::MI_INVALID);
s32 numMembers = candidatesForRemoval.CountMembers();
for (int k = 0; k < numMembers; k++)
{
const u32 uModelIndex = candidatesForRemoval.GetMember(k);
const strLocalIndex ModelIndex = strLocalIndex(uModelIndex);
const fwModelId modelId(ModelIndex);
// don't try to remove the player important vehicle, it won't be removed since the player is in it
const bool interestingVehicle = CVehiclePopulation::ms_pInterestingVehicle && CVehiclePopulation::ms_pInterestingVehicle->GetModelIndex() == uModelIndex;
// don't try and stream out mission required vehicles - they won't get removed
const bool missionRequired = (CModelInfo::GetAssetStreamFlags(modelId) & STRFLAG_MISSION_REQUIRED) != 0;
if(!interestingVehicle && !missionRequired)
{
if (CModelInfo::GetAssetRequiredFlag(modelId)) // If the model hasn't been flagged for removal elsewhere
{
CVehicleModelInfo *pModelInfo = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(modelId);
s32 scaledTimesUsed = pModelInfo->GetNumTimesUsed() * 100 / pModelInfo->GetVehicleFreq();
if ( scaledTimesUsed > MinTimesUsed )
{
ModelIndexToBeRemoved = ModelIndex;
MinTimesUsed = scaledTimesUsed;
}
}
else if(!NetworkInterface::IsGameInProgress()
&& !m_DiscardedCars.IsMemberInGroup(uModelIndex)
&& !m_SpecialLoadedCars.IsMemberInGroup(uModelIndex)
&& !m_LoadedBoats.IsMemberInGroup(uModelIndex))
{
// If the model has been flagged for removal, but hasn't been discarded
// This tends to happen for vehicles with streaming handled elsewhere, like debug and script created ones.
AddDiscardedCar(uModelIndex);
}
}
}
if (ModelIndexToBeRemoved == fwModelId::MI_INVALID && m_DiscardedCars.CountMembers() <= 1)
ModelIndexToBeRemoved = FindOldestModelToStreamOut(false, canStreamCommonOut);
fwModelId modelIdToBeRemoved(ModelIndexToBeRemoved);
if(modelIdToBeRemoved.IsValid())
{
Assert(CModelInfo::HaveAssetsLoaded(modelIdToBeRemoved));
CModelInfo::ClearAssetRequiredFlag(modelIdToBeRemoved, STRFLAG_DONTDELETE);
MarkModelNoLongerRequired(ModelIndexToBeRemoved.Get());
if(!NetworkInterface::IsGameInProgress() && !m_SpecialLoadedCars.IsMemberInGroup(ModelIndexToBeRemoved.Get()) && !m_LoadedBoats.IsMemberInGroup(ModelIndexToBeRemoved.Get()))
AddDiscardedCar(ModelIndexToBeRemoved.Get());
}
}
}
strLocalIndex CPopulationStreaming::FindOldestModelToStreamOut(bool isPed, bool canStreamCommonOut)
{
strLocalIndex modelId = strLocalIndex(fwModelId::MI_INVALID);
// remove a car/ped from memory so we can get a new one in and rotate through the whole group for current pop zone
// do it only if no other car/ped has been flagged for removal, at least s_intervalForAgeRemoval ms have passed since
// last removal and there's no more memory to stream in new models
// Don't do this is network games as MP games have their own rules for when vehicles are streamed out/in, independently
// of the pop zone data files.
if(!NetworkInterface::IsGameInProgress())
{
u32 nextRemovalTime = isPed ? (s_lastPedAgeRemoval + s_intervalForPedRemoval) : (s_lastVehicleAgeRemoval + s_intervalForVehicleRemoval);
//s32 totalMemUsedMain = isPed ? m_TotalMemoryUsedByPedModelsMain : m_TotalMemoryUsedByVehicleModelsMain;
//s32 totalMemUsedVram = isPed ? m_TotalMemoryUsedByPedModelsVram : m_TotalMemoryUsedByVehicleModelsVram;
u32 ageForRemoval = isPed ? s_pedAgeForRemoval : s_vehicleAgeForRemoval;
u32 curTimeMs = fwTimer::GetTimeInMilliseconds();
if ((nextRemovalTime < curTimeMs)/* && ((totalMemUsedMain >= memThresholdMain) || totalMemUsedVram >= memThresholdVram)*/)
{
s32 numInappropriate = isPed ? m_InAppropriateLoadedPeds.CountMembers() : m_InAppropriateLoadedCars.CountMembers();
u32 potentialModelIdx = fwModelId::MI_INVALID;
u32 oldestTime = ~0U;
for (u32 i = 0; i < MAX_STREAMED_IN_MODELS; ++i)
{
if (!CModelInfo::IsValidModelInfo(m_StreamedInModelData[i].m_ModelIndex))
continue;
if ((isPed && CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(m_StreamedInModelData[i].m_ModelIndex)))->GetModelType() != MI_TYPE_PED) ||
(!isPed && CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(m_StreamedInModelData[i].m_ModelIndex)))->GetModelType() != MI_TYPE_VEHICLE))
continue;
// ignore mission entities
if ((CModelInfo::GetAssetStreamFlags(fwModelId(strLocalIndex(m_StreamedInModelData[i].m_ModelIndex))) & STRFLAG_MISSION_REQUIRED) != 0)
continue;
// ignore discarded entries since they're already flagged as removed
if ((isPed && m_DiscardedPeds.IsMemberInGroup(m_StreamedInModelData[i].m_ModelIndex)) || (!isPed && m_DiscardedCars.IsMemberInGroup(m_StreamedInModelData[i].m_ModelIndex)))
continue;
// ignore special peds
if (isPed && m_SpecialLoadedPeds.IsMemberInGroup(m_StreamedInModelData[i].m_ModelIndex))
continue;
#if __BANK
if (m_StreamedInModelData[i].m_ModelIndex == m_vehicleOverrideIndex)
continue;
#endif
// don't try to remove the player important vehicle, it won't be removed since the player is in it
if (CVehiclePopulation::ms_pInterestingVehicle)
if (CVehiclePopulation::ms_pInterestingVehicle->GetModelIndex() == m_StreamedInModelData[i].m_ModelIndex)
continue;
// don't remove common vehicle if we don't have other ones in memory
if (!canStreamCommonOut)
if (CPopCycle::GetPopGroups().IsVehIndexMember(CPopCycle::GetCommonVehicleGroup(), m_StreamedInModelData[i].m_ModelIndex))
continue;
if (isPed)
{
CPedModelInfo* pmi = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(m_StreamedInModelData[i].m_ModelIndex)));
if (pmi->m_isRequestedAsDriver)
{
continue;
}
else if (!pmi->CanStreamOutAsOldest())
{
// Models can be selected as never to be streamed out because of age.
continue;
}
if (numInappropriate > 0 && !m_InAppropriateLoadedPeds.IsMemberInGroup(m_StreamedInModelData[i].m_ModelIndex))
continue;
}
if (m_StreamedInModelData[i].m_TimeStreamedIn < oldestTime)
{
oldestTime = m_StreamedInModelData[i].m_TimeStreamedIn;
potentialModelIdx = m_StreamedInModelData[i].m_ModelIndex;
}
}
u32 age = curTimeMs - oldestTime;
if (age > ageForRemoval)
{
modelId = potentialModelIdx;
}
if (isPed)
s_lastPedAgeRemoval = curTimeMs;
else
s_lastVehicleAgeRemoval = curTimeMs;
}
}
return modelId;
}
void CPopulationStreaming::GetCandidateVehiclesToStreamOutForSP(CLoadedModelGroup &candidatesToRemove, bool canStreamCommonVehicleOut)
{
candidatesToRemove.Clear();
if(!CPopCycle::HasValidCurrentPopAllocation())
return;
// If there are any vehicles in the appropriate list that don't belong in this zone we
// earmark it for deletion.
CLoadedModelGroup tempList;
tempList.Merge(&m_InAppropriateLoadedCars, &m_SpecialLoadedCars, &m_AppropriateLoadedCars);
u32 curTime = fwTimer::GetTimeInMilliseconds();
s32 numMembers = tempList.CountMembers();
for (int i = 0; i < numMembers; i++)
{
u32 mi = tempList.GetMember(i);
fwModelId modelId((strLocalIndex(mi)));
#if __BANK
if (m_vehicleOverrideIndex != fwModelId::MI_INVALID)
{
if (mi != m_vehicleOverrideIndex)
{
candidatesToRemove.AddMember(mi);
}
continue;
}
#endif
u32 timeStreamedIn = 0;
for (unsigned index = 0; index < MAX_STREAMED_IN_MODELS; index++)
{
if (m_StreamedInModelData[index].m_ModelIndex == mi)
{
timeStreamedIn = m_StreamedInModelData[index].m_TimeStreamedIn;
break;
}
}
if ((curTime - timeStreamedIn) < s_vehicleAgeForRemoval)
continue;
if (!canStreamCommonVehicleOut)
if (CPopCycle::GetPopGroups().IsVehIndexMember(CPopCycle::GetCommonVehicleGroup(), mi))
continue;
// candidates for removal are vehicle not belonging in popzone or all of them if budget level is 0 (except if they're law enforcement)
// note that later we skip script vehicles so no need to skip them here
if (!DoesCarModelBelongInCurrentPopulationZone(mi) || s_MemForVehiclesLevel == 0)
{
candidatesToRemove.AddMember(mi);
}
}
}
bool CPopulationStreaming::StreamOneCarOut(bool bForce)
{
s32 MinTimesUsed = 8; // Only remove model if used a reasonable number of times.
strLocalIndex ModelIndexToBeRemoved = strLocalIndex(fwModelId::MI_INVALID);
if(bForce)
MinTimesUsed = -1;
// Count the number of cars we're trying to remove.
// Only do one at a time.
s32 numCurrentlyBeingPhasedOut = 0;
const CLoadedModelGroup* pGroupContainingPhasedOutCars = NetworkInterface::IsGameInProgress() ? &m_AppropriateLoadedCars : &m_DiscardedCars;
const s32 numCarsInGroupContainingPhasedOutCars = pGroupContainingPhasedOutCars->CountMembers();
for (int C = 0; C < numCarsInGroupContainingPhasedOutCars; C++)
{
const strLocalIndex mi = strLocalIndex(pGroupContainingPhasedOutCars->GetMember(C));
fwModelId modelId(mi);
if (!CModelInfo::GetAssetRequiredFlag(modelId))
{
numCurrentlyBeingPhasedOut++;
}
}
if (numCurrentlyBeingPhasedOut > 0)
{
return false;
}
const s32 numAppropriateLoadedCars = m_AppropriateLoadedCars.CountMembers();
for (int C = 0; C < numAppropriateLoadedCars; C++)
{
u32 ModelIndex = m_AppropriateLoadedCars.GetMember(C);
#if __BANK
if (ModelIndex == m_vehicleOverrideIndex)
continue;
#endif
CVehicleModelInfo *pModelInfo = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)));
// Taxis won't be streamed out if they are required
//if ((!CPopCycle::AreTaxisRequired()) || !CVehicle::IsTaxiModelId(fwModelId(ModelIndex)))
{
// Pick the car that has been used most times so far. (More chance that the player has seen it)
// Take the vehicles frequency into account so that rare vehicle also will get streamed out.
s32 scaledTimesUsed = pModelInfo->GetNumTimesUsed() * 100 / pModelInfo->GetVehicleFreq();
if (scaledTimesUsed > MinTimesUsed)
{
ModelIndexToBeRemoved = ModelIndex;
MinTimesUsed = scaledTimesUsed;
}
}
}
fwModelId modelIdToBeRemoved(ModelIndexToBeRemoved);
if(modelIdToBeRemoved.IsValid())
{
Assert(CModelInfo::HaveAssetsLoaded(modelIdToBeRemoved));
CModelInfo::ClearAssetRequiredFlag(modelIdToBeRemoved, STRFLAG_DONTDELETE);
if(!NetworkInterface::IsGameInProgress())
AddDiscardedCar(ModelIndexToBeRemoved.Get());
if(!CModelInfo::GetAssetRequiredFlag(modelIdToBeRemoved))
return true;
}
return false;
}
bool CPopulationStreaming::StreamOnePedOut(bool bForce)
{
s32 MinTimesUsed = 8; // Only remove model if used a reasonable number of times.
strLocalIndex ModelIndexToBeRemoved = strLocalIndex(fwModelId::MI_INVALID);
if(bForce)
MinTimesUsed = -1;
// Count the number of peds we're trying to remove.
// Only do one at a time.
s32 numCurrentlyBeingPhasedOut = 0;
s32 numMembers = m_AppropriateLoadedPeds.CountMembers();
for (int C = 0; C < numMembers; C++)
{
strLocalIndex mi = strLocalIndex(m_AppropriateLoadedPeds.GetMember(C));
fwModelId modelId(mi);
if (!CModelInfo::GetAssetRequiredFlag(modelId))
{
numCurrentlyBeingPhasedOut++;
}
}
if (numCurrentlyBeingPhasedOut > 0)
{
return false;
}
for (int C = 0; C < numMembers; C++)
{
strLocalIndex ModelIndex = strLocalIndex(m_AppropriateLoadedPeds.GetMember(C));
CPedModelInfo *pModelInfo = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)));
if (pModelInfo->m_isRequestedAsDriver)
continue;
// Pick the car that has been used most times so far. (More chance that the player has seen it)
if (pModelInfo->GetNumTimesUsed() > MinTimesUsed)
{
ModelIndexToBeRemoved = ModelIndex;
MinTimesUsed = pModelInfo->GetNumTimesUsed();
}
}
fwModelId modelIdToBeRemoved(ModelIndexToBeRemoved);
if(modelIdToBeRemoved.IsValid())
{
Assert(CModelInfo::HaveAssetsLoaded(modelIdToBeRemoved));
CModelInfo::ClearAssetRequiredFlag(modelIdToBeRemoved, STRFLAG_DONTDELETE);
if (!NetworkInterface::IsGameInProgress())
AddDiscardedPed(ModelIndexToBeRemoved.Get());
if(!CModelInfo::GetAssetRequiredFlag(modelIdToBeRemoved))
return true;
}
return false;
}
void CPopulationStreaming::RemoveUnusedModels()
{
// Vehicles and peds that are earmarked to be removed and that aren't used anymore
// should be removed.
bool bIsNetworkInProgress = NetworkInterface::IsGameInProgress();
if ( !(fwTimer::GetSystemFrameCount() & 31) || ((AreWeOverBudget(MTT_PED) || AreWeOverBudget(MTT_VEHICLE)) && !bIsNetworkInProgress) )
{
STRVIS_AUTO_CONTEXT(strStreamingVisualize::POPSTREAMER);
CLoadedModelGroup allVehs;
allVehs.Merge(&m_DiscardedCars, &m_InAppropriateLoadedCars, &m_SpecialLoadedCars, &m_AppropriateLoadedCars);
CLoadedModelGroup tempList;
tempList.Merge(&allVehs, &m_LoadedBoats);
s32 numMembers = tempList.CountMembers();
for (int i = 0; i < numMembers; i++)
{
strLocalIndex ModelIndex = strLocalIndex(tempList.GetMember(i));
fwModelId modelId(ModelIndex);
if (!modelId.IsValid())
break;
CVehicleModelInfo* pVMI = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(modelId);
// Handle ones that are to be streamed out.
if (!CModelInfo::GetAssetRequiredFlag(modelId) &&
pVMI->GetNumRefs() == 0)
{
Assertf(pVMI->GetDrawable(), "Model %s expected to be in memory but is not loaded", pVMI->GetModelName());
CModelInfo::RemoveAssets(modelId);
}
}
tempList.Merge(&m_DiscardedPeds, &m_InAppropriateLoadedPeds, &m_SpecialLoadedPeds, &m_AppropriateLoadedPeds);
numMembers = tempList.CountMembers();
for (int i = 0; i < numMembers; i++)
{
strLocalIndex ModelIndex = strLocalIndex(tempList.GetMember(i));
fwModelId modelId(ModelIndex);
if (!modelId.IsValid())
break;
CPedModelInfo *pPedModelInfo = (CPedModelInfo *)CModelInfo::GetBaseModelInfo(modelId);
// Don't take into account the ones that are to be streamed out.
if (!CModelInfo::GetAssetRequiredFlag(modelId) &&
pPedModelInfo->GetNumRefs() == 0 && !pPedModelInfo->m_isRequestedAsDriver)
{
Assert(CModelInfo::GetBaseModelInfo(modelId)->GetDrawable()); // Make sure it is loaded in at this point
CModelInfo::RemoveAssets(modelId);
}
}
}
}
void CPopulationStreaming::UpdateHDCosts()
{
if ((fwTimer::GetSystemFrameCount() & 15))
{
return;
}
// vehicles
CLoadedModelGroup cars;
cars.Merge(&m_AppropriateLoadedCars, &m_InAppropriateLoadedCars, &m_SpecialLoadedCars, &m_LoadedBoats);
CLoadedModelGroup allLoadedCars;
allLoadedCars.Merge(&cars, &m_DiscardedCars);
strIndex backingStore[STREAMING_MAX_DEPENDENCIES];
atUserArray<strIndex> deps(backingStore, STREAMING_MAX_DEPENDENCIES);
s32 numCars = allLoadedCars.CountMembers();
for (s32 i = 0; i < numCars; ++i)
{
u32 modelIndex = allLoadedCars.GetMember(i);
if (!CModelInfo::IsValidModelInfo(modelIndex) || CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)))->GetModelType() != MI_TYPE_VEHICLE)
continue;
CVehicleModelInfo* modelInfo = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)));
// find streamed data
unsigned index = 0;
bool found = false;
for(index = 0; index < MAX_STREAMED_IN_MODELS; index++)
{
if (m_StreamedInModelData[index].m_ModelIndex == modelIndex)
{
found = true;
break;
}
}
if (!found)
continue;
if (modelInfo->GetAreHDFilesLoaded() && !m_StreamedInModelData[index].m_IsHD)
{
deps.ResetCount();
fwModelId modelId((strLocalIndex(modelIndex)));
CModelInfo::GetObjectAndDependencies(modelId, deps, NULL, 0);
strIndex hdTxdIndex;
if (modelInfo->GetHDTxdIndex() > -1)
{
hdTxdIndex = g_TxdStore.GetStreamingIndex(strLocalIndex(modelInfo->GetHDTxdIndex()));
}
strIndex hdFragIndex = g_FragmentStore.GetStreamingIndex(strLocalIndex(modelInfo->GetHDFragmentIndex()));
// if hd assets aren't dependencies, add them to the memory. some vehicles don't have hd assets so these
// indices are the regular txd/frag indices, don't want to count them twice
m_StreamedInModelData[index].m_HDAssetSizeMain = 0;
m_StreamedInModelData[index].m_HDAssetSizeVram = 0;
if (deps.Find(hdTxdIndex) == -1)
{
if (modelInfo->GetHDTxdIndex() > -1 && CStreaming::HasObjectLoaded(strLocalIndex(modelInfo->GetHDTxdIndex()), g_TxdStore.GetStreamingModuleId()) )
{
m_StreamedInModelData[index].m_HDAssetSizeMain = CStreaming::GetObjectVirtualSize(strLocalIndex(modelInfo->GetHDTxdIndex()), g_TxdStore.GetStreamingModuleId());
m_StreamedInModelData[index].m_HDAssetSizeVram = CStreaming::GetObjectPhysicalSize(strLocalIndex(modelInfo->GetHDTxdIndex()), g_TxdStore.GetStreamingModuleId());
}
}
if (deps.Find(hdFragIndex) == -1)
{
m_StreamedInModelData[index].m_HDAssetSizeMain += CStreaming::GetObjectVirtualSize(strLocalIndex(modelInfo->GetHDFragmentIndex()), g_FragmentStore.GetStreamingModuleId());
m_StreamedInModelData[index].m_HDAssetSizeVram += CStreaming::GetObjectPhysicalSize(strLocalIndex(modelInfo->GetHDFragmentIndex()), g_FragmentStore.GetStreamingModuleId());
}
AddTotalMemoryUsed(m_StreamedInModelData[index].m_HDAssetSizeMain, m_StreamedInModelData[index].m_HDAssetSizeVram, MTT_VEHICLE);
m_StreamedInModelData[index].m_IsHD = true;
streamDebugf1("UpdateHDCosts: Found HD assets for vehicle '%s', added M%dK V%dK", CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)))->GetModelName(), m_StreamedInModelData[index].m_HDAssetSizeMain/1024, m_StreamedInModelData[index].m_HDAssetSizeVram/1024);
}
else if (!modelInfo->GetAreHDFilesLoaded() && m_StreamedInModelData[index].m_IsHD)
{
streamDebugf1("UpdateHDCosts: Removing HD assets for vehicle '%s', removed M%dK V%dK", CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)))->GetModelName(), m_StreamedInModelData[index].m_HDAssetSizeMain/1024, m_StreamedInModelData[index].m_HDAssetSizeVram/1024);
AddTotalMemoryUsed(-(s32)m_StreamedInModelData[index].m_HDAssetSizeMain, -(s32)m_StreamedInModelData[index].m_HDAssetSizeVram, MTT_VEHICLE);
m_StreamedInModelData[index].m_IsHD = false;
m_StreamedInModelData[index].m_HDAssetSizeMain = 0;
m_StreamedInModelData[index].m_HDAssetSizeVram = 0;
}
}
// peds
CLoadedModelGroup allLoadedPeds;
allLoadedPeds.Merge(&m_AppropriateLoadedPeds, &m_InAppropriateLoadedPeds, &m_SpecialLoadedPeds, &m_DiscardedPeds);
s32 numPeds = allLoadedPeds.CountMembers();
for (s32 i = 0; i < numPeds; ++i)
{
u32 modelIndex = allLoadedPeds.GetMember(i);
if (!CModelInfo::IsValidModelInfo(modelIndex) || CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)))->GetModelType() != MI_TYPE_PED)
continue;
CPedModelInfo* modelInfo = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)));
// find streamed data
unsigned index = 0;
bool found = false;
for(index = 0; index < MAX_STREAMED_IN_MODELS; index++)
{
if (m_StreamedInModelData[index].m_ModelIndex == modelIndex)
{
found = true;
break;
}
}
if (!found)
continue;
if (modelInfo->GetAreHDFilesLoaded() && !m_StreamedInModelData[index].m_IsHD)
{
deps.ResetCount();
fwModelId modelId((strLocalIndex(modelIndex)));
CModelInfo::GetObjectAndDependencies(modelId, deps, NULL, 0);
strIndex hdTxdIndex = g_TxdStore.GetStreamingIndex(strLocalIndex(modelInfo->GetHDTxdIndex()));
// if hd assets aren't dependencies, add them to the memory. some peds don't have hd assets so these
// indices are the regular txd indices, don't want to count them twice
if (deps.Find(hdTxdIndex) == -1)
{
m_StreamedInModelData[index].m_HDAssetSizeMain = CStreaming::GetObjectVirtualSize(strLocalIndex(modelInfo->GetHDTxdIndex()), g_TxdStore.GetStreamingModuleId());
m_StreamedInModelData[index].m_HDAssetSizeVram = CStreaming::GetObjectPhysicalSize(strLocalIndex(modelInfo->GetHDTxdIndex()), g_TxdStore.GetStreamingModuleId());
}
AddTotalMemoryUsed(m_StreamedInModelData[index].m_HDAssetSizeMain, m_StreamedInModelData[index].m_HDAssetSizeVram, MTT_PED);
m_StreamedInModelData[index].m_IsHD = true;
streamDebugf1("UpdateHDCosts: Found HD assets for ped '%s', added M%dK V%dK", CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)))->GetModelName(), m_StreamedInModelData[index].m_HDAssetSizeMain/1024, m_StreamedInModelData[index].m_HDAssetSizeVram/1024);
}
else if (!modelInfo->GetAreHDFilesLoaded() && m_StreamedInModelData[index].m_IsHD)
{
streamDebugf1("UpdateHDCosts: Removing HD assets for ped '%s', removed M%dK V%dK", CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)))->GetModelName(), m_StreamedInModelData[index].m_HDAssetSizeMain/1024, m_StreamedInModelData[index].m_HDAssetSizeVram/1024);
AddTotalMemoryUsed(-(s32)m_StreamedInModelData[index].m_HDAssetSizeMain, -(s32)m_StreamedInModelData[index].m_HDAssetSizeVram, MTT_PED);
m_StreamedInModelData[index].m_IsHD = false;
m_StreamedInModelData[index].m_HDAssetSizeMain = 0;
m_StreamedInModelData[index].m_HDAssetSizeVram = 0;
}
}
// weapons
for(unsigned index = 0; index < MAX_STREAMED_IN_MODELS; index++)
{
if (m_StreamedInModelData[index].m_IsWeapon)
{
fwModelId modelId(strLocalIndex(m_StreamedInModelData[index].m_ModelIndex));
Assert(CModelInfo::IsValidModelInfo(m_StreamedInModelData[index].m_ModelIndex));
Assert(CModelInfo::GetBaseModelInfo(modelId)->GetModelType() == MI_TYPE_WEAPON);
CWeaponModelInfo* modelInfo = (CWeaponModelInfo*)CModelInfo::GetBaseModelInfo(modelId);
if (modelInfo->GetAreHDFilesLoaded() && !m_StreamedInModelData[index].m_IsHD)
{
deps.ResetCount();
CModelInfo::GetObjectAndDependencies(modelId, deps, NULL, 0);
strIndex hdTxdIndex;
if (modelInfo->GetHDTxdIndex() > -1)
{
hdTxdIndex = g_TxdStore.GetStreamingIndex(strLocalIndex(modelInfo->GetHDTxdIndex()));
}
// if hd assets aren't dependencies, add them to the memory. some peds don't have hd assets so these
// indices are the regular txd indices, don't want to count them twice
if (deps.Find(hdTxdIndex) == -1)
{
if (modelInfo->GetHDTxdIndex() > -1)
{
m_StreamedInModelData[index].m_HDAssetSizeMain = CStreaming::GetObjectVirtualSize(strLocalIndex(modelInfo->GetHDTxdIndex()), g_TxdStore.GetStreamingModuleId());
m_StreamedInModelData[index].m_HDAssetSizeVram = CStreaming::GetObjectPhysicalSize(strLocalIndex(modelInfo->GetHDTxdIndex()), g_TxdStore.GetStreamingModuleId());
}
}
AddTotalMemoryUsed(m_StreamedInModelData[index].m_HDAssetSizeMain, m_StreamedInModelData[index].m_HDAssetSizeVram, MTT_WEAPON);
m_StreamedInModelData[index].m_IsHD = true;
streamDebugf1("UpdateHDCosts: Found HD assets for weapon '%s', added M%dK V%dK", CModelInfo::GetBaseModelInfo(modelId)->GetModelName(), m_StreamedInModelData[index].m_HDAssetSizeMain/1024, m_StreamedInModelData[index].m_HDAssetSizeVram/1024);
}
else if (!modelInfo->GetAreHDFilesLoaded() && m_StreamedInModelData[index].m_IsHD)
{
streamDebugf1("UpdateHDCosts: Removing HD assets for weapon '%s', removed M%dK V%dK", CModelInfo::GetBaseModelInfo(modelId)->GetModelName(), m_StreamedInModelData[index].m_HDAssetSizeMain/1024, m_StreamedInModelData[index].m_HDAssetSizeVram/1024);
AddTotalMemoryUsed(-(s32)m_StreamedInModelData[index].m_HDAssetSizeMain, -(s32)m_StreamedInModelData[index].m_HDAssetSizeVram, MTT_WEAPON);
m_StreamedInModelData[index].m_IsHD = false;
m_StreamedInModelData[index].m_HDAssetSizeMain = 0;
m_StreamedInModelData[index].m_HDAssetSizeVram = 0;
}
}
}
}
u32 CPopulationStreaming::ChooseCarModelToLoad(s32* groupArray, s32 numGroups, u32 restrictions)
{
const u32 MAX_NUM_VEH_MODELS = 164;
sStreamData vehs[MAX_NUM_VEH_MODELS];
u32 numVehs = 0;
bool bDuringOfficeHours = ((CClock::GetHour() > 7) && (CClock::GetHour() < 5));
u32 maxAmbientVehs = (u32)(CVehiclePopulation::GetDesiredNumberAmbientVehicles() + 0.5f);
for (s32 g = 0; g < numGroups; ++g)
{
const u32 numVehsInGroup = CPopCycle::GetPopGroups().GetNumVehs(groupArray[g]);
for (u32 vehIndex = 0; vehIndex < numVehsInGroup; vehIndex++)
{
u32 modelIndex = CPopCycle::GetPopGroups().GetVehIndex(groupArray[g], vehIndex);
fwModelId modelId((strLocalIndex(modelIndex)));
if (!CModelInfo::HaveAssetsLoaded(modelId) || !IsModelDataStreamedIn(modelIndex) || m_DiscardedCars.IsMemberInGroup(modelIndex))
{
if (CScriptCars::GetSuppressedCarModels().HasModelBeenSuppressed(modelIndex))
continue;
if (IsIncompatibleWithAlreadyLoadedCar(modelIndex))
continue;
CVehicleModelInfo *pModelInfo = static_cast<CVehicleModelInfo *>(CModelInfo::GetBaseModelInfo(modelId));
Assert( pModelInfo );
if (!bDuringOfficeHours && (!pModelInfo || pModelInfo->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_ONLY_DURING_OFFICE_HOURS)) )
continue;
u32 vehFlags = 0;
if (pModelInfo->GetIsBoat())
vehFlags |= VSR_BOAT;
if (!pModelInfo->GetIsBike())
vehFlags |= VSR_NO_BIKE;
if (CPopCycle::GetPopGroups().IsVehIndexMember(CPopCycle::GetCommonVehicleGroup(), modelIndex) && pModelInfo->GetVehicleMaxNumber() >= maxAmbientVehs)
vehFlags |= VSR_COMMON_VEH;
if (!Verifyf(numVehs < MAX_NUM_VEH_MODELS, "There's more than %d different vehicle models in this pop zone, bump the array size please!", MAX_NUM_VEH_MODELS))
break;
sStreamData& newVeh = vehs[numVehs++];
newVeh.flags = vehFlags;
newVeh.lastTimeUsed = pModelInfo->GetLastTimeUsed();
newVeh.mi = modelIndex;
newVeh.groupIndex = (s16)g;
newVeh.randVal = (s16)fwRandom::GetRandomNumber();
// if the preferred group is set, change its index to -1 to rank the highest when sorting
// just to raise the chance of getting a vehicle from this group
if (CPopCycle::GetPreferredGroupForVehs() != -1 && CPopCycle::GetPreferredGroupForVehs() == groupArray[g])
newVeh.groupIndex = -1;
}
}
}
// sort first by group index then by last time used and return the first one (the one not seen for the longest amount of time)
// that matches the restrictions passed in
// we sort by group first to prioritize vehicles in the earlier groups, as these are the ones most needing a new vehicle based on the percentages
qsort(vehs, numVehs, sizeof(sStreamData), StreamDataCompare);
for (u32 i = 0; i < numVehs; ++i)
{
if (vehs[i].mi == fwModelId::MI_INVALID)
continue;
if ((vehs[i].flags & restrictions) == restrictions)
{
if (vehs[i].groupIndex == -1)
CPopCycle::SetPreferredGroupForVehs(-1);
return vehs[i].mi;
}
}
if (vehs[0].groupIndex == (s16)CPopCycle::GetPreferredGroupForVehs())
CPopCycle::SetPreferredGroupForVehs(-1);
return vehs[0].mi;
}
bool CPopulationStreaming::IsIncompatibleWithAlreadyLoadedCar(u32 model)
{
CVehicleModelInfo* pVehModelInfo = ((CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(model))));
Assert( pVehModelInfo );
if( !pVehModelInfo )
return false;
// prevent too many bikes from being streamed in
if (pVehModelInfo && pVehModelInfo->GetIsBike())
{
if (m_AppropriateLoadedCars.CountMembers() <= 2)
{
return true;
}
s32 numMembers = m_AppropriateLoadedCars.CountMembers();
for (int i = 0; i < numMembers; i++)
{
strLocalIndex ModelIndex = strLocalIndex(m_AppropriateLoadedCars.GetMember(i));
if (((CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex))))->GetIsBike())
{
return true;
}
}
}
// prevent too many boring vehicles being streamed in
const u32 boringVehiclesGroupNameHash = ATSTRINGHASH("VEH_BORING", 0x047cb2f40);
u32 popGroupIndexOfBoringVehicles = 0;
bool boringVehiclePopGroupExists = CPopCycle::GetPopGroups().FindVehGroupFromNameHash(boringVehiclesGroupNameHash, popGroupIndexOfBoringVehicles);
if(!boringVehiclePopGroupExists)
{
return false;
}
if (CPopCycle::GetPopGroups().IsVehIndexMember(popGroupIndexOfBoringVehicles, model))
{
int numLoaded = 0;
// Count the number of boring saloons already loaded.
for (u32 i = 0; i < CPopCycle::GetPopGroups().GetNumVehs(popGroupIndexOfBoringVehicles); i++)
{
u32 mi = CPopCycle::GetPopGroups().GetVehIndex(popGroupIndexOfBoringVehicles, i);
fwModelId modelId((strLocalIndex(mi)));
if (CModelInfo::HaveAssetsLoaded(modelId) && CModelInfo::GetAssetRequiredFlag(modelId) )
{
numLoaded++;
}
}
if (numLoaded >= 1)
{
return true;
}
}
return false;
}
u32 CPopulationStreaming::ChoosePedModelToLoad(s32* groupArray, s32 numGroups, u32 restrictions)
{
const u32 MAX_NUM_PED_MODELS = 128;
sStreamData peds[MAX_NUM_PED_MODELS];
u32 numPeds = 0;
for (s32 g = 0; g < numGroups; ++g)
{
const u32 numPedsInGroup = CPopCycle::GetPopGroups().GetNumPeds(groupArray[g]);
for (u32 pedIndex = 0; pedIndex < numPedsInGroup; pedIndex++)
{
u32 modelIndex = CPopCycle::GetPopGroups().GetPedIndex(groupArray[g], pedIndex);
fwModelId modelId((strLocalIndex(modelIndex)));
if (!CModelInfo::HaveAssetsLoaded(modelId) || !IsModelDataStreamedIn(modelIndex) || m_DiscardedPeds.IsMemberInGroup(modelIndex))
{
if (!CScriptPeds::HasPedModelBeenRestrictedOrSuppressed(modelIndex))
{
u32 pedFlags = 0;
CPedModelInfo *pModelInfo = static_cast<CPedModelInfo *>(CModelInfo::GetBaseModelInfo(modelId));
if (pModelInfo->GetPersonalitySettings().GetIsMale())
pedFlags |= PSR_MALE;
else
pedFlags |= PSR_FEMALE;
if (CanPedModelDrive(modelIndex))
pedFlags |= PSR_DRIVER;
if (CanPedModelRideBike(modelIndex))
pedFlags |= PSR_BIKER;
if (!Verifyf(numPeds < MAX_NUM_PED_MODELS, "There's more than %d different ped models in this pop zone, bump the array size please!", MAX_NUM_PED_MODELS))
break;
sStreamData& newPed = peds[numPeds++];
newPed.flags = pedFlags;
newPed.lastTimeUsed = pModelInfo->GetLastTimeUsed();
newPed.mi = modelIndex;
newPed.groupIndex = (s16)g;
newPed.randVal = (s16)fwRandom::GetRandomNumber();
}
}
}
}
// sort peds first by group index then by last time used and return the first one (the one not seen for the longest amount of time)
// that matches the restrictions passed in
// we sort by group first to prioritize peds in the earlier groups, as these are the ones most needing a new ped based on the percentages
qsort(peds, numPeds, sizeof(sStreamData), StreamDataCompare);
for (u32 i = 0; i < numPeds; ++i)
{
if (peds[i].mi == fwModelId::MI_INVALID)
continue;
if ((peds[i].flags & restrictions) == restrictions)
return peds[i].mi;
}
return peds[0].mi;
}
bool CPopulationStreaming::CanPedModelDrive(u32 mi)
{
if (CModelInfo::IsValidModelInfo(mi))
{
CPedModelInfo *pModelInfo = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi)));
if ( (!pModelInfo->GetPersonalitySettings().DrivesVehicleType(PED_DRIVES_POOR_CAR)) && (!pModelInfo->GetPersonalitySettings().DrivesVehicleType(PED_DRIVES_AVERAGE_CAR)) && (!pModelInfo->GetPersonalitySettings().DrivesVehicleType(PED_DRIVES_RICH_CAR)) &&
(!pModelInfo->GetPersonalitySettings().DrivesVehicleType(PED_DRIVES_BIG_CAR)) )
{
return false;
}
return true;
}
return false;
}
bool CPopulationStreaming::CanPedModelRideBike(u32 mi)
{
if (CModelInfo::IsValidModelInfo(mi))
{
CPedModelInfo *pmi = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi)));
if (pmi->GetPersonalitySettings().DrivesVehicleType(PED_DRIVES_MOTORCYCLE) && (pmi->HasHelmetProp() || pmi->GetCanRideBikeWithNoHelmet()))
{
return true;
}
}
return false;
}
void CPopulationStreaming::UpdateModelsRequiredForNetwork()
{
u32 networkTimeInterval = GetCurrentNetworkTimeInterval();
bool popScheduleChanged = CPopCycle::GetCurrentZoneIndex() != m_LastZoneUsedForStreamingNetModels;
bool timeAdvanced = networkTimeInterval != m_LastTimeUsedForStreamingNetPeds;
if(m_PedModelsRequiredForNetwork.IsEmpty() || timeAdvanced || popScheduleChanged)
{
u32 adjustedModelStartOffset = GetAdjustedModelStartOffset(PED_MODEL_OFFSET, networkTimeInterval, popScheduleChanged);
if((adjustedModelStartOffset != m_LastTimeUsedForStreamingNetPeds) || popScheduleChanged)
{
UpdatePedModelsRequiredForNetwork(adjustedModelStartOffset);
m_LastTimeUsedForStreamingNetPeds = adjustedModelStartOffset;
}
}
timeAdvanced = networkTimeInterval != m_LastTimeUsedForStreamingNetVehicles;
if(m_VehicleModelsRequiredForNetwork.IsEmpty() || timeAdvanced || popScheduleChanged)
{
u32 adjustedModelStartOffset = GetAdjustedModelStartOffset(VEHICLE_MODEL_OFFSET, networkTimeInterval, popScheduleChanged);
if((adjustedModelStartOffset != m_LastTimeUsedForStreamingNetVehicles) || popScheduleChanged)
{
UpdateVehicleModelsRequiredForNetwork(adjustedModelStartOffset);
m_LastTimeUsedForStreamingNetVehicles = adjustedModelStartOffset;
}
}
m_LastZoneUsedForStreamingNetModels = CPopCycle::GetCurrentZoneIndex();
}
u32 CPopulationStreaming::GetAdjustedModelStartOffset(ModelOffsetType offsetType, u32 desiredModelStartOffset, bool /*popScheduleChanged*/)
{
u32 adjustedModelStartOffset = desiredModelStartOffset;
u32 lowestModelStartOffset = adjustedModelStartOffset;
CLoadedModelGroup *inAppropriateModels = 0;
u32 lastTimeUsedForStreaming = 0;
if(offsetType == PED_MODEL_OFFSET)
{
u32 lowestRemoteOffset = 0;
if(NetworkInterface::GetLowestPedModelStartOffset(lowestRemoteOffset))
{
lowestModelStartOffset = lowestRemoteOffset;
}
inAppropriateModels = &m_InAppropriateLoadedPeds;
lastTimeUsedForStreaming = m_LastTimeUsedForStreamingNetPeds;
}
else
{
Assertf(offsetType == VEHICLE_MODEL_OFFSET, "Unexpected model offset type!");
u32 lowestRemoteOffset = 0;
if(NetworkInterface::GetLowestVehicleModelStartOffset(lowestRemoteOffset))
{
lowestModelStartOffset = lowestRemoteOffset;
}
inAppropriateModels = &m_InAppropriateLoadedCars;
lastTimeUsedForStreaming = m_LastTimeUsedForStreamingNetVehicles;
}
int numInappropriateModels = inAppropriateModels->CountMembers();
unsigned numModelsAwaitingStreamingRemoval = 0;
for(int index = 0; index < numInappropriateModels; index++)
{
strLocalIndex modelIndex = strLocalIndex(inAppropriateModels->GetMember(index));
fwModelId modelId(modelIndex);
if(modelId.IsValid())
{
bool requiredByScript = ((CModelInfo::GetAssetStreamFlags(modelId) & STRFLAG_MISSION_REQUIRED) != 0);
if (!requiredByScript)
{
numModelsAwaitingStreamingRemoval++;
}
}
}
if(numModelsAwaitingStreamingRemoval >= MAX_NETWORK_MODELS_OF_TYPE_PENDING_STREAMING_REMOVAL)
{
adjustedModelStartOffset = lastTimeUsedForStreaming;
}
else
{
u32 difference = desiredModelStartOffset - lastTimeUsedForStreaming;
difference = MIN(difference, MAX_NETWORK_MODELS_OF_TYPE_PENDING_STREAMING_REMOVAL);
difference -= numModelsAwaitingStreamingRemoval;
adjustedModelStartOffset = lastTimeUsedForStreaming + difference;
adjustedModelStartOffset %= MAX_NETWORK_MODEL_INTERVALS;
}
if(offsetType == PED_MODEL_OFFSET)
{
m_LocalAllowedPedModelStartOffset = adjustedModelStartOffset;
}
else
{
m_LocalAllowedVehicleModelStartOffset = adjustedModelStartOffset;
}
if(IsNetworkModelOffsetLower(lowestModelStartOffset, adjustedModelStartOffset))
{
adjustedModelStartOffset = lowestModelStartOffset;
}
return adjustedModelStartOffset;
}
#if __BANK
void CPopulationStreaming::LogModelsPendingRemovalInfo(ModelOffsetType offsetType, u32 desiredModelStartOffset, u32 adjustedModelStartOffset)
{
if(desiredModelStartOffset != adjustedModelStartOffset)
{
if(offsetType == VEHICLE_MODEL_OFFSET)
{
NetworkDebug::LogVehiclesUsingInappropriateModels();
}
else if(offsetType == PED_MODEL_OFFSET)
{
NetworkDebug::LogPedsUsingInappropriateModels();
}
}
}
#endif // __BANK
u32 CPopulationStreaming::GetCurrentNetworkTimeInterval()
{
const unsigned TIME_INTERVAL_TO_CHANGE_MODELS = 3 * 60 * 1000; // update the streamed models list every 3 minutes
u32 networkTimeInterval = NetworkInterface::GetSyncedTimeInMilliseconds() / TIME_INTERVAL_TO_CHANGE_MODELS;
networkTimeInterval += s_NetworkModelVariations[m_NetworkModelVariationID].m_NetworkTimeOffset;
networkTimeInterval %= MAX_NETWORK_MODEL_INTERVALS;
return networkTimeInterval;
}
void CPopulationStreaming::UpdatePedModelsRequiredForNetwork(u32 modelStartOffset)
{
m_PedModelsRequiredForNetwork.Clear();
CLoadedModelGroup ambientPedCandidates;
CLoadedModelGroup gangPedCandidates;
CPopCycle::GetNetworkPedModelsForCurrentZone(ambientPedCandidates, gangPedCandidates);
const unsigned numAmbientPedModels = ambientPedCandidates.CountMembers();
const unsigned numGangPedModels = gangPedCandidates.CountMembers();
const unsigned targetNumGangPeds = MIN(numGangPedModels, TARGET_NUM_GANG_PEDS);
const unsigned targetNumAmbientPeds = MIN(numAmbientPedModels, (TARGET_NUM_AMBIENT_PEDS + (TARGET_NUM_GANG_PEDS - targetNumGangPeds)));
if(numGangPedModels > 0)
{
unsigned gangPedStart = modelStartOffset % numGangPedModels;
unsigned gangPedEnd = gangPedStart + targetNumGangPeds;
for(unsigned index = gangPedStart; index < gangPedEnd; index++)
{
unsigned pedIndex = index % numGangPedModels;
m_PedModelsRequiredForNetwork.AddMember(gangPedCandidates.GetMember(pedIndex));
}
}
bool hasDriver = false;
if(numAmbientPedModels > 0)
{
unsigned ambientPedStart = modelStartOffset % numAmbientPedModels;
unsigned ambientPedEnd = ambientPedStart + targetNumAmbientPeds;
for(unsigned index = ambientPedStart; index < ambientPedEnd; index++)
{
unsigned pedIndex = index % numAmbientPedModels;
m_PedModelsRequiredForNetwork.AddMember(ambientPedCandidates.GetMember(pedIndex));
hasDriver |= CanPedModelDrive(ambientPedCandidates.GetMember(pedIndex));
}
}
if (!hasDriver)
{
for (s32 i = 0; i < numAmbientPedModels; ++i)
{
if (CanPedModelDrive(ambientPedCandidates.GetMember(i)))
{
m_PedModelsRequiredForNetwork.AddMember(ambientPedCandidates.GetMember(i));
break;
}
}
}
}
bool CPopulationStreaming::CanSpawnInMp(u32 modelIndex)
{
if (modelIndex == MI_CAR_TAXI_CURRENT_1.GetModelIndex())
return false;
if (modelIndex == MI_CAR_TOWTRUCK.GetModelIndex())
return false;
if (modelIndex == MI_CAR_TOWTRUCK_2.GetModelIndex())
return false;
if (modelIndex == MI_CAR_FORKLIFT.GetModelIndex())
return false;
return true;
}
void CPopulationStreaming::UpdateVehicleModelsRequiredForNetwork(u32 modelStartOffset)
{
m_VehicleModelsRequiredForNetwork.Clear();
CLoadedModelGroup commonVehicles;
CLoadedModelGroup zoneVehicles;
CPopCycle::GetNetworkVehicleModelsForCurrentZone(commonVehicles, zoneVehicles);
float multiplier = 1.f;
#if (RSG_PC || RSG_DURANGO || RSG_ORBIS) && 0 // NO multiplier on networking
multiplier = CVehiclePopulation::ms_vehicleMemoryBudgetMultiplierNG;
#endif // (RSG_PC || RSG_DURANGO || RSG_ORBIS)
const unsigned numCommonVehicleModels = commonVehicles.CountMembers();
const unsigned numZoneVehicleModels = zoneVehicles.CountMembers();
const unsigned targetNumCommonModels = MIN(numCommonVehicleModels, (unsigned)((NUMBER_STREAMED_CARS_NETWORK - NUMBER_ZONE_SPECIFIC_CARS_NETWORK) * multiplier));
unsigned targetNumZoneModels = MIN(numZoneVehicleModels, (unsigned)(NUMBER_ZONE_SPECIFIC_CARS_NETWORK * multiplier));
if (numCommonVehicleModels == 0)
targetNumZoneModels = MIN(numZoneVehicleModels, (unsigned)(NUMBER_STREAMED_CARS_NETWORK * multiplier));
if(numCommonVehicleModels > 0)
{
unsigned commonVehicleStart = modelStartOffset % numCommonVehicleModels;
unsigned numCommonModelsAdded = 0;
for(unsigned index = 0; index < numCommonVehicleModels && numCommonModelsAdded < targetNumCommonModels; index++)
{
// adjust the vehicle to stream based off the number of shuffled indices we have and the number of possible models.
// In the case where the number of models is greater than the number of indices, we split the list into blocks of models
// that have the shuffled indices applied to them
unsigned blockIndex = (commonVehicleStart+index) % NUM_SHUFFLED_INDICES;
unsigned shuffleBlockValue = (commonVehicleStart+index) - blockIndex;
unsigned shuffledIndex = shuffleBlockValue + s_NetworkModelVariations[m_NetworkModelVariationID].m_ShuffledIndices[blockIndex];
unsigned vehicleIndex = shuffledIndex % numCommonVehicleModels;
u32 modelIndex = commonVehicles.GetMember(vehicleIndex);
if (!CanSpawnInMp(modelIndex))
continue;
if(!m_VehicleModelsRequiredForNetwork.IsMemberInGroup(modelIndex))
{
m_VehicleModelsRequiredForNetwork.AddMember(modelIndex);
numCommonModelsAdded++;
}
}
}
if(numZoneVehicleModels > 0 && targetNumZoneModels > 0)
{
unsigned zoneVehicleStart = modelStartOffset % numZoneVehicleModels;
unsigned numZoneModelsAdded = 0;
for(unsigned index = 0; index < numZoneVehicleModels && numZoneModelsAdded < targetNumZoneModels; index++)
{
// adjust the vehicle to stream based off the number of shuffled indices we have and the number of possible models.
// In the case where the number of models is greater than the number of indices, we split the list into blocks of models
// that have the shuffled indices applied to them
unsigned blockIndex = (zoneVehicleStart+index) % NUM_SHUFFLED_INDICES;
unsigned shuffleBlockValue = (zoneVehicleStart+index) - blockIndex;
unsigned shuffledIndex = shuffleBlockValue + s_NetworkModelVariations[m_NetworkModelVariationID].m_ShuffledIndices[blockIndex];
unsigned vehicleIndex = shuffledIndex % numZoneVehicleModels;
u32 modelIndex = zoneVehicles.GetMember(vehicleIndex);
if (!CanSpawnInMp(modelIndex))
continue;
if(!m_VehicleModelsRequiredForNetwork.IsMemberInGroup(modelIndex))
{
m_VehicleModelsRequiredForNetwork.AddMember(modelIndex);
numZoneModelsAdded++;
}
}
}
}
bool CPopulationStreaming::IsPedAvailableForSpawn(u32 modelIndex)
{
if (modelIndex == fwModelId::MI_INVALID)
return false;
if (m_AppropriateLoadedPeds.IsMemberInGroup(modelIndex))
return true;
if (m_SpecialLoadedPeds.IsMemberInGroup(modelIndex))
return true;
if (m_InAppropriateLoadedPeds.IsMemberInGroup(modelIndex))
return true;
if (m_DiscardedPeds.IsMemberInGroup(modelIndex))
return true;
return false;
}
bool CPopulationStreaming::IsDriverAvailable(u32 modelIndex)
{
if (modelIndex == fwModelId::MI_INVALID)
return false;
CVehicleModelInfo* mi = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)));
Assert(mi);
// if no drivers are specified we don't care
if (mi->GetDriverCount() == 0)
return true;
return IsPedAvailableForSpawn(CPedPopulation::FindSpecificDriverModelForCarToUse(fwModelId(strLocalIndex(modelIndex))).GetModelIndex());
}
void CPopulationStreaming::ReclassifyLoadedCars()
{
CLoadedModelGroup Temp1 = m_AppropriateLoadedCars;
CLoadedModelGroup Temp2 = m_InAppropriateLoadedCars;
m_AppropriateLoadedCars.Clear();
m_InAppropriateLoadedCars.Clear();
s32 numMembers = Temp1.CountMembers();
for (int C = 0; C < numMembers; C++)
{
u32 ModelIndex = Temp1.GetMember(C);
//m_AppropriateLoadedCars.AddMember(ModelIndex);
if (IsCarModelNeededCurrently(ModelIndex))
{
m_AppropriateLoadedCars.AddMember(ModelIndex);
}
else
{
m_InAppropriateLoadedCars.AddMember(ModelIndex);
}
}
numMembers = Temp2.CountMembers();
for (int C = 0; C < numMembers; C++)
{
u32 ModelIndex = Temp2.GetMember(C);
//m_AppropriateLoadedCars.AddMember(ModelIndex);
if (IsCarModelNeededCurrently(ModelIndex))
{
m_AppropriateLoadedCars.AddMember(ModelIndex);
// Make sure any appropriate loaded car has a driver requested.
RequestVehicleDriver(ModelIndex);
}
else
{
m_InAppropriateLoadedCars.AddMember(ModelIndex);
}
}
CVehicleFactory::GetFactory()->RemoveVehGroupFromCache(m_InAppropriateLoadedCars);
}
void CPopulationStreaming::ReclassifyLoadedVehiclesForNetwork()
{
CLoadedModelGroup tempList;
tempList.Merge(&m_AppropriateLoadedCars, &m_InAppropriateLoadedCars);
m_AppropriateLoadedCars.Clear();
m_InAppropriateLoadedCars.Clear();
int numCars = tempList.CountMembers();
for(int index = 0; index < numCars; index++)
{
u32 modelIndex = tempList.GetMember(index);
if(CModelInfo::IsValidModelInfo(modelIndex))
{
if(m_VehicleModelsRequiredForNetwork.IsMemberInGroup(modelIndex))
{
m_AppropriateLoadedCars.AddMember(modelIndex);
// Make sure any appropriate loaded car has a driver requested.
RequestVehicleDriver(modelIndex);
}
else
{
m_InAppropriateLoadedCars.AddMember(modelIndex);
}
}
}
}
void CPopulationStreaming::ReclassifyLoadedPeds()
{
CLoadedModelGroup Temp1 = m_AppropriateLoadedPeds;
CLoadedModelGroup Temp2 = m_InAppropriateLoadedPeds;
m_AppropriateLoadedPeds.Clear();
m_InAppropriateLoadedPeds.Clear();
s32 numMembers = Temp1.CountMembers();
for (int C = 0; C < numMembers; C++)
{
u32 ModelIndex = Temp1.GetMember(C);
if(!CPed::IsSuperLodFromIndex(ModelIndex))
{
if (IsPedModelNeededCurrently(ModelIndex))
{
m_AppropriateLoadedPeds.AddMember(ModelIndex);
}
else
{
m_InAppropriateLoadedPeds.AddMember(ModelIndex);
}
}
else
{
Assertf(false, "Found a superlod ped in one of the population lists!");
}
}
numMembers = Temp2.CountMembers();
for (int C = 0; C < numMembers; C++)
{
u32 ModelIndex = Temp2.GetMember(C);
if(!CPed::IsSuperLodFromIndex(ModelIndex))
{
if (IsPedModelNeededCurrently(ModelIndex))
{
m_AppropriateLoadedPeds.AddMember(ModelIndex);
}
else
{
m_InAppropriateLoadedPeds.AddMember(ModelIndex);
}
}
else
{
Assertf(false, "Found a superlod ped in one of the population lists!");
}
}
CPedFactory::GetFactory()->RemovePedGroupFromCache(m_InAppropriateLoadedPeds);
}
void CPopulationStreaming::ReclassifyLoadedPedsForNetwork()
{
CLoadedModelGroup tempList;
tempList.Merge(&m_AppropriateLoadedPeds, &m_InAppropriateLoadedPeds);
m_AppropriateLoadedPeds.Clear();
m_InAppropriateLoadedPeds.Clear();
int numPeds = tempList.CountMembers();
for(int index = 0; index < numPeds; index++)
{
u32 modelIndex = tempList.GetMember(index);
if(CModelInfo::IsValidModelInfo(modelIndex))
{
if(m_PedModelsRequiredForNetwork.IsMemberInGroup(modelIndex))
{
m_AppropriateLoadedPeds.AddMember(modelIndex);
}
else
{
m_InAppropriateLoadedPeds.AddMember(modelIndex);
}
}
}
}
u32 CPopulationStreaming::GetTotalNumberOfCarsStreamedIn() const
{
const u32 discardedCars = m_DiscardedCars.CountMembers();
const u32 appropriateCars = m_AppropriateLoadedCars.CountMembers();
const u32 inappropriateCars = m_InAppropriateLoadedCars.CountMembers();
const u32 specialCars = m_SpecialLoadedCars.CountMembers();
const u32 boats = m_LoadedBoats.CountMembers();
return discardedCars + appropriateCars + inappropriateCars + specialCars + boats;
}
void CPopulationStreaming::AddDiscardedCar(u32 modelIndex)
{
m_DiscardedCars.AddMember(modelIndex);
m_AppropriateLoadedCars.RemoveMember(modelIndex);
m_InAppropriateLoadedCars.RemoveMember(modelIndex);
Assert(!m_SpecialLoadedCars.IsMemberInGroup(modelIndex));
m_SpecialLoadedCars.RemoveMember(modelIndex);
Assert(!m_LoadedBoats.IsMemberInGroup(modelIndex));
m_LoadedBoats.RemoveMember(modelIndex);
}
void CPopulationStreaming::ReinstateDiscardedCar(u32 modelIndex)
{
m_DiscardedCars.RemoveMember(modelIndex);
bool bIsBoat = ((CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex))))->GetVehicleType()==VEHICLE_TYPE_BOAT;
bool bIsTrain = ((CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex))))->GetVehicleType()==VEHICLE_TYPE_TRAIN;
bool bIsHeli = ((CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex))))->GetVehicleType()==VEHICLE_TYPE_HELI;
bool bIsAmbulance = (modelIndex == MI_CAR_AMBULANCE);
bool bIsFiretruck = (modelIndex == MI_CAR_FIRETRUCK);
bool requestDriver = true;
if (bIsHeli || bIsTrain || bIsAmbulance || bIsFiretruck)
{
m_SpecialLoadedCars.AddMember(modelIndex);
// Don't request a driver for the members of m_SpecialLoadedCars, has to be done elsewhere
// by users that need it.
requestDriver = false;
}
else if (bIsBoat)
{
m_LoadedBoats.AddMember(modelIndex);
// Only request drivers for boats if we actually want boats in the general population.
// Otherwise, they are probably just needed for car generators or vehicle scenarios (which
// will request drivers if needed).
requestDriver = m_bBoatsNeeded;
}
else if (IsCarModelNeededCurrently(modelIndex))
{
m_AppropriateLoadedCars.AddMember(modelIndex);
}
else
{
m_InAppropriateLoadedCars.AddMember(modelIndex);
// Car generator and scenario vehicles often end up here. We don't need drivers to be
// requested for those, at this level of the code.
requestDriver = false;
}
if(requestDriver)
{
RequestVehicleDriver(modelIndex);
}
}
void CPopulationStreaming::AddDiscardedPed(u32 modelIndex)
{
m_DiscardedPeds.AddMember(modelIndex);
m_AppropriateLoadedPeds.RemoveMember(modelIndex);
m_InAppropriateLoadedPeds.RemoveMember(modelIndex);
Assert(!m_SpecialLoadedPeds.IsMemberInGroup(modelIndex) || IsPedModelNeededCurrently(modelIndex));
m_SpecialLoadedPeds.RemoveMember(modelIndex);
}
void CPopulationStreaming::ReinstateDiscardedPed(u32 modelIndex)
{
m_DiscardedPeds.RemoveMember(modelIndex);
if (AddPedModelToCorrectList(modelIndex))
{
// only add this flag to appropriate or inappropriate peds. any other types might not have the flag removed by us
CModelInfo::SetAssetRequiredFlag(fwModelId(strLocalIndex(modelIndex)), STRFLAG_DONTDELETE);
}
Assertf(m_AppropriateLoadedPeds.IsMemberInGroup(modelIndex) ||
m_InAppropriateLoadedPeds.IsMemberInGroup(modelIndex) ||
m_SpecialLoadedPeds.IsMemberInGroup(modelIndex), "Ped has been reinstated but isn't in any of the lists!");
}
s32 CPopulationStreaming::AddStreamedInModelData(u32 modelIndex)
{
bool added = false;
Assertf(!IsModelDataStreamedIn(modelIndex), "Model data already tracked is being added again!");
s32 ret = -1;
for(unsigned index = 0; index < MAX_STREAMED_IN_MODELS && !added; index++)
{
if(!CModelInfo::IsValidModelInfo(m_StreamedInModelData[index].m_ModelIndex))
{
m_StreamedInModelData[index].m_ModelIndex = modelIndex;
m_StreamedInModelData[index].m_TimeStreamedIn = fwTimer::GetTimeInMilliseconds();
m_StreamedInModelData[index].m_TimeNoLongerRequired = 0;
m_StreamedInModelData[index].m_HDAssetSizeMain = 0;
m_StreamedInModelData[index].m_HDAssetSizeVram = 0;
m_StreamedInModelData[index].m_IsHD = false;
added = true;
ret = (s32)index;
if (CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(m_StreamedInModelData[index].m_ModelIndex)))->GetModelType() == MI_TYPE_WEAPON)
m_StreamedInModelData[index].m_IsWeapon = true;
}
}
#if __BANK
if (!Verifyf(added, "Failed to add streamed in model data!"))
{
for(unsigned index = 0; index < MAX_STREAMED_IN_MODELS && !added; index++)
{
if(CModelInfo::IsValidModelInfo(m_StreamedInModelData[index].m_ModelIndex))
{
Displayf("%s: %d\n", CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(m_StreamedInModelData[index].m_ModelIndex)))->GetModelName(),
m_StreamedInModelData[index].m_ModelIndex);
}
else
{
Displayf("INVALID: %d\n", m_StreamedInModelData[index].m_ModelIndex);
}
}
}
#endif // __BANK
return ret;
}
bool CPopulationStreaming::RemoveStreamedInModelData(u32 modelIndex)
{
bool removed = false;
for(unsigned index = 0; index < MAX_STREAMED_IN_MODELS && !removed; index++)
{
if(m_StreamedInModelData[index].m_ModelIndex == modelIndex)
{
if (m_StreamedInModelData[index].m_IsHD && (m_StreamedInModelData[index].m_HDAssetSizeMain > 0 || m_StreamedInModelData[index].m_HDAssetSizeVram))
{
streamDebugf1("RemoveStreamedInModelData: Removing HD assets for '%s'! Removed M%dK V%dK", CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)))->GetModelName(), m_StreamedInModelData[index].m_HDAssetSizeMain/1024, m_StreamedInModelData[index].m_HDAssetSizeVram/1024);
if (CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)))->GetModelType() == MI_TYPE_VEHICLE)
{
AddTotalMemoryUsed(-(s32)m_StreamedInModelData[index].m_HDAssetSizeMain, -(s32)m_StreamedInModelData[index].m_HDAssetSizeVram, MTT_VEHICLE);
}
else if (CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)))->GetModelType() == MI_TYPE_PED)
{
AddTotalMemoryUsed(-(s32)m_StreamedInModelData[index].m_HDAssetSizeMain, -(s32)m_StreamedInModelData[index].m_HDAssetSizeVram, MTT_PED);
}
else if (CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)))->GetModelType() == MI_TYPE_WEAPON)
{
AddTotalMemoryUsed(-(s32)m_StreamedInModelData[index].m_HDAssetSizeMain, -(s32)m_StreamedInModelData[index].m_HDAssetSizeVram, MTT_WEAPON);
}
else
{
Assertf(false, "Unexpected model type with HD assets (%s)", CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)))->GetModelName());
}
}
streamDebugf1("RemoveStreamedInModelData: Removing data streamed in at %d (%d ms ago)", m_StreamedInModelData[index].m_TimeStreamedIn, fwTimer::GetTimeInMilliseconds() - m_StreamedInModelData[index].m_TimeStreamedIn);
m_StreamedInModelData[index].m_ModelIndex = fwModelId::MI_INVALID;
m_StreamedInModelData[index].m_TimeStreamedIn = 0;
m_StreamedInModelData[index].m_TimeNoLongerRequired = 0;
m_StreamedInModelData[index].m_HDAssetSizeMain = 0;
m_StreamedInModelData[index].m_HDAssetSizeVram = 0;
m_StreamedInModelData[index].m_IsHD = false;
m_StreamedInModelData[index].m_IsWeapon = false;
#if DEBUG_POPULATION_MEMORY
m_StreamedInModelData[index].m_mainUsed = 0;
m_StreamedInModelData[index].m_vramUsed = 0;
#endif // DEBUG_POPULATION_MEMORY
removed = true;
}
}
Assertf(!IsModelDataStreamedIn(modelIndex), "Model data still tracked after removal!");
return removed;
}
bool CPopulationStreaming::IsModelDataStreamedIn(u32 modelIndex)
{
bool found = false;
for(unsigned index = 0; index < MAX_STREAMED_IN_MODELS && !found; index++)
{
if(m_StreamedInModelData[index].m_ModelIndex == modelIndex)
{
found = true;
}
}
return found;
}
void CPopulationStreaming::AddStreamedInComponent(u32 modelIndex, atArray<sPopEntry>& componentList, u32& totalVirtual, u32& totalPhysical, eMemoryTrackType type)
{
strIndex backingStore[STREAMING_MAX_DEPENDENCIES];
atUserArray<strIndex> deps(backingStore, STREAMING_MAX_DEPENDENCIES);
fwModelId modelId((strLocalIndex(modelIndex)));
CModelInfo::GetObjectAndDependencies(modelId, deps, m_residentObjects.GetElements(), m_residentObjects.GetCount());
AddStreamedInDependencies(deps, componentList, totalVirtual, totalPhysical, type);
}
void CPopulationStreaming::AddStreamedInDependencies(const atArray<strIndex>& deps, atArray<sPopEntry>& componentList, u32& totalVirtual, u32& totalPhysical, eMemoryTrackType type)
{
for (s32 i = 0; i < deps.GetCount(); ++i)
{
streamDebugf1("AddStreamedInDependencies: dep %d: %s", i, strStreamingEngine::GetObjectName(deps[i]));
s32 entrySlot = -1;
s32 firstEmptySlot = -1;
for (s32 f = 0; f < componentList.GetCount(); ++f)
{
if (firstEmptySlot == -1 && componentList[f].refCount == 0)
{
firstEmptySlot = f;
}
if (componentList[f].streamIdx == deps[i])
{
entrySlot = f;
break;
}
}
if (entrySlot == -1)
{
// the current strIndex wasn't found in our list, add it and increment memory usage
if (firstEmptySlot != -1)
{
// reuse empty slot to avoid memory allocations when possible
componentList[firstEmptySlot].streamIdx = deps[i];
componentList[firstEmptySlot].refCount = 1;
}
else
{
sPopEntry newEntry = { deps[i], 1 };
componentList.PushAndGrow(newEntry);
}
u32 virtualMemory = strStreamingEngine::GetInfo().GetObjectVirtualSize(deps[i]);
u32 physicalMemory = strStreamingEngine::GetInfo().GetObjectPhysicalSize(deps[i]);
AddTotalMemoryUsed(virtualMemory, physicalMemory, type);
streamDebugf1("[actual] AddStreamedInDependencies: dep %d: %s %d/%d", i, strStreamingEngine::GetObjectName(deps[i]), virtualMemory, physicalMemory);
totalVirtual += virtualMemory;
totalPhysical += physicalMemory;
}
else
{
Assert(deps[i] == componentList[entrySlot].streamIdx);
// if we found the index in our list add its memory cost only if this is the first reference
if (componentList[entrySlot].refCount == 0)
{
u32 virtualMemory = strStreamingEngine::GetInfo().GetObjectVirtualSize(deps[i]);
u32 physicalMemory = strStreamingEngine::GetInfo().GetObjectPhysicalSize(deps[i]);
AddTotalMemoryUsed(virtualMemory, physicalMemory, type);
totalVirtual += virtualMemory;
totalPhysical += physicalMemory;
}
componentList[entrySlot].refCount++;
}
}
}
void CPopulationStreaming::RemoveStreamedInComponent(u32 modelIndex, atArray<sPopEntry>& componentList, u32& totalVirtual, u32& totalPhysical, eMemoryTrackType type)
{
strIndex backingStore[STREAMING_MAX_DEPENDENCIES];
atUserArray<strIndex> deps(backingStore, STREAMING_MAX_DEPENDENCIES);
fwModelId modelId((strLocalIndex(modelIndex)));
CModelInfo::GetObjectAndDependencies(modelId, deps, m_residentObjects.GetElements(), m_residentObjects.GetCount());
RemoveStreamedInDependencies(deps, componentList, totalVirtual, totalPhysical, type);
switch (type)
{
case MTT_PED:
{
Assert(m_TotalMemoryUsedByPedModelsMain >= 0);
m_TotalMemoryUsedByPedModelsMain = MAX(0, m_TotalMemoryUsedByPedModelsMain);
Assert(m_TotalMemoryUsedByPedModelsVram >= 0);
m_TotalMemoryUsedByPedModelsVram = MAX(0, m_TotalMemoryUsedByPedModelsVram);
}
break;
case MTT_VEHICLE:
case MTT_WEAPON:
{
Assert(m_TotalMemoryUsedByVehicleModelsMain >= 0);
m_TotalMemoryUsedByVehicleModelsMain = MAX(0, m_TotalMemoryUsedByVehicleModelsMain);
Assert(m_TotalMemoryUsedByVehicleModelsVram >= 0);
m_TotalMemoryUsedByVehicleModelsVram = MAX(0, m_TotalMemoryUsedByVehicleModelsVram);
}
break;
default:
break;
}
}
void CPopulationStreaming::RemoveStreamedInDependencies(const atArray<strIndex>& deps, atArray<sPopEntry>& componentList, u32& totalVirtual, u32& totalPhysical, eMemoryTrackType type)
{
for (s32 i = 0; i < deps.GetCount(); ++i)
{
streamDebugf1("RemoveStreamedInDependencies: dep %d: %s", i, strStreamingEngine::GetObjectName(deps[i]));
s32 entrySlot = -1;
for (s32 f = 0; f < componentList.GetCount(); ++f)
{
if (componentList[f].streamIdx == deps[i])
{
entrySlot = f;
break;
}
}
if (entrySlot == -1 || componentList[entrySlot].refCount == 0)
{
continue;
}
Assert(deps[i] == componentList[entrySlot].streamIdx);
if (componentList[entrySlot].refCount == 1)
{
u32 virtualMemory = strStreamingEngine::GetInfo().GetObjectVirtualSize(deps[i]);
u32 physicalMemory = strStreamingEngine::GetInfo().GetObjectPhysicalSize(deps[i]);
AddTotalMemoryUsed(-(s32)virtualMemory, -(s32)physicalMemory, type);
streamDebugf1("[actual] RemoveStreamedInDependencies: dep %d: %s %d/%d", i, strStreamingEngine::GetObjectName(deps[i]), virtualMemory, physicalMemory);
totalVirtual += virtualMemory;
totalPhysical += physicalMemory;
}
Assert(componentList[entrySlot].refCount > 0);
componentList[entrySlot].refCount = MAX(0, componentList[entrySlot].refCount - 1);
}
}
s32 CPopulationStreaming::GetTimeModelStreamedIn(u32 modelIndex)
{
s32 time = 0;
for(unsigned index = 0; index < MAX_STREAMED_IN_MODELS && time==0; index++)
{
if(m_StreamedInModelData[index].m_ModelIndex == modelIndex)
{
time = m_StreamedInModelData[index].m_TimeStreamedIn;
}
}
return time;
}
void CPopulationStreaming::SetTimeModelStreamedIn(fwModelId PedModelId, s32 time)
{
for(unsigned index = 0; index < MAX_STREAMED_IN_MODELS; index++)
{
if(m_StreamedInModelData[index].m_ModelIndex == PedModelId.GetModelIndex())
{
m_StreamedInModelData[index].m_TimeStreamedIn = time;
return;
}
}
}
void CPopulationStreaming::MarkModelNoLongerRequired(u32 modelIndex)
{
bool found = false;
for(unsigned index = 0; index < MAX_STREAMED_IN_MODELS && !found; index++)
{
if(m_StreamedInModelData[index].m_ModelIndex == modelIndex)
{
m_StreamedInModelData[index].m_TimeNoLongerRequired = fwTimer::GetTimeInMilliseconds();
found = true;
}
}
}
bool CPopulationStreaming::IsCarModelNeededCurrently(u32 ModelIndex)
{
//Boats are now allowed in SP and MP - process first.
if ( ((CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex))))->GetIsBoat() )
{
return m_bBoatsNeeded;
}
if (NetworkInterface::IsGameInProgress())
{
if(m_VehicleModelsRequiredForNetwork.IsMemberInGroup(ModelIndex))
{
return true;
}
return false;
}
return DoesCarModelBelongInCurrentPopulationZone(ModelIndex);
}
void CPopulationStreaming::DisallowScenarioPedStreamingForDuration(u32 timeInMS)
{
streamDebugf1("Streaming of new scenario peds disabled for %d ms.", (int)timeInMS);
m_ScenarioPedStreamingDisabledUntilTimeMS = Max(
m_ScenarioPedStreamingDisabledUntilTimeMS,
fwTimer::GetTimeInMilliseconds() + timeInMS);
}
bool CPopulationStreaming::DoesCarModelBelongInCurrentPopulationZone(u32 ModelIndex)
{
if(CPopCycle::HasValidCurrentPopAllocation())
{
const u32 popGroupCount = CPopCycle::GetPopGroups().GetVehCount();
for (u32 popGroupIndex = 0; popGroupIndex < popGroupCount; popGroupIndex++)
{
if (CPopCycle::GetCurrentVehGroupPercentage(popGroupIndex) > 0)
{ // This group is needed. Now test whether the car is a member of this group.
if (CPopCycle::GetPopGroups().IsVehIndexMember(popGroupIndex, ModelIndex))
{
return true;
}
}
}
}
return false;
}
void CPopulationStreaming::SpawnedVehicleOnHighway()
{
m_LastVehicleOnHighwaySpawn = fwTimer::GetTimeInMilliseconds();
}
bool CPopulationStreaming::IsPedModelNeededCurrently(u32 ModelIndex)
{
if (NetworkInterface::IsGameInProgress())
{
if(m_PedModelsRequiredForNetwork.IsMemberInGroup(ModelIndex))
{
return true;
}
return false;
}
if(!CPopCycle::HasValidCurrentPopAllocation())
return false;
const u32 popGroupCount = CPopCycle::GetPopGroups().GetPedCount();
for (u32 popGroupIndex = 0; popGroupIndex < popGroupCount; popGroupIndex++)
{
if (CPopCycle::GetCurrentPedGroupPercentage(popGroupIndex) > 0)
{ // This group is needed. Now test whether the car is a member of this group.
if (CPopCycle::GetPopGroups().IsPedIndexMember(popGroupIndex, ModelIndex))
{
return true;
}
}
}
return false;
}
bool CPopulationStreaming::IsPedModelNeededInAnyZone(u32 ModelIndex)
{
const u32 popGroupCount = CPopCycle::GetPopGroups().GetPedCount();
for (u32 popGroupIndex = 0; popGroupIndex < popGroupCount; popGroupIndex++)
{
if (CPopCycle::GetPopGroups().IsPedIndexMember(popGroupIndex, ModelIndex))
{
return true;
}
}
return false;
}
void CPopulationStreaming::TellStreamingAboutDeletableVehicles()
{
// Vehicles that are not native to the current zone and that are in the ONLY_IN_NATIVE_ZONE list should be streamed out.
const u32 nativeZoneOnlysGroupNameHash = ATSTRINGHASH("ONLY_IN_NATIVE_ZONE", 0x068c476fc);
u32 popGroupIndexOfNativeZoneOnlys = 0;
bool nativeZoneOnlyPopGroupExists = CPopCycle::GetPopGroups().FindVehGroupFromNameHash(nativeZoneOnlysGroupNameHash, popGroupIndexOfNativeZoneOnlys);
s32 numMembers = m_InAppropriateLoadedCars.CountMembers();
for (int C = 0; C < numMembers; C++)
{
u32 ModelIndex = m_InAppropriateLoadedCars.GetMember(C);
fwModelId modelId((strLocalIndex(ModelIndex)));
if (!modelId.IsValid())
break;
Assert(CModelInfo::HaveAssetsLoaded(modelId));
#if __BANK
if (ModelIndex == m_vehicleOverrideIndex)
continue;
#endif
if (nativeZoneOnlyPopGroupExists && CPopCycle::GetPopGroups().IsVehIndexMember(popGroupIndexOfNativeZoneOnlys, ModelIndex) && !DoesCarModelBelongInCurrentPopulationZone(ModelIndex))
{
CModelInfo::ClearAssetRequiredFlag(modelId, STRFLAG_DONTDELETE);
MarkModelNoLongerRequired(ModelIndex);
if(!NetworkInterface::IsGameInProgress())
{
AddDiscardedCar(ModelIndex);
--C;
}
}
}
// Boats can be removed if we don't need them.
numMembers = m_LoadedBoats.CountMembers();
for (int C = 0; C < numMembers; C++)
{
u32 ModelIndex = m_LoadedBoats.GetMember(C);
fwModelId modelId((strLocalIndex(ModelIndex)));
if (!modelId.IsValid())
break;
Assert(CModelInfo::HaveAssetsLoaded(modelId));
#if __BANK
if (ModelIndex == m_vehicleOverrideIndex)
continue;
#endif
if (m_bBoatsNeeded)
{
CModelInfo::SetAssetRequiredFlag(modelId, STRFLAG_DONTDELETE);
}
else
{
CModelInfo::ClearAssetRequiredFlag(modelId, STRFLAG_DONTDELETE);
MarkModelNoLongerRequired(ModelIndex);
}
}
// If the player drives a car that is appropriate we make sure it is not marked for removal.
// We may as well continue to use it.
if (CGameWorld::FindLocalPlayerVehicle())
{
u32 playerCarMI = CGameWorld::FindLocalPlayerVehicle()->GetModelIndex();
if ( m_AppropriateLoadedCars.IsMemberInGroup(playerCarMI) )
{
if ( !nativeZoneOnlyPopGroupExists || !CPopCycle::GetPopGroups().IsVehIndexMember(popGroupIndexOfNativeZoneOnlys, playerCarMI) || DoesCarModelBelongInCurrentPopulationZone(playerCarMI))
{
fwModelId modelId((strLocalIndex(playerCarMI)));
CModelInfo::SetAssetRequiredFlag(modelId, STRFLAG_DONTDELETE);
}
}
}
}
void CPopulationStreaming::TellStreamingAboutDeletablePeds()
{
s32 numMembers = m_InAppropriateLoadedPeds.CountMembers();
for (int C = 0; C < numMembers; C++)
{
u32 ModelIndex = m_InAppropriateLoadedPeds.GetMember(C);
fwModelId modelId((strLocalIndex(ModelIndex)));
if (!modelId.IsValid())
break;
Assert(CModelInfo::HaveAssetsLoaded(modelId));
CPedModelInfo *pPedModelInfo = (CPedModelInfo *)CModelInfo::GetBaseModelInfo(modelId);
if (pPedModelInfo->m_isRequestedAsDriver)
continue;
CModelInfo::ClearAssetRequiredFlag(modelId, STRFLAG_DONTDELETE);
MarkModelNoLongerRequired(ModelIndex);
if (!NetworkInterface::IsGameInProgress())
{
AddDiscardedPed(ModelIndex);
--C;
}
}
}
void CPopulationStreaming::ModelHasBeenStreamedIn(u32 ModelIndex)
{
STRVIS_AUTO_CONTEXT(strStreamingVisualize::POPSTREAMER);
if (CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)))->GetModelType() == MI_TYPE_PED)
{
PedHasBeenStreamedIn(ModelIndex);
}
else if (CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)))->GetModelType() == MI_TYPE_VEHICLE)
{
VehicleHasBeenStreamedIn(ModelIndex);
}
else if (CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)))->GetModelType() == MI_TYPE_WEAPON)
{
WeaponHasBeenStreamedIn(ModelIndex);
}
}
void CPopulationStreaming::ModelHasBeenStreamedOut(u32 ModelIndex)
{
if (CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)))->GetModelType() == MI_TYPE_PED)
{
PedHasBeenStreamedOut(ModelIndex);
}
else if (CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)))->GetModelType() == MI_TYPE_VEHICLE)
{
VehicleHasBeenStreamedOut(ModelIndex);
}
else if (CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)))->GetModelType() == MI_TYPE_WEAPON)
{
WeaponHasBeenStreamedOut(ModelIndex);
}
}
void CPopulationStreaming::PedHasBeenStreamedIn(u32 ModelIndex)
{
Assert( (!m_AppropriateLoadedPeds.IsMemberInGroup(ModelIndex)) && (!m_InAppropriateLoadedPeds.IsMemberInGroup(ModelIndex)) );
CPedModelInfo* pPMI = ((CPedModelInfo *)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex))));
Assert(pPMI);
pPMI->ResetNumTimesUsed();
if (pPMI->GetPersonalitySettings().GetIsMale())
{
s_numMalePedsRequested--;
if (s_numMalePedsRequested < 0)
s_numMalePedsRequested = 0;
}
else
{
s_numFemalePedsRequested--;
if (s_numFemalePedsRequested < 0)
s_numFemalePedsRequested = 0;
}
// don't add player models to any of the population lists - this is handled in CPedVariationMgr
if (!pPMI->GetIsPlayerModel() && (!CPed::IsSuperLodFromIndex(ModelIndex)))
{
ReinstateDiscardedPed(ModelIndex);
#if DEBUG_POPULATION_MEMORY
s32 indexAdded =
#endif // DEBUG_POPULATION_MEMORY
AddStreamedInModelData(ModelIndex);
u32 totalVirtual = 0;
u32 totalPhysical = 0;
AddStreamedInComponent(ModelIndex, m_pedComponents, totalVirtual, totalPhysical, MTT_PED);
#if DEBUG_POPULATION_MEMORY
m_StreamedInModelData[indexAdded].m_mainUsed = totalVirtual;
m_StreamedInModelData[indexAdded].m_vramUsed = totalPhysical;
#endif // DEBUG_POPULATION_MEMORY
streamDebugf1("PedHasBeenStreamedIn: %s uses %dK/%dK", CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)))->GetModelName(), totalVirtual/1024, totalPhysical/1024);
}
}
void CPopulationStreaming::AddStreamedPedVariation(const CPedStreamRenderGfx* gfx)
{
if (!gfx)
return;
strIndex backingStore[STREAMING_MAX_DEPENDENCIES];
atUserArray<strIndex> deps(backingStore, STREAMING_MAX_DEPENDENCIES);
u32 totalVirtual = 0;
u32 totalPhysical = 0;
for (s32 i = 0; i < PV_MAX_COMP; ++i)
{
if (gfx->m_dwdIdx[i] != -1)
{
deps.ResetCount();
CStreaming::GetObjectAndDependencies(strLocalIndex(gfx->m_dwdIdx[i]), g_DwdStore.GetStreamingModuleId(), deps, m_residentObjects.GetElements(), m_residentObjects.GetCount());
AddStreamedInDependencies(deps, m_pedComponents, totalVirtual, totalPhysical, MTT_PED);
}
if (gfx->m_txdIdx[i] != -1)
{
deps.ResetCount();
CStreaming::GetObjectAndDependencies(strLocalIndex(gfx->m_txdIdx[i]), g_TxdStore.GetStreamingModuleId(), deps, m_residentObjects.GetElements(), m_residentObjects.GetCount());
AddStreamedInDependencies(deps, m_pedComponents, totalVirtual, totalPhysical, MTT_PED);
}
if (gfx->m_cldIdx[i] != -1)
{
deps.ResetCount();
CStreaming::GetObjectAndDependencies(strLocalIndex(gfx->m_cldIdx[i]), g_ClothStore.GetStreamingModuleId(), deps, m_residentObjects.GetElements(), m_residentObjects.GetCount());
AddStreamedInDependencies(deps, m_pedComponents, totalVirtual, totalPhysical, MTT_PED);
}
if (gfx->m_headBlendIdx[i] != -1)
{
deps.ResetCount();
if (i < HBS_HEAD_DRAWABLES)
CStreaming::GetObjectAndDependencies(strLocalIndex(gfx->m_headBlendIdx[i]), g_DwdStore.GetStreamingModuleId(), deps, m_residentObjects.GetElements(), m_residentObjects.GetCount());
else if (i < HBS_MICRO_MORPH_SLOTS)
CStreaming::GetObjectAndDependencies(strLocalIndex(gfx->m_headBlendIdx[i]), g_TxdStore.GetStreamingModuleId(), deps, m_residentObjects.GetElements(), m_residentObjects.GetCount());
else
CStreaming::GetObjectAndDependencies(strLocalIndex(gfx->m_headBlendIdx[i]), g_DrawableStore.GetStreamingModuleId(), deps, m_residentObjects.GetElements(), m_residentObjects.GetCount());
AddStreamedInDependencies(deps, m_pedComponents, totalVirtual, totalPhysical, MTT_PED);
}
if (gfx->m_fpAltIdx[i] != -1)
{
deps.ResetCount();
CStreaming::GetObjectAndDependencies(strLocalIndex(gfx->m_fpAltIdx[i]), g_DwdStore.GetStreamingModuleId(), deps, m_residentObjects.GetElements(), m_residentObjects.GetCount());
AddStreamedInDependencies(deps, m_pedComponents, totalVirtual, totalPhysical, MTT_PED);
}
}
streamDebugf1("AddStreamedPedVariation: %dK/%dK", totalVirtual/1024, totalPhysical/1024);
m_TotalStreamedPedMemoryMain += totalVirtual;
m_TotalStreamedPedMemoryVram += totalPhysical;
}
void CPopulationStreaming::AddStreamedVehicleVariation(const CVehicleStreamRenderGfx* gfx)
{
if (!gfx)
return;
strIndex backingStore[STREAMING_MAX_DEPENDENCIES];
atUserArray<strIndex> deps(backingStore, STREAMING_MAX_DEPENDENCIES);
u32 totalVirtual = 0;
u32 totalPhysical = 0;
for (u8 i = 0; i < VMT_RENDERABLE + MAX_LINKED_MODS; ++i)
{
if (gfx->GetFragIndex(i) != -1)
{
streamDebugf1("AddStreamedVehicleVariation: Adding mod %d", i);
deps.ResetCount();
CStreaming::GetObjectAndDependencies(gfx->GetFragIndex(i), g_FragmentStore.GetStreamingModuleId(), deps, NULL, 0);
AddStreamedInDependencies(deps, m_vehicleComponents, totalVirtual, totalPhysical, MTT_VEHICLE);
}
}
streamDebugf1("AddStreamedVehicleVariation: %dK/%dK", totalVirtual/1024, totalPhysical/1024);
m_TotalStreamedVehicleMemoryMain += totalVirtual;
m_TotalStreamedVehicleMemoryVram += totalPhysical;
}
void CPopulationStreaming::RemoveStreamedPedVariation(const CPedStreamRenderGfx* gfx)
{
if (!gfx)
return;
strIndex backingStore[STREAMING_MAX_DEPENDENCIES];
atUserArray<strIndex> deps(backingStore, STREAMING_MAX_DEPENDENCIES);
u32 totalVirtual = 0;
u32 totalPhysical = 0;
for (s32 i = 0; i < PV_MAX_COMP; ++i)
{
if (gfx->m_dwdIdx[i] != -1)
{
deps.ResetCount();
CStreaming::GetObjectAndDependencies(strLocalIndex(gfx->m_dwdIdx[i]), g_DwdStore.GetStreamingModuleId(), deps, m_residentObjects.GetElements(), m_residentObjects.GetCount());
RemoveStreamedInDependencies(deps, m_pedComponents, totalVirtual, totalPhysical, MTT_PED);
}
if (gfx->m_txdIdx[i] != -1)
{
deps.ResetCount();
CStreaming::GetObjectAndDependencies(strLocalIndex(gfx->m_txdIdx[i]), g_TxdStore.GetStreamingModuleId(), deps, m_residentObjects.GetElements(), m_residentObjects.GetCount());
RemoveStreamedInDependencies(deps, m_pedComponents, totalVirtual, totalPhysical, MTT_PED);
}
if (gfx->m_cldIdx[i] != -1)
{
deps.ResetCount();
CStreaming::GetObjectAndDependencies(strLocalIndex(gfx->m_cldIdx[i]), g_ClothStore.GetStreamingModuleId(), deps, m_residentObjects.GetElements(), m_residentObjects.GetCount());
RemoveStreamedInDependencies(deps, m_pedComponents, totalVirtual, totalPhysical, MTT_PED);
}
if (gfx->m_headBlendIdx[i] != -1)
{
deps.ResetCount();
if (i < HBS_HEAD_DRAWABLES)
CStreaming::GetObjectAndDependencies(strLocalIndex(gfx->m_headBlendIdx[i]), g_DwdStore.GetStreamingModuleId(), deps, m_residentObjects.GetElements(), m_residentObjects.GetCount());
else if (i < HBS_MICRO_MORPH_SLOTS)
CStreaming::GetObjectAndDependencies(strLocalIndex(gfx->m_headBlendIdx[i]), g_TxdStore.GetStreamingModuleId(), deps, m_residentObjects.GetElements(), m_residentObjects.GetCount());
else
CStreaming::GetObjectAndDependencies(strLocalIndex(gfx->m_headBlendIdx[i]), g_DrawableStore.GetStreamingModuleId(), deps, m_residentObjects.GetElements(), m_residentObjects.GetCount());
RemoveStreamedInDependencies(deps, m_pedComponents, totalVirtual, totalPhysical, MTT_PED);
}
if (gfx->m_fpAltIdx[i] != -1)
{
deps.ResetCount();
CStreaming::GetObjectAndDependencies(strLocalIndex(gfx->m_fpAltIdx[i]), g_DwdStore.GetStreamingModuleId(), deps, m_residentObjects.GetElements(), m_residentObjects.GetCount());
RemoveStreamedInDependencies(deps, m_pedComponents, totalVirtual, totalPhysical, MTT_PED);
}
}
streamDebugf1("RemoveStreamedPedVariation: %dK/%dK", totalVirtual/1024, totalPhysical/1024);
m_TotalStreamedPedMemoryMain -= totalVirtual;
m_TotalStreamedPedMemoryVram -= totalPhysical;
}
void CPopulationStreaming::RemoveStreamedVehicleVariation(const CVehicleStreamRenderGfx* gfx)
{
if (!gfx)
return;
strIndex backingStore[STREAMING_MAX_DEPENDENCIES];
atUserArray<strIndex> deps(backingStore, STREAMING_MAX_DEPENDENCIES);
u32 totalVirtual = 0;
u32 totalPhysical = 0;
for (u8 i = 0; i < VMT_RENDERABLE + MAX_LINKED_MODS; ++i)
{
if (gfx->GetFragIndex(i) != -1)
{
streamDebugf1("RemoveStreamedVehicleVariation: Adding mod %d", i);
deps.ResetCount();
CStreaming::GetObjectAndDependencies(gfx->GetFragIndex(i), g_FragmentStore.GetStreamingModuleId(), deps, NULL, 0);
RemoveStreamedInDependencies(deps, m_vehicleComponents, totalVirtual, totalPhysical, MTT_VEHICLE);
}
}
streamDebugf1("RemoveStreamedVehicleVariation: %dK/%dK", totalVirtual/1024, totalPhysical/1024);
m_TotalStreamedVehicleMemoryMain -= totalVirtual;
m_TotalStreamedVehicleMemoryVram -= totalPhysical;
}
void CPopulationStreaming::PedHasBeenStreamedOut(u32 ModelIndex)
{
m_AppropriateLoadedPeds.RemoveMember(ModelIndex);
m_InAppropriateLoadedPeds.RemoveMember(ModelIndex);
m_SpecialLoadedPeds.RemoveMember(ModelIndex); // Mission requested peds will end up in the SpecialPeds and need to be removed also.
m_DiscardedPeds.RemoveMember(ModelIndex);
// Set the last time used to the current time so that the other vehicles in the same group will get loaded in before this one.
CPedModelInfo *pPedModelInfo = (CPedModelInfo *)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)));
// don't add player models to any of the population lists - this is handled in CPedVariationMgr
if (!pPedModelInfo->GetIsPlayerModel() && (!CPed::IsSuperLodFromIndex(ModelIndex)))
{
if (!RemoveStreamedInModelData(ModelIndex))
return;
pPedModelInfo->SetLastTimeUsed(fwTimer::GetTimeInMilliseconds());
// If this ped was counted as a scenario ped we reduce the count of those.
if (pPedModelInfo->GetCountedAsScenarioPed())
{
streamDebugf1("%s no longer counted as scenario ped.", pPedModelInfo->GetModelName());
pPedModelInfo->SetCountedAsScenarioPed(false);
//Decrement the number of models streamed in for the given slot.
int scenarioPedStreamingSlot = pPedModelInfo->GetScenarioPedStreamingSlot();
m_aNumScenarioPedModelsLoaded[scenarioPedStreamingSlot]--;
Assert(m_aNumScenarioPedModelsLoaded[scenarioPedStreamingSlot] >= 0);
}
u32 totalVirtual = 0;
u32 totalPhysical = 0;
RemoveStreamedInComponent(ModelIndex, m_pedComponents, totalVirtual, totalPhysical, MTT_PED);
streamDebugf1("PedHasBeenStreamedOut: %s uses %dK/%dK", CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)))->GetModelName(), totalVirtual/1024, totalPhysical/1024);
}
streamAssertf(!pPedModelInfo->GetCountedAsScenarioPed(),
"Ped model %s just streamed out, but is still marked as a scenario ped.",
pPedModelInfo->GetModelName());
}
void CPopulationStreaming::RequestVehicleDriver(u32 vehicleModelIndex)
{
fwModelId modelId((strLocalIndex(vehicleModelIndex)));
if (!modelId.IsValid())
return;
CBaseModelInfo* bmi = CModelInfo::GetBaseModelInfo(modelId);
if (!bmi || bmi->GetModelType() != MI_TYPE_VEHICLE)
return;
if (!CModelInfo::GetAssetRequiredFlag(modelId))
return;
CVehicleModelInfo* vmi = (CVehicleModelInfo*)bmi;
if (!vmi->HasRequestedDrivers() && !CScriptCars::GetSuppressedCarModels().HasModelBeenSuppressed(vehicleModelIndex))
CPedPopulation::LoadSpecificDriverModelsForCar(modelId);
}
void CPopulationStreaming::UnrequestVehicleDriver(u32 vehicleModelIndex)
{
fwModelId modelId((strLocalIndex(vehicleModelIndex)));
if (!modelId.IsValid())
return;
CBaseModelInfo* bmi = CModelInfo::GetBaseModelInfo(modelId);
if (!bmi || bmi->GetModelType() != MI_TYPE_VEHICLE)
return;
CPedPopulation::RemoveSpecificDriverModelsForCar(modelId);
}
void CPopulationStreaming::VehicleHasBeenStreamedIn(u32 ModelIndex)
{
Assert( (!m_AppropriateLoadedCars.IsMemberInGroup(ModelIndex)) && (!m_InAppropriateLoadedCars.IsMemberInGroup(ModelIndex)) );
CVehicleModelInfo* vmi = (CVehicleModelInfo *)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)));
vmi->ResetNumTimesUsed();
vmi->SetLastTimeUsed(fwTimer::GetTimeInMilliseconds());
bool bIsBoat = ((CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex))))->GetVehicleType()==VEHICLE_TYPE_BOAT;
bool bIsTrain = ((CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex))))->GetVehicleType()==VEHICLE_TYPE_TRAIN;
bool bIsHeli = ((CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex))))->GetVehicleType()==VEHICLE_TYPE_HELI;
bool bIsAmbulance = (ModelIndex == MI_CAR_AMBULANCE);
bool bIsFiretruck = (ModelIndex == MI_CAR_FIRETRUCK);
REPLAY_ONLY(bool bIsReplayInCharge = CReplayMgr::IsReplayInControlOfWorld();)
bool requestDriver = true;
if (bIsHeli || bIsTrain || bIsAmbulance || bIsFiretruck REPLAY_ONLY(|| bIsReplayInCharge))
{
// AF: Vehicles are added to the special list for some unknown reason. I am assuming this list is for vehicles whose streaming
// is managed somewhere else
m_SpecialLoadedCars.AddMember(ModelIndex);
// Don't request a driver for the members of m_SpecialLoadedCars, has to be done elsewhere
// by users that need it.
requestDriver = false;
}
else if (bIsBoat)
{
m_LoadedBoats.AddMember(ModelIndex);
// Only request drivers for boats if we actually want boats in the general population.
// Otherwise, they are probably just needed for car generators or vehicle scenarios (which
// will request drivers if needed).
requestDriver = m_bBoatsNeeded;
}
else if (IsCarModelNeededCurrently(ModelIndex))
{
m_AppropriateLoadedCars.AddMember(ModelIndex);
}
else
{
m_InAppropriateLoadedCars.AddMember(ModelIndex);
// Car generator and scenario vehicles often end up here. We don't need drivers to be
// requested for those, at this level of the code.
requestDriver = false;
}
if(requestDriver)
{
RequestVehicleDriver(ModelIndex);
}
m_DiscardedCars.RemoveMember(ModelIndex);
#if DEBUG_POPULATION_MEMORY
s32 indexAdded =
#endif // DEBUG_POPULATION_MEMORY
AddStreamedInModelData(ModelIndex);
u32 totalVirtual = 0;
u32 totalPhysical = 0;
AddStreamedInComponent(ModelIndex, m_vehicleComponents, totalVirtual, totalPhysical, MTT_VEHICLE);
#if DEBUG_POPULATION_MEMORY
m_StreamedInModelData[indexAdded].m_mainUsed = totalVirtual;
m_StreamedInModelData[indexAdded].m_vramUsed = totalPhysical;
streamDebugf1("VehicleHasBeenStreamedIn (%d, %d ms ago, index: %d): %s uses %dK/%dK additional memory", m_StreamedInModelData[indexAdded].m_TimeStreamedIn, fwTimer::GetTimeInMilliseconds() - m_StreamedInModelData[indexAdded].m_TimeStreamedIn, indexAdded, CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)))->GetModelName(), totalVirtual/1024, totalPhysical/1024);
#else
streamDebugf1("VehicleHasBeenStreamedIn: %s uses %dK/%dK additional memory", CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)))->GetModelName(), totalVirtual/1024, totalPhysical/1024);
#endif // DEBUG_POPULATION_MEMORY
if(NetworkInterface::IsGameInProgress())
{
ReclassifyLoadedVehiclesForNetwork();
}
m_numQueuedVehicleModels = (s8)rage::Max(0, m_numQueuedVehicleModels - 1);
EmergencyRemoveVehicleStructures();
}
void CPopulationStreaming::VehicleHasBeenStreamedOut(u32 ModelIndex)
{
m_AppropriateLoadedCars.RemoveMember(ModelIndex);
m_InAppropriateLoadedCars.RemoveMember(ModelIndex);
m_SpecialLoadedCars.RemoveMember(ModelIndex);
m_LoadedBoats.RemoveMember(ModelIndex);
m_DiscardedCars.RemoveMember(ModelIndex);
UnrequestVehicleDriver(ModelIndex);
CTheCarGenerators::VehicleHasBeenStreamedOut(ModelIndex);
if(NetworkInterface::IsGameInProgress())
{
ReclassifyLoadedVehiclesForNetwork();
}
if (!RemoveStreamedInModelData(ModelIndex))
return;
// Set the last time used to the current time so that the other vehicles in the same group will get loaded in before this one.
((CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex))))->SetLastTimeUsed(fwTimer::GetTimeInMilliseconds());
u32 totalVirtual = 0;
u32 totalPhysical = 0;
RemoveStreamedInComponent(ModelIndex, m_vehicleComponents, totalVirtual, totalPhysical, MTT_VEHICLE);
streamDebugf1("VehicleHasBeenStreamedOut: %s freed %dK/%dK", CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)))->GetModelName(), totalVirtual/1024, totalPhysical/1024);
}
void CPopulationStreaming::WeaponHasBeenStreamedIn(u32 ModelIndex)
{
#if DEBUG_POPULATION_MEMORY
s32 indexAdded =
#endif // DEBUG_POPULATION_MEMORY
AddStreamedInModelData(ModelIndex);
u32 totalVirtual = 0;
u32 totalPhysical = 0;
AddStreamedInComponent(ModelIndex, m_weaponComponents, totalVirtual, totalPhysical, MTT_WEAPON);
#if DEBUG_POPULATION_MEMORY
m_StreamedInModelData[indexAdded].m_mainUsed = totalVirtual;
m_StreamedInModelData[indexAdded].m_vramUsed = totalPhysical;
streamDebugf1("WeaponHasBeenStreamedIn (%d, %d ms ago, index: %d): %s uses %dK/%dK additional memory", m_StreamedInModelData[indexAdded].m_TimeStreamedIn, fwTimer::GetTimeInMilliseconds() - m_StreamedInModelData[indexAdded].m_TimeStreamedIn, indexAdded, CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)))->GetModelName(), totalVirtual/1024, totalPhysical/1024);
#else
streamDebugf1("WeaponHasBeenStreamedIn: %s uses %dK/%dK additional memory", CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)))->GetModelName(), totalVirtual/1024, totalPhysical/1024);
#endif // DEBUG_POPULATION_MEMORY
}
void CPopulationStreaming::WeaponHasBeenStreamedOut(u32 ModelIndex)
{
if (!RemoveStreamedInModelData(ModelIndex))
return;
u32 totalVirtual = 0;
u32 totalPhysical = 0;
RemoveStreamedInComponent(ModelIndex, m_weaponComponents, totalVirtual, totalPhysical, MTT_WEAPON);
streamDebugf1("WeaponHasBeenStreamedOut: %s freed %dK/%dK", CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex)))->GetModelName(), totalVirtual/1024, totalPhysical/1024);
}
u32 CPopulationStreaming::GetFallbackPedModelIndex(bool bDriversOnly)
{
float lowestWeirdness = 1000.0f;
u32 MostAveragePedModelIndex = fwModelId::MI_INVALID;
CLoadedModelGroup fallbackPeds;
GetFallbackPedGroup(fallbackPeds, bDriversOnly);
// if we have appropriate peds use only those to choose an index, otherwise use all fallback peds
CLoadedModelGroup appropriateCarPeds = m_AppropriateLoadedPeds;
appropriateCarPeds.RemovePedModelsThatCantDriveCars(true);
s32 numMembers = appropriateCarPeds.CountMembers();
if (numMembers == 0)
numMembers = fallbackPeds.CountMembers();
for (int p = 0; p < numMembers; p++)
{
float Weirdness = 0.0f;
strLocalIndex TestMI = strLocalIndex(fallbackPeds.GetMember(p));
CPedModelInfo *pModelInfo = ((CPedModelInfo *)CModelInfo::GetBaseModelInfo(fwModelId(TestMI)));
if (!pModelInfo->IsMale())
{
Weirdness += 0.1f;
}
if (!pModelInfo->GetPersonalitySettings().DrivesVehicleType(PED_DRIVES_POOR_CAR) && !pModelInfo->GetPersonalitySettings().DrivesVehicleType(PED_DRIVES_AVERAGE_CAR) && !pModelInfo->GetPersonalitySettings().DrivesVehicleType(PED_DRIVES_RICH_CAR))
{
Weirdness += 0.2f;
}
if (Weirdness < lowestWeirdness)
{
lowestWeirdness = Weirdness;
MostAveragePedModelIndex = TestMI.Get();
}
}
Assert(CModelInfo::IsValidModelInfo(MostAveragePedModelIndex)); // If there wasn't a fallback ped the calling code should have called IsFallbackPedAvailable first.
return MostAveragePedModelIndex;
}
bool CPopulationStreaming::IsFallbackPedAvailable(bool bikesOnly, bool bDriversOnly)
{
CLoadedModelGroup group;
GetFallbackPedGroup(group, bDriversOnly);
if (bikesOnly)
group.RemoveNonMotorcyclePeds();
return !group.IsEmpty();
}
void CPopulationStreaming::GetFallbackPedGroup(CLoadedModelGroup& fallbackGroup, bool carDrivers)
{
fallbackGroup.Clear();
fallbackGroup.Merge(&m_AppropriateLoadedPeds, &m_InAppropriateLoadedPeds, &m_DiscardedPeds);
fallbackGroup.RemoveSuppresedPeds();
if (carDrivers)
fallbackGroup.RemovePedModelsThatCantDriveCars(true);
}
bool CPopulationStreaming::StreamScenarioPeds(fwModelId PedModelId, bool highPri, bool startupMode, const char* ASSERT_ONLY(debugSourceName))
{
if (Verifyf( PedModelId.IsValid(), "We need a valid model specified" ))
{
STRVIS_AUTO_CONTEXT(strStreamingVisualize::POPSTREAMER);
CPedModelInfo *pPedModelInfo = (CPedModelInfo *) CModelInfo::GetBaseModelInfo(PedModelId);
Assertf(pPedModelInfo, "Valid ped model ID, but invalid CPedModelInfo.");
eScenarioPopStreamingSlot eStreamSlot = pPedModelInfo->GetScenarioPedStreamingSlot();
if (!CModelInfo::HaveAssetsLoaded(PedModelId))
{
// We're not going to stream a scenario ped back in straight away.
// - Update: we do now, if it's for a high priority scenario.
u32 lastTimeUsed = pPedModelInfo->GetLastTimeUsed();
if ( ( (fwTimer::GetTimeInMilliseconds() - lastTimeUsed) < SCENARIO_DONT_GET_RELOADED_DURATION) && lastTimeUsed != 0
&& !highPri && !startupMode)
{
return false;
}
if (!CModelInfo::AreAssetsRequested(PedModelId))
{
if ( !CModelInfo::AreAssetsLoading(PedModelId) )
{
streamAssertf( !pPedModelInfo->GetCountedAsScenarioPed(), "%s already counted as a scenario ped, which was not expected.", pPedModelInfo->GetModelName() );
if (!pPedModelInfo->GetCountedAsScenarioPed())
{
if(!IsAllowedToRequestScenarioPedModel(highPri, startupMode, eStreamSlot))
{
streamDebugf1("Not allowed to stream in %s, too many scenario peds (%d/%d/%d).",
pPedModelInfo->GetModelName(),
m_aNumScenarioPedModelsLoaded[eStreamSlot], m_MaxScenarioPedModelsLoadedPerSlot, m_MaxScenarioPedModelsLoadedOverride);
return false;
}
if(streamVerifyf(CModelInfo::RequestAssets(PedModelId, STRFLAG_FORCE_LOAD | STRFLAG_DONTDELETE),
"Failed to request model %s for scenario ped, may be missing from streaming archives (requested from %s).", pPedModelInfo->GetModelName(), debugSourceName))
{
pPedModelInfo->SetCountedAsScenarioPed(true);
streamDebugf1("%s is now counted as scenario ped.", pPedModelInfo->GetModelName());
//Increment the number of scenario peds streamed in for a given slot.
m_aNumScenarioPedModelsLoaded[eStreamSlot]++;
}
}
}
return false;
}
}
else
{
// If the model is loaded but in the process of being removed grab it allow it to be used again.
// This is temporary whilst we properly apply scenario limits
// We also need to bump the streamed in timer to prevent the ped population from immediately pushing it back out again.
if (!CModelInfo::GetAssetRequiredFlag(PedModelId))
{
if(!IsAllowedToRequestScenarioPedModel(highPri, startupMode, eStreamSlot))
{
streamDebugf1("Not allowed to stream in %s, too many scenario peds (%d/%d/%d).",
pPedModelInfo->GetModelName(),
m_aNumScenarioPedModelsLoaded[eStreamSlot], m_MaxScenarioPedModelsLoadedPerSlot, m_MaxScenarioPedModelsLoadedOverride);
return false;
}
ReinstateDiscardedPed(PedModelId.GetModelIndex());
SetTimeModelStreamedIn(PedModelId, fwTimer::GetTimeInMilliseconds());
// ReinstateDiscaredPed() doesn't necessarily set STRFLAG_DONTDELETE for scenario peds, but
// if we get here the ped is loaded and counted as a scenario ped, so it needs the flag to
// prevent it from getting streamed out.
// Note: I think this should be done here, but not sure that I want to change it right now
// since it was probably unrelated to the bug I looked at:
// CModelInfo::SetAssetRequiredFlag(PedModelId, STRFLAG_DONTDELETE);
if( !pPedModelInfo->GetCountedAsScenarioPed() )
{
pPedModelInfo->SetCountedAsScenarioPed(true);
streamDebugf1("%s is now counted as scenario ped (was about to be removed).", pPedModelInfo->GetModelName());
//Increment the number of scenario peds loaded in for a given slot.
m_aNumScenarioPedModelsLoaded[eStreamSlot]++;
}
return true;
}
}
return true;
}
return false;
}
void CPopulationStreaming::RemoveStreamedPedModelsOnShutdown()
{
CPedFactory::GetFactory()->ClearDestroyedPedCache();
s32 k, num = m_AppropriateLoadedPeds.CountMembers();
u32 mi = fwModelId::MI_INVALID;
for (k = (num-1); k >= 0; k--)
{
mi = m_AppropriateLoadedPeds.GetMember(k);
fwModelId modelId((strLocalIndex(mi)));
ASSERT_ONLY(CBaseModelInfo* pModelInfo = CModelInfo::GetBaseModelInfo(modelId); )
#if DEBUG_POPULATION_MEMORY && __ASSERT
Warningf("CPopulationStreaming::RemoveStreamedPedModelsOnShutdown: Appropriate ped: %s\n", pModelInfo->GetModelName());
#endif
Assert(pModelInfo->GetModelType() == MI_TYPE_PED);
Assertf(((CPedModelInfo*)pModelInfo)->GetNumPedModelRefs() == 0, "m_AppropriateLoadedPeds - %s NumPedModelRefs %d", pModelInfo->GetModelName(), ((CPedModelInfo*)pModelInfo)->GetNumPedModelRefs());
Assert(pModelInfo->GetDrawable());
CModelInfo::ClearAssetRequiredFlag(modelId, STR_DONTDELETE_MASK);
CModelInfo::RemoveAssets(modelId);
}
num = m_InAppropriateLoadedPeds.CountMembers();
for (k = (num-1); k >= 0; k--)
{
mi = m_InAppropriateLoadedPeds.GetMember(k);
fwModelId modelId((strLocalIndex(mi)));
ASSERT_ONLY(CBaseModelInfo* pModelInfo = CModelInfo::GetBaseModelInfo(modelId); )
#if DEBUG_POPULATION_MEMORY && __ASSERT
Warningf("CPopulationStreaming::RemoveStreamedPedModelsOnShutdown: InAppropriate ped: %s\n", pModelInfo->GetModelName());
#endif
Assert(pModelInfo->GetModelType() == MI_TYPE_PED);
Assertf(((CPedModelInfo*)pModelInfo)->GetNumPedModelRefs() == 0, "m_InAppropriateLoadedPeds - %s NumPedModelRefs %d", pModelInfo->GetModelName(), ((CPedModelInfo*)pModelInfo)->GetNumPedModelRefs());
Assert(pModelInfo->GetDrawable());
CModelInfo::ClearAssetRequiredFlag(modelId, STR_DONTDELETE_MASK);
CModelInfo::RemoveAssets(modelId);
}
num = m_SpecialLoadedPeds.CountMembers();
for (k = (num-1); k >= 0; k--)
{
mi = m_SpecialLoadedPeds.GetMember(k);
fwModelId modelId((strLocalIndex(mi)));
ASSERT_ONLY(CBaseModelInfo* pModelInfo = CModelInfo::GetBaseModelInfo(modelId); )
#if DEBUG_POPULATION_MEMORY && __ASSERT
Warningf("CPopulationStreaming::RemoveStreamedPedModelsOnShutdown: Special ped: %s\n", pModelInfo->GetModelName());
#endif
Assert(CModelInfo::IsValidModelInfo(mi));
Assert(pModelInfo->GetModelType() == MI_TYPE_PED);
Assertf(((CPedModelInfo*)pModelInfo)->GetNumPedModelRefs() == 0, "m_SpecialLoadedPeds - %s NumPedModelRefs %d", pModelInfo->GetModelName(), ((CPedModelInfo*)pModelInfo)->GetNumPedModelRefs());
Assert(pModelInfo->GetDrawable());
CModelInfo::ClearAssetRequiredFlag(modelId, STR_DONTDELETE_MASK);
CModelInfo::RemoveAssets(modelId);
}
num = m_DiscardedPeds.CountMembers();
for (k = (num-1); k >= 0; k--)
{
mi = m_DiscardedPeds.GetMember(k);
fwModelId modelId((strLocalIndex(mi)));
ASSERT_ONLY(CBaseModelInfo* pModelInfo = CModelInfo::GetBaseModelInfo(modelId); )
#if DEBUG_POPULATION_MEMORY && __ASSERT
Warningf("CPopulationStreaming::RemoveStreamedPedModelsOnShutdown: Discarded ped: %s\n", pModelInfo->GetModelName());
#endif
Assert(CModelInfo::IsValidModelInfo(mi));
Assert(pModelInfo->GetModelType() == MI_TYPE_PED);
Assertf(((CPedModelInfo*)pModelInfo)->GetNumPedModelRefs() == 0, "m_SpecialLoadedPeds - %s NumPedModelRefs %d", pModelInfo->GetModelName(), ((CPedModelInfo*)pModelInfo)->GetNumPedModelRefs());
Assert(pModelInfo->GetDrawable());
CModelInfo::ClearAssetRequiredFlag(modelId, STR_DONTDELETE_MASK);
CModelInfo::RemoveAssets(modelId);
}
}
void CPopulationStreaming::RemoveStreamedCarModelsOnShutdown()
{
CTheCarGenerators::ClearScheduledQueue();
CVehicleFactory::GetFactory()->ClearDestroyedVehCache();
s32 k, num = m_AppropriateLoadedCars.CountMembers();
u32 mi = fwModelId::MI_INVALID;
for (k = (num-1); k >= 0; k--)
{
mi = m_AppropriateLoadedCars.GetMember(k);
fwModelId modelId((strLocalIndex(mi)));
ASSERT_ONLY(CBaseModelInfo* pModelInfo = CModelInfo::GetBaseModelInfo(modelId); )
#if DEBUG_POPULATION_MEMORY && __ASSERT
Warningf("CPopulationStreaming::RemoveStreamedCarModelsOnShutdown: Appropriate car: %s\n", pModelInfo->GetModelName());
#endif
Assert(pModelInfo->GetModelType() == MI_TYPE_VEHICLE);
Assertf(((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefs() == 0, "m_AppropriateLoadedCars - %s NumRefs %d", pModelInfo->GetModelName(), ((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefs());
Assertf(((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefsParked() == 0, "m_AppropriateLoadedCars - %s NumRefsParked %d", pModelInfo->GetModelName(), ((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefsParked());
Assert(pModelInfo->GetDrawable());
CModelInfo::ClearAssetRequiredFlag(modelId, STR_DONTDELETE_MASK);
CModelInfo::RemoveAssets(modelId);
}
num = m_InAppropriateLoadedCars.CountMembers();
for (k = (num-1); k >= 0; k--)
{
mi = m_InAppropriateLoadedCars.GetMember(k);
fwModelId modelId((strLocalIndex(mi)));
ASSERT_ONLY(CBaseModelInfo* pModelInfo = CModelInfo::GetBaseModelInfo(modelId); )
#if DEBUG_POPULATION_MEMORY && __ASSERT
Warningf("CPopulationStreaming::RemoveStreamedCarModelsOnShutdown: InAppropriate car: %s\n", pModelInfo->GetModelName());
#endif
Assert(pModelInfo->GetModelType() == MI_TYPE_VEHICLE);
Assertf(((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefs() == 0, "m_InAppropriateLoadedCars - %s NumRefs %d", pModelInfo->GetModelName(), ((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefs());
Assertf(((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefsParked() == 0, "m_InAppropriateLoadedCars - %s NumRefsParked %d", pModelInfo->GetModelName(), ((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefsParked());
Assert(pModelInfo->GetDrawable());
CModelInfo::ClearAssetRequiredFlag(modelId, STR_DONTDELETE_MASK);
CModelInfo::RemoveAssets(modelId);
}
num = m_SpecialLoadedCars.CountMembers();
for (k = (num-1); k >= 0; k--)
{
mi = m_SpecialLoadedCars.GetMember(k);
fwModelId modelId((strLocalIndex(mi)));
ASSERT_ONLY(CBaseModelInfo* pModelInfo = CModelInfo::GetBaseModelInfo(modelId); )
#if DEBUG_POPULATION_MEMORY && __ASSERT
Warningf("CPopulationStreaming::RemoveStreamedCarModelsOnShutdown: Special car: %s\n", pModelInfo->GetModelName());
#endif
Assert(pModelInfo->GetModelType() == MI_TYPE_VEHICLE);
Assertf(((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefs() == 0, "m_SpecialLoadedCars - %s NumRefs %d", pModelInfo->GetModelName(), ((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefs());
Assertf(((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefsParked() == 0, "m_SpecialLoadedCars - %s NumRefsParked %d", pModelInfo->GetModelName(), ((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefsParked());
Assert(pModelInfo->GetDrawable());
CModelInfo::ClearAssetRequiredFlag(modelId, STR_DONTDELETE_MASK);
CModelInfo::RemoveAssets(modelId);
}
num = m_LoadedBoats.CountMembers();
for (k = (num-1); k >= 0; k--)
{
mi = m_LoadedBoats.GetMember(k);
fwModelId modelId((strLocalIndex(mi)));
ASSERT_ONLY(CBaseModelInfo* pModelInfo = CModelInfo::GetBaseModelInfo(modelId); )
#if DEBUG_POPULATION_MEMORY && __ASSERT
Warningf("CPopulationStreaming::RemoveStreamedCarModelsOnShutdown: Boat: %s\n", pModelInfo->GetModelName());
#endif
Assert(pModelInfo->GetModelType() == MI_TYPE_VEHICLE);
Assertf(((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefs() == 0, "m_LoadedBoats - %s NumRefs %d", pModelInfo->GetModelName(), ((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefs());
Assertf(((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefsParked() == 0, "m_LoadedBoats - %s NumRefsParked %d", pModelInfo->GetModelName(), ((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefsParked());
Assert(pModelInfo->GetDrawable());
CModelInfo::ClearAssetRequiredFlag(modelId, STR_DONTDELETE_MASK);
CModelInfo::RemoveAssets(modelId);
}
num = m_DiscardedCars.CountMembers();
for (k = (num-1); k >= 0; k--)
{
mi = m_DiscardedCars.GetMember(k);
fwModelId modelId((strLocalIndex(mi)));
ASSERT_ONLY(CBaseModelInfo* pModelInfo = CModelInfo::GetBaseModelInfo(modelId); )
#if DEBUG_POPULATION_MEMORY && __ASSERT
Warningf("CPopulationStreaming::RemoveStreamedCarModelsOnShutdown: Discarded car: %s\n", pModelInfo->GetModelName());
#endif
Assert(pModelInfo->GetModelType() == MI_TYPE_VEHICLE);
Assertf(((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefs() == 0, "m_LoadedBoats - %s NumRefs %d", pModelInfo->GetModelName(), ((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefs());
Assertf(((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefsParked() == 0, "m_LoadedBoats - %s NumRefsParked %d", pModelInfo->GetModelName(), ((CVehicleModelInfo*)pModelInfo)->GetNumVehicleModelRefsParked());
Assert(pModelInfo->GetDrawable());
CModelInfo::ClearAssetRequiredFlag(modelId, STR_DONTDELETE_MASK);
CModelInfo::RemoveAssets(modelId);
}
}
void CPopulationStreaming::EmergencyRemoveVehicleStructures()
{
const unsigned REMOVAL_THRESHOLD = 20;
int numToDitch = REMOVAL_THRESHOLD - CVehicleStructure::m_pInfoPool->GetNoOfFreeSpaces();
if (numToDitch > 0)
{
streamDebugf2("Emergency vehicle removal");
CLoadedModelGroup tempList;
tempList.Merge(&m_InAppropriateLoadedCars, &m_DiscardedCars, &m_AppropriateLoadedCars);
int numVehicles = tempList.CountMembers();
for(int index = (numVehicles-1); (index >= 0) && (numToDitch > 0); index--)
{
strLocalIndex modelIndex = strLocalIndex(tempList.GetMember(index));
fwModelId modelId(modelIndex);
if (CModelInfo::GetBaseModelInfo(modelId)->GetNumRefs() == 0 &&
!CModelInfo::GetAssetRequiredFlag(modelId))
{
CModelInfo::RemoveAssets(modelId);
numToDitch--;
}
}
}
}
//////////////////////////////////////////////////////////////////
// CLoadedModelGroup
void CLoadedModelGroup::Clear()
{
for (int C = 0; C < MAX_NUM_IN_LOADED_MODEL_GROUP; C++)
{
aMembers[C] = fwModelId::MI_INVALID;
}
}
bool CLoadedModelGroup::AddMember(u32 NewMember)
{
if(IsMemberInGroup(NewMember))
{
return false;
}
for (int C = 0; C < MAX_NUM_IN_LOADED_MODEL_GROUP; C++)
{
if (!CModelInfo::IsValidModelInfo(aMembers[C]))
{
aMembers[C] = NewMember;
return true;
}
}
Assert(0); // List is full?
return false;
}
bool CLoadedModelGroup::RemoveMember(u32 OldMember)
{
for (int C = 0; C < MAX_NUM_IN_LOADED_MODEL_GROUP; C++)
{
if (aMembers[C] == OldMember)
{
// Found the one to remove. Now shift the rest up a bit.
for (int K = C; K < MAX_NUM_IN_LOADED_MODEL_GROUP-1; K++)
{
aMembers[K] = aMembers[K + 1];
}
aMembers[MAX_NUM_IN_LOADED_MODEL_GROUP-1] = fwModelId::MI_INVALID;
return true;
}
}
return false;
}
u32 CLoadedModelGroup::GetMember(u32 Member) const
{
//Assert(aMembers[Member] >= 0);
return(aMembers[Member]);
}
int CLoadedModelGroup::CountMembers() const
{
int Result = 0;
for (int C = 0; C < MAX_NUM_IN_LOADED_MODEL_GROUP; C++)
{
if (CModelInfo::IsValidModelInfo(aMembers[C]))
{
Result++;
}
else if (!CModelInfo::IsValidModelInfo(aMembers[C]))
{
return Result;
}
}
return Result;
}
bool CLoadedModelGroup::IsEmpty()
{
return !CModelInfo::IsValidModelInfo(aMembers[0]);
}
int CLoadedModelGroup::CountUsableOnes()
{
int Result = 0;
for (int C = 0; C < MAX_NUM_IN_LOADED_MODEL_GROUP; C++)
{
// Only consider models that aren't about to be streamed out.
fwModelId modelId((strLocalIndex(aMembers[C])));
if (modelId.IsValid() && CModelInfo::GetAssetRequiredFlag(modelId))
{
Result++;
}
else if (!(modelId.IsValid()))
{
return Result;
}
}
return Result;
}
bool CLoadedModelGroup::IsMemberInGroup(u32 ModelIndex) const
{
for (int C = 0; C < MAX_NUM_IN_LOADED_MODEL_GROUP; C++)
{
if (aMembers[C] == ModelIndex)
{
return true;
}
}
return false;
}
bool CLoadedModelGroup::IsMaleInPedGroup()
{
for (int C = 0; C < MAX_NUM_IN_LOADED_MODEL_GROUP; C++)
{
if (aMembers[C] == fwModelId::MI_INVALID)
{
return false;
}
if ( ((CPedModelInfo *)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(aMembers[C]))))->IsMale() )
{
return true;
}
}
return false;
}
bool CLoadedModelGroup::IsFemaleInPedGroup()
{
for (int C = 0; C < MAX_NUM_IN_LOADED_MODEL_GROUP; C++)
{
if (aMembers[C] == fwModelId::MI_INVALID)
{
return false;
}
if ( !((CPedModelInfo *)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(aMembers[C]))))->IsMale() )
{
return true;
}
}
return false;
}
bool CLoadedModelGroup::IsBikeDriverInPedGroup()
{
for (int C = 0; C < MAX_NUM_IN_LOADED_MODEL_GROUP; C++)
{
if (aMembers[C] == fwModelId::MI_INVALID)
{
return false;
}
if (gPopStreaming.CanPedModelRideBike(aMembers[C]))
{
return true;
}
}
return false;
}
bool CLoadedModelGroup::IsBikeInVehGroup()
{
for (int C = 0; C < MAX_NUM_IN_LOADED_MODEL_GROUP; C++)
{
if (aMembers[C] == fwModelId::MI_INVALID)
{
return false;
}
CVehicleModelInfo* vmi = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(aMembers[C])));
if (vmi->GetIsBike())
{
return true;
}
}
return false;
}
strLocalIndex CLoadedModelGroup::PickRandomCarModel(bool cargens)
{
int Num = CountMembers();
if (Num == 0)
return strLocalIndex(fwModelId::MI_INVALID);
int C;
u32 aApplicableMembers[MAX_NUM_IN_LOADED_MODEL_GROUP];
float aScaledOccurence[MAX_NUM_IN_LOADED_MODEL_GROUP];
int NumberOfApplicableCars = 0;
for (C = 0; C < Num; C++)
{
u32 ModelIndex = aMembers[C];
fwModelId modelId((strLocalIndex(ModelIndex)));
Assert(((CVehicleModelInfo *)CModelInfo::GetBaseModelInfo(modelId))->GetVehicleFreq() > 0);
// Only consider models that aren't about to be streamed out.
if (CModelInfo::GetAssetRequiredFlag(modelId))
{
if (!CScriptCars::GetSuppressedCarModels().HasModelBeenSuppressed(ModelIndex)) // Only consider cars that haven't been suppressed by the script.
{
aApplicableMembers[NumberOfApplicableCars] = ModelIndex;
CVehicleModelInfo* pVMI = (CVehicleModelInfo*)(CModelInfo::GetBaseModelInfo(modelId));
s32 refs = cargens ? pVMI->GetNumVehicleModelRefsParked() : pVMI->GetNumVehicleModelRefs();
aScaledOccurence[NumberOfApplicableCars] = float(refs) / pVMI->GetVehicleFreq();
NumberOfApplicableCars++;
}
}
}
// Now pick the vehicle that is used least (relative to its frequency)
u32 smallestMI = fwModelId::MI_INVALID;
float smallestVal = 999999.9f;
for (int i = 0; i < NumberOfApplicableCars; i++)
{
if (aScaledOccurence[i] < smallestVal)
{
smallestMI = aApplicableMembers[i];
smallestVal = aScaledOccurence[i];
}
}
return strLocalIndex(smallestMI);
}
u32 CLoadedModelGroup::PickRandomPedModel()
{
int Num = CountMembers();
if (Num == 0)
return fwModelId::MI_INVALID;
int C;
u32 aApplicableMembers[MAX_NUM_IN_LOADED_MODEL_GROUP];
u32 aOccurence[MAX_NUM_IN_LOADED_MODEL_GROUP];
int NumberOfApplicablePeds = 0;
for (C = 0; C < Num; C++)
{
u32 ModelIndex = aMembers[C];
// Only consider models that aren't about to be streamed out.
fwModelId modelId((strLocalIndex(ModelIndex)));
if (CModelInfo::GetAssetRequiredFlag(modelId) && !CScriptPeds::HasPedModelBeenRestrictedOrSuppressed(ModelIndex))
{
aApplicableMembers[NumberOfApplicablePeds] = ModelIndex;
CPedModelInfo* pMI = (CPedModelInfo*)(CModelInfo::GetBaseModelInfo(modelId));
aOccurence[NumberOfApplicablePeds] = pMI->GetNumPedModelRefs();
NumberOfApplicablePeds++;
}
}
// Now pick the ped that is used least
u32 smallestMI = fwModelId::MI_INVALID;
u32 smallestVal = 999999;
for (int i = 0; i < NumberOfApplicablePeds; i++)
{
if (aOccurence[i] < smallestVal)
{
smallestMI = aApplicableMembers[i];
smallestVal = aOccurence[i];
}
}
return smallestMI;
}
void CLoadedModelGroup::SortBasedOnUsage()
{
int Num = CountMembers();
bool bChangeMade = true;
while (bChangeMade)
{
bChangeMade = false;
for (int C = 0; C < Num-1; C++)
{
if ( ((CVehicleModelInfo *)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(aMembers[C]))))->GetNumTimesUsed() <
((CVehicleModelInfo *)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(aMembers[C+1]))))->GetNumTimesUsed() )
{
u32 Temp = aMembers[C];
aMembers[C] = aMembers[C+1];
aMembers[C+1] = Temp;
bChangeMade = true;
}
}
}
}
u32 CLoadedModelGroup::PickLeastUsedModel(int MaxOfThem)
{
int Num = CountMembers();
u32 ModelIndex = fwModelId::MI_INVALID;
int BestRefs = 999;
int BestUsage = 999;
for (int C = 0; C < Num; C++)
{
// Pick this one if is used less than the previous best or if it is
// used equally but hasn't been used as much in the past.
CVehicleModelInfo* pVMI = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(aMembers[C])));
if ( pVMI->GetNumVehicleModelRefs() < BestRefs ||
(pVMI->GetNumVehicleModelRefs() == BestRefs &&
pVMI->GetNumTimesUsed() < BestUsage))
{
if (!CScriptCars::GetSuppressedCarModels().HasModelBeenSuppressed(aMembers[C])) // Only consider cars that haven't been suppressed by the script.
{
ModelIndex = aMembers[C];
BestRefs = ((CVehicleModelInfo *)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex))))->GetNumVehicleModelRefs();
BestUsage = ((CVehicleModelInfo *)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex))))->GetNumTimesUsed();
}
}
}
if (BestRefs <= MaxOfThem)
{
return ModelIndex;
}
return fwModelId::MI_INVALID;
}
u32 CLoadedModelGroup::PickLeastUsedPedModel()
{
int Num = CountMembers();
u32 ModelIndex = fwModelId::MI_INVALID;
int BestRefs = 999;
int BestUsage = 999;
for (int C = 0; C < Num; C++)
{
// Pick this one if is used less than the previous best or if it is
// used equally but hasn't been used as much in the past.
CPedModelInfo* pPMI = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(aMembers[C])));
if ( pPMI->GetNumPedModelRefs() < BestRefs ||
(pPMI->GetNumPedModelRefs() == BestRefs &&
pPMI->GetNumTimesUsed() < BestUsage))
{
if (!CScriptPeds::HasPedModelBeenRestrictedOrSuppressed(aMembers[C])) // Only consider peds that haven't been suppressed by the script.
{
ModelIndex = aMembers[C];
BestRefs = ((CPedModelInfo *)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex))))->GetNumPedModelRefs();
BestUsage = ((CPedModelInfo *)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(ModelIndex))))->GetNumTimesUsed();
}
}
}
return ModelIndex;
}
void CLoadedModelGroup::Merge(CLoadedModelGroup *pList1, CLoadedModelGroup *pList2, CLoadedModelGroup *pList3, CLoadedModelGroup *pList4)
{
Assert(pList1 != this && pList2 != this && pList3 != this);
this->Clear();
Assert(pList1 && pList2);
s32 numMembers = pList1->CountMembers();
for (int C = 0; C < numMembers; C++)
{
this->AddMember(pList1->GetMember(C));
}
numMembers = pList2->CountMembers();
for (int C = 0; C < numMembers; C++)
{
this->AddMember(pList2->GetMember(C));
}
if (pList3)
{
numMembers = pList3->CountMembers();
for (int C = 0; C < numMembers; C++)
{
this->AddMember(pList3->GetMember(C));
}
}
if (pList4)
{
numMembers = pList4->CountMembers();
for (int C = 0; C < numMembers; C++)
{
this->AddMember(pList4->GetMember(C));
}
}
}
void CLoadedModelGroup::Copy(CLoadedModelGroup *pSource)
{
Clear();
s32 numMembers = pSource->CountMembers();
for (int i = 0; i < numMembers; i++)
{
AddMember(pSource->GetMember(i));
}
}
void CLoadedModelGroup::CopyCarsSmallWorkers(CLoadedModelGroup *pSource, bool bSmallWorkers)
{
Clear();
s32 numMembers = pSource->CountMembers();
for (int i = 0; i < numMembers; i++)
{
u32 testModel = pSource->GetMember(i);
Assert(CModelInfo::IsValidModelInfo(testModel));
if (bSmallWorkers == ( ((CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(testModel))))->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_SMALL_WORKER) ) )
{
AddMember(testModel);
}
}
}
void CLoadedModelGroup::CopyEligibleCars(const Vector3 & vPos, CLoadedModelGroup* source, bool smallWorkers, bool removeNonNative, bool removeCopCars, bool removeOnlyOnHighway, bool removeNonOffroad, bool removeBigVehicles)
{
bool haveBikeRider = gPopStreaming.GetAppropriateLoadedPeds().IsBikeDriverInPedGroup();
u32 numMembers = 0;
for (s32 i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; ++i)
{
u32 next = source->GetMember(i);
if (!CModelInfo::IsValidModelInfo(next))
break; // reached end, safe to break
CVehicleModelInfo* vmi = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(next)));
Assert(vmi);
if(CVehiclePopulation::IsModelReservedForSpecificStreet(next, vPos))
continue;
if (smallWorkers != vmi->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_SMALL_WORKER))
continue;
if (removeNonNative && !gPopStreaming.DoesCarModelBelongInCurrentPopulationZone(next))
continue;
if (removeCopCars && CVehicle::IsLawEnforcementVehicleModelId(fwModelId(strLocalIndex(next))))
continue;
if (removeOnlyOnHighway && vmi->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_ONLY_ON_HIGHWAYS))
continue;
if (removeNonOffroad && !vmi->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_IS_OFFROAD_VEHICLE))
continue;
//not too happy about this hack--if this vehicle is bad at turning, only spawn it on roads with
//multiple lanes
if (removeBigVehicles && vmi->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_AVOID_TURNS))
continue;
// remove cars that have hit max number
if ((vmi->GetNumVehicleModelRefs() + vmi->GetNumVehicleModelRefsParked()) >= vmi->GetVehicleMaxNumber())
continue;
if (CScriptCars::GetSuppressedCarModels().HasModelBeenSuppressed(next))
continue;
if (!haveBikeRider && vmi->GetIsBike())
continue;
aMembers[numMembers++] = next;
}
// clear the rest of the entries
for (s32 i = numMembers; i < MAX_NUM_IN_LOADED_MODEL_GROUP; ++i)
aMembers[i] = fwModelId::MI_INVALID;
}
void CLoadedModelGroup::RemoveCarsFromGroup(s32 popGroup)
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (CModelInfo::IsValidModelInfo(mi) && CPopCycle::GetPopGroups().IsVehIndexMember(popGroup, mi))
{ // Remove this model from the list
RemoveIndex(i--);
}
}
}
void CLoadedModelGroup::RemoveModelsNotInGroup(s32 popGroup, bool peds)
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (!CModelInfo::IsValidModelInfo(mi))
continue;
bool isMember = peds ? CPopCycle::GetPopGroups().IsPedIndexMember(popGroup, mi) : CPopCycle::GetPopGroups().IsVehIndexMember(popGroup, mi);
if (!isMember)
{ // Remove this model from the list
RemoveIndex(i--);
}
}
}
void CLoadedModelGroup::RemoveModelsNotInModelSet(int modelSetType, int modelSetIndex)
{
// Get the model set from the manager.
const CAmbientModelSet& modelSet = CAmbientModelSetManager::GetInstance().GetModelSet((CAmbientModelSetManager::ModelSetType)modelSetType, modelSetIndex);
for(int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
const u32 mi = aMembers[i];
if(!CModelInfo::IsValidModelInfo(mi))
{
continue;
}
// Get the model info, so we can get to the model name hash.
const CBaseModelInfo* pModelInfo = CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi)));
if(!pModelInfo)
{
continue;
}
// Check if it's a member of the model set.
const bool isMember = modelSet.GetContainsModel(pModelInfo->GetModelNameHash());
if(!isMember)
{ // Remove this model from the list
RemoveIndex(i--);
}
}
}
void CLoadedModelGroup::RemoveOnlyOnHighwayCarsFromGroup()
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (CModelInfo::IsValidModelInfo(mi) && ((CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi))))->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_ONLY_ON_HIGHWAYS) )
{ // Remove this model from the list
RemoveIndex(i--);
}
}
}
void CLoadedModelGroup::RemoveNonOffroadCars()
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (CModelInfo::IsValidModelInfo(mi) && !((CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi))))->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_IS_OFFROAD_VEHICLE))
{ // Remove this model from the list
RemoveIndex(i--);
}
}
}
void CLoadedModelGroup::RemoveNonNativeCars()
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (CModelInfo::IsValidModelInfo(mi) && !gPopStreaming.DoesCarModelBelongInCurrentPopulationZone(mi))
{ // Remove this model from the list
RemoveIndex(i--);
}
}
}
void CLoadedModelGroup::RemoveBoats()
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (CModelInfo::IsValidModelInfo(mi) && ((CVehicleModelInfo *)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi))))->GetIsBoat())
{ // Remove this model from the list
RemoveIndex(i--);
}
}
}
void CLoadedModelGroup::RemoveTaxiVehs()
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if(CModelInfo::IsValidModelInfo(mi) && CVehicle::IsTaxiModelId(fwModelId(strLocalIndex(mi))))
{ // Remove this model from the list
RemoveIndex(i--);
}
}
}
void CLoadedModelGroup::RemoveCopVehs()
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if(CModelInfo::IsValidModelInfo(mi) && CVehicle::IsLawEnforcementVehicleModelId(fwModelId(strLocalIndex(mi))))
{ // Remove this model from the list
RemoveIndex(i--);
}
}
}
void CLoadedModelGroup::RemoveEmergencyServices()
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if ( CModelInfo::IsValidModelInfo(mi) && ((CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi))))->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_EMERGENCY_SERVICE))
{ // Remove this model from the list
RemoveIndex(i--);
}
}
}
void CLoadedModelGroup::RemoveBigVehicles()
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (CModelInfo::IsValidModelInfo(mi) && ((CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi))))->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_BIG) )
{
// Remove this model from the list
RemoveIndex(i--);
}
}
}
void CLoadedModelGroup::RemoveDiscardedVehicles()
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (CModelInfo::IsValidModelInfo(mi) && gPopStreaming.GetDiscardedCars().IsMemberInGroup(mi))
{
// Remove this model from the list
RemoveIndex(i--);
}
}
}
void CLoadedModelGroup::RemoveModelsThatHaveHitMaximumNumber()
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (!CModelInfo::IsValidModelInfo(mi))
continue;
CVehicleModelInfo* vmi = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi)));
if (((vmi->GetNumVehicleModelRefs() + vmi->GetNumVehicleModelRefsParked()) >= vmi->GetVehicleMaxNumber() || !vmi->HasAnyColorsToSpawn()))
{ // Remove this model from the list
RemoveIndex(i--);
}
}
RemoveRareModels();
}
void CLoadedModelGroup::RemoveRareModels()
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
// check for rare vehicles, maximum number is 1 for those
if (CModelInfo::IsValidModelInfo(mi))
{
CVehicleModelInfo* vmi = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi)));
if (vmi->GetNumVehicleModelRefs() + vmi->GetNumVehicleModelRefsParked() > 0)
{
for (s32 f = 0; f < CPopCycle::GetPopGroups().GetVehCount(); ++f)
{
if (CPopCycle::GetPopGroups().GetVehGroup(f).IsFlagSet(POPGROUP_RARE) && CPopCycle::GetPopGroups().IsVehIndexMember(f, mi))
{
// remove this model
RemoveIndex(i--);
break;
}
}
}
}
}
}
#define CUTOFF_DIST (10.0f)
void CLoadedModelGroup::RemoveModelsThatAreUsedNearby(const Vector3 *coors)
{
const Vec3V posV = VECTOR3_TO_VEC3V(*coors);
ScalarV radiusV(CUTOFF_DIST);
CSpatialArrayNode* results[64];
const int numFound = CPed::GetSpatialArray().FindInCylinderXY(posV.GetXY(), radiusV, results, NELEM(results));
for(int i = 0; i < numFound; i++)
{
CPed* pPed = CPed::GetPedFromSpatialArrayNode(results[i]);
RemoveMember(pPed->GetModelIndex());
}
// check for models that are queued to spawn in the ped population system
CLoadedModelGroup tempGroup;
tempGroup.Copy(this);
for(int i = 0; i < tempGroup.CountMembers(); i++)
{
u32 modelIndex = tempGroup.GetMember(i);
float scheduledDistSqr = CPedPopulation::GetDistanceSqrToClosestScheduledPedWithModelId(*coors, modelIndex);
if(scheduledDistSqr < (CUTOFF_DIST*CUTOFF_DIST))
{
RemoveMember(modelIndex);
}
}
}
void CLoadedModelGroup::RemoveNonMotorcyclePeds()
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (CModelInfo::IsValidModelInfo(mi))
{
if (!gPopStreaming.CanPedModelRideBike(mi) || CModelInfo::GetAssetsAreDeletable(fwModelId(strLocalIndex(mi))))
{ // Remove this model from the list
RemoveIndex(i--);
}
}
}
}
void CLoadedModelGroup::RemoveVehModelsWithFlagSet(CVehicleModelInfoFlags::Flags flag, bool flagValue)
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (CModelInfo::IsValidModelInfo(mi))
{
bool isFlagSet = ((CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi))))->GetVehicleFlag(flag);
if (isFlagSet == flagValue)
{ // Remove this model from the list
RemoveIndex(i--);
}
}
}
}
void CLoadedModelGroup::RemovePedModelsThatCantDriveCars(bool forDriver)
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (CModelInfo::IsValidModelInfo(mi))
{
CPedModelInfo *pModelInfo = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi)));
if (((!pModelInfo->GetPersonalitySettings().DrivesVehicleType(PED_DRIVES_POOR_CAR)) && (!pModelInfo->GetPersonalitySettings().DrivesVehicleType(PED_DRIVES_AVERAGE_CAR)) && (!pModelInfo->GetPersonalitySettings().DrivesVehicleType(PED_DRIVES_RICH_CAR)) &&
(!pModelInfo->GetPersonalitySettings().DrivesVehicleType(PED_DRIVES_BIG_CAR))) || CScriptPeds::HasPedModelBeenRestrictedOrSuppressed(mi) || pModelInfo->GetIsCop() || (!forDriver && !pModelInfo->GetCanSpawnInCar())
|| pModelInfo->GetOnlyBulkyItemVariations() // Peds with only bulky item variations can't be in cars [6/26/2013 mdawe]
)
{ // Remove this model from the list
RemoveIndex(i--);
}
}
}
}
void CLoadedModelGroup::RemoveSuppresedPeds()
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (CModelInfo::IsValidModelInfo(mi))
{
if (CScriptPeds::HasPedModelBeenRestrictedOrSuppressed(mi))
{ // Remove this model from the list
RemoveIndex(i--);
}
}
}
}
void CLoadedModelGroup::RemovePedsThatCantDriveCarType(ePedVehicleTypes carType)
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (CModelInfo::IsValidModelInfo(mi))
{
CPedModelInfo *pModelInfo = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi)));
if (!pModelInfo->GetPersonalitySettings().DrivesVehicleType(carType))
{ // Remove this model from the list
RemoveIndex(i--);
}
}
}
}
void CLoadedModelGroup::RemoveSuppressedAndNonAmbientCars()
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (CModelInfo::IsValidModelInfo(mi))
{
CVehicleModelInfo* vmi = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi)));
if (vmi->GetVehicleType() == VEHICLE_TYPE_BOAT)
continue;
// remove cars that shouldn't spawn ambiently on roads
if (CScriptCars::GetSuppressedCarModels().HasModelBeenSuppressed(mi) || vmi->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_DONT_SPAWN_AS_AMBIENT))
{
RemoveIndex(i--);
}
}
}
}
void CLoadedModelGroup::RemoveMaleOrFemale(bool male)
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (CModelInfo::IsValidModelInfo(mi))
{
CPedModelInfo* pmi = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi)));
// remove either all males or female, as specified by the parameter
if (male != pmi->IsMale())
{
RemoveIndex(i--);
}
}
}
}
void CLoadedModelGroup::RemoveNonPassengerPeds()
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (CModelInfo::IsValidModelInfo(mi))
{
CPedModelInfo* pmi = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi)));
if (!pmi->GetCanSpawnInCar())
{
RemoveIndex(i--);
}
}
}
}
void CLoadedModelGroup::RemoveBulkyItemPeds()
{
for (int i = 0; i < MAX_NUM_IN_LOADED_MODEL_GROUP; i++)
{
u32 mi = aMembers[i];
if (CModelInfo::IsValidModelInfo(mi))
{
CPedModelInfo* pmi = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(mi)));
if (pmi->GetOnlyBulkyItemVariations()) // Peds with only bulky item variations can't be in cars [6/26/2013 mdawe]
{
RemoveIndex(i--);
}
}
}
}
void CLoadedModelGroup::RemoveIndex(s32 index)
{
for (s32 i = index; i < MAX_NUM_IN_LOADED_MODEL_GROUP - 1; ++i)
{
aMembers[i] = aMembers[i + 1];
}
aMembers[MAX_NUM_IN_LOADED_MODEL_GROUP - 1] = fwModelId::MI_INVALID;
}
bool includeScheduledCargens = false;
const Vector3* comparePos = NULL;
int CompareCarDistance(const void* lhs, const void* rhs)
{
Assert(comparePos);
const u32 a = *(const u32*)lhs;
const u32 b = *(const u32*)rhs;
if (a == fwModelId::MI_INVALID)
return 1;
if (b == fwModelId::MI_INVALID)
return -1;
CVehicleModelInfo* aVmi = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(a)));
CVehicleModelInfo* bVmi = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(b)));
float aDist = aVmi ? aVmi->GetDistanceSqrToClosestInstance(*comparePos, includeScheduledCargens) : 0.f;
float bDist = bVmi ? bVmi->GetDistanceSqrToClosestInstance(*comparePos, includeScheduledCargens) : 0.f;
return (aDist > bDist) ? -1 : 1;
}
void PopulationStreamingDependencyCost(strLocalIndex assetIndex, s32 streamingModuleId, atArray<strIndex>& allDeps, u32& totalMain, u32& totalVram)
{
strIndex backingNewDepsStore[STREAMING_MAX_DEPENDENCIES*2];
atUserArray<strIndex> newDeps(backingNewDepsStore, STREAMING_MAX_DEPENDENCIES*2);
CStreaming::GetObjectAndDependencies(assetIndex, streamingModuleId, newDeps, NULL, 0);
for (s32 i = 0; i < newDeps.GetCount(); ++i)
{
if (allDeps.Find(newDeps[i]) == -1)
{
totalMain += strStreamingEngine::GetInfo().GetObjectVirtualSize(newDeps[i]);
totalVram += strStreamingEngine::GetInfo().GetObjectPhysicalSize(newDeps[i]);
if( Verifyf( allDeps.GetCapacity() > allDeps.GetCount(), "PopulationStreamingDependencyCost: Passed deps array has insufficient space. Skipping!" ) )
{
allDeps.Append() = newDeps[i];
}
}
}
}
u32 GetCarMemoryUse(const u32 carIdx)
{
strLocalIndex modelIndex = strLocalIndex(carIdx);
fwModelId modelId(modelIndex);
CVehicleModelInfo* modelInfo = (CVehicleModelInfo*)CModelInfo::GetBaseModelInfo(modelId);
strIndex backingDepStore[STREAMING_MAX_DEPENDENCIES*4];
atUserArray<strIndex> deps(backingDepStore, STREAMING_MAX_DEPENDENCIES*4);
strIndex backingHDDepsStore[STREAMING_MAX_DEPENDENCIES];
atUserArray<strIndex> hdDeps(backingHDDepsStore, STREAMING_MAX_DEPENDENCIES);
deps.ResetCount();
// find out HD status
u32 virtualSizeHd = 0;
u32 physicalSizeHd = 0;
if (modelInfo->GetAreHDFilesLoaded())
{
fwModelId modelId(modelIndex);
hdDeps.ResetCount();
CModelInfo::GetObjectAndDependencies(modelId, hdDeps, NULL, 0);
// if hd assets aren't dependencies, add them to the memory. some vehicles don't have hd assets so these
// indices are the regular txd/frag indices, don't want to count them twice
if (modelInfo->GetHDTxdIndex() != -1)
{
strIndex hdTxdIndex = g_TxdStore.GetStreamingIndex(strLocalIndex(modelInfo->GetHDTxdIndex()));
if (hdDeps.Find(hdTxdIndex) == -1)
{
deps.Append() = hdTxdIndex;
virtualSizeHd += CStreaming::GetObjectVirtualSize(strLocalIndex(modelInfo->GetHDTxdIndex()), g_TxdStore.GetStreamingModuleId());
physicalSizeHd += CStreaming::GetObjectPhysicalSize(strLocalIndex(modelInfo->GetHDTxdIndex()), g_TxdStore.GetStreamingModuleId());
}
}
if (modelInfo->GetHDFragmentIndex() != -1)
{
strIndex hdFragIndex = g_FragmentStore.GetStreamingIndex(strLocalIndex(modelInfo->GetHDFragmentIndex()));
if (hdDeps.Find(hdFragIndex) == -1)
{
deps.Append() = hdFragIndex;
virtualSizeHd += CStreaming::GetObjectVirtualSize(strLocalIndex(modelInfo->GetHDFragmentIndex()), g_FragmentStore.GetStreamingModuleId());
physicalSizeHd += CStreaming::GetObjectPhysicalSize(strLocalIndex(modelInfo->GetHDFragmentIndex()), g_FragmentStore.GetStreamingModuleId());
}
}
}
// get potential mods
u32 totalVirtualMods = 0;
u32 totalPhysicalMods = 0;
for (s32 f = 0; f < modelInfo->GetNumVehicleInstances(); ++f)
{
if (modelInfo->GetVehicleInstance(f))
{
CVehicleStreamRenderGfx* gfx = modelInfo->GetVehicleInstance(f)->GetVehicleDrawHandler().GetVariationInstance().GetVehicleRenderGfx();
if (gfx)
{
for (u8 g = 0; g < VMT_RENDERABLE + MAX_LINKED_MODS; ++g)
{
if (gfx->GetFragIndex(g) != -1)
PopulationStreamingDependencyCost(gfx->GetFragIndex(g), g_FragmentStore.GetStreamingModuleId(), deps, totalVirtualMods, totalPhysicalMods);
}
}
}
}
// Find size of the vehicle model
u32 virtualSize = 0;
u32 physicalSize = 0;
CModelInfo::GetObjectAndDependenciesSizes(modelId, virtualSize, physicalSize, CVehicleModelInfo::GetResidentObjects().GetElements(), CVehicleModelInfo::GetResidentObjects().GetCount(), true);
virtualSize += virtualSizeHd + totalVirtualMods;
physicalSize += physicalSizeHd + totalPhysicalMods;
return virtualSize + physicalSize;
}
u32 GetPedMemoryUse(const u32 pedIdx)
{
strLocalIndex modelIndex = strLocalIndex(pedIdx);
fwModelId modelId(modelIndex);
CPedModelInfo* modelInfo = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(modelId);
strIndex backingDepsStore[STREAMING_MAX_DEPENDENCIES*6];
atUserArray<strIndex> deps(backingDepsStore, STREAMING_MAX_DEPENDENCIES*6);
strIndex backingHDDepsStore[STREAMING_MAX_DEPENDENCIES];
atUserArray<strIndex> hdDeps(backingHDDepsStore, STREAMING_MAX_DEPENDENCIES);
deps.ResetCount();
// find out HD status
u32 virtualSizeHd = 0;
u32 physicalSizeHd = 0;
if (modelInfo->GetAreHDFilesLoaded())
{
hdDeps.ResetCount();
fwModelId modelId(modelIndex);
CModelInfo::GetObjectAndDependencies(modelId, hdDeps, NULL, 0);
if (modelInfo->GetHDTxdIndex() != -1)
{
strIndex hdTxdIndex = g_TxdStore.GetStreamingIndex(strLocalIndex(modelInfo->GetHDTxdIndex()));
// if hd assets aren't dependencies, add them to the memory. some peds don't have hd assets so these
// indices are the regular txd indices, don't want to count them twice
if (hdDeps.Find(hdTxdIndex) == -1)
{
deps.Append() = hdTxdIndex;
virtualSizeHd += CStreaming::GetObjectVirtualSize(strLocalIndex(modelInfo->GetHDTxdIndex()), g_TxdStore.GetStreamingModuleId());
physicalSizeHd += CStreaming::GetObjectPhysicalSize(strLocalIndex(modelInfo->GetHDTxdIndex()), g_TxdStore.GetStreamingModuleId());
}
}
}
// get streamed assets
u32 totalVirtualStreamed = 0;
u32 totalPhysicalStreamed = 0;
for (s32 f = 0; f < modelInfo->GetNumPedInstances(); ++f)
{
if (modelInfo->GetPedInstance(f))
{
CPedStreamRenderGfx* gfx = modelInfo->GetPedInstance(f)->GetPedDrawHandler().GetPedRenderGfx();
if (gfx)
{
for (s32 g = 0; g < PV_MAX_COMP; ++g)
{
// drawables
if (gfx->m_dwdIdx[g] != -1)
{
PopulationStreamingDependencyCost(strLocalIndex(gfx->m_dwdIdx[g]), g_DwdStore.GetStreamingModuleId(), deps, totalVirtualStreamed, totalPhysicalStreamed);
}
if (gfx->m_hdDwdIdx[g] != -1)
{
PopulationStreamingDependencyCost(strLocalIndex(gfx->m_hdDwdIdx[g]), g_DwdStore.GetStreamingModuleId(), deps, totalVirtualStreamed, totalPhysicalStreamed);
}
// textures
if (gfx->m_txdIdx[g] != -1)
{
PopulationStreamingDependencyCost(strLocalIndex(gfx->m_txdIdx[g]), g_TxdStore.GetStreamingModuleId(), deps, totalVirtualStreamed, totalPhysicalStreamed);
}
if (gfx->m_hdTxdIdx[g] != -1)
{
PopulationStreamingDependencyCost(strLocalIndex(gfx->m_hdTxdIdx[g]), g_TxdStore.GetStreamingModuleId(), deps, totalVirtualStreamed, totalPhysicalStreamed);
}
// cloth
if (gfx->m_cldIdx[g] != -1)
{
PopulationStreamingDependencyCost(strLocalIndex(gfx->m_cldIdx[g]), g_ClothStore.GetStreamingModuleId(), deps, totalVirtualStreamed, totalPhysicalStreamed);
}
// first person alternate drawables
if (gfx->m_fpAltIdx[g] != -1)
{
PopulationStreamingDependencyCost(strLocalIndex(gfx->m_fpAltIdx[g]), g_DwdStore.GetStreamingModuleId(), deps, totalVirtualStreamed, totalPhysicalStreamed);
}
}
// head blend data
for (u32 g = 0; g < HBS_MAX; ++g)
{
if (gfx->m_headBlendIdx[g] > -1)
{
PopulationStreamingDependencyCost(strLocalIndex(gfx->m_headBlendIdx[g]), (g < HBS_HEAD_DRAWABLES) ? g_DwdStore.GetStreamingModuleId() : (g < HBS_MICRO_MORPH_SLOTS ? g_TxdStore.GetStreamingModuleId() : g_DrawableStore.GetStreamingModuleId()), deps, totalVirtualStreamed, totalPhysicalStreamed);
}
}
}
}
}
// Find size of the ped model
u32 virtualSize = 0;
u32 physicalSize = 0;
CModelInfo::GetObjectAndDependenciesSizes(modelId, virtualSize, physicalSize, CVehicleModelInfo::GetResidentObjects().GetElements(), CVehicleModelInfo::GetResidentObjects().GetCount(), true);
virtualSize += virtualSizeHd + totalVirtualStreamed;
physicalSize += physicalSizeHd + totalPhysicalStreamed;
return virtualSize + physicalSize;
}
int CompareCarMemoryUse(const void* lhs, const void* rhs)
{
const u32 a = *(const u32*)lhs;
const u32 b = *(const u32*)rhs;
if (a == fwModelId::MI_INVALID)
return 1;
if (b == fwModelId::MI_INVALID)
return -1;
u32 aMem = GetCarMemoryUse(a);
u32 bMem = GetCarMemoryUse(b);
return (aMem > bMem) ? -1 : 1;
}
int CompareName(const void* lhs, const void* rhs)
{
const u32 a = *(const u32*)lhs;
const u32 b = *(const u32*)rhs;
if (a == fwModelId::MI_INVALID)
return 1;
if (b == fwModelId::MI_INVALID)
return -1;
strLocalIndex modelIndex_a = strLocalIndex(a);
strLocalIndex modelIndex_b = strLocalIndex(b);
fwModelId amID(modelIndex_a);
fwModelId bmID(modelIndex_b);
#if !__NO_OUTPUT
return strcmp(CModelInfo::GetBaseModelInfoName(amID), CModelInfo::GetBaseModelInfoName(bmID));
#else
return 0;
#endif
}
int ComparePedDistance(const void* lhs, const void* rhs)
{
Assert(comparePos);
const u32 a = *(const u32*)lhs;
const u32 b = *(const u32*)rhs;
if (a == fwModelId::MI_INVALID)
return 1;
if (b == fwModelId::MI_INVALID)
return -1;
CPedModelInfo* aPmi = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(a)));
CPedModelInfo* bPmi = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(b)));
float aDist = aPmi ? aPmi->GetDistanceSqrToClosestInstance(*comparePos) : 0.f;
float bDist = bPmi ? bPmi->GetDistanceSqrToClosestInstance(*comparePos) : 0.f;
return (aDist > bDist) ? -1 : 1;
}
int ComparePedMemoryUse(const void* lhs, const void* rhs)
{
const u32 a = *(const u32*)lhs;
const u32 b = *(const u32*)rhs;
if (a == fwModelId::MI_INVALID)
return 1;
if (b == fwModelId::MI_INVALID)
return -1;
u32 aMem = GetPedMemoryUse(a);
u32 bMem = GetPedMemoryUse(b);
return (aMem > bMem) ? -1 : 1;
}
void CLoadedModelGroup::SortCarsByDistance(const Vector3& pos, bool cargens)
{
comparePos = &pos;
includeScheduledCargens = cargens;
const u32 numMembers = CountMembers();
qsort(aMembers, numMembers, sizeof(u32), CompareCarDistance);
}
void CLoadedModelGroup::SortCarsByMemoryUse()
{
const u32 numMembers = CountMembers();
qsort(aMembers, numMembers, sizeof(u32), CompareCarMemoryUse);
}
void CLoadedModelGroup::SortCarsByName()
{
const u32 numMembers = CountMembers();
qsort(aMembers, numMembers, sizeof(u32), CompareName);
}
void CLoadedModelGroup::SortPedsByDistance(const Vector3& pos)
{
comparePos = &pos;
const u32 numMembers = CountMembers();
qsort(aMembers, numMembers, sizeof(u32), ComparePedDistance);
}
void CLoadedModelGroup::SortPedsByMemoryUse()
{
const u32 numMembers = CountMembers();
qsort(aMembers, numMembers, sizeof(u32), ComparePedMemoryUse);
}
void CLoadedModelGroup::SortPedsByName()
{
const u32 numMembers = CountMembers();
qsort(aMembers, numMembers, sizeof(u32), CompareName);
}
static void AppendModelDescription(char *description, unsigned length, u32 modelID)
{
#if !__FINAL
snprintf(description, length, "%s %s", description, CModelInfo::GetBaseModelInfoName(fwModelId(strLocalIndex(modelID))));
#else
snprintf(description, length, "%s %d", description, modelID);
#endif
}
bool CPopulationStreaming::IsCarDiscarded(u32 modelIndex)
{
s32 numCars = m_DiscardedCars.CountMembers();
for (s32 i = 0; i < numCars; ++i)
{
if (m_DiscardedCars.GetMember(i) == modelIndex)
return true;
}
return false;
}
void CPopulationStreaming::PrintDebugForPeds()
{
#if DEBUG_DRAW
const unsigned DEBUG_TEXT_LEN = 255;
char debugText[DEBUG_TEXT_LEN];
// display the car groups
const unsigned NUM_PED_GROUPS = 4;
const char *groupNames[] =
{
"Appropriate peds",
"InAppropriate peds",
"Special peds",
"Discarded peds"
};
const CLoadedModelGroup *groups[] =
{
&m_AppropriateLoadedPeds,
&m_InAppropriateLoadedPeds,
&m_SpecialLoadedPeds,
&m_DiscardedPeds,
};
for(unsigned group = 0; group < NUM_PED_GROUPS; group++)
{
snprintf(debugText, DEBUG_TEXT_LEN, "%s:", groupNames[group]);
unsigned numCars = groups[group]->CountMembers();
for(int index = 0; index < numCars; index++)
{
AppendModelDescription(debugText, DEBUG_TEXT_LEN, groups[group]->GetMember(index));
}
grcDebugDraw::AddDebugOutput(debugText);
}
if (NetworkInterface::IsGameInProgress())
{
snprintf(debugText, DEBUG_TEXT_LEN, "Network streamed peds:");
unsigned numRequiredPeds = m_PedModelsRequiredForNetwork.CountMembers();
for(unsigned index = 0; index < numRequiredPeds; index++)
{
u32 modelIndex = m_PedModelsRequiredForNetwork.GetMember(index);
if(CModelInfo::IsValidModelInfo(modelIndex))
{
AppendModelDescription(debugText, DEBUG_TEXT_LEN, modelIndex);
}
}
grcDebugDraw::AddDebugOutput(debugText);
}
// Print the suppressed and restricted ped models. (specific ped models can be suppressed by script)
CScriptPeds::GetSuppressedPedModels().DisplaySuppressedModels();
CScriptPeds::GetRestrictedPedModels().DisplayRestrictedModels();
if(NetworkInterface::IsGameInProgress())
{
u32 currentTimeInterval = GetCurrentNetworkTimeInterval();
u32 lowestRemoteOffset = currentTimeInterval;
const char *playerName = "None";
NetworkInterface::GetLowestPedModelStartOffset(lowestRemoteOffset, &playerName);
snprintf(debugText, DEBUG_TEXT_LEN, "Ped Model Offset(Current:%d Locally Allowed:%d Remotely Allowed:%d (Lowest remote player is %s))", currentTimeInterval, m_LocalAllowedPedModelStartOffset, lowestRemoteOffset, playerName);
grcDebugDraw::AddDebugOutput(debugText);
}
// Print some details regarding the peds in mem.
CLoadedModelGroup allLoadedPeds;
allLoadedPeds.Merge(&m_AppropriateLoadedPeds, &m_InAppropriateLoadedPeds, &m_SpecialLoadedPeds, &m_DiscardedPeds);
const unsigned numLoadedPeds = allLoadedPeds.CountMembers();
for (int index = 0; index < numLoadedPeds; index++)
{
u32 modelIndex = allLoadedPeds.GetMember(index);
fwModelId modelId((strLocalIndex(modelIndex)));
char infoString[128] = "";
if (!CModelInfo::GetAssetRequiredFlag(modelId))
{
snprintf(infoString, DEBUG_TEXT_LEN, "Can be removed by streaming");
}
if (CModelInfo::GetAssetStreamFlags(modelId) & STRFLAG_MISSION_REQUIRED)
{
snprintf(infoString, DEBUG_TEXT_LEN, "%s Required by script", infoString);
}
if (((CPedModelInfo *)CModelInfo::GetBaseModelInfo(modelId))->GetCountedAsScenarioPed())
{
snprintf(infoString, DEBUG_TEXT_LEN, "%s (Scenario)", infoString);
}
// Find size of the ped model
u32 virtualSize = 0;
u32 physicalSize = 0;
CModelInfo::GetObjectAndDependenciesSizes(modelId, virtualSize, physicalSize, m_residentObjects.GetElements(), m_residentObjects.GetCount(), true);
char pedDescription[128];
snprintf(pedDescription, 128, "%d", modelIndex);
AppendModelDescription(pedDescription, 128, modelIndex);
CPedModelInfo *pedModelInfo = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(modelId);
if(pedModelInfo)
{
snprintf(debugText, DEBUG_TEXT_LEN, "%s TimeStreamed:%d Refs:%d OnFoot:%d InCar:%d Used:%d Size:%d %s %s",
pedDescription,
fwTimer::GetTimeInMilliseconds() - GetTimeModelStreamedIn(modelIndex),
pedModelInfo->GetNumPedModelRefs(),
pedModelInfo->GetNumTimesOnFoot(),
pedModelInfo->GetNumTimesInCar(),
pedModelInfo->GetNumTimesUsed(),
virtualSize+physicalSize,
infoString,
pedModelInfo->m_isRequestedAsDriver ? "driver" : "");
if (m_DiscardedPeds.IsMemberInGroup(modelIndex))
{
grcDebugDraw::AddDebugOutput(Color32(200, 0, 0), debugText);
safecpy(debugText, "Instances: ");
char addr[16];
CPed::Pool* pool = CPed::GetPool();
for (s32 i = 0; i < pool->GetSize(); ++i)
{
CPed* ped = pool->GetSlot(i);
if (ped && ped->GetModelIndex() == modelIndex)
{
snprintf(addr, DEBUG_TEXT_LEN, " 0x%8p ", ped);
safecat(debugText, addr);
}
}
grcDebugDraw::AddDebugOutput(Color32(200, 0, 0), debugText);
}
else
{
grcDebugDraw::AddDebugOutput(debugText);
}
}
}
snprintf(debugText, DEBUG_TEXT_LEN, "Memory used by peds: %d / %d (over %d)", m_TotalMemoryUsedByPedModelsMain, GetMemForPeds(MEMTYPE_GAME_VIRTUAL, s_MemForPedsLevel), m_MemOverBudgetMainPeds + m_MemOverBudgetVramPeds);
grcDebugDraw::AddDebugOutput(debugText);
#if !MERGED_BUDGET
snprintf(debugText, DEBUG_TEXT_LEN, "Physical memory used by peds: %d / %d (over %d)", m_TotalMemoryUsedByPedModelsVram, GetMemForPeds(MEMTYPE_GAME_PHYSICAL, s_MemForPedsLevel), m_MemOverBudgetVramPeds);
grcDebugDraw::AddDebugOutput(debugText);
#endif // !MERGED_BUDGET
snprintf(debugText, DEBUG_TEXT_LEN, "Streamed memory used by peds: %d", m_TotalStreamedPedMemoryMain + m_TotalStreamedPedMemoryVram);
grcDebugDraw::AddDebugOutput(debugText);
#endif // DEBUG_DRAW
}
void CPopulationStreaming::InitialVehicleRequest(u32 numRequests)
{
NOTFINAL_ONLY(if(gbAllowVehGenerationOrRemoval))
{
for (u32 i = 0; i < numRequests; ++i)
{
if (IsThereAnyMemoryLeft(MTT_VEHICLE))
{
StreamOneNewCar();
}
}
}
}
void CPopulationStreaming::ResetVehMemoryBudgetLevel()
{
SetVehMemoryBudgetLevel(DEFAULT_MEM_FOR_VEHICLES_LEVEL);
}
void CPopulationStreaming::SetVehMemoryBudgetLevel(u32 level)
{
Assert(level < MAX_MEM_LEVELS);
if (level >= MAX_MEM_LEVELS)
return;
s_MemForVehiclesLevel = level;
CVehiclePopulation::ms_vehicleMemoryBudgetMultiplier = (float)level / (MAX_MEM_LEVELS - 1);
}
void CPopulationStreaming::ResetPedMemoryBudgetLevel()
{
SetPedMemoryBudgetLevel(DEFAULT_MEM_FOR_PEDS_LEVEL);
}
void CPopulationStreaming::SetPedMemoryBudgetLevel(u32 level)
{
Assert(level < MAX_MEM_LEVELS);
if (level >= MAX_MEM_LEVELS)
return;
s_MemForPedsLevel = level;
CPedPopulation::ms_pedMemoryBudgetMultiplier = (float)level / (MAX_MEM_LEVELS - 1);
}
bool CPopulationStreaming::IsVehicleInAllowedNetworkArray(u32 modelIndex) const
{
if (!NetworkInterface::IsGameInProgress() || modelIndex == fwModelId::MI_INVALID)
return true;
if(m_VehicleModelsRequiredForNetwork.IsMemberInGroup(modelIndex))
return true;
return false;
}
void CPopulationStreaming::FlushAllVehicleModelsHard()
{
// Disable defragging - we're about to unload some files.
#if USE_DEFRAGMENTATION
bool defragEnabled = strStreamingEngine::GetDefragmentation()->GetEnabled();
strStreamingEngine::GetDefragmentation()->FlushAndDisable();
#endif // USE_DEFRAGMENTATION
CRandomEventManager::GetInstance().FlushCopAsserts();
CStreaming::LoadAllRequestedObjects();
CDispatchManager::GetInstance().Flush();
CVehiclePopulation::RemoveAllVehsHard();
CVehicleFactory::GetFactory()->ClearDestroyedVehCache();
gRenderThreadInterface.Flush();
CObjectPopulation::ManageObjectPopulation(true, true); // parts that fell off a vehicle will be objects but use vehicle drawables, keeping references
gPopStreaming.RemoveStreamedCarModelsOnShutdown();
Assertf(!CVehicle::GetPool()->GetNoOfUsedSpaces(), "%d vehicle instances still present after a vehicle flush!", (int) CVehicle::GetPool()->GetNoOfUsedSpaces());
#if DEBUG_POPULATION_MEMORY
for (s32 i = 0; i < MAX_STREAMED_IN_MODELS; ++i)
{
if (gPopStreaming.m_StreamedInModelData[i].m_ModelIndex != fwModelId::MI_INVALID && CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(gPopStreaming.m_StreamedInModelData[i].m_ModelIndex))))
{
Assertf(CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(gPopStreaming.m_StreamedInModelData[i].m_ModelIndex)))->GetModelType() != MI_TYPE_VEHICLE, "CPopulationStreaming::FlushAllVehicleModelsHard: Non cleaned up model data for model '%s' found. M%dK V%dK HD M%dK V%dK", CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(gPopStreaming.m_StreamedInModelData[i].m_ModelIndex)))->GetModelName(), gPopStreaming.m_StreamedInModelData[i].m_mainUsed>>10, gPopStreaming.m_StreamedInModelData[i].m_vramUsed>>10, gPopStreaming.m_StreamedInModelData[i].m_HDAssetSizeMain>>10, gPopStreaming.m_StreamedInModelData[i].m_HDAssetSizeVram>>10);
}
}
for (s32 i = 0; i < gPopStreaming.m_vehicleComponents.GetCount(); ++i)
{
if (gPopStreaming.m_vehicleComponents[i].refCount != 0 && gPopStreaming.m_StreamedInModelData[i].m_ModelIndex != fwModelId::MI_INVALID)
{
Assertf(CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(gPopStreaming.m_StreamedInModelData[i].m_ModelIndex)))->GetModelType() != MI_TYPE_VEHICLE, "CPopulationStreaming::FlushAllVehicleModelsHard: Found vehicle component with ref count %d, streaming index: %d!", gPopStreaming.m_vehicleComponents[i].refCount, gPopStreaming.m_vehicleComponents[i].streamIdx.Get());
}
}
#endif // DEBUG_POPULATION_MEMORY
#if USE_DEFRAGMENTATION
if (defragEnabled)
strStreamingEngine::GetDefragmentation()->Enable();
#endif // USE_DEFRAGMENTATION
}
void CPopulationStreaming::FlushAllVehicleModelsSoft()
{
// CPlayerSwitchMgrLong::SetState() calls this. A switch could happen at any time during the game so it's not safe for us to delete any script vehicles or unload any script-requested models
CRandomEventManager::GetInstance().FlushCopAsserts();
CDispatchManager::GetInstance().Flush();
CVehiclePopulation::RemoveAllVehsSoft(); // we can't remove any script vehicles so call RemoveAllVehsSoft() instead of RemoveAllVehsHard()
CVehicleFactory::GetFactory()->ClearDestroyedVehCache();
gRenderThreadInterface.Flush();
CObjectPopulation::ManageObjectPopulation(true, true); // parts that fell off a vehicle will be objects but use vehicle drawables, keeping references
// gPopStreaming.RemoveStreamedCarModelsOnShutdown(); // This function unloads models that have been requested by script so it's not safe to call here
}
#if !__FINAL
//PURPOSE
// Gets a string name of the slot for debugging purposes.
const char* CPopulationStreaming::GetScenarioSlotName(eScenarioPopStreamingSlot eSlotType)
{
switch(eSlotType)
{
case SCENARIO_POP_STREAMING_NORMAL:
{
return "Normal Scenario Peds";
}
case SCENARIO_POP_STREAMING_SMALL:
{
return "Small Scenario Peds";
}
default:
{
return "Unknown Type Scenario Peds";
}
}
}
#endif
bool CPopulationStreaming::IsNetworkModelOffsetLower(u32 firstModelOffset, u32 secondModelOffset)
{
bool isLower = false;
// as the model offsets can wrap we need to take this into account when deciding whether which start
// offset is the lower. As the model offset changes relatively infrequently, we are considering the top
// quarter of the range to be less than the first quarter of the range. Other ranges are handled normally
const unsigned QUARTER_RANGE = MAX_NETWORK_MODEL_INTERVALS / 4;
if(firstModelOffset < secondModelOffset ||
((firstModelOffset > (MAX_NETWORK_MODEL_INTERVALS - QUARTER_RANGE)) && (secondModelOffset < QUARTER_RANGE)))
{
isLower = true;
}
return isLower;
}
void CPopulationStreaming::PrintCarGroups()
{
const unsigned DEBUG_TEXT_LEN = 255;
char debugText[DEBUG_TEXT_LEN];
// display the appropriate car groups, this
// needs special handling as we need to check whether
// the vehicles are appropriate for the current zone
unsigned numCars = m_AppropriateLoadedCars.CountMembers();
char validForZone[DEBUG_TEXT_LEN];
char invalidForZone[DEBUG_TEXT_LEN];
snprintf(validForZone, DEBUG_TEXT_LEN, "Appropriate cars (belonging in this zone):");
snprintf(invalidForZone, DEBUG_TEXT_LEN, "Appropriate cars (not belonging in this zone):");
for(int index = 0; index < numCars; index++)
{
int modelID = m_AppropriateLoadedCars.GetMember(index);
if(DoesCarModelBelongInCurrentPopulationZone(modelID))
{
AppendModelDescription(validForZone, DEBUG_TEXT_LEN, modelID);
}
else
{
AppendModelDescription(invalidForZone, DEBUG_TEXT_LEN, modelID);
}
}
#if DEBUG_DRAW
grcDebugDraw::AddDebugOutput(validForZone);
grcDebugDraw::AddDebugOutput(invalidForZone);
#endif
// display the other car groups
const unsigned NUM_CAR_GROUPS = 4;
const char *groupNames[] =
{
"Discarded cars",
"InAppropriate cars",
"Special cars",
"Boats"
};
const CLoadedModelGroup *groups[] =
{
&m_DiscardedCars,
&m_InAppropriateLoadedCars,
&m_SpecialLoadedCars,
&m_LoadedBoats
};
for(unsigned group = 0; group < NUM_CAR_GROUPS; group++)
{
snprintf(debugText, DEBUG_TEXT_LEN, "%s:", groupNames[group]);
unsigned numCars = groups[group]->CountMembers();
for(int index = 0; index < numCars; index++)
{
AppendModelDescription(debugText, DEBUG_TEXT_LEN, groups[group]->GetMember(index));
}
#if DEBUG_DRAW
grcDebugDraw::AddDebugOutput(debugText);
#endif
}
}
void CPopulationStreaming::PrintNetworkCarGroups()
{
const unsigned DEBUG_TEXT_LEN = 255;
char debugText[DEBUG_TEXT_LEN];
const unsigned NUM_CAR_GROUPS = 4;
const char *groupNames[] =
{
"Appropriate cars",
"InAppropriate cars",
"Discarded cars",
"Special cars",
"Boats"
};
const CLoadedModelGroup *groups[] =
{
&m_AppropriateLoadedCars,
&m_InAppropriateLoadedCars,
&m_DiscardedCars,
&m_SpecialLoadedCars,
&m_LoadedBoats
};
for(unsigned group = 0; group < NUM_CAR_GROUPS; group++)
{
snprintf(debugText, DEBUG_TEXT_LEN, "%s:", groupNames[group]);
unsigned numCars = groups[group]->CountMembers();
for(int index = 0; index < numCars; index++)
{
AppendModelDescription(debugText, DEBUG_TEXT_LEN, groups[group]->GetMember(index));
}
#if DEBUG_DRAW
grcDebugDraw::AddDebugOutput(debugText);
#endif
}
}
bool CPopulationStreaming::IsThereAnyMemoryLeft(eMemoryTrackType type)
{
switch (type)
{
case MTT_PED:
{
// keep a buffer at the end of the ped budget to account for half the scenario models needed in current zone
// plus the cost for one ped (the new ped to stream in)
int limit = (m_MaxScenarioPedModelsLoadedOverride >= 0 ? m_MaxScenarioPedModelsLoadedOverride : m_MaxScenarioPedModelsLoadedPerSlot);
limit = limit >> 1;
limit++;
const s32 pedBuffer = limit * BUFFER_MEM_FOR_PED_MODELS;
#if !MERGED_BUDGET
const s32 pedBufferVram = limit * BUFFER_MEM_FOR_PED_MODELS_VRAM;
#endif // !MERGED_BUDGET
return
#if !MERGED_BUDGET
(m_TotalMemoryUsedByPedModelsVram < (GetMemForPeds(MEMTYPE_GAME_PHYSICAL, s_MemForPedsLevel) - pedBufferVram)) &&
#endif // !MERGED_BUDGET
(m_TotalMemoryUsedByPedModelsMain < (GetMemForPeds(MEMTYPE_GAME_VIRTUAL, s_MemForPedsLevel) - pedBuffer));
}
break;
case MTT_VEHICLE:
case MTT_WEAPON:
{
return ((m_TotalMemoryUsedByVehicleModelsMain < (GetMemForVehicles(MEMTYPE_GAME_VIRTUAL, s_MemForVehiclesLevel) - BUFFER_MEM_FOR_VEHICLE_MODELS))
#if !MERGED_BUDGET
&& (m_TotalMemoryUsedByVehicleModelsVram < (GetMemForVehicles(MEMTYPE_GAME_PHYSICAL, s_MemForVehiclesLevel) - BUFFER_MEM_FOR_VEHICLE_MODELS_VRAM))
#endif
);
}
break;
default:
break;
}
return true;
}
bool CPopulationStreaming::AreWeOverBudget(eMemoryTrackType type)
{
switch (type)
{
case MTT_PED:
return (m_MemOverBudgetMainPeds + m_MemOverBudgetVramPeds) > 0;
case MTT_VEHICLE:
case MTT_WEAPON:
return (m_MemOverBudgetMainVehs + m_MemOverBudgetVramVehs) > 0;
default:
break;
}
return false;
}
#if __BANK
atArray<void*> s_allocs;
void CPopulationStreaming::AllocateVehicleAndPedResourceMem()
{
MEM_USE_USERDATA(MEMUSERDATA_POPULATION);
USE_MEMBUCKET(MEMBUCKET_DEFAULT);
strStreamingAllocator& allocator = strStreamingEngine::GetAllocator();
for (u32 uMemType = 0; uMemType < (MERGED_BUDGET ? 1 : 2); uMemType++)
{
eMemoryType eType = (uMemType == 0) ? MEMTYPE_GAME_VIRTUAL : MEMTYPE_GAME_PHYSICAL;
size_t taken = 0;
size_t memory = GetMemForPeds(eType, MAX_MEM_LEVELS-1) + GetMemForVehicles(eType, MAX_MEM_LEVELS-1);
memory -= (eType == MEMTYPE_GAME_VIRTUAL) ? m_TotalMemoryUsedByPedModelsMain + m_TotalMemoryUsedByVehicleModelsMain :
m_TotalMemoryUsedByPedModelsVram + m_TotalMemoryUsedByVehicleModelsVram;
size_t leafSize = (eType == MEMTYPE_GAME_VIRTUAL) ? g_rscVirtualLeafSize : g_rscPhysicalLeafSize;
size_t chunk = leafSize << 8;
while (memory > leafSize)
{
while (chunk > memory)
{
chunk >>= 1;
}
if (chunk < leafSize)
break;
void* alloc = allocator.TryAllocate(chunk, 128, eType);
if (alloc)
{
s_allocs.Grow() = alloc;
memory -= chunk;
taken += chunk;
}
else
{
chunk >>= 1;
}
}
// Displayf("Stolen MAIN %dKb + PEDS %dKb + VEHICLES %dKb = %dKb (remain %dKb)",
// taken>>10,m_TotalMemoryUsedByPedModelsMain>>10,m_TotalMemoryUsedByVehicleModelsMain>>10,
// (taken+m_TotalMemoryUsedByPedModelsMain+m_TotalMemoryUsedByVehicleModelsMain)>>10,
// memory>>10);
}
}
void CPopulationStreaming::FreeVehicleAndPedResourceMem()
{
MEM_USE_USERDATA(MEMUSERDATA_POPULATION);
strStreamingAllocator& allocator = strStreamingEngine::GetAllocator();
for (int i = 0; i < s_allocs.GetCount();++i)
{
allocator.Free(s_allocs[i]);
}
s_allocs.Reset();
}
#endif // __BANK
extern bool g_maponlyHogEnabled;
void CPopulationStreaming::AddTotalMemoryUsed(s32 main, s32 vram, eMemoryTrackType type)
{
#if __BANK
static int recursioncount = 0;
if (g_maponlyHogEnabled && (main != 0 || vram != 0) && recursioncount == 0)
{
++recursioncount;
FreeVehicleAndPedResourceMem();
}
#endif // BANK
switch (type)
{
case MTT_PED:
{
#if MERGED_BUDGET
m_TotalMemoryUsedByPedModelsMain += main + vram;
m_MemOverBudgetMainPeds = rage::Max(0, (s32)(m_TotalMemoryUsedByPedModelsMain - GetMemForPeds(MEMTYPE_GAME_VIRTUAL, s_MemForPedsLevel)));
#else
m_TotalMemoryUsedByPedModelsMain += main;
m_TotalMemoryUsedByPedModelsVram += vram;
m_MemOverBudgetMainPeds = rage::Max(0, (s32)(m_TotalMemoryUsedByPedModelsMain - GetMemForPeds(MEMTYPE_GAME_VIRTUAL, s_MemForPedsLevel)));
m_MemOverBudgetVramPeds = rage::Max(0, (s32)(m_TotalMemoryUsedByPedModelsVram - GetMemForPeds(MEMTYPE_GAME_PHYSICAL, s_MemForPedsLevel)));
#endif
}
break;
case MTT_WEAPON:
#if MERGED_BUDGET
m_TotalWeaponMemoryMain += main + vram;
#else
m_TotalWeaponMemoryMain += main;
m_TotalWeaponMemoryVram += vram;
#endif
// NOTE: no break; on purpose, we want weapons to count in the vehicle budget
case MTT_VEHICLE:
{
#if MERGED_BUDGET
m_TotalMemoryUsedByVehicleModelsMain += main + vram;
m_MemOverBudgetMainVehs = rage::Max(0, (s32)(m_TotalMemoryUsedByVehicleModelsMain - GetMemForVehicles(MEMTYPE_GAME_VIRTUAL, s_MemForVehiclesLevel)));
#else
m_TotalMemoryUsedByVehicleModelsMain += main;
m_TotalMemoryUsedByVehicleModelsVram += vram;
m_MemOverBudgetMainVehs = rage::Max(0, (s32)(m_TotalMemoryUsedByVehicleModelsMain - GetMemForVehicles(MEMTYPE_GAME_VIRTUAL, s_MemForVehiclesLevel)));
m_MemOverBudgetVramVehs = rage::Max(0, (s32)(m_TotalMemoryUsedByVehicleModelsVram - GetMemForVehicles(MEMTYPE_GAME_PHYSICAL, s_MemForVehiclesLevel)));
#endif
}
break;
default:
break;
}
#if __BANK
if (g_maponlyHogEnabled && (main != 0 || vram != 0) && recursioncount == 1)
{
AllocateVehicleAndPedResourceMem();
--recursioncount;
}
#endif // __BANK
}
bool CPopulationStreaming::IsAllowedToRequestScenarioPedModel(bool highPri, bool startupMode, eScenarioPopStreamingSlot eSlotNumber) const
{
// At least for now, since we've been running without a limit, it's probably
// safest to not respect the limit if a new model is needed for a high priority scenario.
if(!highPri)
{
// If we recently switched population zones, deny requests for new scenario models, to give some generic
// peds a chance to stream in first. Those are important for scenarios too.
if(!startupMode && fwTimer::GetTimeInMilliseconds() < m_ScenarioPedStreamingDisabledUntilTimeMS)
{
streamDebugf2("IsAllowedToRequestScenarioPedModel() returning false due to recent pop zone switch.");
return false;
}
// Get the limit, respecting script overrides.
const int limit = (m_MaxScenarioPedModelsLoadedOverride >= 0 ? m_MaxScenarioPedModelsLoadedOverride : m_MaxScenarioPedModelsLoadedPerSlot);
if(m_aNumScenarioPedModelsLoaded[eSlotNumber] >= limit)
{
return false;
}
}
return true;
}
void CPopulationStreaming::PrintDebugForVehicles()
{
#if DEBUG_DRAW
const unsigned DEBUG_TEXT_LEN = 255;
char debugText[DEBUG_TEXT_LEN];
#if MERGED_BUDGET
snprintf(debugText, DEBUG_TEXT_LEN, "Memory used by vehicles: %d / %d (over %d)", m_TotalMemoryUsedByVehicleModelsMain, GetMemForVehicles(MEMTYPE_GAME_VIRTUAL, s_MemForVehiclesLevel), m_MemOverBudgetMainVehs + m_MemOverBudgetVramVehs);
grcDebugDraw::AddDebugOutput(debugText);
snprintf(debugText, DEBUG_TEXT_LEN, "Streamed memory used by vehicles: %d", m_TotalStreamedVehicleMemoryMain + m_TotalStreamedVehicleMemoryVram);
grcDebugDraw::AddDebugOutput(debugText);
snprintf(debugText, DEBUG_TEXT_LEN, "Memory used by weapons: %d", m_TotalWeaponMemoryMain + m_TotalWeaponMemoryVram);
grcDebugDraw::AddDebugOutput(debugText);
#else
snprintf(debugText, DEBUG_TEXT_LEN, "Main memory used by vehicles: %d / %d (over %d)", m_TotalMemoryUsedByVehicleModelsMain, GetMemForVehicles(MEMTYPE_GAME_VIRTUAL, s_MemForVehiclesLevel), m_MemOverBudgetMainVehs);
grcDebugDraw::AddDebugOutput(debugText);
snprintf(debugText, DEBUG_TEXT_LEN, "Physical memory used by vehicles: %d / %d (over %d)", m_TotalMemoryUsedByVehicleModelsVram, GetMemForVehicles(MEMTYPE_GAME_PHYSICAL, s_MemForVehiclesLevel), m_MemOverBudgetVramVehs);
grcDebugDraw::AddDebugOutput(debugText);
snprintf(debugText, DEBUG_TEXT_LEN, "Streamed memory used by vehicles: m%d - v%d", m_TotalStreamedVehicleMemoryMain, m_TotalStreamedVehicleMemoryMain);
grcDebugDraw::AddDebugOutput(debugText);
snprintf(debugText, DEBUG_TEXT_LEN, "Memory used by weapons: m%d - v%d", m_TotalWeaponMemoryMain, m_TotalWeaponMemoryVram);
grcDebugDraw::AddDebugOutput(debugText);
#endif // MERGED_BUDGET
#endif // DEBUG_DRAW
// display the car groups
if(NetworkInterface::IsGameInProgress())
{
PrintNetworkCarGroups();
}
else
{
PrintCarGroups();
}
// Print the suppressed vehicles. (specific car models can be suppressed by script)
CScriptCars::GetSuppressedCarModels().DisplaySuppressedModels();
#if DEBUG_DRAW
if(NetworkInterface::IsGameInProgress())
{
u32 currentTimeInterval = GetCurrentNetworkTimeInterval();
u32 lowestRemoteOffset = currentTimeInterval;
const char *playerName = "None";
NetworkInterface::GetLowestVehicleModelStartOffset(lowestRemoteOffset, &playerName);
snprintf(debugText, DEBUG_TEXT_LEN, "Vehicle Model Offset(Current:%d Locally Allowed:%d Remotely Allowed:%d (Lowest remote player is %s))", currentTimeInterval, m_LocalAllowedVehicleModelStartOffset, lowestRemoteOffset, playerName);
grcDebugDraw::AddDebugOutput(debugText);
snprintf(debugText, DEBUG_TEXT_LEN, "Model Variation Set ID: %d", m_NetworkModelVariationID);
grcDebugDraw::AddDebugOutput(debugText);
}
// Print some details regarding the cars in mem.
CLoadedModelGroup cars;
cars.Merge(&m_AppropriateLoadedCars, &m_InAppropriateLoadedCars, &m_SpecialLoadedCars, &m_LoadedBoats);
CLoadedModelGroup allLoadedCars;
allLoadedCars.Merge(&cars, &m_DiscardedCars);
s32 numMembers = allLoadedCars.CountMembers();
for (int index = 0; index < numMembers; index++)
{
u32 modelIndex = allLoadedCars.GetMember(index);
fwModelId modelId((strLocalIndex(modelIndex)));
char infoString[128] = "";
if (!CModelInfo::GetAssetRequiredFlag(modelId))
{
snprintf(infoString, DEBUG_TEXT_LEN, "Can be removed by streaming");
}
if (CModelInfo::GetAssetStreamFlags(modelId) & STRFLAG_MISSION_REQUIRED)
{
snprintf(infoString, DEBUG_TEXT_LEN, "%s Required by script", infoString);
}
// Find size of the vehicle model
u32 virtualSize = 0;
u32 physicalSize = 0;
CModelInfo::GetObjectAndDependenciesSizes(modelId, virtualSize, physicalSize, m_residentObjects.GetElements(), m_residentObjects.GetCount(), true);
char modelDescription[128];
formatf(modelDescription, 128, "%d", modelIndex);
AppendModelDescription(modelDescription, 128, modelIndex);
CVehicleModelInfo* vmi = (CVehicleModelInfo *)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex)));
if (!vmi->m_data)
continue;
snprintf(debugText, DEBUG_TEXT_LEN, "%s%s TimeStreamed:%d Refs(parked):%d(%d) Used:%d %s Size:%d",
modelDescription,
vmi->GetAreHDFilesLoaded() ? "(HD)" : "",
fwTimer::GetTimeInMilliseconds() - GetTimeModelStreamedIn(modelIndex),
vmi->GetNumVehicleModelRefs(),
vmi->GetNumVehicleModelRefsParked(),
vmi->GetNumTimesUsed(),
infoString, virtualSize+physicalSize);
if (m_DiscardedCars.IsMemberInGroup(modelIndex))
{
grcDebugDraw::AddDebugOutput(Color32(200, 0, 0), debugText);
safecpy(debugText, "Instances: ");
char addr[16];
CVehicle::Pool* pool = CVehicle::GetPool();
for (s32 i = 0; i < pool->GetSize(); ++i)
{
CVehicle* veh = pool->GetSlot(i);
if (veh && veh->GetModelIndex() == modelIndex)
{
snprintf(addr, DEBUG_TEXT_LEN, " 0x%8p ", veh);
safecat(debugText, addr);
}
}
grcDebugDraw::AddDebugOutput(Color32(200, 0, 0), debugText);
}
else
{
grcDebugDraw::AddDebugOutput(debugText);
}
}
#endif // DEBUG_DRAW
}
#if __BANK
void CPopulationStreaming::DumpVehicleMemoryInfo()
{
CLoadedModelGroup allLoadedCars;
allLoadedCars.Merge(&m_AppropriateLoadedCars, &m_InAppropriateLoadedCars, &m_SpecialLoadedCars, &m_LoadedBoats);
u32 totalVirtual = 0;
u32 totalPhysical = 0;
atArray<strIndex> allDeps;
char buf[128] = {0};
strIndex backingStore[STREAMING_MAX_DEPENDENCIES];
atUserArray<strIndex> deps(backingStore, STREAMING_MAX_DEPENDENCIES);
Displayf("************** Loaded vehicle memory info **************\n");
s32 numMembers = allLoadedCars.CountMembers();
for (int index = 0; index < numMembers; index++)
{
u32 modelIndex = allLoadedCars.GetMember(index);
fwModelId modelId((strLocalIndex(modelIndex)));
// Find size of the vehicle model
u32 virtualSize = 0;
u32 physicalSize = 0;
CModelInfo::GetObjectAndDependenciesSizes(modelId, virtualSize, physicalSize, m_residentObjects.GetElements(), m_residentObjects.GetCount(), true);
Displayf("%s main: %d - phys: %d - total: %d\n", CModelInfo::GetBaseModelInfoName(modelId), virtualSize, physicalSize, virtualSize + physicalSize);
deps.ResetCount();
CModelInfo::GetObjectAndDependencies(modelId, deps, m_residentObjects.GetElements(), m_residentObjects.GetCount());
Displayf("*** %d dependencies:\n", deps.GetCount());
for (s32 i = 0; i < deps.GetCount(); ++i)
{
u32 virtualMemory = strStreamingEngine::GetInfo().GetObjectVirtualSize(deps[i]);
u32 physicalMemory = strStreamingEngine::GetInfo().GetObjectPhysicalSize(deps[i]);
const char* name = strStreamingEngine::GetInfo().GetObjectName(deps[i], buf, sizeof(buf));
bool dupe = false;
if (allDeps.Find(deps[i]) != -1)
dupe = true;
else
{
totalVirtual += virtualMemory;
totalPhysical += physicalMemory;
}
Displayf("*** %d (%s) - main: %d - phys: %d - total: %d %s\n", deps[i].Get(), name, virtualMemory, physicalMemory, virtualMemory + physicalMemory, dupe ? "(SHARED)" : "");
allDeps.PushAndGrow(deps[i]);
}
}
Displayf("%d resident assets:\n", CVehicleModelInfo::GetResidentObjects().GetCount());
for (int i = 0; i < CVehicleModelInfo::GetResidentObjects().GetCount(); i++)
{
strIndex index = CVehicleModelInfo::GetResidentObjects()[i];
u32 virtualSize=0, physicalSize=0;
strStreamingEngine::GetInfo().GetObjectAndDependenciesSizes(index, virtualSize, physicalSize);
const char* name = strStreamingEngine::GetInfo().GetObjectName(index, buf, sizeof(buf));
Displayf("*** %d (%s) - main: %d - phys: %d - total: %d\n", index.Get(), name, virtualSize, physicalSize, virtualSize + physicalSize);
totalVirtual += virtualSize;
totalPhysical += physicalSize;
}
Displayf("Total main: %d\n", totalVirtual);
Displayf("Total phys: %d\n", totalPhysical);
Displayf("********************************************************\n");
}
#endif // __BANK
bool CPopulationStreaming::AddPedModelToCorrectList( u32 modelIndex )
{
CPedModelInfo* pPMI = ((CPedModelInfo *)CModelInfo::GetBaseModelInfo(fwModelId(strLocalIndex(modelIndex))));
Assert(pPMI);
if (pPMI->GetIsCop())
{
m_SpecialLoadedPeds.AddMember(modelIndex);
return false;
}
else if (IsPedModelNeededCurrently(modelIndex))
{
m_AppropriateLoadedPeds.AddMember(modelIndex);
}
else if (IsPedModelNeededInAnyZone(modelIndex))
{
m_InAppropriateLoadedPeds.AddMember(modelIndex);
}
else
{
m_SpecialLoadedPeds.AddMember(modelIndex);
return false;
}
return true;
}
void CPopulationStreamingWrapper::Init(unsigned initMode)
{
gPopStreaming.Init(initMode);
}
void CPopulationStreamingWrapper::Shutdown(unsigned shutdownMode)
{
gPopStreaming.Shutdown(shutdownMode);
}
void CPopulationStreamingWrapper::Update()
{
gPopStreaming.Update();
}
#if __BANK
#include "debug/OverviewScreen.h"
float CPopulationStreaming::RenderPedMemoryUse(float x, float y)
{
char theText[256];
Color32 textColour = Color32(255, 255, 255, 255);
#if MERGED_BUDGET
sprintf(theText, "Memory used by peds: %d / %d (over %d)", m_TotalMemoryUsedByPedModelsMain, GetMemForPeds(MEMTYPE_GAME_VIRTUAL, s_MemForPedsLevel), m_MemOverBudgetMainPeds + m_MemOverBudgetVramPeds);
grcDebugDraw::Text(GET_TEXT_SCREEN_COORDS(x, y), textColour, theText, false);
y+=DEBUG_CHAR_HEIGHT;
sprintf(theText, "Streamed memory used by peds: %d", m_TotalStreamedPedMemoryMain + m_TotalStreamedPedMemoryVram);
grcDebugDraw::Text(GET_TEXT_SCREEN_COORDS(x, y), textColour, theText, false);
y+=DEBUG_CHAR_HEIGHT;
#else
sprintf(theText, "Main memory used by peds: %d / %d (over %d)", m_TotalMemoryUsedByPedModelsMain, GetMemForPeds(MEMTYPE_GAME_VIRTUAL, s_MemForPedsLevel), m_MemOverBudgetMainPeds);
grcDebugDraw::Text(GET_TEXT_SCREEN_COORDS(x, y), textColour, theText, false);
y+=DEBUG_CHAR_HEIGHT;
sprintf(theText, "Physical memory used by peds: %d / %d (over %d)", m_TotalMemoryUsedByPedModelsVram, GetMemForPeds(MEMTYPE_GAME_PHYSICAL, s_MemForPedsLevel), m_MemOverBudgetVramPeds);
grcDebugDraw::Text(GET_TEXT_SCREEN_COORDS(x, y), textColour, theText, false);
y+=DEBUG_CHAR_HEIGHT;
sprintf(theText, "Streamed memory used by peds: m%d - v%d", m_TotalStreamedPedMemoryMain, m_TotalStreamedPedMemoryVram);
grcDebugDraw::Text(GET_TEXT_SCREEN_COORDS(x, y), textColour, theText, false);
y+=DEBUG_CHAR_HEIGHT;
#endif // MERGED_BUDGET
return y;
}
float CPopulationStreaming::RenderVehicleMemoryUse(float x, float y)
{
char theText[256];
Color32 textColour = Color32(255, 255, 255, 255);
#if MERGED_BUDGET
sprintf(theText, "Memory used by vehicles: %d / %d (over %d)", m_TotalMemoryUsedByVehicleModelsMain, GetMemForVehicles(MEMTYPE_GAME_VIRTUAL, s_MemForVehiclesLevel), m_MemOverBudgetMainVehs + m_MemOverBudgetVramVehs);
grcDebugDraw::Text(GET_TEXT_SCREEN_COORDS(x, y), textColour, theText, false);
y+=DEBUG_CHAR_HEIGHT;
sprintf(theText, "Streamed memory used by vehicles: %d", m_TotalStreamedVehicleMemoryMain + m_TotalStreamedVehicleMemoryVram);
grcDebugDraw::Text(GET_TEXT_SCREEN_COORDS(x, y), textColour, theText, false);
y+=DEBUG_CHAR_HEIGHT;
#else
sprintf(theText, "Main memory used by vehicles: %d / %d (over %d)", m_TotalMemoryUsedByVehicleModelsMain, GetMemForVehicles(MEMTYPE_GAME_VIRTUAL, s_MemForVehiclesLevel), m_MemOverBudgetMainVehs);
grcDebugDraw::Text(GET_TEXT_SCREEN_COORDS(x, y), textColour, theText, false);
y+=DEBUG_CHAR_HEIGHT;
sprintf(theText, "Physical memory used by vehicles: %d / %d (over %d)", m_TotalMemoryUsedByVehicleModelsVram, GetMemForVehicles(MEMTYPE_GAME_PHYSICAL, s_MemForVehiclesLevel), m_MemOverBudgetVramVehs);
grcDebugDraw::Text(GET_TEXT_SCREEN_COORDS(x, y), textColour, theText, false);
y+=DEBUG_CHAR_HEIGHT;
sprintf(theText, "Streamed memory used by vehicles: m%d - v%d", m_TotalStreamedVehicleMemoryMain, m_TotalStreamedVehicleMemoryMain);
grcDebugDraw::Text(GET_TEXT_SCREEN_COORDS(x, y), textColour, theText, false);
y+=DEBUG_CHAR_HEIGHT;
sprintf(theText, "Memory used by weapons: m%d - v%d", m_TotalWeaponMemoryMain, m_TotalWeaponMemoryVram);
grcDebugDraw::Text(GET_TEXT_SCREEN_COORDS(x, y), textColour, theText, false);
y+=DEBUG_CHAR_HEIGHT;
#endif // MERGED_BUDGET
return y;
}
#endif // __BANK