123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- /*
- * 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 <AzCore/Component/TransformBus.h>
- #include <AzFramework/Viewport/CameraState.h>
- #include <AzFramework/Visibility/BoundsBus.h>
- #include <AzFramework/Visibility/EntityBoundsUnionBus.h>
- #include <AzFramework/Visibility/EntityVisibilityQuery.h>
- #include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
- namespace UnitTest
- {
- static auto ScreenDimensions = AzFramework::ScreenSize(1280, 720);
- class EditorVisibilityFixture : public ToolsApplicationFixture<>
- {
- public:
- void SetUpEditorFixtureImpl() override {}
- void CreateEditorEntities(const size_t entityCount)
- {
- std::generate_n(
- AZStd::back_inserter(m_editorEntityIds), entityCount,
- [number = 0]() mutable
- {
- return CreateDefaultEditorEntity(AZStd::string::format("Entity %d", number++).c_str());
- });
- }
- void SetupRowOfEntities(const AZ::Vector3& worldStartPosition, const AZ::Vector3& worldStepVector)
- {
- for (size_t entityIndex = 0; entityIndex < m_editorEntityIds.size(); ++entityIndex)
- {
- AZ::TransformBus::Event(
- m_editorEntityIds[entityIndex], &AZ::TransformBus::Events::SetWorldTranslation,
- worldStartPosition + worldStepVector * aznumeric_cast<float>(entityIndex));
- }
- }
- AZStd::vector<AZ::EntityId> m_editorEntityIds;
- };
- TEST_F(EditorVisibilityFixture, VisibilityQueryReturnsEntitiesInFrustumWithNoOrientation)
- {
- using ::testing::UnorderedElementsAreArray;
- constexpr size_t EditorEntityCount = 21;
- constexpr size_t BeginVisibleEntityRangeOffset = 7;
- constexpr size_t EndVisibleEntityRangeOffset = 14;
- // setup row of editor entities
- CreateEditorEntities(EditorEntityCount);
- SetupRowOfEntities(AZ::Vector3::CreateAxisX(-20.0f), AZ::Vector3::CreateAxisX(2.0f));
- // request the entity union bounds system to update
- AzFramework::IEntityBoundsUnionRequestBus::Broadcast(
- &AzFramework::IEntityBoundsUnionRequestBus::Events::ProcessEntityBoundsUnionRequests);
- // create default camera looking down the negative y-axis moved just back from the origin
- AzFramework::CameraState cameraState = AzFramework::CreateDefaultCamera(
- AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(-5.0f)), ScreenDimensions);
- // perform a visibility query based on the state of the camera
- AzFramework::EntityVisibilityQuery entityVisibilityQuery;
- entityVisibilityQuery.UpdateVisibility(cameraState);
- // build a vector of visible entities
- AZStd::vector<AZ::EntityId> visibleEditorEntityIds;
- AZStd::copy(
- entityVisibilityQuery.Begin(), entityVisibilityQuery.End(), AZStd::back_inserter(visibleEditorEntityIds));
- // build the expected vector of entity ids (the middle portion of the row based on the centered position of the
- // camera
- AZStd::vector<AZ::EntityId> expectedEditorEntities;
- AZStd::copy(
- m_editorEntityIds.begin() + BeginVisibleEntityRangeOffset,
- m_editorEntityIds.begin() + EndVisibleEntityRangeOffset, AZStd::back_inserter(expectedEditorEntities));
- EXPECT_THAT(visibleEditorEntityIds, UnorderedElementsAreArray(expectedEditorEntities));
- }
- TEST_F(EditorVisibilityFixture, VisibilityQueryReturnsEntitiesInFrustumWithOrientationAndOffset)
- {
- using ::testing::UnorderedElementsAreArray;
- constexpr size_t EditorEntityCount = 21;
- constexpr size_t BeginVisibleEntityRangeOffset = 0;
- constexpr size_t EndVisibleEntityRangeOffset = 10;
- // setup row of editor entities
- CreateEditorEntities(EditorEntityCount);
- SetupRowOfEntities(AZ::Vector3::CreateAxisX(-20.0f), AZ::Vector3::CreateAxisX(2.0f));
- // request the entity union bounds system to update
- AzFramework::IEntityBoundsUnionRequestBus::Broadcast(
- &AzFramework::IEntityBoundsUnionRequestBus::Events::ProcessEntityBoundsUnionRequests);
- // create default camera looking down the negative x-axis moved along the x-axis and tilted slightly down
- AzFramework::CameraState cameraState = AzFramework::CreateDefaultCamera(
- AZ::Transform::CreateFromQuaternionAndTranslation(
- AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)) *
- AZ::Quaternion::CreateRotationX(AZ::DegToRad(-25.0f)),
- AZ::Vector3(2.0f, 0.0f, 5.0f)),
- ScreenDimensions);
- // perform a visibility query based on the state of the camera
- AzFramework::EntityVisibilityQuery entityVisibilityQuery;
- entityVisibilityQuery.UpdateVisibility(cameraState);
- // build the expected vector of entity ids (the first 10 entities in the row)
- AZStd::vector<AZ::EntityId> expectedEditorEntities;
- AZStd::copy(
- m_editorEntityIds.begin() + BeginVisibleEntityRangeOffset,
- m_editorEntityIds.begin() + EndVisibleEntityRangeOffset, AZStd::back_inserter(expectedEditorEntities));
- // build a vector of visible entities
- AZStd::vector<AZ::EntityId> visibleEditorEntityIds;
- AZStd::copy(
- entityVisibilityQuery.Begin(), entityVisibilityQuery.End(), AZStd::back_inserter(visibleEditorEntityIds));
- EXPECT_THAT(visibleEditorEntityIds, UnorderedElementsAreArray(expectedEditorEntities));
- }
- TEST_F(EditorVisibilityFixture, TranslatedEntityIsRemovedFromVisibilityQueryWhenOutsideFrustum)
- {
- using ::testing::UnorderedElementsAreArray;
- constexpr size_t EditorEntityCount = 21;
- constexpr size_t BeginVisibleEntityRangeOffset = 7;
- constexpr size_t EndVisibleEntityRangeOffset = 14;
- // setup row of editor entities
- CreateEditorEntities(EditorEntityCount);
- SetupRowOfEntities(AZ::Vector3::CreateAxisX(-20.0f), AZ::Vector3::CreateAxisX(2.0f));
- // request the entity union bounds system to update
- AzFramework::IEntityBoundsUnionRequestBus::Broadcast(
- &AzFramework::IEntityBoundsUnionRequestBus::Events::ProcessEntityBoundsUnionRequests);
- const AZ::EntityId entityIdToMove = m_editorEntityIds[10];
- AZ::TransformBus::Event(
- entityIdToMove, &AZ::TransformBus::Events::SetWorldTranslation, AZ::Vector3::CreateAxisZ(100.0f));
- AzFramework::IEntityBoundsUnionRequestBus::Broadcast(
- &AzFramework::IEntityBoundsUnionRequestBus::Events::ProcessEntityBoundsUnionRequests);
- // create default camera looking down the negative y-axis moved just back from the origin
- AzFramework::CameraState cameraState = AzFramework::CreateDefaultCamera(
- AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(-5.0f)), ScreenDimensions);
- // perform a visibility query based on the state of the camera
- AzFramework::EntityVisibilityQuery entityVisibilityQuery;
- entityVisibilityQuery.UpdateVisibility(cameraState);
- // build a vector of visible entities
- AZStd::vector<AZ::EntityId> visibleEditorEntityIds;
- AZStd::copy(
- entityVisibilityQuery.Begin(), entityVisibilityQuery.End(), AZStd::back_inserter(visibleEditorEntityIds));
- // build the expected vector of entity ids (the middle portion of the row based on the centered position of the
- // camera
- AZStd::vector<AZ::EntityId> expectedEditorEntities;
- AZStd::copy(
- m_editorEntityIds.begin() + BeginVisibleEntityRangeOffset,
- m_editorEntityIds.begin() + EndVisibleEntityRangeOffset, AZStd::back_inserter(expectedEditorEntities));
- // remove moved entity from expected vector
- expectedEditorEntities.erase(
- AZStd::remove(expectedEditorEntities.begin(), expectedEditorEntities.end(), entityIdToMove),
- expectedEditorEntities.end());
- EXPECT_THAT(visibleEditorEntityIds, UnorderedElementsAreArray(expectedEditorEntities));
- }
- class TestBoundComponent
- : public AZ::Component
- , public AzFramework::BoundsRequestBus::Handler
- {
- public:
- AZ_COMPONENT(TestBoundComponent, "{20BB6DB0-B6C0-4D11-A963-B2884F764C4E}");
- static void Reflect(AZ::ReflectContext* context);
- TestBoundComponent() = default;
- // BoundsRequestBus overrides
- AZ::Aabb GetWorldBounds() const override;
- AZ::Aabb GetLocalBounds() const override;
- void ChangeBounds(const AZ::Aabb& localAabb);
- protected:
- // AZ::Component overrides ...
- void Activate() override;
- void Deactivate() override;
- private:
- AZ::Aabb m_localAabb = AZ::Aabb::CreateNull();
- };
- void TestBoundComponent::Reflect(AZ::ReflectContext* context)
- {
- if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
- {
- serializeContext->Class<TestBoundComponent, AZ::Component>()->Version(1);
- }
- }
- void TestBoundComponent::Activate()
- {
- m_localAabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
- AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId());
- }
- void TestBoundComponent::Deactivate()
- {
- AzFramework::BoundsRequestBus::Handler::BusDisconnect();
- }
- AZ::Aabb TestBoundComponent::GetWorldBounds() const
- {
- AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
- AZ::TransformBus::EventResult(worldFromLocal, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
- return m_localAabb.GetTransformedAabb(worldFromLocal);
- }
- AZ::Aabb TestBoundComponent::GetLocalBounds() const
- {
- return m_localAabb;
- }
- void TestBoundComponent::ChangeBounds(const AZ::Aabb& localAabb)
- {
- m_localAabb = localAabb;
- AzFramework::IEntityBoundsUnionRequestBus::Broadcast(
- &AzFramework::IEntityBoundsUnionRequestBus::Events::RefreshEntityLocalBoundsUnion, GetEntityId());
- }
- TEST_F(EditorVisibilityFixture, UpdatedBoundsIntersectingFrustumAddsVisibleEntity)
- {
- using ::testing::ElementsAre;
- // register new test component
- GetApplication()->RegisterComponentDescriptor(TestBoundComponent::CreateDescriptor());
- AZ::Entity* entity = nullptr;
- const auto entityId = CreateDefaultEditorEntity("Entity", &entity);
- entity->Deactivate();
- auto testBoundComponent = static_cast<TestBoundComponent*>(entity->CreateComponent<TestBoundComponent>());
- entity->Activate();
- // move the entity just out of view (to the right of the view frustum)
- AZ::TransformBus::Event(
- entityId, &AZ::TransformBus::Events::SetWorldTranslation, AZ::Vector3(40.0f, -3.0f, 20.0f));
- // request the entity union bounds system to update
- AzFramework::IEntityBoundsUnionRequestBus::Broadcast(
- &AzFramework::IEntityBoundsUnionRequestBus::Events::ProcessEntityBoundsUnionRequests);
- // create default camera looking down the positive x-axis moved to position offset from world origin
- AzFramework::CameraState cameraState = AzFramework::CreateDefaultCamera(
- AZ::Transform::CreateFromQuaternionAndTranslation(
- AZ::Quaternion::CreateRotationZ(AZ::DegToRad(-90.0f)), AZ::Vector3(20.0f, 20.0f, 20.0f)),
- ScreenDimensions);
- // perform a visibility query based on the state of the camera
- AzFramework::EntityVisibilityQuery entityVisibilityQuery;
- entityVisibilityQuery.UpdateVisibility(cameraState);
- // build a vector of visible entities
- AZStd::vector<AZ::EntityId> visibleEditorEntityIds;
- AZStd::copy(
- entityVisibilityQuery.Begin(), entityVisibilityQuery.End(), AZStd::back_inserter(visibleEditorEntityIds));
- EXPECT_TRUE(visibleEditorEntityIds.empty());
- // increase the size of the bounds
- testBoundComponent->ChangeBounds(AZ::Aabb::CreateFromMinMax(AZ::Vector3(-2.5f), AZ::Vector3(2.5f)));
- // perform an 'update' of the visibility system
- AzFramework::IEntityBoundsUnionRequestBus::Broadcast(
- &AzFramework::IEntityBoundsUnionRequestBus::Events::ProcessEntityBoundsUnionRequests);
- entityVisibilityQuery.UpdateVisibility(cameraState);
- AZStd::copy(
- entityVisibilityQuery.Begin(), entityVisibilityQuery.End(), AZStd::back_inserter(visibleEditorEntityIds));
- // check the entity is now visible as its bound intersects the view volume
- EXPECT_THAT(visibleEditorEntityIds, ElementsAre(entityId));
- }
- } // namespace UnitTest
|