You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Framework/AzFramework/AzFramework/Visibility/EntityVisibilityBoundsUnion...

180 lines
7.9 KiB
C++

/*
* 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.
*
*/
#include "EntityVisibilityBoundsUnionSystem.h"
#include <AzFramework/Visibility/BoundsBus.h>
#include <cstring>
namespace AzFramework
{
EntityVisibilityBoundsUnionSystem::EntityVisibilityBoundsUnionSystem()
: m_entityActivatedEventHandler([this](AZ::Entity* entity) { OnEntityActivated(entity); })
, m_entityDeactivatedEventHandler([this](AZ::Entity* entity) { OnEntityDeactivated(entity); })
{
;
}
void EntityVisibilityBoundsUnionSystem::Connect()
{
AZ::Interface<IEntityBoundsUnion>::Register(this);
IEntityBoundsUnionRequestBus::Handler::BusConnect();
AZ::TransformNotificationBus::Router::BusRouterConnect();
AZ::TickBus::Handler::BusConnect();
AZ::Interface<AZ::ComponentApplicationRequests>::Get()->RegisterEntityActivatedEventHandler(m_entityActivatedEventHandler);
AZ::Interface<AZ::ComponentApplicationRequests>::Get()->RegisterEntityDeactivatedEventHandler(m_entityDeactivatedEventHandler);
}
void EntityVisibilityBoundsUnionSystem::Disconnect()
{
AZ::TickBus::Handler::BusDisconnect();
IEntityBoundsUnionRequestBus::Handler::BusDisconnect();
AZ::TransformNotificationBus::Router::BusRouterDisconnect();
AZ::Interface<IEntityBoundsUnion>::Unregister(this);
}
void EntityVisibilityBoundsUnionSystem::OnEntityActivated(AZ::Entity* entity)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzFramework);
// ignore any entity that might activate which does not have a TransformComponent
if (entity->GetTransform() == nullptr)
{
return;
}
if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entity);
instance_it == m_entityVisibilityBoundsUnionInstanceMapping.end())
{
AZ::TransformInterface* transformInterface = entity->GetTransform();
const AZ::Vector3 entityPosition = transformInterface->GetWorldTranslation();
EntityVisibilityBoundsUnionInstance instance;
instance.m_localEntityBoundsUnion = CalculateEntityLocalBoundsUnion(entity);
instance.m_visibilityEntry.m_typeFlags = VisibilityEntry::TYPE_Entity;
instance.m_visibilityEntry.m_userData = static_cast<void*>(entity);
auto next_it = m_entityVisibilityBoundsUnionInstanceMapping.insert({ entity, instance });
UpdateVisibilitySystem(entity, next_it.first->second);
}
}
void EntityVisibilityBoundsUnionSystem::OnEntityDeactivated(AZ::Entity* entity)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzFramework);
// ignore any entity that might activate which does not have a TransformComponent
if (entity->GetTransform() == nullptr)
{
return;
}
if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entity);
instance_it != m_entityVisibilityBoundsUnionInstanceMapping.end())
{
if (IVisibilitySystem* visibilitySystem = AZ::Interface<IVisibilitySystem>::Get())
{
visibilitySystem->GetDefaultVisibilityScene()->RemoveEntry(instance_it->second.m_visibilityEntry);
m_entityVisibilityBoundsUnionInstanceMapping.erase(instance_it);
}
}
}
void EntityVisibilityBoundsUnionSystem::UpdateVisibilitySystem(AZ::Entity* entity, EntityVisibilityBoundsUnionInstance& instance)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzFramework);
if (const auto& localEntityBoundsUnions = instance.m_localEntityBoundsUnion; localEntityBoundsUnions.IsValid())
{
// note: worldEntityBounds will not be a 'tight-fit' Aabb but that of a transformed local aabb
// there will be some wasted space but it should be sufficient for the visibility system
AZ::TransformInterface* transformInterface = entity->GetTransform();
const AZ::Aabb worldEntityBoundsUnion = localEntityBoundsUnions.GetTransformedAabb(transformInterface->GetWorldTM());
IVisibilitySystem* visibilitySystem = AZ::Interface<IVisibilitySystem>::Get();
if (visibilitySystem && !worldEntityBoundsUnion.IsClose(instance.m_visibilityEntry.m_boundingVolume))
{
instance.m_visibilityEntry.m_boundingVolume = worldEntityBoundsUnion;
visibilitySystem->GetDefaultVisibilityScene()->InsertOrUpdateEntry(instance.m_visibilityEntry);
}
}
}
void EntityVisibilityBoundsUnionSystem::RefreshEntityLocalBoundsUnion(const AZ::EntityId entityId)
{
AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(entityId);
if (entity != nullptr)
{
// track entities that need their bounds union to be recalculated
m_entityBoundsDirty.insert(entity);
}
}
AZ::Aabb EntityVisibilityBoundsUnionSystem::GetEntityLocalBoundsUnion(const AZ::EntityId entityId) const
{
AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(entityId);
if (entity != nullptr)
{
// if the entity is not found in the mapping then return a null Aabb, this is to mimic
// as closely as possible the behavior of an individual GetLocalBounds call to an Entity that
// had been deleted (there would be no response, leaving the default value assigned)
if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entity);
instance_it != m_entityVisibilityBoundsUnionInstanceMapping.end())
{
return instance_it->second.m_localEntityBoundsUnion;
}
}
return AZ::Aabb::CreateNull();
}
void EntityVisibilityBoundsUnionSystem::ProcessEntityBoundsUnionRequests()
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzFramework);
// iterate over all entities whose bounds changed and recalculate them
for (const auto& entity : m_entityBoundsDirty)
{
if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entity);
instance_it != m_entityVisibilityBoundsUnionInstanceMapping.end())
{
instance_it->second.m_localEntityBoundsUnion = CalculateEntityLocalBoundsUnion(entity);
UpdateVisibilitySystem(entity, instance_it->second);
}
}
// clear dirty entities once the visibility system has been updated
m_entityBoundsDirty.clear();
}
void EntityVisibilityBoundsUnionSystem::OnTransformChanged(const AZ::Transform&, const AZ::Transform&)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzFramework);
const AZ::EntityId entityId = *AZ::TransformNotificationBus::GetCurrentBusId();
AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(entityId);
// update the world transform of the visibility bounds union
if (auto instance_it = m_entityVisibilityBoundsUnionInstanceMapping.find(entity);
instance_it != m_entityVisibilityBoundsUnionInstanceMapping.end())
{
UpdateVisibilitySystem(entity, instance_it->second);
}
}
void EntityVisibilityBoundsUnionSystem::OnTick(
[[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
{
ProcessEntityBoundsUnionRequests();
}
} // namespace AzFramework