Files
o3de/Code/Framework/AzFramework/AzFramework/Visibility/EntityVisibilityBoundsUnionSystem.cpp
T
Steve Pham 38261d0800 Shorten copyright headers by splitting into 2 lines (#2213)
* Updated all copyright headers to split the longer original copyright line into 2 shorter lines

Signed-off-by: Steve Pham <spham@amazon.com>
2021-07-16 15:25:48 -07:00

174 lines
7.4 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#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::TickBus::Handler::BusConnect();
AZ::Interface<AZ::ComponentApplicationRequests>::Get()->RegisterEntityActivatedEventHandler(m_entityActivatedEventHandler);
AZ::Interface<AZ::ComponentApplicationRequests>::Get()->RegisterEntityDeactivatedEventHandler(m_entityDeactivatedEventHandler);
}
void EntityVisibilityBoundsUnionSystem::Disconnect()
{
m_entityActivatedEventHandler.Disconnect();
m_entityDeactivatedEventHandler.Disconnect();
AZ::TickBus::Handler::BusDisconnect();
IEntityBoundsUnionRequestBus::Handler::BusDisconnect();
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 deactivate 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::OnTransformUpdated(AZ::Entity* entity)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzFramework);
// 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