/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "CryLegacy_precompiled.h" #include "WalkabilityCacheManager.h" #include "ObjectContainer.h" #include "DebugDrawContext.h" WalkabilityCacheManager::WalkabilityCacheManager() : m_walkabilityRequestCount(0) , m_walkabilityCacheHitCount(0) , m_floorRequestCount(0) , m_floorCacheHitCount(0) , m_preservedFloorCache(0) { } WalkabilityCacheManager::~WalkabilityCacheManager() { Reset(); } void WalkabilityCacheManager::Reset() { while (!m_caches.empty()) { EnableActor(m_caches.begin()->first, false); } m_alloc.FreeMemory(); m_walkabilityRequestCount = 0; m_walkabilityCacheHitCount = 0; m_floorRequestCount = 0; m_floorCacheHitCount = 0; m_preservedFloorCache = 0; } void WalkabilityCacheManager::PreUpdate() { m_currentFrameID = gEnv->pRenderer->GetFrameID(); m_walkabilityRequestCount = 0; m_walkabilityCacheHitCount = 0; m_floorRequestCount = 0; m_floorCacheHitCount = 0; m_preservedFloorCache = 0; } void WalkabilityCacheManager::PostUpdate() { } void WalkabilityCacheManager::Draw() { ActorWalkabilityCaches::iterator it = m_caches.begin(); ActorWalkabilityCaches::iterator end = m_caches.end(); size_t memoryUsage = 0; for (; it != end; ++it) { ActorWalkabilityCache& actorCache = it->second; actorCache.cache->Draw(); memoryUsage += actorCache.cache->GetMemoryUsage(); } memoryUsage += m_alloc.GetTotalMemory().nAlloc; const float startY = 380.0f; float x = 1024.0f - 10.0f - 175.0f; float y = startY; CDebugDrawContext dc; const float FontSize = 1.2f; const float LineHeight = 11.25f * FontSize; dc->Draw2dLabel(x, y, FontSize * 1.25f, Col_BlueViolet, false, "WalkabilityCheck Stats"); y += LineHeight * 1.25f; dc->Draw2dLabel(x, y, FontSize, Col_BlueViolet, false, "Actors: %" PRISIZE_T "", m_caches.size()); y += LineHeight; dc->Draw2dLabel(x, y, FontSize, Col_BlueViolet, false, "Memory: %.2fK", memoryUsage / 1024.0f); y += LineHeight; dc->Draw2dLabel(x, y, FontSize, Col_BlueViolet, false, "Requested: %" PRISIZE_T "", m_walkabilityRequestCount); y += LineHeight; dc->Draw2dLabel(x, y, FontSize, Col_BlueViolet, false, "Hit Cache: %" PRISIZE_T " (%.1f%%)", m_walkabilityCacheHitCount, m_walkabilityRequestCount ? (m_walkabilityCacheHitCount / (float)m_walkabilityRequestCount) * 100.0f : 0.0f); y += LineHeight; dc->Draw2dLabel(x, y, FontSize, Col_BlueViolet, false, "Preserved Floor Caches: %" PRISIZE_T "", m_preservedFloorCache); y += LineHeight; dc->Draw2dLabel(x, y, FontSize, Col_BlueViolet, false, "Floor Checks: %" PRISIZE_T "", m_floorRequestCount); y += LineHeight; dc->Draw2dLabel(x, y, FontSize, Col_BlueViolet, false, "Floor Hit Cache: %" PRISIZE_T " (%.1f%%)", m_floorCacheHitCount, m_floorRequestCount ? (m_floorCacheHitCount / (float)m_floorRequestCount) * 100.0f : 0.0f); y += LineHeight; } void WalkabilityCacheManager::EnableActor(tAIObjectID actorID, bool enabled) { if (!enabled) { ActorWalkabilityCaches::iterator it = m_caches.find(actorID); if (it != m_caches.end()) { ActorWalkabilityCache& actorCache = it->second; if (actorCache.cache) { actorCache.cache->~WalkabilityCache(); m_alloc.Deallocate(actorCache.cache); } m_caches.erase(it); } } } void WalkabilityCacheManager::PrepareActor(tAIObjectID actorID, const AABB& aabb) { ActorWalkabilityCaches::iterator it = m_caches.find(actorID); if (it == m_caches.end()) { std::pair<ActorWalkabilityCaches::iterator, bool> iresult = m_caches.insert(ActorWalkabilityCaches::value_type(actorID, ActorWalkabilityCache())); it = iresult.first; } if (it != m_caches.end()) { ActorWalkabilityCache& actorCache = it->second; if (!actorCache.cache) { actorCache.cache = new (m_alloc.Allocate())WalkabilityCache(actorID); } if (m_currentFrameID != actorCache.frameID) { IAIObject* actorObject = gAIEnv.pObjectContainer->GetAIObject(it->first); assert(actorObject); if (actorObject) { if (!actorCache.cache->Cache(aabb)) { ++m_preservedFloorCache; } } actorCache.frameID = m_currentFrameID; } } } bool WalkabilityCacheManager::IsFloorCached(tAIObjectID actorID, const Vec3& position, Vec3& floor) { ++m_floorRequestCount; if (!m_caches.empty()) { ActorWalkabilityCaches::iterator it = m_caches.find(actorID); ActorWalkabilityCaches::iterator end = m_caches.end(); if (it != end) { ActorWalkabilityCache& actorCache = it->second; if (actorCache.frameID == m_currentFrameID) { if (actorCache.cache->IsFloorCached(position, floor)) { ++m_floorCacheHitCount; return true; } } } // check other actors' aabbs it = m_caches.begin(); for (; it != end; ++it) { ActorWalkabilityCache& actorCache = it->second; if ((actorCache.frameID == m_currentFrameID) && (it->first != actorID)) { if (actorCache.cache->IsFloorCached(position, floor)) { ++m_floorCacheHitCount; return true; } } } } return false; } bool WalkabilityCacheManager::FindFloor(tAIObjectID actorID, const Vec3& position, Vec3& floor) { ++m_floorRequestCount; if (!m_caches.empty()) { ActorWalkabilityCaches::iterator it = m_caches.find(actorID); ActorWalkabilityCaches::iterator end = m_caches.end(); if (it != end) { ActorWalkabilityCache& actorCache = it->second; if (actorCache.frameID == m_currentFrameID) { if (actorCache.cache->IsFloorCached(position, floor)) { ++m_floorCacheHitCount; return floor.z < FLT_MAX; } } } // check other actors' aabbs it = m_caches.begin(); for (; it != end; ++it) { ActorWalkabilityCache& actorCache = it->second; if ((actorCache.frameID == m_currentFrameID) && (it->first != actorID)) { if (actorCache.cache->IsFloorCached(position, floor)) { ++m_floorCacheHitCount; return floor.z < FLT_MAX; } } } } // TODO: Keep track of the best containing cache and perform the floor search in there, so it's stored in the cache and // uses the already filtered physical entities return ::FindFloor(position, floor); } bool WalkabilityCacheManager::CheckWalkability(tAIObjectID actorID, const Vec3& origin, const Vec3& target, float radius, Vec3* finalFloor, bool* flatFloor) { ++m_walkabilityRequestCount; if (!m_caches.empty()) { float minZ = min(origin.z, target.z) - WalkabilityFloorDownDist; float maxZ = max(origin.z, target.z) + WalkabilityTotalHeight; AABB enclosingAABB(AABB::RESET); enclosingAABB.Add(Vec3(origin.x, origin.y, minZ), radius); enclosingAABB.Add(Vec3(target.x, target.y, maxZ), radius); ActorWalkabilityCaches::iterator it = m_caches.find(actorID); ActorWalkabilityCaches::iterator end = m_caches.end(); if (it != end) { ActorWalkabilityCache& actorCache = it->second; if (actorCache.frameID == m_currentFrameID) { if (actorCache.cache->FullyContaints(enclosingAABB)) { ++m_walkabilityCacheHitCount; return actorCache.cache->CheckWalkability(origin, target, radius, finalFloor, flatFloor); } } } // check other actors' aabbs it = m_caches.begin(); for (; it != end; ++it) { ActorWalkabilityCache& actorCache = it->second; if ((actorCache.frameID == m_currentFrameID) && (it->first != actorID)) { if (actorCache.cache->FullyContaints(enclosingAABB)) { ++m_walkabilityCacheHitCount; return actorCache.cache->CheckWalkability(origin, target, radius, finalFloor, flatFloor); } } } } // need to add floor flatness computation return ::CheckWalkability(origin, target, radius, finalFloor, flatFloor); } bool WalkabilityCacheManager::CheckWalkability(tAIObjectID actorID, const Vec3& origin, const Vec3& target, float radius, const ListPositions& boundary, Vec3* finalFloor, bool* flatFloor, const AABB* boundaryAABB) { if (Overlap::Lineseg_Polygon2D(Lineseg(origin, target), boundary, boundaryAABB)) { return false; } return CheckWalkability(actorID, origin, target, radius, finalFloor, flatFloor); }