TerrainTestFixtures.cpp 24 KB

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