TerrainSystem.h 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  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. #pragma once
  9. #include <AzCore/std/containers/fixed_vector.h>
  10. #include <AzCore/Math/Vector3.h>
  11. #include <AzCore/std/algorithm.h>
  12. #include <AzCore/std/smart_ptr/make_shared.h>
  13. #include <AzCore/std/parallel/condition_variable.h>
  14. #include <AzCore/std/parallel/shared_mutex.h>
  15. #include <AzCore/std/containers/map.h>
  16. #include <AzCore/std/containers/span.h>
  17. #include <AzCore/Math/Color.h>
  18. #include <AzCore/Math/Aabb.h>
  19. #include <AzCore/Component/TickBus.h>
  20. #include <AzCore/Jobs/JobManagerBus.h>
  21. #include <AzCore/Jobs/JobFunction.h>
  22. #include <AzFramework/Terrain/TerrainDataRequestBus.h>
  23. #include <TerrainRaycast/TerrainRaycastContext.h>
  24. #include <TerrainSystem/TerrainSystemBus.h>
  25. AZ_DECLARE_BUDGET(Terrain);
  26. namespace Terrain
  27. {
  28. struct TerrainLayerPriorityComparator
  29. {
  30. bool operator()(const AZ::EntityId& layer1id, const AZ::EntityId& layer2id) const;
  31. };
  32. class TerrainSystem
  33. : public AzFramework::Terrain::TerrainDataRequestBus::Handler
  34. , private Terrain::TerrainSystemServiceRequestBus::Handler
  35. , private AZ::TickBus::Handler
  36. {
  37. public:
  38. TerrainSystem();
  39. ~TerrainSystem();
  40. ///////////////////////////////////////////
  41. // TerrainSystemServiceRequestBus::Handler Impl
  42. void Activate() override;
  43. void Deactivate() override;
  44. void RegisterArea(AZ::EntityId areaId) override;
  45. void UnregisterArea(AZ::EntityId areaId) override;
  46. void RefreshArea(
  47. AZ::EntityId areaId, AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask) override;
  48. void RefreshRegion(
  49. const AZ::Aabb& dirtyRegion, AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask) override;
  50. ///////////////////////////////////////////
  51. // TerrainDataRequestBus::Handler Impl
  52. float GetTerrainHeightQueryResolution() const override;
  53. void SetTerrainHeightQueryResolution(float queryResolution) override;
  54. float GetTerrainSurfaceDataQueryResolution() const override;
  55. void SetTerrainSurfaceDataQueryResolution(float queryResolution) override;
  56. virtual AZ::Aabb GetTerrainAabb() const override;
  57. AzFramework::Terrain::FloatRange GetTerrainHeightBounds() const override;
  58. void SetTerrainHeightBounds(const AzFramework::Terrain::FloatRange& heightRange) override;
  59. bool TerrainAreaExistsInBounds(const AZ::Aabb& bounds) const override;
  60. //! Returns terrains height in meters at location x,y.
  61. //! @terrainExistsPtr: Can be nullptr. If != nullptr then, if there's no terrain at location x,y or location x,y is inside a terrain
  62. //! HOLE then *terrainExistsPtr will become false,
  63. //! otherwise *terrainExistsPtr will become true.
  64. float GetHeight(const AZ::Vector3& position, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override;
  65. float GetHeightFromVector2(const AZ::Vector2& position, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override;
  66. float GetHeightFromFloats(float x, float y, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override;
  67. //! Given an XY coordinate, return the max surface type and weight.
  68. //! @terrainExists: Can be nullptr. If != nullptr then, if there's no terrain at location x,y or location x,y is inside a terrain
  69. //! HOLE then *terrainExistsPtr will be set to false,
  70. //! otherwise *terrainExistsPtr will be set to true.
  71. AzFramework::SurfaceData::SurfaceTagWeight GetMaxSurfaceWeight(
  72. const AZ::Vector3& position, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override;
  73. AzFramework::SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromVector2(
  74. const AZ::Vector2& inPosition, Sampler sampler = Sampler::DEFAULT, bool* terrainExistsPtr = nullptr) const override;
  75. AzFramework::SurfaceData::SurfaceTagWeight GetMaxSurfaceWeightFromFloats(
  76. const float x, const float y, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override;
  77. void GetSurfaceWeights(
  78. const AZ::Vector3& inPosition,
  79. AzFramework::SurfaceData::SurfaceTagWeightList& outSurfaceWeights,
  80. Sampler sampler = Sampler::DEFAULT,
  81. bool* terrainExistsPtr = nullptr) const override;
  82. void GetSurfaceWeightsFromVector2(
  83. const AZ::Vector2& inPosition,
  84. AzFramework::SurfaceData::SurfaceTagWeightList& outSurfaceWeights,
  85. Sampler sampler = Sampler::DEFAULT,
  86. bool* terrainExistsPtr = nullptr) const override;
  87. void GetSurfaceWeightsFromFloats(
  88. float x,
  89. float y,
  90. AzFramework::SurfaceData::SurfaceTagWeightList& outSurfaceWeights,
  91. Sampler sampler = Sampler::DEFAULT,
  92. bool* terrainExistsPtr = nullptr) const override;
  93. //! Convenience function for low level systems that can't do a reverse lookup from Crc to string. Everyone else should use
  94. //! GetMaxSurfaceWeight or GetMaxSurfaceWeightFromFloats. Not available in the behavior context. Returns nullptr if the position is
  95. //! inside a hole or outside of the terrain boundaries.
  96. const char* GetMaxSurfaceName(
  97. const AZ::Vector3& position, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override;
  98. //! Returns true if there's a hole at location x,y.
  99. //! Also returns true if there's no terrain data at location x,y.
  100. bool GetIsHole(const AZ::Vector3& position, Sampler sampler = Sampler::BILINEAR) const override;
  101. bool GetIsHoleFromVector2(const AZ::Vector2& position, Sampler sampler = Sampler::BILINEAR) const override;
  102. bool GetIsHoleFromFloats(float x, float y, Sampler sampler = Sampler::BILINEAR) const override;
  103. // Given an XY coordinate, return the surface normal.
  104. //! @terrainExists: Can be nullptr. If != nullptr then, if there's no terrain at location x,y or location x,y is inside a terrain
  105. //! HOLE then *terrainExistsPtr will be set to false,
  106. //! otherwise *terrainExistsPtr will be set to true.
  107. AZ::Vector3 GetNormal(
  108. const AZ::Vector3& position, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override;
  109. AZ::Vector3 GetNormalFromVector2(
  110. const AZ::Vector2& position, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override;
  111. AZ::Vector3 GetNormalFromFloats(
  112. float x, float y, Sampler sampler = Sampler::BILINEAR, bool* terrainExistsPtr = nullptr) const override;
  113. void GetSurfacePoint(
  114. const AZ::Vector3& inPosition,
  115. AzFramework::SurfaceData::SurfacePoint& outSurfacePoint,
  116. Sampler sampler = Sampler::DEFAULT,
  117. bool* terrainExistsPtr = nullptr) const override;
  118. void GetSurfacePointFromVector2(
  119. const AZ::Vector2& inPosition,
  120. AzFramework::SurfaceData::SurfacePoint& outSurfacePoint,
  121. Sampler sampler = Sampler::DEFAULT,
  122. bool* terrainExistsPtr = nullptr) const override;
  123. void GetSurfacePointFromFloats(
  124. float x,
  125. float y,
  126. AzFramework::SurfaceData::SurfacePoint& outSurfacePoint,
  127. Sampler sampler = Sampler::DEFAULT,
  128. bool* terrainExistsPtr = nullptr) const override;
  129. //! Given a list of XY coordinates, call the provided callback function with surface data corresponding to each
  130. //! XY coordinate in the list.
  131. void QueryList(
  132. const AZStd::span<const AZ::Vector3>& inPositions,
  133. TerrainDataMask requestedData,
  134. AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
  135. Sampler sampler = Sampler::DEFAULT) const override;
  136. void QueryListOfVector2(
  137. const AZStd::span<const AZ::Vector2>& inPositions,
  138. TerrainDataMask requestedData,
  139. AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
  140. Sampler sampler = Sampler::DEFAULT) const override;
  141. //! Given a region(aabb) and a step size, call the provided callback function with surface data corresponding to the
  142. //! coordinates in the region.
  143. void QueryRegion(
  144. const AzFramework::Terrain::TerrainQueryRegion& queryRegion,
  145. TerrainDataMask requestedData,
  146. AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback,
  147. Sampler sampler = Sampler::DEFAULT) const override;
  148. AzFramework::EntityContextId GetTerrainRaycastEntityContextId() const override;
  149. AzFramework::RenderGeometry::RayResult GetClosestIntersection(
  150. const AzFramework::RenderGeometry::RayRequest& ray) const override;
  151. AZStd::shared_ptr<AzFramework::Terrain::TerrainJobContext> QueryListAsync(
  152. const AZStd::span<const AZ::Vector3>& inPositions,
  153. TerrainDataMask requestedData,
  154. AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
  155. Sampler sampler = Sampler::DEFAULT,
  156. AZStd::shared_ptr<AzFramework::Terrain::QueryAsyncParams> params = nullptr) const override;
  157. AZStd::shared_ptr<AzFramework::Terrain::TerrainJobContext> QueryListOfVector2Async(
  158. const AZStd::span<const AZ::Vector2>& inPositions,
  159. TerrainDataMask requestedData,
  160. AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
  161. Sampler sampler = Sampler::DEFAULT,
  162. AZStd::shared_ptr<AzFramework::Terrain::QueryAsyncParams> params = nullptr) const override;
  163. AZStd::shared_ptr<AzFramework::Terrain::TerrainJobContext> QueryRegionAsync(
  164. const AzFramework::Terrain::TerrainQueryRegion& queryRegion,
  165. TerrainDataMask requestedData,
  166. AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback,
  167. Sampler sampler = Sampler::DEFAULT,
  168. AZStd::shared_ptr<AzFramework::Terrain::QueryAsyncParams> params = nullptr) const override;
  169. private:
  170. //! Given a set of async parameters, calculate the max number of jobs that we can use for the async call.
  171. int32_t CalculateMaxJobs(AZStd::shared_ptr<AzFramework::Terrain::QueryAsyncParams> params) const;
  172. //! Given the number of samples in a region and the desired number of jobs, choose the best subdivision of the region into jobs.
  173. static void SubdivideRegionForJobs(
  174. int32_t numSamplesX, int32_t numSamplesY, int32_t maxNumJobs, int32_t minPointsPerJob,
  175. int32_t& subdivisionsX, int32_t& subdivisionsY);
  176. static bool ContainedAabbTouchesEdge(const AZ::Aabb& outerAabb, const AZ::Aabb& innerAabb);
  177. //! This performs the logic for QueryRegion, but also accepts x and y index offsets so that the subregions for QueryRegionAsync
  178. //! can pass the correct x and y indices down to the subregion perPositionCallbacks.
  179. void QueryRegionInternal(
  180. const AzFramework::Terrain::TerrainQueryRegion& queryRegion,
  181. size_t xIndexOffset, size_t yIndexOffset,
  182. TerrainDataMask requestedData,
  183. AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback,
  184. Sampler sampler) const;
  185. template<typename VectorType>
  186. AZStd::shared_ptr<AzFramework::Terrain::TerrainJobContext> ProcessFromListAsync(
  187. const AZStd::span<const VectorType>& inPositions,
  188. TerrainDataMask requestedData,
  189. AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
  190. Sampler sampler = Sampler::DEFAULT,
  191. AZStd::shared_ptr<AzFramework::Terrain::QueryAsyncParams> params = nullptr) const;
  192. static void ClampPosition(float x, float y, float queryResolution, AZ::Vector2& outPosition, AZ::Vector2& normalizedDelta);
  193. static void RoundPosition(float x, float y, float queryResolution, AZ::Vector2& outPosition);
  194. static void InterpolateHeights(const AZStd::array<float, 4>& heights, const AZStd::array<bool, 4>& exists,
  195. float lerpX, float lerpY, float& outHeight, bool& outExists);
  196. AZ::EntityId FindBestAreaEntityAtPosition(const AZ::Vector3& position, AZ::Aabb& bounds) const;
  197. void GetOrderedSurfaceWeights(
  198. const float x,
  199. const float y,
  200. Sampler sampler,
  201. AzFramework::SurfaceData::SurfaceTagWeightList& outSurfaceWeights,
  202. bool* terrainExistsPtr) const;
  203. float GetHeightSynchronous(float x, float y, Sampler sampler, bool* terrainExistsPtr) const;
  204. float GetTerrainAreaHeight(float x, float y, bool& terrainExists) const;
  205. AZ::Vector3 GetNormalSynchronous(const AZ::Vector3& position, Sampler sampler, bool* terrainExistsPtr) const;
  206. typedef AZStd::function<void(
  207. const AZStd::span<const AZ::Vector3> inPositions,
  208. AZStd::span<AZ::Vector3> outPositions,
  209. AZStd::span<bool> outTerrainExists,
  210. AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeights,
  211. AZ::EntityId areaId)> BulkQueriesCallback;
  212. void GetHeightsSynchronous(
  213. const AZStd::span<const AZ::Vector3>& inPositions,
  214. Sampler sampler, AZStd::span<float> heights,
  215. AZStd::span<bool> terrainExists) const;
  216. void GetNormalsSynchronous(
  217. const AZStd::span<const AZ::Vector3>& inPositions,
  218. Sampler sampler, AZStd::span<AZ::Vector3> normals,
  219. AZStd::span<bool> terrainExists) const;
  220. void GetNormalsSynchronousExact(
  221. const AZStd::span<const AZ::Vector3>& inPositions,
  222. AZStd::span<AZ::Vector3> normals,
  223. AZStd::span<bool> terrainExists) const;
  224. void GetNormalsSynchronousClamp(
  225. const AZStd::span<const AZ::Vector3>& inPositions,
  226. AZStd::span<AZ::Vector3> normals,
  227. AZStd::span<bool> terrainExists) const;
  228. void GetNormalsSynchronousBilinear(
  229. const AZStd::span<const AZ::Vector3>& inPositions,
  230. AZStd::span<AZ::Vector3> normals,
  231. AZStd::span<bool> terrainExists) const;
  232. void GetOrderedSurfaceWeightsFromList(
  233. const AZStd::span<const AZ::Vector3>& inPositions, Sampler sampler,
  234. AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWeightsList,
  235. AZStd::span<bool> terrainExists) const;
  236. void MakeBulkQueries(
  237. const AZStd::span<const AZ::Vector3> inPositions,
  238. AZStd::span<AZ::Vector3> outPositions,
  239. AZStd::span<bool> outTerrainExists,
  240. AZStd::span<AzFramework::SurfaceData::SurfaceTagWeightList> outSurfaceWieghts,
  241. BulkQueriesCallback queryCallback) const;
  242. void GenerateQueryPositions(const AZStd::span<const AZ::Vector3>& inPositions,
  243. AZStd::vector<AZ::Vector3>& outPositions, float queryResolution,
  244. Sampler sampler) const;
  245. AZStd::vector<AZ::Vector3> GenerateInputPositionsFromRegion(
  246. const AzFramework::Terrain::TerrainQueryRegion& queryRegion) const;
  247. AZStd::vector<AZ::Vector3> GenerateInputPositionsFromListOfVector2(
  248. const AZStd::span<const AZ::Vector2> inPositionsVec2) const;
  249. // AZ::TickBus::Handler overrides ...
  250. void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
  251. void RecalculateCachedBounds();
  252. AZ::Aabb ClampZBoundsToHeightBounds(const AZ::Aabb& aabb) const;
  253. struct TerrainSystemSettings
  254. {
  255. AzFramework::Terrain::FloatRange m_heightRange;
  256. float m_heightQueryResolution{ 1.0f };
  257. float m_surfaceDataQueryResolution{ 1.0f };
  258. bool m_systemActive{ false };
  259. };
  260. TerrainSystemSettings m_currentSettings;
  261. TerrainSystemSettings m_requestedSettings;
  262. AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask m_terrainDirtyMask =
  263. AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask::Settings;
  264. AZ::Aabb m_dirtyRegion;
  265. AZ::Aabb m_cachedAreaBounds;
  266. // Cached data for each terrain area to use when looking up terrain data.
  267. struct TerrainAreaData
  268. {
  269. AZ::Aabb m_areaBounds{ AZ::Aabb::CreateNull() };
  270. bool m_useGroundPlane{ false };
  271. };
  272. mutable AZStd::shared_mutex m_areaMutex;
  273. AZStd::map<AZ::EntityId, TerrainAreaData, TerrainLayerPriorityComparator> m_registeredAreas;
  274. mutable TerrainRaycastContext m_terrainRaycastContext;
  275. AZ::JobManager* m_terrainJobManager = nullptr;
  276. mutable AZStd::mutex m_activeTerrainJobContextMutex;
  277. mutable AZStd::condition_variable m_activeTerrainJobContextMutexConditionVariable;
  278. mutable AZStd::deque<AZStd::shared_ptr<AzFramework::Terrain::TerrainJobContext>> m_activeTerrainJobContexts;
  279. };
  280. template<typename VectorType>
  281. inline AZStd::shared_ptr<AzFramework::Terrain::TerrainJobContext> TerrainSystem::ProcessFromListAsync(
  282. const AZStd::span<const VectorType>& inPositions,
  283. TerrainDataMask requestedData,
  284. AzFramework::Terrain::SurfacePointListFillCallback perPositionCallback,
  285. Sampler sampler,
  286. AZStd::shared_ptr<AzFramework::Terrain::QueryAsyncParams> params) const
  287. {
  288. AZ_PROFILE_FUNCTION(Terrain);
  289. const int32_t numPositionsToProcess = static_cast<int32_t>(inPositions.size());
  290. if (numPositionsToProcess == 0)
  291. {
  292. AZ_Warning("TerrainSystem", false, "No positions to process.");
  293. return nullptr;
  294. }
  295. // Determine the maximum number of jobs, and the minimum number of positions that should be processed per job.
  296. const int32_t numJobsMax = CalculateMaxJobs(params);
  297. const int32_t minPositionsPerJob = params && (params->m_minPositionsPerJob > 0)
  298. ? params->m_minPositionsPerJob
  299. : AzFramework::Terrain::QueryAsyncParams::MinPositionsPerJobDefault;
  300. // Based on the above, we'll create the maximum number of jobs possible that meet both criteria:
  301. // - processes at least minPositionsPerJob for each job
  302. // - creates no more than numJobsMax
  303. const int32_t numJobs = AZStd::clamp(numPositionsToProcess / minPositionsPerJob, 1, numJobsMax);
  304. // Create a terrain job context, track it, and split the work across multiple jobs.
  305. AZStd::shared_ptr<AzFramework::Terrain::TerrainJobContext> jobContext =
  306. AZStd::make_shared<AzFramework::Terrain::TerrainJobContext>(*m_terrainJobManager, numJobs);
  307. {
  308. AZStd::unique_lock<AZStd::mutex> lock(m_activeTerrainJobContextMutex);
  309. m_activeTerrainJobContexts.push_back(jobContext);
  310. }
  311. const int32_t numPositionsPerJob = numPositionsToProcess / numJobs;
  312. for (int32_t i = 0; i < numJobs; ++i)
  313. {
  314. // If the number of positions can't be divided evenly by the number of jobs,
  315. // ensure we still process the remaining positions along with the final job.
  316. const size_t subSpanOffset = i * numPositionsPerJob;
  317. const size_t subSpanCount = (i < numJobs - 1) ? numPositionsPerJob : AZStd::dynamic_extent;
  318. // Define the job function using the sub span of positions to process.
  319. const AZStd::span<const VectorType>& positionsToProcess = inPositions.subspan(subSpanOffset, subSpanCount);
  320. auto jobFunction = [this, positionsToProcess, requestedData, perPositionCallback, sampler, jobContext, params]()
  321. {
  322. // Process the sub span of positions, unless the associated job context has been cancelled.
  323. if (!jobContext->IsCancelled())
  324. {
  325. if constexpr (AZStd::is_same<VectorType, AZ::Vector3>::value)
  326. {
  327. QueryList(positionsToProcess, requestedData, perPositionCallback, sampler);
  328. }
  329. else
  330. {
  331. QueryListOfVector2(positionsToProcess, requestedData, perPositionCallback, sampler);
  332. }
  333. }
  334. // Decrement the number of completions remaining, invoke the completion callback if this happens
  335. // to be the final job completed, and remove this TerrainJobContext from the list of active ones.
  336. const bool wasLastJobCompleted = jobContext->OnJobCompleted();
  337. if (wasLastJobCompleted)
  338. {
  339. if (params && params->m_completionCallback)
  340. {
  341. params->m_completionCallback(jobContext);
  342. }
  343. {
  344. AZStd::unique_lock<AZStd::mutex> lock(m_activeTerrainJobContextMutex);
  345. m_activeTerrainJobContexts.erase(AZStd::find(m_activeTerrainJobContexts.begin(),
  346. m_activeTerrainJobContexts.end(),
  347. jobContext));
  348. m_activeTerrainJobContextMutexConditionVariable.notify_one();
  349. }
  350. }
  351. };
  352. // Create the job and start it immediately.
  353. AZ::Job* processJob = AZ::CreateJobFunction(jobFunction, true, jobContext.get());
  354. processJob->Start();
  355. }
  356. return jobContext;
  357. }
  358. } // namespace Terrain