// File header #include "ai/EntityScanner.h" // Rage headers #include "fwscene/world/WorldRepMulti.h" // Game headers #include "Peds/PedIntelligence.h" #include "Peds/Ped.h" #include "Physics/Physics.h" #include "Scene/World/GameWorld.h" AI_OPTIMISATIONS() // Profiling EXT_PF_TIMER(EntityScanner); // Defines #define SORT_AFTER 1 #define USE_POOLS 1 static dev_float OBJECT_SCAN_RADIUS = 10.0f; atQueue CObjectScanner::ms_ObjectScannerUpdateQueue; CObjectScanner* CObjectScanner::ms_ObjectScannerCurrentlySearching[OBJECT_SCANNER_NUM_CONCURRENT_SEARCHES] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; fwSearch CObjectScanner::ms_ObjectScannerSearch[OBJECT_SCANNER_NUM_CONCURRENT_SEARCHES] = { fwSearch(512), fwSearch(512), fwSearch(512), fwSearch(512), fwSearch(512), fwSearch(512), fwSearch(512), fwSearch(512), fwSearch(512), fwSearch(512), fwSearch(512), fwSearch(512), fwSearch(512), fwSearch(512), fwSearch(512), fwSearch(512) }; //////////////////////////////////////////////////////////////////////////////// CEntityScanner::CEntityScanner(ProcessType processType, s32 iScanPeriod) : CExpensiveProcess(processType) { m_Timer.SetPeriod(iScanPeriod); } //////////////////////////////////////////////////////////////////////////////// CEntityScanner::~CEntityScanner() { Clear(); } //////////////////////////////////////////////////////////////////////////////// void CEntityScanner::ScanForEntitiesInRange(CEntity& scanningEntity, bool bForceUpdate) { PF_PUSH_TIMEBAR_DETAIL("ScanForEntitiesInRange"); // Signal started StartProcess(); if(!bForceUpdate) { if(IsRegistered()) { if(!ShouldBeProcessedThisFrame()) { StopProcess(); PF_POP_TIMEBAR_DETAIL(); return; } } else if(!m_Timer.Tick()) { StopProcess(); PF_POP_TIMEBAR_DETAIL(); return; } } // Should we scan? if(ShouldPerformScan(scanningEntity)) { // Derived scan function EntityInfos entities; ScanForEntitiesInRangeImpl(scanningEntity, entities); // Sort the list SortAndStoreEntities(entities); } // Signal stopped StopProcess(); PF_POP_TIMEBAR_DETAIL(); } //////////////////////////////////////////////////////////////////////////////// void CEntityScanner::Clear() { int count = m_Entities.GetCount(); for(s32 i = 0; i < count; i++) { m_Entities[i] = NULL; } m_Entities.Reset(); } //////////////////////////////////////////////////////////////////////////////// #if DEBUG_DRAW void CEntityScanner::DebugRender(const Color32& col) const { Color32 debugCol(col); if(IsRegistered() && ShouldBeProcessedThisFrameSimple()) { debugCol = Color_white; } for(s32 i = 0; i < m_Entities.GetCount(); i++) { float fScale = (1.0f - ((float)i / (float)m_Entities.GetCount())); debugCol = Color32(debugCol.GetRedf() * fScale, debugCol.GetGreenf() * fScale, debugCol.GetBluef() * fScale); if(m_Entities[i]) { spdAABB tempBox; const spdAABB &box = m_Entities[i]->GetBoundBox(tempBox); Vec3V vExtent = box.GetExtent(); grcDebugDraw::BoxOriented(-vExtent, vExtent, m_Entities[i]->GetMatrix(), debugCol, false); } } } #endif // DEBUG_DRAW //////////////////////////////////////////////////////////////////////////////// bool CEntityScanner::ScanForEntitiesInRange_PerEntityCallback(CEntity* pEntity, void* pData) { sEntityScannerCallbackData* pEntityScannerCallbackData = reinterpret_cast(pData); EntityInfos& entities = pEntityScannerCallbackData->entities; // We don't want to include the scanning pEntity in the list of nearby peds if(pEntity == &pEntityScannerCallbackData->scanningEntity) { return true; } const Vec3V entityPos = pEntity->GetTransform().GetPosition(); const ScalarV dist2 = DistSquared(entityPos, pEntityScannerCallbackData->vCentre); const ScalarV zero(V_ZERO); const ScalarV searchRadius2 = pEntityScannerCallbackData->vRadiusSqr; if (IsGreaterThanAll(searchRadius2, zero) && IsGreaterThanAll(dist2, searchRadius2)) { return true; } float fDistSqr = dist2.Getf(); Assertf(((FloatToIntBitwise(fDistSqr) & 0x80000000) == 0) && ((FloatToIntBitwise(fDistSqr) & 0x7F800000) != 0x7F800000), "Bad float for entity distance!"); #if SORT_AFTER if(entities.IsFull()) { s32 iFurthestIndex = -1; f32 fFurthestDistSqr = 0.0f; for(s32 i = 0; i < entities.GetCount(); i++) { float entDistSqr = entities[i].fDistSqr; if(entDistSqr > fFurthestDistSqr && entDistSqr > fDistSqr) { iFurthestIndex = i; fFurthestDistSqr = entDistSqr; } } if(iFurthestIndex == -1) { // New entity is further away than any in our list return true; } // Delete furthest entity entities.DeleteFast(iFurthestIndex); } // Exclude procedural objects that are tiny const float fMinBoundRadiusToBeProcessed = 0.2f; const float fEntityBoundRadius = pEntity->GetBoundRadius(); if(pEntity->GetOwnedBy() != ENTITY_OWNEDBY_PROCEDURAL || fEntityBoundRadius > fMinBoundRadiusToBeProcessed) { entities.Push(sEntityInfo(pEntity, fDistSqr)); } #else // SORT_AFTER s32 i; for(i = 0; i < entities.GetCount(); i++) { if(fDistSqr < entities[i].fDistSqr) { if(entities.IsFull()) { // Array is full, remove the furthest away entity entities.Pop(); } sEntityInfo& entry = entities.Insert(i); entry.pEntity = pEntity; entry.fDistSqr = fDistSqr; break; } } if(i == entities.GetCount() && !entities.IsFull()) { // Our new entity must be further away than all the others, so add it to the back entities.Push(sEntityInfo(pEntity, fDistSqr)); } #endif //SORT_AFTER return true; } //////////////////////////////////////////////////////////////////////////////// bool CEntityScanner::ShouldPerformScan(CEntity& scanningEntity) const { if(scanningEntity.GetIsTypePed()) { CPed& ped = static_cast(scanningEntity); if(ped.IsDead()) { // Don't scan if dead return false; } } return true; } //////////////////////////////////////////////////////////////////////////////// float CEntityScanner::ComputeRadius(CEntity& scanningEntity) const { if(scanningEntity.GetIsTypePed()) { CPed& scanningPed = static_cast(scanningEntity); return rage::Max(scanningPed.GetPedIntelligence()->GetPedPerception().GetSeeingRange(), CPedPerception::ms_fSenseRange); } else if(scanningEntity.GetIsTypeVehicle()) { static dev_float VEHICLE_SCAN_RADIUS = 50.0f; static dev_float FAST_VEHICLE_SCAN_RADIUS = 90.0f; static dev_float FAST_VEHICLE_SPEED_THRESHOLD_SQR = 32.0f * 32.0f; const float fCurrentVelSqr = static_cast(scanningEntity).GetVelocity().Mag2(); return fCurrentVelSqr > FAST_VEHICLE_SPEED_THRESHOLD_SQR ? FAST_VEHICLE_SCAN_RADIUS : VEHICLE_SCAN_RADIUS; } else { return 0.0f; } } //////////////////////////////////////////////////////////////////////////////// void CEntityScanner::SortAndStoreEntities(EntityInfos& entities) { if(entities.GetCount() > 0) { int count = entities.GetCount(); #if SORT_AFTER std::sort(&entities[0], &entities[0] + count, sEntityInfo::LessThan); #endif // SORT_AFTER // Remove all references that are no longer active. int oldCount = m_Entities.GetCount(); for (int x=count; xIsEmpty()) { if (fRadius <= 0.0f) { fRadius = FLT_MAX; } int maxNumPeds = CPed::GetPool()->GetSize(); CSpatialArray::FindResult* results = Alloca(CSpatialArray::FindResult, maxNumPeds); int numFound = CPed::ms_spatialArray->FindInSphere(vScanningEntityPosition, fRadius, results, maxNumPeds); if (numFound > MAX_NUM_ENTITIES) { std::sort(results, results+numFound, CompareSpatialArrayResults); // Find our player's spatial array node and if the player is found outside of the closest MAX_NUM_ENTITIES // then replace the last (furthest) entity in the list with the player node CPed *pPlayer = CGameWorld::FindLocalPlayer(); if(pPlayer && pPlayer != &scanningEntity) { const CSpatialArrayNode& playerNode = pPlayer->GetSpatialArrayNode(); for(int i = MAX_NUM_ENTITIES; i < numFound; i++) { if(results[i].m_Node == &playerNode) { results[MAX_NUM_ENTITIES - 1] = results[i]; break; } } } numFound = MAX_NUM_ENTITIES; } entities.Reset(); for(int i = 0; i < numFound; i++) { CPed* veh = CPed::GetPedFromSpatialArrayNode(results[i].m_Node); if (veh != &scanningEntity) { sEntityInfo& info = entities.Append(); // We don't want to include the scanning pEntity in the list of nearby vehicles info.pEntity = veh; info.fDistSqr = results[i].m_DistanceSq; } } return; } // Create the callback data to be passed through to the callback function sPedScannerCallbackData callbackData(scanningEntity, entities, VEC3V_TO_VECTOR3(vScanningEntityPosition), rage::square(fRadius), bIgnoreDeadPeds); CPed::Pool* pool = CPed::GetPool(); s32 i = pool->GetSize(); while(i--) { CPed* pPed = pool->GetSlot(i); if(pPed) { ScanForEntitiesInRange_PerEntityCallback(pPed, (void*)&callbackData); } } #else // USE_POOLS phIterator iterator(phIterator::PHITERATORLOCKTYPE_READLOCK); iterator.InitCull_Sphere(scanningEntity.GetPosition(), fRadius); iterator.SetIncludeFlags(ArchetypeFlags::GTA_PED_TYPE); iterator.SetTypeFlags(ArchetypeFlags::GTA_AI_TEST); iterator.SetStateIncludeFlags(phLevelBase::STATE_FLAGS_ALL); u16 culledLevelIndex = CPhysics::GetLevel()->GetFirstCulledObject(iterator); while(culledLevelIndex != phInst::INVALID_INDEX) { phInst* pCulledInstance = CPhysics::GetLevel()->GetInstance(culledLevelIndex); aiAssert(pCulledInstance); CEntity* pCulledEntity = CPhysics::GetEntityFromInst(pCulledInstance); if( pCulledEntity ) { ScanForEntitiesInRange_PerEntityCallback(pCulledEntity, (void*)&callbackData); } culledLevelIndex = CPhysics::GetLevel()->GetNextCulledObject(iterator); } #endif // USE_POOLS } //////////////////////////////////////////////////////////////////////////////// bool CPedScanner::ScanForEntitiesInRange_PerEntityCallback(CEntity* pEntity, void* pData) { sPedScannerCallbackData* pPedScannerCallbackData = reinterpret_cast(pData); // Ignore dead peds if instructed too.. if(pPedScannerCallbackData->bIgnoreDeadPeds) { if(pEntity->GetIsTypePed() && static_cast(pEntity)->IsDead()) { return true; } } // Base class return CEntityScanner::ScanForEntitiesInRange_PerEntityCallback(pEntity, pData); } //////////////////////////////////////////////////////////////////////////////// CVehicleScanner::CVehicleScanner(ProcessType processType) : CEntityScanner(processType) , m_fTimeLastUpdated(-1.0f) { RegisterSlot(); } //////////////////////////////////////////////////////////////////////////////// CVehicleScanner::~CVehicleScanner() { } //////////////////////////////////////////////////////////////////////////////// bool CVehicleScanner::ShouldPerformScan(CEntity& scanningEntity) const { // Base Class if(!CEntityScanner::ShouldPerformScan(scanningEntity)) { return false; } if(scanningEntity.GetIsTypePed()) { CPed& ped = static_cast(scanningEntity); if(ped.GetVehiclePedInside() != NULL) { if(!ped.PopTypeIsMission()) { // Don't scan when in a vehicle return false; } } } return true; } //////////////////////////////////////////////////////////////////////////////// void CVehicleScanner::ScanForEntitiesInRangeImpl(CEntity& scanningEntity, EntityInfos& entities) { // Compute the radius for the scan float fRadius = ComputeRadius(scanningEntity); Vec3V vScanningEntityPosition = scanningEntity.GetTransform().GetPosition(); //Offset scan for velocity float fCurrentTime = ((float)fwTimer::GetGameTimer().GetTimeInMilliseconds() ) * 0.001f; if (m_fTimeLastUpdated >= 0.0f) { const CVehicle* scanningVehicle = 0; if(scanningEntity.GetIsTypeVehicle()) { scanningVehicle = static_cast(&scanningEntity); } else if (scanningEntity.GetIsTypePed()) { const CPed& ped = static_cast(scanningEntity); scanningVehicle = ped.GetVehiclePedInside(); } if(scanningVehicle) { ScalarV vLookAheadTime(Min(fCurrentTime - m_fTimeLastUpdated, 0.5f)); Vec3V vVelocity = RCC_VEC3V(scanningVehicle->GetVelocity()); vScanningEntityPosition += vVelocity * vLookAheadTime; } } m_fTimeLastUpdated = fCurrentTime; #if USE_POOLS // Do a faster search using the spatial array, if there is data in it if (!CVehicle::ms_spatialArray->IsEmpty()) { if (fRadius <= 0.0f) { fRadius = FLT_MAX; } int maxNumVehicles = (int) CVehicle::GetPool()->GetSize(); CSpatialArray::FindResult* results = Alloca(CSpatialArray::FindResult, maxNumVehicles); int numFound = CVehicle::ms_spatialArray->FindInSphere(vScanningEntityPosition, fRadius, results, maxNumVehicles); if (numFound > MAX_NUM_ENTITIES) { std::sort(results, results+numFound, CompareSpatialArrayResults); numFound = MAX_NUM_ENTITIES; } entities.Reset(); for(int i = 0; i < numFound; i++) { CVehicle* veh = CVehicle::GetVehicleFromSpatialArrayNode(results[i].m_Node); if (veh != &scanningEntity) { sEntityInfo& info = entities.Append(); // We don't want to include the scanning pEntity in the list of nearby vehicles info.pEntity = veh; info.fDistSqr = results[i].m_DistanceSq; #if __ASSERT // sanity check the position values Vec3V arrayPosV; CVehicle::GetSpatialArray().GetPosition(*results[i].m_Node, arrayPosV); Vec3V vehPos = veh->GetTransform().GetPosition(); Assertf(IsBetweenNegAndPosBounds(arrayPosV - vehPos, Vec3V(V_FLT_SMALL_6)), "Vehicle spatial array is out of date!"); #endif } } #if __ASSERT if(entities.GetCount() == 0) { // This stuff is basically a more informative assert so we can track down cases // where we scanned but didn't find the vehicle we are supposedly inside. if(scanningEntity.GetIsTypePed()) { const CPed& ped = static_cast(scanningEntity); const CVehicle* pInside = ped.GetVehiclePedInside(); if(pInside) { const Vec3V pedPosV = ped.GetTransform().GetPosition(); const Vec3V vehPosV = pInside->GetTransform().GetPosition(); const CSpatialArrayNode& node = pInside->GetSpatialArrayNode(); if(node.IsInserted()) { //Vec3V arrayPosV; //CVehicle::GetSpatialArray().GetPosition(node, arrayPosV); //Assertf(0, // "Scanning ped is inside a vehicle that's in the spatial array, but not in the right position. " // "Ped is at %.1f, %.1f, %.1f, vehicle is at %.1f, %.1f, %.1f, but its spatial array position is %.1f, %.1f, %.1f. " // "Ped model is %s, vehicle model is %s.", // pedPosV.GetXf(), pedPosV.GetYf(), pedPosV.GetZf(), // vehPosV.GetXf(), vehPosV.GetYf(), vehPosV.GetZf(), // arrayPosV.GetXf(), arrayPosV.GetYf(), arrayPosV.GetZf(), // ped.GetModelName(), pInside->GetModelName() // ); } else { Assertf(0, "Scanning ped is inside a vehicle that's not in the spatial array. " "Ped is at %.1f, %.1f, %.1f, vehicle is at %.1f, %.1f, %.1f. " "Ped model is %s, vehicle model is %s.", pedPosV.GetXf(), pedPosV.GetYf(), pedPosV.GetZf(), vehPosV.GetXf(), vehPosV.GetYf(), vehPosV.GetZf(), ped.GetModelName(), pInside->GetModelName() ); } } } } #endif // __ASSERT return; } // Sphere to search for entities inside const spdSphere scanningSphere(vScanningEntityPosition, ScalarV(fRadius)); ScalarV vSearchRadius2(square(fRadius)); if (fRadius <= 0.0f) { vSearchRadius2 = ScalarV(V_INF); } CVehicle::Pool* pool = CVehicle::GetPool(); s32 i = (s32) pool->GetSize(); while(i--) { CVehicle* pVehicle = pool->GetSlot(i); if(pVehicle) { // We don't want to include the scanning pEntity in the list of nearby vehicles if(pVehicle == &scanningEntity) { continue; } Vec3V entityPos = pVehicle->GetTransform().GetPosition(); ScalarV dist2 = DistSquared(entityPos, vScanningEntityPosition); if (IsGreaterThanAll(dist2, vSearchRadius2)) { continue; } float fDistSqr = dist2.Getf(); if(entities.IsFull()) { s32 iFurthestIndex = -1; f32 fFurthestDistSqr = 0.0f; int entCount = entities.GetCount(); for(s32 i = 0; i < entCount; i++) { float entDistSqr = entities[i].fDistSqr; if(entDistSqr > fFurthestDistSqr && entDistSqr > fDistSqr) { iFurthestIndex = i; fFurthestDistSqr = entDistSqr; } } if(iFurthestIndex == -1) { // New entity is further away than any in our list continue; } // Delete furthest entity entities.DeleteFast(iFurthestIndex); } // Add entities.Push(sEntityInfo(pVehicle, fDistSqr)); } } #else // USE_POOLS phIterator iterator(phIterator::PHITERATORLOCKTYPE_READLOCK); iterator.InitCull_Sphere(scanningEntity.GetPosition(), fRadius); iterator.SetIncludeFlags(ArchetypeFlags::GTA_VEHICLE_TYPE); iterator.SetTypeFlags(ArchetypeFlags::GTA_AI_TEST); iterator.SetStateIncludeFlags(phLevelBase::STATE_FLAGS_ALL); u16 culledLevelIndex = CPhysics::GetLevel()->GetFirstCulledObject(iterator); while(culledLevelIndex != phInst::INVALID_INDEX) { phInst* pCulledInstance = CPhysics::GetLevel()->GetInstance(culledLevelIndex); aiAssert(pCulledInstance); CEntity* pCulledEntity = CPhysics::GetEntityFromInst(pCulledInstance); if( pCulledEntity ) { ScanForEntitiesInRange_PerEntityCallback(pCulledEntity, (void*)&callbackData); } culledLevelIndex = CPhysics::GetLevel()->GetNextCulledObject(iterator); } #endif // USE_POOLS // If we have no entities nearby if(entities.GetCount() == 0) { if(scanningEntity.GetIsTypePed()) { CPed& scanningPed = static_cast(scanningEntity); CVehicle* pVehicle = scanningPed.GetVehiclePedInside(); if(pVehicle) { // Add our own vehicle entities.Push(sEntityInfo(pVehicle, 0.0f)); } } } } //////////////////////////////////////////////////////////////////////////////// dev_float CDoorScanner::ms_fScanningRadius = 10.0f; CDoorScanner::CDoorScanner(ProcessType processType) : CEntityScanner(processType ) { // Register scanner with expensive process RegisterSlot(); } //////////////////////////////////////////////////////////////////////////////// CDoorScanner::~CDoorScanner() { } //////////////////////////////////////////////////////////////////////////////// void CDoorScanner::ScanForEntitiesInRangeImpl(CEntity& scanningEntity, EntityInfos& entities) { // Sphere to search for entities inside const Vector3 vScanningEntityPosition = VEC3V_TO_VECTOR3( scanningEntity.GetTransform().GetPosition() ); // Create the callback data to be passed through to the callback function sDoorScannerCallbackData callbackData( scanningEntity, entities, vScanningEntityPosition, rage::square( ms_fScanningRadius ) ); #if ENABLE_PHYSICS_LOCK phIterator iterator( phIterator::PHITERATORLOCKTYPE_READLOCK ); #else // ENABLE_PHYSICS_LOCK phIterator iterator; #endif // ENABLE_PHYSICS_LOCK iterator.InitCull_Sphere( vScanningEntityPosition, ms_fScanningRadius ); iterator.SetIncludeFlags( ArchetypeFlags::GTA_OBJECT_TYPE ); iterator.SetTypeFlags( ArchetypeFlags::GTA_AI_TEST ); iterator.SetStateIncludeFlags( phLevelBase::STATE_FLAGS_ALL ); u16 culledLevelIndex = CPhysics::GetLevel()->GetFirstCulledObject(iterator); while(culledLevelIndex != phInst::INVALID_INDEX) { phInst* pCulledInstance = CPhysics::GetLevel()->GetInstance(culledLevelIndex); aiAssert(pCulledInstance); CEntity* pCulledEntity = CPhysics::GetEntityFromInst(pCulledInstance); if( pCulledEntity ) { ScanForEntitiesInRange_PerEntityCallback(pCulledEntity, (void*)&callbackData); } culledLevelIndex = CPhysics::GetLevel()->GetNextCulledObject(iterator); } } //////////////////////////////////////////////////////////////////////////////// bool CDoorScanner::ScanForEntitiesInRange_PerEntityCallback(CEntity* pEntity, void* pData) { aiAssert(pEntity); if( !((CObject*)pEntity)->IsADoor() ) { return true; } // Base class return CEntityScanner::ScanForEntitiesInRange_PerEntityCallback(pEntity, pData); } //////////////////////////////////////////////////////////////////////////////// CObjectScanner::CObjectScanner(ProcessType processType) : CEntityScanner(processType, 48) , m_fTimeLastUpdated(-1.0f) { if (processType == CExpensiveProcess::VPT_ObjectScanner) { RegisterSlot(); } } //////////////////////////////////////////////////////////////////////////////// CObjectScanner::~CObjectScanner() { this->RemoveObjectScannerFromUpdateQueue(); } void CObjectScanner::ProcessScanningEntityPosition(CEntity& scanningEntity, Vec3V_Ref vScanPositionInOut) { //Offset scan for velocity float fCurrentTime = ((float)fwTimer::GetGameTimer().GetTimeInMilliseconds() ) * 0.001f; if (m_fTimeLastUpdated >= 0.0f) { const CVehicle* scanningVehicle = 0; if(scanningEntity.GetIsTypeVehicle()) { scanningVehicle = static_cast(&scanningEntity); } else if (scanningEntity.GetIsTypePed()) { const CPed& ped = static_cast(scanningEntity); scanningVehicle = ped.GetVehiclePedInside(); } if(scanningVehicle) { ScalarV vLookAheadTime(Min(fCurrentTime - m_fTimeLastUpdated, 0.5f)); Vec3V vVelocity = RCC_VEC3V(scanningVehicle->GetVelocity()); vScanPositionInOut += vVelocity * vLookAheadTime; } } m_fTimeLastUpdated = fCurrentTime; } //////////////////////////////////////////////////////////////////////////////// void CObjectScanner::ScanForEntitiesInRangeImpl(CEntity& scanningEntity, EntityInfos& entities) { PF_AUTO_PUSH_TIMEBAR("CObjectScanner::ScanForEntitiesInRangeImpl"); Vec3V vScanningEntityPosition = scanningEntity.GetTransform().GetPosition(); ProcessScanningEntityPosition(scanningEntity, vScanningEntityPosition); // Sphere to search for entities inside const spdSphere scanningSphere(vScanningEntityPosition, ScalarV(OBJECT_SCAN_RADIUS)); // if (scanningEntity.GetIsTypeVehicle()) // { // grcDebugDraw::Sphere(vScanningEntityPosition, OBJECT_SCAN_RADIUS, Color_salmon, false, -1); // } // Create the callback data to be passed through to the callback function sEntityScannerCallbackData callbackData(scanningEntity, entities, VEC3V_TO_VECTOR3(vScanningEntityPosition), 0.0f); // Scan through all intersecting entities finding all entities before fwIsSphereIntersecting intersection(scanningSphere); CGameWorld::ForAllEntitiesIntersecting(&intersection, CObjectScanner::ScanForEntitiesInRange_PerEntityCallback, (void*)&callbackData, ENTITY_TYPE_MASK_OBJECT, SEARCH_LOCATION_INTERIORS|SEARCH_LOCATION_EXTERIORS, SEARCH_LODTYPE_HIGHDETAIL, SEARCH_OPTION_FORCE_PPU_CODEPATH|SEARCH_OPTION_SKIP_PROCEDURAL_ENTITIES, WORLDREP_SEARCHMODULE_PEDS); } void CObjectScanner::Clear() { // If someone is clearing the object list, we then assume then don't want // the async update to finish sometime later and populate it. this->RemoveObjectScannerFromUpdateQueue(); CEntityScanner::Clear(); } void CObjectScanner::RemoveObjectScannerFromUpdateQueue() { #if __ASSERT for(u32 i = 0; i < OBJECT_SCANNER_NUM_CONCURRENT_SEARCHES; ++i) { Assert(ms_ObjectScannerCurrentlySearching[i] != this); } #endif int index = -1; if(ms_ObjectScannerUpdateQueue.Find(this, &index)) { ms_ObjectScannerUpdateQueue.Delete(index); } } bool CObjectScanner::StartAsyncUpdate(fwSearch* pSearchToUse) { Assert(pSearchToUse); if(Unlikely(!m_LastScanningEntity)) { return false; } PF_PUSH_TIMEBAR("CObjectScanner::StartAsyncUpdate"); // create search volume Vec3V vScanningEntityPosition = m_LastScanningEntity->GetTransform().GetPosition(); ProcessScanningEntityPosition(*m_LastScanningEntity, vScanningEntityPosition); // if (m_LastScanningEntity->GetIsTypeVehicle()) // { // grcDebugDraw::Sphere(vScanningEntityPosition, OBJECT_SCAN_RADIUS, Color_salmon, false, -1); // } // Sphere to search for entities inside const spdSphere searchSphere(vScanningEntityPosition, ScalarV(OBJECT_SCAN_RADIUS)); fwIsSphereIntersecting searchVol(searchSphere); // start spu search pSearchToUse->Start( fwWorldRepMulti::GetSceneGraphRoot(), // root scene graph node &searchVol, // search volume ENTITY_TYPE_MASK_OBJECT, // entity types SEARCH_LOCATION_EXTERIORS | SEARCH_LOCATION_INTERIORS, // location flags SEARCH_LODTYPE_HIGHDETAIL, // lod flags SEARCH_OPTION_NONE, // option flags WORLDREP_SEARCHMODULE_PEDS, // module sysDependency::kPriorityCritical // priority ); PF_POP_TIMEBAR(); return true; } void CObjectScanner::EndAsyncUpdate(fwSearch* pSearchToUse) { PF_PUSH_TIMEBAR("CObjectScanner::EndAsyncUpdate"); Assert(pSearchToUse); pSearchToUse->Finalize(); Assert(m_LastScanningEntity); // Create the callback data to be passed through to the callback function EntityInfos entities; sEntityScannerCallbackData callbackData(*m_LastScanningEntity, entities, VEC3V_TO_VECTOR3(m_LastScanningEntity->GetTransform().GetPosition()), 0.0f); pSearchToUse->ExecuteCallbackOnResult(CObjectScanner::AsyncUpdateResultsCB, (void*) &callbackData); // Sort the list SortAndStoreEntities(entities); PF_POP_TIMEBAR(); } bool CObjectScanner::AsyncUpdateResultsCB(fwEntity* pEntity, void* pData) { return CObjectScanner::ScanForEntitiesInRange_PerEntityCallback((CEntity*) pEntity, pData); } void CObjectScanner::ScanImmediately() { if(m_LastScanningEntity) { // Derived scan function EntityInfos entities; ScanForEntitiesInRangeImpl(*m_LastScanningEntity, entities); // Sort the list SortAndStoreEntities(entities); } } #if __STATS PF_PAGE(ObjectScannerPage, "Object Scanner"); PF_GROUP(ObjectScannerGroup); PF_LINK(ObjectScannerPage, ObjectScannerGroup); PF_TIMER(ScanImmediately_Forced, ObjectScannerGroup); PF_TIMER(ScanImmediately_QueueFull, ObjectScannerGroup); PF_COUNTER(UpdateQueue_Queue, ObjectScannerGroup); PF_VALUE_INT(UpdateQueue_Size, ObjectScannerGroup); #endif // __STATS void CObjectScanner::ScanForEntitiesInRange(CEntity& scanningEntity, bool bForceUpdate) { // Signal started StartProcess(); if(!bForceUpdate) { if(IsRegistered()) { if(!ShouldBeProcessedThisFrame()) { StopProcess(); return; } } else if(!m_Timer.Tick()) { StopProcess(); return; } } // Should we scan? if(ShouldPerformScan(scanningEntity)) { m_LastScanningEntity = &scanningEntity; if(bForceUpdate) { PF_START(ScanImmediately_Forced); this->ScanImmediately(); PF_STOP(ScanImmediately_Forced); // Since we just did an immediate scan, find out if // it is already in the queue. If so, remove it. int index = -1; if(ms_ObjectScannerUpdateQueue.Find(this, &index)) { ms_ObjectScannerUpdateQueue.Delete(index); } } else if(!ms_ObjectScannerUpdateQueue.Find(this)) { PF_INCREMENT(UpdateQueue_Queue); PF_SET(UpdateQueue_Size, ms_ObjectScannerUpdateQueue.GetSize()); // If it is not already in the queue, then we need to update it. // If the queue is full, do an immediate scan/update. This should // be rare (when you respawn and lots of peds/vehicles get spawned // within a few frames, for instance). Otherwise, we push the // object scanner onto the queue to get updated asynchronously. if(ms_ObjectScannerUpdateQueue.IsFull()) { // If we're unable to keep up (requests carrying over from the previous frame), // consider increasing OBJECT_SCANNER_NUM_CONCURRENT_SEARCHES. // // If we're filling the entire queue in a single frame, investigate intention, // and, if necessary, increase OBJECT_SCANNER_QUEUE_SIZE. Warningf("Object scanner update queue full"); PF_START(ScanImmediately_QueueFull); this->ScanImmediately(); PF_STOP(ScanImmediately_QueueFull); } else { ms_ObjectScannerUpdateQueue.Push(this); } } } // Signal stopped StopProcess(); } void CObjectScanner::StartAsyncUpdateOfObjectScanners() { for(u32 i = 0; i < OBJECT_SCANNER_NUM_CONCURRENT_SEARCHES && ms_ObjectScannerUpdateQueue.GetCount() > 0; ++i) { ms_ObjectScannerCurrentlySearching[i] = NULL; while(ms_ObjectScannerUpdateQueue.GetCount() > 0) { CObjectScanner* pObjectScanner = ms_ObjectScannerUpdateQueue.Pop(); bool bStartedSearch = pObjectScanner->StartAsyncUpdate(&ms_ObjectScannerSearch[i]); if(bStartedSearch) { ms_ObjectScannerCurrentlySearching[i] = pObjectScanner; break; } } } } void CObjectScanner::EndAsyncUpdateOfObjectScanners() { for(u32 i = 0; i < OBJECT_SCANNER_NUM_CONCURRENT_SEARCHES; ++i) { CObjectScanner* pObjectScanner = ms_ObjectScannerCurrentlySearching[i]; ms_ObjectScannerCurrentlySearching[i] = NULL; if(pObjectScanner) { pObjectScanner->EndAsyncUpdate(&ms_ObjectScannerSearch[i]); } } }