TerrainHeightGradientListTests.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  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/ComponentApplication.h>
  9. #include <AzTest/AzTest.h>
  10. #include <Components/TerrainHeightGradientListComponent.h>
  11. #include <MockAxisAlignedBoxShapeComponent.h>
  12. #include <GradientSignal/Ebuses/MockGradientRequestBus.h>
  13. #include <LmbrCentral/Shape/MockShapes.h>
  14. #include <Terrain/MockTerrainLayerSpawner.h>
  15. #include <Terrain/MockTerrain.h>
  16. #include <Tests/Mocks/Terrain/MockTerrainDataRequestBus.h>
  17. #include <TerrainTestFixtures.h>
  18. using ::testing::_;
  19. using ::testing::Mock;
  20. using ::testing::NiceMock;
  21. using ::testing::Return;
  22. class TerrainHeightGradientListComponentTest
  23. : public UnitTest::TerrainTestFixture
  24. {
  25. protected:
  26. Terrain::TerrainHeightGradientListComponent* AddHeightGradientListToEntity(AZ::Entity* entity)
  27. {
  28. // Create the TerrainHeightGradientListComponent with an entity in its configuration.
  29. Terrain::TerrainHeightGradientListConfig config;
  30. config.m_gradientEntities.push_back(entity->GetId());
  31. auto heightGradientListComponent = entity->CreateComponent<Terrain::TerrainHeightGradientListComponent>(config);
  32. return heightGradientListComponent;
  33. }
  34. void AddRequiredComponentsToEntity(AZ::Entity* entity)
  35. {
  36. // Create the required box component.
  37. entity->CreateComponent<UnitTest::MockAxisAlignedBoxShapeComponent>();
  38. // Create a MockTerrainLayerSpawnerComponent to provide the required TerrainAreaService.
  39. entity->CreateComponent<UnitTest::MockTerrainLayerSpawnerComponent>();
  40. }
  41. };
  42. TEST_F(TerrainHeightGradientListComponentTest, MissingRequiredComponentsActivateFailure)
  43. {
  44. auto entity = CreateEntity();
  45. AddHeightGradientListToEntity(entity.get());
  46. const AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails();
  47. EXPECT_FALSE(sortOutcome.IsSuccess());
  48. }
  49. TEST_F(TerrainHeightGradientListComponentTest, ActivateEntityActivateSuccess)
  50. {
  51. // Check that the entity activates.
  52. auto entity = CreateEntity();
  53. AddHeightGradientListToEntity(entity.get());
  54. AddRequiredComponentsToEntity(entity.get());
  55. ActivateEntity(entity.get());
  56. EXPECT_EQ(entity->GetState(), AZ::Entity::State::Active);
  57. }
  58. TEST_F(TerrainHeightGradientListComponentTest, TerrainHeightGradientRefreshesTerrainSystem)
  59. {
  60. // Check that the HeightGradientListComponent informs the TerrainSystem when the composition changes.
  61. auto entity = CreateEntity();
  62. AddHeightGradientListToEntity(entity.get());
  63. AddRequiredComponentsToEntity(entity.get());
  64. ActivateEntity(entity.get());
  65. NiceMock<UnitTest::MockTerrainSystemService> terrainSystem;
  66. // As the TerrainHeightGradientListComponent subscribes to the dependency monitor, RefreshArea will be called twice:
  67. // once due to OnCompositionChanged being picked up by the the dependency monitor and resending the notification,
  68. // and once when the HeightGradientListComponent gets the OnCompositionChanged directly through the DependencyNotificationBus.
  69. EXPECT_CALL(terrainSystem, RefreshArea(_, _)).Times(2);
  70. LmbrCentral::DependencyNotificationBus::Event(entity->GetId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  71. // Stop the EXPECT_CALL check now, as OnCompositionChanged will get called twice again during the reset.
  72. Mock::VerifyAndClearExpectations(&terrainSystem);
  73. }
  74. TEST_F(TerrainHeightGradientListComponentTest, TerrainHeightGradientListReturnsHeights)
  75. {
  76. // Check that the HeightGradientListComponent returns expected height values.
  77. auto entity = CreateEntity();
  78. AddHeightGradientListToEntity(entity.get());
  79. AddRequiredComponentsToEntity(entity.get());
  80. NiceMock<UnitTest::MockTerrainAreaHeightRequests> heightfieldRequestBus(entity->GetId());
  81. const float mockGradientValue = 0.25f;
  82. NiceMock<UnitTest::MockGradientRequests> gradientRequests(entity->GetId());
  83. ON_CALL(gradientRequests, GetValue).WillByDefault(Return(mockGradientValue));
  84. // Setup a mock to provide the encompassing Aabb to the HeightGradientListComponent.
  85. const float min = 0.0f;
  86. const float max = 1000.0f;
  87. const AZ::Aabb aabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(min), AZ::Vector3(max));
  88. NiceMock<UnitTest::MockShapeComponentRequests> mockShapeRequests(entity->GetId());
  89. ON_CALL(mockShapeRequests, GetEncompassingAabb).WillByDefault(Return(aabb));
  90. const float worldMax = 10000.0f;
  91. NiceMock<UnitTest::MockTerrainDataRequests> mockterrainDataRequests;
  92. ON_CALL(mockterrainDataRequests, GetTerrainHeightQueryResolution).WillByDefault(Return(1.0f));
  93. ON_CALL(mockterrainDataRequests, GetTerrainHeightBounds).WillByDefault(Return(AzFramework::Terrain::FloatRange({0.0f, worldMax})));
  94. ActivateEntity(entity.get());
  95. // Ensure the cached values in the HeightGradientListComponent are up to date.
  96. LmbrCentral::DependencyNotificationBus::Event(entity->GetId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  97. const AZ::Vector3 inPosition = AZ::Vector3::CreateZero();
  98. AZ::Vector3 outPosition = AZ::Vector3::CreateZero();
  99. bool terrainExists = false;
  100. Terrain::TerrainAreaHeightRequestBus::Event(
  101. entity->GetId(), &Terrain::TerrainAreaHeightRequestBus::Events::GetHeight, inPosition, outPosition, terrainExists);
  102. const float height = outPosition.GetZ();
  103. EXPECT_NEAR(height, mockGradientValue * max, 0.01f);
  104. }
  105. TEST_F(TerrainHeightGradientListComponentTest, TerrainHeightGradientListGetHeightAndGetHeightsMatch)
  106. {
  107. // Check that the HeightGradientListComponent returns the same height values from GetHeight as GetHeights.
  108. auto entity = CreateEntity();
  109. AddHeightGradientListToEntity(entity.get());
  110. AddRequiredComponentsToEntity(entity.get());
  111. NiceMock<UnitTest::MockTerrainAreaHeightRequests> heightfieldRequestBus(entity->GetId());
  112. // Create a deterministic but varying result for our mock gradient.
  113. NiceMock<UnitTest::MockGradientRequests> gradientRequests(entity->GetId());
  114. ON_CALL(gradientRequests, GetValue)
  115. .WillByDefault(
  116. [](const GradientSignal::GradientSampleParams& params) -> float
  117. {
  118. double intpart;
  119. return aznumeric_cast<float>(modf(params.m_position.GetX(), &intpart));
  120. });
  121. // Setup a mock to provide the encompassing Aabb to the HeightGradientListComponent.
  122. const float min = 0.0f;
  123. const float max = 1000.0f;
  124. const AZ::Aabb aabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(min), AZ::Vector3(max));
  125. NiceMock<UnitTest::MockShapeComponentRequests> mockShapeRequests(entity->GetId());
  126. ON_CALL(mockShapeRequests, GetEncompassingAabb).WillByDefault(Return(aabb));
  127. NiceMock<UnitTest::MockTerrainDataRequests> mockterrainDataRequests;
  128. ON_CALL(mockterrainDataRequests, GetTerrainHeightQueryResolution).WillByDefault(Return(1.0f));
  129. ActivateEntity(entity.get());
  130. // Ensure the cached values in the HeightGradientListComponent are up to date.
  131. LmbrCentral::DependencyNotificationBus::Event(entity->GetId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  132. AZStd::vector<AZ::Vector3> inOutPositions;
  133. AZStd::vector<bool> terrainExistsList;
  134. // Build up a list of input positions to query with.
  135. for (float y = 0.0f; y <= 10.0f; y += 0.1f)
  136. {
  137. for (float x = 0.0f; x <= 10.0f; x += 0.1f)
  138. {
  139. inOutPositions.emplace_back(x, y, 0.0f);
  140. terrainExistsList.emplace_back(false);
  141. }
  142. }
  143. // Get the values from GetHeights
  144. Terrain::TerrainAreaHeightRequestBus::Event(
  145. entity->GetId(), &Terrain::TerrainAreaHeightRequestBus::Events::GetHeights, inOutPositions, terrainExistsList);
  146. // For each result returned from GetHeights, verify that it matches the result from GetHeight
  147. for (size_t index = 0; index < inOutPositions.size(); index++)
  148. {
  149. AZ::Vector3 inPosition(inOutPositions[index].GetX(), inOutPositions[index].GetY(), 0.0f);
  150. AZ::Vector3 outPosition = AZ::Vector3(0.0f);
  151. bool terrainExists = false;
  152. Terrain::TerrainAreaHeightRequestBus::Event(
  153. entity->GetId(), &Terrain::TerrainAreaHeightRequestBus::Events::GetHeight, inPosition, outPosition, terrainExists);
  154. ASSERT_TRUE(inOutPositions[index].IsClose(outPosition));
  155. ASSERT_EQ(terrainExists, terrainExistsList[index]);
  156. }
  157. }