EditorVisibilityTests.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/Component/TransformBus.h>
  9. #include <AzFramework/Viewport/CameraState.h>
  10. #include <AzFramework/Visibility/BoundsBus.h>
  11. #include <AzFramework/Visibility/EntityBoundsUnionBus.h>
  12. #include <AzFramework/Visibility/EntityVisibilityQuery.h>
  13. #include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
  14. namespace UnitTest
  15. {
  16. static auto ScreenDimensions = AzFramework::ScreenSize(1280, 720);
  17. class EditorVisibilityFixture : public ToolsApplicationFixture<>
  18. {
  19. public:
  20. void SetUpEditorFixtureImpl() override {}
  21. void CreateEditorEntities(const size_t entityCount)
  22. {
  23. std::generate_n(
  24. AZStd::back_inserter(m_editorEntityIds), entityCount,
  25. [number = 0]() mutable
  26. {
  27. return CreateDefaultEditorEntity(AZStd::string::format("Entity %d", number++).c_str());
  28. });
  29. }
  30. void SetupRowOfEntities(const AZ::Vector3& worldStartPosition, const AZ::Vector3& worldStepVector)
  31. {
  32. for (size_t entityIndex = 0; entityIndex < m_editorEntityIds.size(); ++entityIndex)
  33. {
  34. AZ::TransformBus::Event(
  35. m_editorEntityIds[entityIndex], &AZ::TransformBus::Events::SetWorldTranslation,
  36. worldStartPosition + worldStepVector * aznumeric_cast<float>(entityIndex));
  37. }
  38. }
  39. AZStd::vector<AZ::EntityId> m_editorEntityIds;
  40. };
  41. TEST_F(EditorVisibilityFixture, VisibilityQueryReturnsEntitiesInFrustumWithNoOrientation)
  42. {
  43. using ::testing::UnorderedElementsAreArray;
  44. constexpr size_t EditorEntityCount = 21;
  45. constexpr size_t BeginVisibleEntityRangeOffset = 7;
  46. constexpr size_t EndVisibleEntityRangeOffset = 14;
  47. // setup row of editor entities
  48. CreateEditorEntities(EditorEntityCount);
  49. SetupRowOfEntities(AZ::Vector3::CreateAxisX(-20.0f), AZ::Vector3::CreateAxisX(2.0f));
  50. // request the entity union bounds system to update
  51. AzFramework::IEntityBoundsUnionRequestBus::Broadcast(
  52. &AzFramework::IEntityBoundsUnionRequestBus::Events::ProcessEntityBoundsUnionRequests);
  53. // create default camera looking down the negative y-axis moved just back from the origin
  54. AzFramework::CameraState cameraState = AzFramework::CreateDefaultCamera(
  55. AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(-5.0f)), ScreenDimensions);
  56. // perform a visibility query based on the state of the camera
  57. AzFramework::EntityVisibilityQuery entityVisibilityQuery;
  58. entityVisibilityQuery.UpdateVisibility(cameraState);
  59. // build a vector of visible entities
  60. AZStd::vector<AZ::EntityId> visibleEditorEntityIds;
  61. AZStd::copy(
  62. entityVisibilityQuery.Begin(), entityVisibilityQuery.End(), AZStd::back_inserter(visibleEditorEntityIds));
  63. // build the expected vector of entity ids (the middle portion of the row based on the centered position of the
  64. // camera
  65. AZStd::vector<AZ::EntityId> expectedEditorEntities;
  66. AZStd::copy(
  67. m_editorEntityIds.begin() + BeginVisibleEntityRangeOffset,
  68. m_editorEntityIds.begin() + EndVisibleEntityRangeOffset, AZStd::back_inserter(expectedEditorEntities));
  69. EXPECT_THAT(visibleEditorEntityIds, UnorderedElementsAreArray(expectedEditorEntities));
  70. }
  71. TEST_F(EditorVisibilityFixture, VisibilityQueryReturnsEntitiesInFrustumWithOrientationAndOffset)
  72. {
  73. using ::testing::UnorderedElementsAreArray;
  74. constexpr size_t EditorEntityCount = 21;
  75. constexpr size_t BeginVisibleEntityRangeOffset = 0;
  76. constexpr size_t EndVisibleEntityRangeOffset = 10;
  77. // setup row of editor entities
  78. CreateEditorEntities(EditorEntityCount);
  79. SetupRowOfEntities(AZ::Vector3::CreateAxisX(-20.0f), AZ::Vector3::CreateAxisX(2.0f));
  80. // request the entity union bounds system to update
  81. AzFramework::IEntityBoundsUnionRequestBus::Broadcast(
  82. &AzFramework::IEntityBoundsUnionRequestBus::Events::ProcessEntityBoundsUnionRequests);
  83. // create default camera looking down the negative x-axis moved along the x-axis and tilted slightly down
  84. AzFramework::CameraState cameraState = AzFramework::CreateDefaultCamera(
  85. AZ::Transform::CreateFromQuaternionAndTranslation(
  86. AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)) *
  87. AZ::Quaternion::CreateRotationX(AZ::DegToRad(-25.0f)),
  88. AZ::Vector3(2.0f, 0.0f, 5.0f)),
  89. ScreenDimensions);
  90. // perform a visibility query based on the state of the camera
  91. AzFramework::EntityVisibilityQuery entityVisibilityQuery;
  92. entityVisibilityQuery.UpdateVisibility(cameraState);
  93. // build the expected vector of entity ids (the first 10 entities in the row)
  94. AZStd::vector<AZ::EntityId> expectedEditorEntities;
  95. AZStd::copy(
  96. m_editorEntityIds.begin() + BeginVisibleEntityRangeOffset,
  97. m_editorEntityIds.begin() + EndVisibleEntityRangeOffset, AZStd::back_inserter(expectedEditorEntities));
  98. // build a vector of visible entities
  99. AZStd::vector<AZ::EntityId> visibleEditorEntityIds;
  100. AZStd::copy(
  101. entityVisibilityQuery.Begin(), entityVisibilityQuery.End(), AZStd::back_inserter(visibleEditorEntityIds));
  102. EXPECT_THAT(visibleEditorEntityIds, UnorderedElementsAreArray(expectedEditorEntities));
  103. }
  104. TEST_F(EditorVisibilityFixture, TranslatedEntityIsRemovedFromVisibilityQueryWhenOutsideFrustum)
  105. {
  106. using ::testing::UnorderedElementsAreArray;
  107. constexpr size_t EditorEntityCount = 21;
  108. constexpr size_t BeginVisibleEntityRangeOffset = 7;
  109. constexpr size_t EndVisibleEntityRangeOffset = 14;
  110. // setup row of editor entities
  111. CreateEditorEntities(EditorEntityCount);
  112. SetupRowOfEntities(AZ::Vector3::CreateAxisX(-20.0f), AZ::Vector3::CreateAxisX(2.0f));
  113. // request the entity union bounds system to update
  114. AzFramework::IEntityBoundsUnionRequestBus::Broadcast(
  115. &AzFramework::IEntityBoundsUnionRequestBus::Events::ProcessEntityBoundsUnionRequests);
  116. const AZ::EntityId entityIdToMove = m_editorEntityIds[10];
  117. AZ::TransformBus::Event(
  118. entityIdToMove, &AZ::TransformBus::Events::SetWorldTranslation, AZ::Vector3::CreateAxisZ(100.0f));
  119. AzFramework::IEntityBoundsUnionRequestBus::Broadcast(
  120. &AzFramework::IEntityBoundsUnionRequestBus::Events::ProcessEntityBoundsUnionRequests);
  121. // create default camera looking down the negative y-axis moved just back from the origin
  122. AzFramework::CameraState cameraState = AzFramework::CreateDefaultCamera(
  123. AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(-5.0f)), ScreenDimensions);
  124. // perform a visibility query based on the state of the camera
  125. AzFramework::EntityVisibilityQuery entityVisibilityQuery;
  126. entityVisibilityQuery.UpdateVisibility(cameraState);
  127. // build a vector of visible entities
  128. AZStd::vector<AZ::EntityId> visibleEditorEntityIds;
  129. AZStd::copy(
  130. entityVisibilityQuery.Begin(), entityVisibilityQuery.End(), AZStd::back_inserter(visibleEditorEntityIds));
  131. // build the expected vector of entity ids (the middle portion of the row based on the centered position of the
  132. // camera
  133. AZStd::vector<AZ::EntityId> expectedEditorEntities;
  134. AZStd::copy(
  135. m_editorEntityIds.begin() + BeginVisibleEntityRangeOffset,
  136. m_editorEntityIds.begin() + EndVisibleEntityRangeOffset, AZStd::back_inserter(expectedEditorEntities));
  137. // remove moved entity from expected vector
  138. expectedEditorEntities.erase(
  139. AZStd::remove(expectedEditorEntities.begin(), expectedEditorEntities.end(), entityIdToMove),
  140. expectedEditorEntities.end());
  141. EXPECT_THAT(visibleEditorEntityIds, UnorderedElementsAreArray(expectedEditorEntities));
  142. }
  143. class TestBoundComponent
  144. : public AZ::Component
  145. , public AzFramework::BoundsRequestBus::Handler
  146. {
  147. public:
  148. AZ_COMPONENT(TestBoundComponent, "{20BB6DB0-B6C0-4D11-A963-B2884F764C4E}");
  149. static void Reflect(AZ::ReflectContext* context);
  150. TestBoundComponent() = default;
  151. // BoundsRequestBus overrides
  152. AZ::Aabb GetWorldBounds() const override;
  153. AZ::Aabb GetLocalBounds() const override;
  154. void ChangeBounds(const AZ::Aabb& localAabb);
  155. protected:
  156. // AZ::Component overrides ...
  157. void Activate() override;
  158. void Deactivate() override;
  159. private:
  160. AZ::Aabb m_localAabb = AZ::Aabb::CreateNull();
  161. };
  162. void TestBoundComponent::Reflect(AZ::ReflectContext* context)
  163. {
  164. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  165. {
  166. serializeContext->Class<TestBoundComponent, AZ::Component>()->Version(1);
  167. }
  168. }
  169. void TestBoundComponent::Activate()
  170. {
  171. m_localAabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
  172. AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId());
  173. }
  174. void TestBoundComponent::Deactivate()
  175. {
  176. AzFramework::BoundsRequestBus::Handler::BusDisconnect();
  177. }
  178. AZ::Aabb TestBoundComponent::GetWorldBounds() const
  179. {
  180. AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
  181. AZ::TransformBus::EventResult(worldFromLocal, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
  182. return m_localAabb.GetTransformedAabb(worldFromLocal);
  183. }
  184. AZ::Aabb TestBoundComponent::GetLocalBounds() const
  185. {
  186. return m_localAabb;
  187. }
  188. void TestBoundComponent::ChangeBounds(const AZ::Aabb& localAabb)
  189. {
  190. m_localAabb = localAabb;
  191. AzFramework::IEntityBoundsUnionRequestBus::Broadcast(
  192. &AzFramework::IEntityBoundsUnionRequestBus::Events::RefreshEntityLocalBoundsUnion, GetEntityId());
  193. }
  194. TEST_F(EditorVisibilityFixture, UpdatedBoundsIntersectingFrustumAddsVisibleEntity)
  195. {
  196. using ::testing::ElementsAre;
  197. // register new test component
  198. GetApplication()->RegisterComponentDescriptor(TestBoundComponent::CreateDescriptor());
  199. AZ::Entity* entity = nullptr;
  200. const auto entityId = CreateDefaultEditorEntity("Entity", &entity);
  201. entity->Deactivate();
  202. auto testBoundComponent = static_cast<TestBoundComponent*>(entity->CreateComponent<TestBoundComponent>());
  203. entity->Activate();
  204. // move the entity just out of view (to the right of the view frustum)
  205. AZ::TransformBus::Event(
  206. entityId, &AZ::TransformBus::Events::SetWorldTranslation, AZ::Vector3(40.0f, -3.0f, 20.0f));
  207. // request the entity union bounds system to update
  208. AzFramework::IEntityBoundsUnionRequestBus::Broadcast(
  209. &AzFramework::IEntityBoundsUnionRequestBus::Events::ProcessEntityBoundsUnionRequests);
  210. // create default camera looking down the positive x-axis moved to position offset from world origin
  211. AzFramework::CameraState cameraState = AzFramework::CreateDefaultCamera(
  212. AZ::Transform::CreateFromQuaternionAndTranslation(
  213. AZ::Quaternion::CreateRotationZ(AZ::DegToRad(-90.0f)), AZ::Vector3(20.0f, 20.0f, 20.0f)),
  214. ScreenDimensions);
  215. // perform a visibility query based on the state of the camera
  216. AzFramework::EntityVisibilityQuery entityVisibilityQuery;
  217. entityVisibilityQuery.UpdateVisibility(cameraState);
  218. // build a vector of visible entities
  219. AZStd::vector<AZ::EntityId> visibleEditorEntityIds;
  220. AZStd::copy(
  221. entityVisibilityQuery.Begin(), entityVisibilityQuery.End(), AZStd::back_inserter(visibleEditorEntityIds));
  222. EXPECT_TRUE(visibleEditorEntityIds.empty());
  223. // increase the size of the bounds
  224. testBoundComponent->ChangeBounds(AZ::Aabb::CreateFromMinMax(AZ::Vector3(-2.5f), AZ::Vector3(2.5f)));
  225. // perform an 'update' of the visibility system
  226. AzFramework::IEntityBoundsUnionRequestBus::Broadcast(
  227. &AzFramework::IEntityBoundsUnionRequestBus::Events::ProcessEntityBoundsUnionRequests);
  228. entityVisibilityQuery.UpdateVisibility(cameraState);
  229. AZStd::copy(
  230. entityVisibilityQuery.Begin(), entityVisibilityQuery.End(), AZStd::back_inserter(visibleEditorEntityIds));
  231. // check the entity is now visible as its bound intersects the view volume
  232. EXPECT_THAT(visibleEditorEntityIds, ElementsAre(entityId));
  233. }
  234. } // namespace UnitTest