Gems/Umbra/Code/Source/UmbraSceneComponent/UmbraSceneComponentController.h (108 lines of code) (raw):
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Component/Component.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzFramework/Visibility/OcclusionBus.h>
#include <Umbra/UmbraSceneComponent/UmbraSceneComponentBus.h>
#include <Umbra/UmbraSceneComponent/UmbraSceneComponentConfig.h>
#include <runtime/umbraQuery.hpp>
#include <runtime/umbraTome.hpp>
#include <umbraInfo.hpp>
namespace Umbra
{
//! UmbraSceneComponentController loads and manages umbra scene assets containing precomputed visibility data. Once this asset is
//! loaded, occlusion queries can be made against the precomputed visibility data. Entity visibility checks will first determine if the
//! entity was part of the occlusion scene And return immediately if the query provides precomputed data for that entity. Otherwise,
//! entity visibility checks will do simple AABB tests against the occlusion buffer. This class implements OcclusionRequestBus and
//! supports creating multiple occlusion views from different perspectives and parallel occlusion queries.
class UmbraSceneComponentController final
: public UmbraSceneComponentRequestBus::Handler
, public AzFramework::OcclusionRequestBus::Handler
, public AzFramework::EntityDebugDisplayEventBus::Handler
, public AZ::Data::AssetBus::Handler
{
public:
friend class EditorUmbraSceneComponent;
AZ_CLASS_ALLOCATOR(UmbraSceneComponentController, AZ::SystemAllocator);
AZ_RTTI(UmbraSceneComponentController, "{F90A5E6D-49F7-4B59-BF3C-4D66D5B7B257}");
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services);
UmbraSceneComponentController() = default;
UmbraSceneComponentController(const UmbraSceneComponentConfig& config);
void Activate(AZ::EntityId entityId);
void Deactivate();
void SetConfiguration(const UmbraSceneComponentConfig& config);
const UmbraSceneComponentConfig& GetConfiguration() const;
//! UmbraSceneComponentRequestBus overrides...
void SetSceneAsset(AZ::Data::Asset<UmbraSceneAsset> asset) override;
void SetSceneAssetId(const AZ::Data::AssetId& assetId) override;
void SetSceneAssetPath(const AZStd::string& path) override;
AZ::Data::Asset<const UmbraSceneAsset> GetSceneAsset() const override;
AZ::Data::AssetId GetSceneAssetId() const override;
bool IsSceneReady() const override;
//! AzFramework::OcclusionRequestBus::Handler overrides...
void ClearOcclusionViewDebugInfo() override;
bool IsOcclusionViewValid(const AZ::Name& viewName) const override;
bool CreateOcclusionView(const AZ::Name& viewName) override;
bool DestroyOcclusionView(const AZ::Name& viewName) override;
bool UpdateOcclusionView(const AZ::Name& viewName, const AZ::Vector3& cameraWorldPos, const AZ::Matrix4x4& cameraWorldToClip) override;
AzFramework::OcclusionState GetOcclusionViewEntityVisibility(const AZ::Name& viewName, const AZ::EntityId& entityId) const override;
AzFramework::OcclusionState GetOcclusionViewAabbVisibility(const AZ::Name& viewName, const AZ::Aabb& bounds) const override;
AZStd::vector<AzFramework::OcclusionState> GetOcclusionViewAabbToAabbVisibility(
const AZ::Name& viewName, const AZ::Aabb& sourceAabb, const AZStd::vector<AZ::Aabb>& targetAabbs) const override;
AZStd::vector<AzFramework::OcclusionState> GetOcclusionViewSphereToSphereVisibility(
const AZ::Name& viewName, const AZ::Sphere& sourceSphere, const AZStd::vector<AZ::Sphere>& targetSpheres) const override;
AZStd::vector<AzFramework::OcclusionState> GetOcclusionViewEntityToEntityVisibility(
const AZ::Name& viewName, const AZ::EntityId& sourceEntityId, const AZStd::vector<AZ::EntityId>& targetEntityIds) const override;
private:
AZ_DISABLE_COPY_MOVE(UmbraSceneComponentController);
//! AzFramework::DebugDisplayRequestBus::Handler overrides...
void DisplayEntityViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
//! AZ::Data::AssetBus overrides...
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
void OnAssetError(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
void OnAssetReloadError(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
//! Request loading the scene asset from the component configuration
void QueueScene();
//! Once the scene asset has been loaded, create an umbra tome and dependencies to support occlusion queries
void CreateScene();
//! Destroy the umbra tome dependencies
void ReleaseScene();
AZ::EntityId m_entityId;
UmbraSceneComponentConfig m_configuration;
// This is the runtime version of the precomputed visibility data created from data serialized with the scene asset.
const Umbra::Tome* m_tome = nullptr;
// Vector of entity IDs, whose indices correspond to object indices in the tome.
AZStd::vector<AZ::EntityId> m_objectIndexToEntityIdTable;
// Collection of entity ids were referenced in the baked scene data.
AZStd::unordered_set<AZ::EntityId> m_storedEntityIds;
// Capturing data for unique line rendering requests. The data is stored as a set of tuples to automatically eliminate duplicates
// without implementing custom sort and removal logic. Each tuple contains 10 floats, the first four represent rgba color values,
// the next three represent the starting point for the line segment, the last three represent the end point for the line segment.
using UmbraDebugRendererLine = AZStd::tuple<float, float, float, float, float, float, float, float, float, float>;
using UmbraDebugRendererLineSet = AZStd::set<UmbraDebugRendererLine>;
//! UmbraDebugRenderer works with occlusion queries to capture all of the data for visualizing stats and line segments for umbra
//! objects like bounding boxes, view frustums, and the visibility buffer.
class UmbraDebugRenderer : public Umbra::DebugRenderer
{
public:
UmbraDebugRenderer() = default;
void addLine(const Umbra::Vector3& start, const Umbra::Vector3& end, const Umbra::Vector4& color) override;
void addStat(const char* stat, int val) override;
UmbraDebugRendererLineSet m_debugLines;
AZStd::set<AZStd::pair<AZStd::string, int>> m_debugStats;
};
//! UmbraOcclusionView embodies the data for a single occlusion query. Multiple UmbraOcclusionView can exist simultaneously
//! alongside corresponding graphics views. They can be updated and queried in parallel.
class UmbraOcclusionView final
{
public:
AZ_CLASS_ALLOCATOR(UmbraOcclusionView, AZ::SystemAllocator);
AZ_RTTI(UmbraOcclusionView, "{22FA80C6-A096-4A2E-BF93-3AF75E688656}");
UmbraOcclusionView(UmbraSceneComponentController& controller);
~UmbraOcclusionView();
bool Update(const AZ::Vector3& cameraWorldPos, const AZ::Matrix4x4& cameraWorldToClip);
AzFramework::OcclusionState GetEntityVisibility(const AZ::EntityId& entityId) const;
AzFramework::OcclusionState GetAabbVisibility(const AZ::Aabb& bounds) const;
AZStd::vector<AzFramework::OcclusionState> GetAabbToAabbVisibility(const AZ::Aabb& sourceAabb, const AZStd::vector<AZ::Aabb>& targetAabbs) const;
AZStd::vector<AzFramework::OcclusionState> GetSphereToSphereVisibility(const AZ::Sphere& sourceSphere, const AZStd::vector<AZ::Sphere>& targetSpheres) const;
AZStd::vector<AzFramework::OcclusionState> GetEntityToEntityVisibility(const AZ::EntityId& sourceEntityId, const AZStd::vector<AZ::EntityId>& targetEntityIds) const;
private:
AZ_DISABLE_COPY_MOVE(UmbraOcclusionView);
// Reference to controller to access tome, assets, and debug containers
UmbraSceneComponentController& m_controller;
// Umbra query object that performs occlusion culling queries against the tome
AZStd::unique_ptr<Umbra::QueryExt> m_query;
// Additional working memory for Umbra queries.
AZStd::vector<AZ::u8> m_queryWorkMem;
// Vector used as storage for object indices, pre allocating enough space for all object indices in the tome
AZStd::vector<int32_t> m_objectIndexListStorage;
// Object list using the above vector as storage. The list will be populated with with indices after each query is executed.
AZStd::unique_ptr<Umbra::IndexList> m_objectIndexList;
// Occlusion buffer contains depth data from the previous query to test visibility for dynamic object bounding boxes.
AZStd::unique_ptr<Umbra::OcclusionBuffer> m_occlusionBuffer;
// Collection of entity IDs visible since the last query.
AZStd::unordered_set<AZ::EntityId> m_visibleEntityIds;
// Interfaces with umbra and the query object to gather debug lines and statistics
AZStd::unique_ptr<UmbraDebugRenderer> m_debugRenderer;
};
// All currently active occlusion views.
AZStd::unordered_map<AZ::Name, AZStd::unique_ptr<UmbraOcclusionView>> m_occlusionViewMap;
// Sets of debug data accumulated from all occlusion views.
AZStd::mutex m_debugMutex;
UmbraDebugRendererLineSet m_debugLines;
AZStd::set<AZStd::pair<AZStd::string, int>> m_debugStats;
};
} // namespace Umbra