3
0

TerrainTestFixtures.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  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 <TerrainTestFixtures.h>
  9. #include <Atom/RPI.Reflect/Image/ImageMipChainAsset.h>
  10. #include <Atom/RPI.Reflect/Image/StreamingImageAssetHandler.h>
  11. #include <Atom/RPI.Public/RPISystem.h>
  12. #include <AzFramework/Components/TransformComponent.h>
  13. #include <AzFramework/Scene/SceneSystemComponent.h>
  14. #include <GradientSignal/Components/GradientSurfaceDataComponent.h>
  15. #include <GradientSignal/Components/GradientTransformComponent.h>
  16. #include <GradientSignal/Components/RandomGradientComponent.h>
  17. #include <GradientSignal/Components/SurfaceAltitudeGradientComponent.h>
  18. #include <GradientSignal/Components/SurfaceMaskGradientComponent.h>
  19. #include <LmbrCentral/Shape/BoxShapeComponentBus.h>
  20. #include <LmbrCentral/Shape/SphereShapeComponentBus.h>
  21. #include <SurfaceData/Components/SurfaceDataShapeComponent.h>
  22. #include <SurfaceData/Components/SurfaceDataSystemComponent.h>
  23. #include <Components/TerrainHeightGradientListComponent.h>
  24. #include <Components/TerrainLayerSpawnerComponent.h>
  25. #include <Components/TerrainPhysicsColliderComponent.h>
  26. #include <Components/TerrainSurfaceDataSystemComponent.h>
  27. #include <Components/TerrainSurfaceGradientListComponent.h>
  28. #include <Components/TerrainSystemComponent.h>
  29. #include <Components/TerrainWorldComponent.h>
  30. #include <Components/TerrainWorldDebuggerComponent.h>
  31. #include <Components/TerrainWorldRendererComponent.h>
  32. #include <TerrainRenderer/Components/TerrainMacroMaterialComponent.h>
  33. #include <TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h>
  34. #include <MockAxisAlignedBoxShapeComponent.h>
  35. #include <Terrain/MockTerrainLayerSpawner.h>
  36. namespace UnitTest
  37. {
  38. void TerrainTestEnvironment::AddGemsAndComponents()
  39. {
  40. AddDynamicModulePaths({ "LmbrCentral", "SurfaceData", "GradientSignal" });
  41. AddComponentDescriptors({
  42. AzFramework::SceneSystemComponent::CreateDescriptor(),
  43. AzFramework::TransformComponent::CreateDescriptor(),
  44. Terrain::TerrainHeightGradientListComponent::CreateDescriptor(),
  45. Terrain::TerrainLayerSpawnerComponent::CreateDescriptor(),
  46. Terrain::TerrainPhysicsColliderComponent::CreateDescriptor(),
  47. Terrain::TerrainSurfaceDataSystemComponent::CreateDescriptor(),
  48. Terrain::TerrainSurfaceGradientListComponent::CreateDescriptor(),
  49. Terrain::TerrainSystemComponent::CreateDescriptor(),
  50. Terrain::TerrainWorldComponent::CreateDescriptor(),
  51. Terrain::TerrainWorldDebuggerComponent::CreateDescriptor(),
  52. Terrain::TerrainWorldRendererComponent::CreateDescriptor(),
  53. Terrain::TerrainMacroMaterialComponent::CreateDescriptor(),
  54. Terrain::TerrainSurfaceMaterialsListComponent::CreateDescriptor(),
  55. UnitTest::MockAxisAlignedBoxShapeComponent::CreateDescriptor(),
  56. UnitTest::MockTerrainLayerSpawnerComponent::CreateDescriptor(),
  57. });
  58. }
  59. void TerrainTestEnvironment::PostCreateApplication()
  60. {
  61. // Ebus usage will allocate a global context on first usage. If that first usage occurs in a DLL, then the context will be
  62. // invalid on subsequent unit test runs if using gtest_repeat. However, if we force the ebus to create their global context in
  63. // the main test DLL (this one), the context will remain active throughout repeated runs. By creating them in
  64. // PostCreateApplication(), they will be created before the DLLs get loaded and any system components from those DLLs run, so we
  65. // can guarantee this will be the first usage.
  66. // These ebuses need their contexts created here before any of the dependent DLLs get loaded:
  67. AZ::AssetTypeInfoBus::GetOrCreateContext();
  68. GradientSignal::GradientRequestBus::GetOrCreateContext();
  69. SurfaceData::SurfaceDataSystemRequestBus::GetOrCreateContext();
  70. SurfaceData::SurfaceDataProviderRequestBus::GetOrCreateContext();
  71. SurfaceData::SurfaceDataModifierRequestBus::GetOrCreateContext();
  72. LmbrCentral::ShapeComponentRequestsBus::GetOrCreateContext();
  73. // Call the AZ::RPI::RPISystem reflection for use with the terrain rendering component unit tests.
  74. auto serializeContext = AZ::ReflectionEnvironment::GetReflectionManager()
  75. ? AZ::ReflectionEnvironment::GetReflectionManager()->GetReflectContext<AZ::SerializeContext>()
  76. : nullptr;
  77. AZ::RPI::RPISystem::Reflect(serializeContext);
  78. }
  79. void TerrainBaseFixture::SetupCoreSystems()
  80. {
  81. }
  82. void TerrainBaseFixture::TearDownCoreSystems()
  83. {
  84. }
  85. AZStd::unique_ptr<AZ::Entity> TerrainBaseFixture::CreateTestBoxEntity(float boxHalfBounds) const
  86. {
  87. // Create the base entity
  88. AZStd::unique_ptr<AZ::Entity> testEntity = CreateEntity();
  89. LmbrCentral::BoxShapeConfig boxConfig(AZ::Vector3(boxHalfBounds * 2.0f));
  90. auto boxComponent = testEntity->CreateComponent(LmbrCentral::AxisAlignedBoxShapeComponentTypeId);
  91. boxComponent->SetConfiguration(boxConfig);
  92. // Create a transform that locates our gradient in the center of our desired Shape.
  93. auto transform = testEntity->CreateComponent<AzFramework::TransformComponent>();
  94. transform->SetWorldTM(AZ::Transform::CreateTranslation(AZ::Vector3(boxHalfBounds)));
  95. return testEntity;
  96. }
  97. AZStd::unique_ptr<AZ::Entity> TerrainBaseFixture::CreateTestBoxEntity(const AZ::Aabb& box) const
  98. {
  99. // Create the base entity
  100. AZStd::unique_ptr<AZ::Entity> testEntity = CreateEntity();
  101. LmbrCentral::BoxShapeConfig boxConfig(box.GetExtents());
  102. auto boxComponent = testEntity->CreateComponent(LmbrCentral::AxisAlignedBoxShapeComponentTypeId);
  103. boxComponent->SetConfiguration(boxConfig);
  104. // Create a transform that locates our gradient in the center of our desired Shape.
  105. auto transform = testEntity->CreateComponent<AzFramework::TransformComponent>();
  106. transform->SetWorldTM(AZ::Transform::CreateTranslation(box.GetCenter()));
  107. return testEntity;
  108. }
  109. AZStd::unique_ptr<AZ::Entity> TerrainBaseFixture::CreateTestSphereEntity(float shapeRadius) const
  110. {
  111. return CreateTestSphereEntity(shapeRadius, AZ::Vector3(shapeRadius));
  112. }
  113. AZStd::unique_ptr<AZ::Entity> TerrainBaseFixture::CreateTestSphereEntity(float shapeRadius, const AZ::Vector3& center) const
  114. {
  115. // Create the base entity
  116. AZStd::unique_ptr<AZ::Entity> testEntity = CreateEntity();
  117. LmbrCentral::SphereShapeConfig sphereConfig(shapeRadius);
  118. auto sphereComponent = testEntity->CreateComponent(LmbrCentral::SphereShapeComponentTypeId);
  119. sphereComponent->SetConfiguration(sphereConfig);
  120. auto transform = testEntity->CreateComponent<AzFramework::TransformComponent>();
  121. transform->SetWorldTM(AZ::Transform::CreateTranslation(center));
  122. return testEntity;
  123. }
  124. AZStd::unique_ptr<AZ::Entity> TerrainBaseFixture::CreateAndActivateTestRandomGradient(
  125. const AZ::Aabb& spawnerBox, uint32_t randomSeed) const
  126. {
  127. // Create a Random Gradient Component with arbitrary parameters.
  128. auto entity = CreateTestBoxEntity(spawnerBox);
  129. GradientSignal::RandomGradientConfig config;
  130. config.m_randomSeed = randomSeed;
  131. entity->CreateComponent<GradientSignal::RandomGradientComponent>(config);
  132. // Create a Gradient Transform Component with arbitrary parameters.
  133. GradientSignal::GradientTransformConfig gradientTransformConfig;
  134. gradientTransformConfig.m_wrappingType = GradientSignal::WrappingType::None;
  135. entity->CreateComponent<GradientSignal::GradientTransformComponent>(gradientTransformConfig);
  136. ActivateEntity(entity.get());
  137. return entity;
  138. }
  139. AZStd::unique_ptr<AZ::Entity> TerrainBaseFixture::CreateTestLayerSpawnerEntity(
  140. const AZ::Aabb& spawnerBox,
  141. const AZ::EntityId& heightGradientEntityId,
  142. const Terrain::TerrainSurfaceGradientListConfig& surfaceConfig) const
  143. {
  144. // Create the base entity
  145. AZStd::unique_ptr<AZ::Entity> testLayerSpawnerEntity = CreateTestBoxEntity(spawnerBox);
  146. // Add a Terrain Layer Spawner
  147. testLayerSpawnerEntity->CreateComponent<Terrain::TerrainLayerSpawnerComponent>();
  148. // Add a Terrain Height Gradient List with one entry pointing to the given gradient entity
  149. Terrain::TerrainHeightGradientListConfig heightConfig;
  150. heightConfig.m_gradientEntities.emplace_back(heightGradientEntityId);
  151. testLayerSpawnerEntity->CreateComponent<Terrain::TerrainHeightGradientListComponent>(heightConfig);
  152. // Add a Terrain Surface Gradient List with however many entries we were given
  153. testLayerSpawnerEntity->CreateComponent<Terrain::TerrainSurfaceGradientListComponent>(surfaceConfig);
  154. return testLayerSpawnerEntity;
  155. }
  156. // Create a terrain system with reasonable defaults for testing, but with the ability to override the defaults
  157. // on a test-by-test basis.
  158. AZStd::unique_ptr<Terrain::TerrainSystem> TerrainBaseFixture::CreateAndActivateTerrainSystem(
  159. float queryResolution, AzFramework::Terrain::FloatRange heightBounds) const
  160. {
  161. const float defaultSurfaceQueryResolution = 1.0f;
  162. return CreateAndActivateTerrainSystem(queryResolution, defaultSurfaceQueryResolution, heightBounds);
  163. }
  164. // Create a terrain system with reasonable defaults for testing, but with the ability to override the defaults
  165. // on a test-by-test basis.
  166. AZStd::unique_ptr<Terrain::TerrainSystem> TerrainBaseFixture::CreateAndActivateTerrainSystem(
  167. float heightQueryResolution, float surfaceQueryResolution, const AzFramework::Terrain::FloatRange& heightBounds) const
  168. {
  169. // Create the terrain system and give it one tick to fully initialize itself.
  170. auto terrainSystem = AZStd::make_unique<Terrain::TerrainSystem>();
  171. terrainSystem->SetTerrainHeightBounds(heightBounds);
  172. terrainSystem->SetTerrainHeightQueryResolution(heightQueryResolution);
  173. terrainSystem->SetTerrainSurfaceDataQueryResolution(surfaceQueryResolution);
  174. terrainSystem->Activate();
  175. AZ::TickBus::Broadcast(&AZ::TickBus::Events::OnTick, 0.f, AZ::ScriptTimePoint{});
  176. return terrainSystem;
  177. }
  178. void TerrainBaseFixture::CreateTestTerrainSystem(const AZ::Aabb& worldBounds, float queryResolution, uint32_t numSurfaces)
  179. {
  180. // Create a Random Gradient to use as our height provider
  181. {
  182. const uint32_t heightRandomSeed = 12345;
  183. auto heightGradientEntity = CreateAndActivateTestRandomGradient(worldBounds, heightRandomSeed);
  184. m_heightGradientEntities.push_back(AZStd::move(heightGradientEntity));
  185. }
  186. // Create a set of Random Gradients to use as our surface providers
  187. Terrain::TerrainSurfaceGradientListConfig surfaceConfig;
  188. for (uint32_t surfaces = 0; surfaces < numSurfaces; surfaces++)
  189. {
  190. const uint32_t surfaceRandomSeed = 23456 + surfaces;
  191. auto surfaceGradientEntity = CreateAndActivateTestRandomGradient(worldBounds, surfaceRandomSeed);
  192. // Give each gradient a new surface tag
  193. surfaceConfig.m_gradientSurfaceMappings.emplace_back(
  194. surfaceGradientEntity->GetId(), SurfaceData::SurfaceTag(AZStd::string::format("test%u", surfaces)));
  195. m_surfaceGradientEntities.emplace_back(AZStd::move(surfaceGradientEntity));
  196. }
  197. // Create a single Terrain Layer Spawner that covers the entire terrain world bounds
  198. // (Do this *after* creating and activating the height and surface gradients)
  199. m_terrainLayerSpawnerEntity = CreateTestLayerSpawnerEntity(worldBounds, m_heightGradientEntities[0]->GetId(), surfaceConfig);
  200. ActivateEntity(m_terrainLayerSpawnerEntity.get());
  201. // Create the terrain system (do this after creating the terrain layer entity to ensure that we don't need any data refreshes)
  202. // Also ensure to do this after creating the global JobManager.
  203. AzFramework::Terrain::FloatRange heightBounds = { worldBounds.GetMin().GetZ(), worldBounds.GetMax().GetZ() };
  204. m_terrainSystem = CreateAndActivateTerrainSystem(queryResolution, heightBounds);
  205. }
  206. void TerrainBaseFixture::DestroyTestTerrainSystem()
  207. {
  208. m_terrainSystem.reset();
  209. m_terrainLayerSpawnerEntity.reset();
  210. m_heightGradientEntities.clear();
  211. m_surfaceGradientEntities.clear();
  212. m_heightGradientEntities.shrink_to_fit();
  213. m_surfaceGradientEntities.shrink_to_fit();
  214. }
  215. void TerrainBaseFixture::CreateTestTerrainSystemWithSurfaceGradients(const AZ::Aabb& worldBounds, float queryResolution)
  216. {
  217. // This will create a testing / benchmarking setup that uses surface-based gradients for terrain data so that we can
  218. // exercise the full pathway of "terrain -> gradient -> surface data" with both surface providers and surface modifiers.
  219. // From a benchmarking perspective, this will also let us verify that we can run multiple simultaneous queries that span
  220. // all three of those systems without hitting any locks.
  221. //
  222. // The specific setup that we create here looks like the following:
  223. // - Height: This comes from an Altitude Gradient looking for an "altitude" tag, and a giant sphere that emits "altitude".
  224. // The Altitude Gradient is constrained to a box that only contains the top part of the sphere.
  225. //
  226. // - Surfaces: This comes from a Surface Mask Gradient looking for a "surface" tag, and a combination of a Random Noise Gradient
  227. // for weight values, and a Gradient Surface Tag Emitter broadcasting "surface" with those weights for any surface points contained
  228. // in its bounds. It is bound to the same box as the Altitude Gradient, so only the top part of the sphere will get the "surface"
  229. // tag.
  230. //
  231. // The net result is a terrain that has a dome shape (from the sphere-based Altitude Gradient) and a surface with some randomly
  232. // distributed surface weights that come from the sphere + Random Noise + Gradient Surface Tag Emitter. With this setup, all
  233. // terrain queries will need to pass through Terrain -> Gradients -> Surface Data -> (Terrain, Shape, Gradient Surface Tag Emitter).
  234. //
  235. // Note that there's a potential recursion loop with Surface Data getting surface points back from terrain. We avoid this by
  236. // locating the sphere, the Altitude Gradient bounds, and the Gradient Surface Tag Emitter bounds, to all be below the terrain
  237. // surface, so that none of the queried terrain points will actually get reused or requeried. If our bounds overlapped the terrain
  238. // surface, then the Gradient Surface Tag Emitter would add its tags to the terrain points too, which would cause the recursion
  239. // loop.
  240. // This is the offset we'll use for locating our entities below the terrain world bounds.
  241. const float belowTerrainZ = worldBounds.GetMin().GetZ() - 100.0f;
  242. // Create our Sphere height surface. This is located in the center of the world bounds, but down below the terrain surface.
  243. {
  244. // We're intentionally making the *radius* (not the diameter) the size of the world bounds. This gives us a sphere large enough
  245. // to make a really nice dome for our heights.
  246. const float sphereRadius = worldBounds.GetXExtent();
  247. // The sphere is centered in the world bounds, but far enough below the terrain that we can modify its surface points without
  248. // also affecting the terrain surface points. We want the top of the sphere to be at our belowTerrainZ height.
  249. AZ::Vector3 sphereCenter = worldBounds.GetCenter();
  250. sphereCenter.SetZ(belowTerrainZ - sphereRadius);
  251. auto heightSurfaceEntity = CreateTestSphereEntity(sphereRadius, sphereCenter);
  252. SurfaceData::SurfaceDataShapeConfig heightSurfaceConfig;
  253. heightSurfaceConfig.m_providerTags.push_back(SurfaceData::SurfaceTag("altitude"));
  254. heightSurfaceEntity->CreateComponent<SurfaceData::SurfaceDataShapeComponent>(heightSurfaceConfig);
  255. ActivateEntity(heightSurfaceEntity.get());
  256. m_heightGradientEntities.push_back(AZStd::move(heightSurfaceEntity));
  257. }
  258. // Create our Altitude Gradient entity. This is located in the center of the world bounds, and contains the top 150 meters of
  259. // the sphere height surface created above.
  260. {
  261. // We'll use the top 150 meters of the sphere for our altitude gradient so that we get a nice dome.
  262. const float altitudeBoxHeight = 150.0f;
  263. AZ::Aabb altitudeBox = AZ::Aabb::CreateFromMinMaxValues(
  264. worldBounds.GetMin().GetX(), worldBounds.GetMin().GetY(), belowTerrainZ - altitudeBoxHeight,
  265. worldBounds.GetMax().GetX(), worldBounds.GetMax().GetY(), belowTerrainZ
  266. );
  267. auto heightGradientEntity = CreateTestBoxEntity(altitudeBox);
  268. GradientSignal::SurfaceAltitudeGradientConfig heightGradientConfig;
  269. heightGradientConfig.m_shapeEntityId = heightGradientEntity->GetId();
  270. heightGradientConfig.m_surfaceTagsToSample.push_back(SurfaceData::SurfaceTag("altitude"));
  271. heightGradientEntity->CreateComponent<GradientSignal::SurfaceAltitudeGradientComponent>(heightGradientConfig);
  272. ActivateEntity(heightGradientEntity.get());
  273. m_heightGradientEntities.push_back(AZStd::move(heightGradientEntity));
  274. }
  275. // Create a Surface Modifier entity so that we're testing both surface providers and surface modifiers.
  276. // This is a Gradient Surface Tag Emitter + Random Noise that will add the "surface" tag with random weights to the sphere
  277. // surface points.
  278. {
  279. // Create a box of arbitrary size centered in the terrain XY, but below the terrain.
  280. float halfBox = 0.5f;
  281. AZ::Aabb gradientBox = AZ::Aabb::CreateFromMinMaxValues(
  282. worldBounds.GetCenter().GetX() - halfBox, worldBounds.GetCenter().GetY() - halfBox, belowTerrainZ - halfBox,
  283. worldBounds.GetCenter().GetX() + halfBox, worldBounds.GetCenter().GetY() + halfBox, belowTerrainZ + halfBox);
  284. auto surfaceModifierEntity = CreateTestBoxEntity(gradientBox);
  285. // Create a Random Gradient Component with arbitrary parameters.
  286. GradientSignal::RandomGradientConfig config;
  287. config.m_randomSeed = 12345;
  288. surfaceModifierEntity->CreateComponent<GradientSignal::RandomGradientComponent>(config);
  289. // Create a Gradient Transform Component with arbitrary parameters.
  290. GradientSignal::GradientTransformConfig gradientTransformConfig;
  291. gradientTransformConfig.m_wrappingType = GradientSignal::WrappingType::None;
  292. surfaceModifierEntity->CreateComponent<GradientSignal::GradientTransformComponent>(gradientTransformConfig);
  293. // Create a Gradient Surface Tag Emitter. Modify surface points to have "surface" with a random weight, but only when the
  294. // Random Gradient has values between 0.5 - 1.0, so that we aren't getting the modification on every point.
  295. GradientSignal::GradientSurfaceDataConfig gradientSurfaceConfig;
  296. gradientSurfaceConfig.m_shapeConstraintEntityId = m_heightGradientEntities[1]->GetId();
  297. gradientSurfaceConfig.m_thresholdMin = 0.5f;
  298. gradientSurfaceConfig.m_thresholdMax = 1.0f;
  299. gradientSurfaceConfig.m_modifierTags.push_back(SurfaceData::SurfaceTag("surface"));
  300. surfaceModifierEntity->CreateComponent<GradientSignal::GradientSurfaceDataComponent>(gradientSurfaceConfig);
  301. ActivateEntity(surfaceModifierEntity.get());
  302. m_surfaceGradientEntities.push_back(AZStd::move(surfaceModifierEntity));
  303. }
  304. // Create a Surface Gradient entity that turns surfaces with "surface" into a gradient.
  305. {
  306. // Create a box of arbitrary size centered in the terrain XY, but below the terrain.
  307. float halfBox = 0.5f;
  308. AZ::Aabb gradientBox = AZ::Aabb::CreateFromMinMaxValues(
  309. worldBounds.GetCenter().GetX() - halfBox, worldBounds.GetCenter().GetY() - halfBox, belowTerrainZ - halfBox,
  310. worldBounds.GetCenter().GetX() + halfBox, worldBounds.GetCenter().GetY() + halfBox, belowTerrainZ + halfBox);
  311. auto surfaceGradientEntity = CreateTestBoxEntity(gradientBox);
  312. GradientSignal::SurfaceMaskGradientConfig gradientSurfaceConfig;
  313. gradientSurfaceConfig.m_surfaceTagList.push_back(SurfaceData::SurfaceTag("surface"));
  314. surfaceGradientEntity->CreateComponent<GradientSignal::SurfaceMaskGradientComponent>(gradientSurfaceConfig);
  315. ActivateEntity(surfaceGradientEntity.get());
  316. m_surfaceGradientEntities.push_back(AZStd::move(surfaceGradientEntity));
  317. }
  318. Terrain::TerrainSurfaceGradientListConfig surfaceConfig;
  319. surfaceConfig.m_gradientSurfaceMappings.emplace_back(
  320. m_surfaceGradientEntities[1]->GetId(), SurfaceData::SurfaceTag("terrain_surface"));
  321. // Create a single Terrain Layer Spawner that covers the entire terrain world bounds
  322. // (Do this *after* creating and activating the height and surface gradients)
  323. m_terrainLayerSpawnerEntity = CreateTestLayerSpawnerEntity(worldBounds, m_heightGradientEntities[1]->GetId(), surfaceConfig);
  324. ActivateEntity(m_terrainLayerSpawnerEntity.get());
  325. // Create the terrain system (do this after creating the terrain layer entity to ensure that we don't need any data refreshes)
  326. // Also ensure to do this after creating the global JobManager.
  327. AzFramework::Terrain::FloatRange heightBounds = { worldBounds.GetMin().GetZ(), worldBounds.GetMax().GetZ() };
  328. m_terrainSystem = CreateAndActivateTerrainSystem(queryResolution, heightBounds);
  329. }
  330. TerrainSystemTestFixture::TerrainSystemTestFixture()
  331. : m_restoreFileIO(m_fileIOMock)
  332. {
  333. // Install Mock File IO, since the ShaderMetricsSystem inside of Atom's RPISystem will try to read/write a file.
  334. AZ::IO::MockFileIOBase::InstallDefaultReturns(m_fileIOMock);
  335. }
  336. void TerrainSystemTestFixture::SetUp()
  337. {
  338. UnitTest::TerrainTestFixture::SetUp();
  339. // Create a system entity with a SceneSystemComponent for Atom and a TerrainSystemComponent for the TerrainWorldComponent.
  340. // However, we don't initialize and activate it until *after* the RPI system is initialized, since the TerrainSystemComponent
  341. // relies on the RPI.
  342. m_systemEntity = CreateEntity();
  343. m_systemEntity->CreateComponent<AzFramework::SceneSystemComponent>();
  344. m_systemEntity->CreateComponent<Terrain::TerrainSystemComponent>();
  345. // Create a stub RHI for use by Atom
  346. m_rhiFactory.reset(aznew UnitTest::StubRHI::Factory());
  347. // Create the Atom RPISystem
  348. AZ::RPI::RPISystemDescriptor rpiSystemDescriptor;
  349. m_rpiSystem = AZStd::make_unique<AZ::RPI::RPISystem>();
  350. m_rpiSystem->Initialize(rpiSystemDescriptor);
  351. AZ::RPI::ImageSystemDescriptor imageSystemDescriptor;
  352. m_imageSystem = AZStd::make_unique<AZ::RPI::ImageSystem>();
  353. m_imageSystem->Init(imageSystemDescriptor);
  354. // Now that the RPISystem is activated, activate the system entity.
  355. m_systemEntity->Init();
  356. m_systemEntity->Activate();
  357. }
  358. void TerrainSystemTestFixture::TearDown()
  359. {
  360. m_imageSystem->Shutdown();
  361. m_rpiSystem->Shutdown();
  362. m_rpiSystem = nullptr;
  363. m_rhiFactory = nullptr;
  364. m_systemEntity.reset();
  365. UnitTest::TerrainTestFixture::TearDown();
  366. }
  367. }