TerrainMeshManager.cpp 70 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480
  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 <TerrainRenderer/TerrainMeshManager.h>
  9. #include <AzCore/Console/Console.h>
  10. #include <AzCore/Math/Frustum.h>
  11. #include <AzCore/Math/ShapeIntersection.h>
  12. #include <AzCore/Jobs/Algorithms.h>
  13. #include <AzCore/Jobs/JobCompletion.h>
  14. #include <AzCore/Jobs/JobFunction.h>
  15. #include <Atom/RHI.Reflect/BufferViewDescriptor.h>
  16. #include <Atom/RHI.Reflect/InputStreamLayoutBuilder.h>
  17. #include <Atom/RHI/RHISystemInterface.h>
  18. #include <Atom/RPI.Public/Scene.h>
  19. #include <Atom/RPI.Public/View.h>
  20. #include <Atom/RPI.Public/AuxGeom/AuxGeomDraw.h>
  21. #include <Atom/RPI.Public/AuxGeom/AuxGeomFeatureProcessorInterface.h>
  22. #include <Atom/RPI.Public/Buffer/Buffer.h>
  23. #include <Atom/RPI.Public/Model/Model.h>
  24. #include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
  25. #include <Atom/Feature/RenderCommon.h>
  26. #include <Atom/Feature/Mesh/MeshCommon.h>
  27. namespace Terrain
  28. {
  29. namespace
  30. {
  31. [[maybe_unused]] static const char* TerrainMeshManagerName = "TerrainMeshManager";
  32. }
  33. AZ_CVAR(bool,
  34. r_debugTerrainLodLevels,
  35. false,
  36. [](const bool& value)
  37. {
  38. AZ::RPI::ShaderSystemInterface::Get()->SetGlobalShaderOption(AZ::Name{ "o_debugTerrainLodLevels" }, AZ::RPI::ShaderOptionValue{ value });
  39. },
  40. AZ::ConsoleFunctorFlags::Null,
  41. "Turns on debug coloring for terrain mesh lods."
  42. );
  43. AZ_CVAR(bool,
  44. r_debugTerrainAabbs,
  45. false,
  46. nullptr,
  47. AZ::ConsoleFunctorFlags::Null,
  48. "Turns on debug aabbs for terrain sectors."
  49. );
  50. TerrainMeshManager::TerrainMeshManager()
  51. {
  52. }
  53. TerrainMeshManager::~TerrainMeshManager()
  54. {
  55. AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect();
  56. }
  57. void TerrainMeshManager::Initialize(AZ::RPI::Scene& parentScene)
  58. {
  59. m_parentScene = &parentScene;
  60. AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect();
  61. m_handleGlobalShaderOptionUpdate = AZ::RPI::ShaderSystemInterface::GlobalShaderOptionUpdatedEvent::Handler
  62. {
  63. [this](const AZ::Name&, AZ::RPI::ShaderOptionValue) { m_rebuildDrawPackets = true; }
  64. };
  65. AZ::RPI::ShaderSystemInterface::Get()->Connect(m_handleGlobalShaderOptionUpdate);
  66. m_meshMovedFlag = m_parentScene->GetViewTagBitRegistry().AcquireTag(AZ::Render::MeshCommon::MeshMovedName);
  67. m_rayTracingFeatureProcessor = m_parentScene->GetFeatureProcessor<AZ::Render::RayTracingFeatureProcessorInterface>();
  68. m_rayTracingEnabled = (AZ::RHI::RHISystemInterface::Get()->GetRayTracingSupport() != AZ::RHI::MultiDevice::NoDevices) && m_rayTracingFeatureProcessor;
  69. m_isInitialized = true;
  70. }
  71. void TerrainMeshManager::SetConfiguration(const MeshConfiguration& config)
  72. {
  73. bool requireRebuild = m_config.CheckWouldRequireRebuild(config);
  74. m_config = config;
  75. if (requireRebuild)
  76. {
  77. m_rebuildSectors = true;
  78. OnTerrainDataChanged(AZ::Aabb::CreateNull(), TerrainDataChangedMask::HeightData);
  79. }
  80. // This will trigger a draw packet rebuild later.
  81. AZ::RPI::ShaderSystemInterface::Get()->SetGlobalShaderOption(AZ::Name{ "o_useTerrainClod" }, AZ::RPI::ShaderOptionValue{ m_config.m_clodEnabled });
  82. }
  83. bool TerrainMeshManager::UpdateGridSize(float distanceToFirstLod)
  84. {
  85. float queryResolution = 1.0f;
  86. AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
  87. queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution);
  88. float quadsToFirstLod = distanceToFirstLod / queryResolution;
  89. uint32_t quadsPerSector = aznumeric_cast<uint32_t>(quadsToFirstLod / 4.0f);
  90. uint32_t gridSize = AZ::RHI::IsPowerOfTwo(quadsPerSector) ? quadsPerSector : (AZ::RHI::NextPowerOfTwo(quadsPerSector) >> 1);
  91. gridSize = AZStd::GetMin(gridSize, 128u); // x/y positions must be able to fix in 8 bits (256 is too large by 1)
  92. gridSize = AZStd::GetMax(gridSize, 8u); // make sure there's enough vertices to be worth drawing.
  93. if (gridSize != m_gridSize)
  94. {
  95. m_gridSize = aznumeric_cast<uint8_t>(gridSize);
  96. m_gridVerts1D = m_gridSize + 1;
  97. m_gridVerts2D = m_gridVerts1D * m_gridVerts1D;
  98. return true;
  99. }
  100. return false;
  101. }
  102. void TerrainMeshManager::SetMaterial(MaterialInstance materialInstance)
  103. {
  104. if (m_materialInstance != materialInstance || m_materialInstance->GetCurrentChangeId() != m_lastMaterialChangeId)
  105. {
  106. m_lastMaterialChangeId = materialInstance->GetCurrentChangeId();
  107. m_materialInstance = materialInstance;
  108. // Queue the load of the material's shaders now since they'll be needed later.
  109. m_materialInstance->ForAllShaderItems(
  110. [&](const AZ::Name&, const AZ::RPI::ShaderCollection::Item& shaderItem)
  111. {
  112. AZ::Data::Asset<AZ::RPI::ShaderAsset> shaderAsset = shaderItem.GetShaderAsset();
  113. if (!shaderAsset.IsReady())
  114. {
  115. shaderAsset.QueueLoad();
  116. }
  117. return true;
  118. });
  119. m_rebuildDrawPackets = true;
  120. }
  121. }
  122. bool TerrainMeshManager::IsInitialized() const
  123. {
  124. return m_isInitialized;
  125. }
  126. void TerrainMeshManager::ClearSectorBuffers()
  127. {
  128. // RemoveRayTracedMeshes() needs to be called first since it uses pointers into the sector data stored in m_sectorLods.
  129. RemoveRayTracedMeshes();
  130. m_candidateSectors.clear();
  131. m_sectorsThatNeedSrgCompiled.clear();
  132. m_sectorLods.clear();
  133. }
  134. void TerrainMeshManager::Reset()
  135. {
  136. if (m_meshMovedFlag.IsValid())
  137. {
  138. m_parentScene->GetViewTagBitRegistry().ReleaseTag(m_meshMovedFlag);
  139. }
  140. ClearSectorBuffers();
  141. m_xyPositions.clear();
  142. m_cachedDrawData.clear();
  143. m_rebuildSectors = true;
  144. }
  145. void TerrainMeshManager::RemoveRayTracedMeshes()
  146. {
  147. AZ_Assert(m_rayTracedItems.empty() || !m_sectorLods.empty(),
  148. "RemoveRayTracedMeshes() is being called after the underlying sector data has been deleted. "
  149. "The pointers stored in it are no longer valid.");
  150. for (RayTracedItem& item : m_rayTracedItems)
  151. {
  152. if (auto& rtData = item.m_sector->m_rtData; rtData)
  153. {
  154. RtSector::MeshGroup& meshGroup = rtData->m_meshGroups.at(item.m_meshGroupIndex);
  155. meshGroup.m_isVisible = false;
  156. m_rayTracingFeatureProcessor->RemoveMesh(meshGroup.m_id);
  157. }
  158. }
  159. m_rayTracedItems.clear();
  160. }
  161. void TerrainMeshManager::Update(const AZ::RPI::ViewPtr mainView, AZ::Data::Instance<AZ::RPI::ShaderResourceGroup>& terrainSrg)
  162. {
  163. if (m_rebuildDrawPackets)
  164. {
  165. // Rebuild the draw packets when the material or shaders change.
  166. RebuildDrawPackets();
  167. m_rebuildDrawPackets = false;
  168. }
  169. if (m_rebuildSectors)
  170. {
  171. // Rebuild the sectors when the configuration or terrain world changes
  172. CreateCommonBuffers();
  173. RebuildSectors();
  174. m_rebuildSectors = false;
  175. }
  176. ShaderMeshData meshData;
  177. mainView->GetCameraTransform().GetTranslation().StoreToFloat3(meshData.m_mainCameraPosition.data());
  178. meshData.m_firstLodDistance = m_config.m_firstLodDistance;
  179. meshData.m_rcpClodDistance = 1.0f / m_config.m_clodDistance;
  180. meshData.m_rcpGridSize = 1.0f / m_gridSize;
  181. meshData.m_gridToQuadScale = m_gridSize / 255.0f;
  182. terrainSrg->SetConstant(m_srgMeshDataIndex, meshData);
  183. }
  184. void TerrainMeshManager::CheckLodGridsForUpdate(AZ::Vector3 newPosition)
  185. {
  186. // lods of sectors that need updating, separated by LOD level.
  187. AZStd::vector<AZStd::vector<Sector*>> sectorsToUpdate(m_sectorLods.size());
  188. bool anySectorsUpdated = false;
  189. for (uint32_t lodLevel = 0; lodLevel < m_sectorLods.size(); ++lodLevel)
  190. {
  191. SectorLodGrid& lodGrid = m_sectorLods.at(lodLevel);
  192. // Figure out what the start coordinate should be for this lod level.
  193. const Vector2i newStartCoord = [&]()
  194. {
  195. const float maxDistance = m_config.m_firstLodDistance * aznumeric_cast<float>(1 << lodLevel);
  196. const float gridMeters = (m_gridSize * m_sampleSpacing) * (1 << lodLevel);
  197. const int32_t startCoordX = aznumeric_cast<int32_t>(AZStd::floorf((newPosition.GetX() - maxDistance) / gridMeters));
  198. const int32_t startCoordY = aznumeric_cast<int32_t>(AZStd::floorf((newPosition.GetY() - maxDistance) / gridMeters));
  199. // If the start coord for the lod level is different, then some of the sectors will need to be updated.
  200. // There's 1 sector of wiggle room, so make sure we've moving the lod's start coord by as little as possible.
  201. auto coordCheck = [&](int32_t newStartCoord, int32_t lodStartCoord) -> int32_t
  202. {
  203. return
  204. newStartCoord > lodStartCoord + 1 ? newStartCoord - 1 :
  205. newStartCoord < lodStartCoord ? newStartCoord :
  206. lodStartCoord;
  207. };
  208. return Vector2i(coordCheck(startCoordX, lodGrid.m_startCoord.m_x), coordCheck(startCoordY, lodGrid.m_startCoord.m_y));
  209. }();
  210. if (lodGrid.m_startCoord != newStartCoord)
  211. {
  212. lodGrid.m_startCoord = newStartCoord;
  213. const uint32_t firstSectorIndexX = (m_1dSectorCount + (newStartCoord.m_x % m_1dSectorCount)) % m_1dSectorCount;
  214. const uint32_t firstSectorIndexY = (m_1dSectorCount + (newStartCoord.m_y % m_1dSectorCount)) % m_1dSectorCount;
  215. for (uint32_t xOffset = 0; xOffset < m_1dSectorCount; ++xOffset)
  216. {
  217. for (uint32_t yOffset = 0; yOffset < m_1dSectorCount; ++yOffset)
  218. {
  219. // Sectors use toroidal addressing to avoid needing to update any more than necessary.
  220. const uint32_t sectorIndexX = (firstSectorIndexX + xOffset) % m_1dSectorCount;
  221. const uint32_t sectorIndexY = (firstSectorIndexY + yOffset) % m_1dSectorCount;
  222. const uint32_t sectorIndex = sectorIndexY * m_1dSectorCount + sectorIndexX;
  223. const Vector2i worldCoord = newStartCoord + Vector2i(xOffset, yOffset);
  224. Sector& sector = lodGrid.m_sectors.at(sectorIndex);
  225. if (sector.m_worldCoord != worldCoord)
  226. {
  227. sector.m_worldCoord = worldCoord;
  228. sectorsToUpdate.at(lodLevel).push_back(&sector);
  229. anySectorsUpdated = true;
  230. }
  231. }
  232. }
  233. }
  234. }
  235. if (anySectorsUpdated)
  236. {
  237. ProcessSectorUpdates(sectorsToUpdate);
  238. return;
  239. }
  240. }
  241. AZ::RHI::StreamBufferView TerrainMeshManager::CreateStreamBufferView(AZ::Data::Instance<AZ::RPI::Buffer>& buffer, uint32_t offset)
  242. {
  243. return
  244. {
  245. *buffer->GetRHIBuffer(),
  246. offset,
  247. aznumeric_cast<uint32_t>(buffer->GetBufferSize()),
  248. buffer->GetBufferViewDescriptor().m_elementSize
  249. };
  250. }
  251. void TerrainMeshManager::BuildDrawPacket(Sector& sector)
  252. {
  253. AZ::RHI::DrawPacketBuilder drawPacketBuilder{AZ::RHI::MultiDevice::AllDevices};
  254. uint32_t indexCount = m_indexBuffer->GetBufferViewDescriptor().m_elementCount;
  255. sector.m_geometryView.SetDrawArguments(AZ::RHI::DrawIndexed(0, indexCount, 0));
  256. sector.m_geometryView.SetIndexBufferView(m_indexBufferView);
  257. drawPacketBuilder.Begin(nullptr);
  258. drawPacketBuilder.SetGeometryView(&sector.m_geometryView);
  259. drawPacketBuilder.AddShaderResourceGroup(sector.m_srg->GetRHIShaderResourceGroup());
  260. drawPacketBuilder.AddShaderResourceGroup(m_materialInstance->GetRHIShaderResourceGroup());
  261. sector.m_perDrawSrgs.clear();
  262. for (CachedDrawData& drawData : m_cachedDrawData)
  263. {
  264. AZ::Data::Instance<AZ::RPI::Shader>& shader = drawData.m_shader;
  265. AZ::RHI::DrawPacketBuilder::DrawRequest drawRequest;
  266. drawRequest.m_listTag = drawData.m_drawListTag;
  267. drawRequest.m_pipelineState = drawData.m_pipelineState;
  268. drawRequest.m_streamIndices = sector.m_geometryView.GetFullStreamBufferIndices();
  269. drawRequest.m_stencilRef = AZ::Render::StencilRefs::UseDiffuseGIPass | AZ::Render::StencilRefs::UseIBLSpecularPass;
  270. if (drawData.m_materialPipelineName != AZ::RPI::MaterialPipelineNone)
  271. {
  272. AZ::RHI::DrawFilterTag pipelineTag = m_parentScene->GetDrawFilterTagRegistry()->AcquireTag(drawData.m_materialPipelineName);
  273. AZ_Assert(pipelineTag.IsValid(), "Could not acquire pipeline filter tag '%s'.", drawData.m_materialPipelineName.GetCStr());
  274. drawRequest.m_drawFilterMask = 1 << pipelineTag.GetIndex();
  275. }
  276. AZ::Data::Instance<AZ::RPI::ShaderResourceGroup> drawSrg;
  277. if (drawData.m_drawSrgLayout)
  278. {
  279. // If the DrawSrg exists we must create and bind it, otherwise the CommandList will fail validation for SRG being null
  280. drawSrg = AZ::RPI::ShaderResourceGroup::Create(shader->GetAsset(), shader->GetSupervariantIndex(), drawData.m_drawSrgLayout->GetName());
  281. if (drawData.m_shaderVariant.UseKeyFallback() && drawData.m_drawSrgLayout->HasShaderVariantKeyFallbackEntry())
  282. {
  283. drawSrg->SetShaderVariantKeyFallbackValue(drawData.m_shaderOptions.GetShaderVariantKeyFallbackValue());
  284. }
  285. drawSrg->Compile();
  286. }
  287. if (drawSrg)
  288. {
  289. drawRequest.m_uniqueShaderResourceGroup = drawSrg->GetRHIShaderResourceGroup();
  290. sector.m_perDrawSrgs.push_back(drawSrg);
  291. }
  292. drawPacketBuilder.AddDrawItem(drawRequest);
  293. }
  294. AZ::RHI::DrawPacketBuilder commonQuadrantDrawPacketBuilder = drawPacketBuilder; // Copy of the draw packet builder to use later.
  295. sector.m_rhiDrawPacket = drawPacketBuilder.End();
  296. // Generate draw packets for each of the quadrants so they can be used to fill in places where the previous LOD didn't draw.
  297. // Due to z-ordered index buffer, no additional data is needed, just a different index offset and index count. Each quarter of
  298. // the index buffer perfectly corresponds to a quadrant of the sector in Z order (TL, TR, BL, BR).
  299. uint32_t lowerLodIndexCount = indexCount / 4;
  300. for (uint32_t i = 0; i < 4; ++i)
  301. {
  302. sector.m_quadrantGeometryViews[i] = sector.m_geometryView;
  303. sector.m_quadrantGeometryViews[i].SetDrawArguments( AZ::RHI::DrawIndexed(0, lowerLodIndexCount, lowerLodIndexCount * i) );
  304. AZ::RHI::DrawPacketBuilder quadrantDrawPacketBuilder = commonQuadrantDrawPacketBuilder;
  305. quadrantDrawPacketBuilder.SetGeometryView(&sector.m_quadrantGeometryViews[i]);
  306. sector.m_rhiDrawPacketQuadrant[i] = quadrantDrawPacketBuilder.End();
  307. }
  308. }
  309. void TerrainMeshManager::BuildRtSector(Sector& sector, uint32_t lodLevel)
  310. {
  311. RtSector& rtSector = *sector.m_rtData;
  312. AZStd::string positionName = AZStd::string::format("Terrain Positions- Lod %u, Sector (%u, %u)", lodLevel, sector.m_worldCoord.m_x, sector.m_worldCoord.m_x);
  313. AZStd::string normalName = AZStd::string::format("Terrain Normals - Lod %u, Sector (%u, %u)", lodLevel, sector.m_worldCoord.m_x, sector.m_worldCoord.m_x);
  314. rtSector.m_positionsBuffer = CreateRayTracingMeshBufferInstance(AZ::RHI::Format::R32G32B32_FLOAT, m_gridVerts2D, nullptr, positionName.c_str());
  315. rtSector.m_normalsBuffer = CreateRayTracingMeshBufferInstance(AZ::RHI::Format::R32G32B32_FLOAT, m_gridVerts2D, nullptr, positionName.c_str());
  316. // setup the stream and shader buffer views
  317. AZ::RHI::Buffer& rhiPositionsBuffer = *rtSector.m_positionsBuffer->GetRHIBuffer();
  318. uint32_t positionsBufferByteCount = aznumeric_cast<uint32_t>(rhiPositionsBuffer.GetDescriptor().m_byteCount);
  319. AZ::RHI::Format positionsBufferFormat = rtSector.m_positionsBuffer->GetBufferViewDescriptor().m_elementFormat;
  320. uint32_t positionsBufferElementSize = AZ::RHI::GetFormatSize(positionsBufferFormat);
  321. AZ::RHI::StreamBufferView positionsVertexBufferView(rhiPositionsBuffer, 0, positionsBufferByteCount, positionsBufferElementSize);
  322. AZ::RHI::BufferViewDescriptor positionsBufferDescriptor = AZ::RHI::BufferViewDescriptor::CreateRaw(0, positionsBufferByteCount);
  323. AZ::RHI::Buffer& rhiNormalsBuffer = *rtSector.m_normalsBuffer->GetRHIBuffer();
  324. uint32_t normalsBufferByteCount = aznumeric_cast<uint32_t>(rhiNormalsBuffer.GetDescriptor().m_byteCount);
  325. AZ::RHI::Format normalsBufferFormat = rtSector.m_normalsBuffer->GetBufferViewDescriptor().m_elementFormat;
  326. uint32_t normalsBufferElementSize = AZ::RHI::GetFormatSize(normalsBufferFormat);
  327. AZ::RHI::StreamBufferView normalsVertexBufferView(rhiNormalsBuffer, 0, normalsBufferByteCount, normalsBufferElementSize);
  328. AZ::RHI::BufferViewDescriptor normalsBufferDescriptor = AZ::RHI::BufferViewDescriptor::CreateRaw(0, normalsBufferByteCount);
  329. AZ::RHI::Buffer& rhiIndexBuffer = *m_rtIndexBuffer->GetRHIBuffer();
  330. AZ::RHI::IndexFormat indexBufferFormat = AZ::RHI::IndexFormat::Uint32;
  331. uint32_t totalIndexBufferByteCount = aznumeric_cast<uint32_t>(rhiIndexBuffer.GetDescriptor().m_byteCount);
  332. uint32_t indexElementSize = AZ::RHI::GetIndexFormatSize(indexBufferFormat);
  333. // Create the ray tracing meshes. Each sector has 5 meshes which all share the same data - one mesh that covers the whole
  334. // sector, and 4 meshes that cover each quadrant of the sector.
  335. auto createMesh = [&](RtSector::MeshGroup& meshGroup, uint32_t indexBufferByteOffset, uint32_t indexBufferByteCount)
  336. {
  337. meshGroup.m_submeshVector.clear();
  338. AZ::Render::RayTracingFeatureProcessorInterface::SubMesh& subMesh = meshGroup.m_submeshVector.emplace_back();
  339. subMesh.m_positionFormat = positionsBufferFormat;
  340. subMesh.m_positionVertexBufferView = positionsVertexBufferView;
  341. subMesh.m_positionShaderBufferView = rhiPositionsBuffer.GetBufferView(positionsBufferDescriptor);
  342. subMesh.m_normalFormat = normalsBufferFormat;
  343. subMesh.m_normalVertexBufferView = normalsVertexBufferView;
  344. subMesh.m_normalShaderBufferView = rhiNormalsBuffer.GetBufferView(normalsBufferDescriptor);
  345. subMesh.m_indexBufferView = AZ::RHI::IndexBufferView(rhiIndexBuffer, indexBufferByteOffset, indexBufferByteCount, indexBufferFormat);
  346. subMesh.m_material.m_baseColor = AZ::Color::CreateFromVector3(AZ::Vector3(0.18f));
  347. AZ::RHI::BufferViewDescriptor indexBufferDescriptor;
  348. indexBufferDescriptor.m_elementOffset = indexBufferByteOffset / indexElementSize;
  349. indexBufferDescriptor.m_elementCount = indexBufferByteCount / indexElementSize;
  350. indexBufferDescriptor.m_elementSize = indexElementSize;
  351. indexBufferDescriptor.m_elementFormat = AZ::RHI::Format::R32_UINT;
  352. subMesh.m_indexShaderBufferView = rhiIndexBuffer.GetBufferView(indexBufferDescriptor);
  353. meshGroup.m_mesh.m_assetId = AZ::Data::AssetId(meshGroup.m_id);
  354. float xyScale = (m_gridSize * m_sampleSpacing) * (1 << lodLevel);
  355. meshGroup.m_mesh.m_transform = AZ::Transform::CreateIdentity();
  356. meshGroup.m_mesh.m_nonUniformScale = AZ::Vector3(xyScale, xyScale, m_worldHeightBounds.m_max - m_worldHeightBounds.m_min);
  357. meshGroup.m_mesh.m_instanceMask |=
  358. static_cast<uint32_t>(AZ::RHI::RayTracingAccelerationStructureInstanceInclusionMask::STATIC_MESH);
  359. };
  360. createMesh(rtSector.m_meshGroups[0], 0, totalIndexBufferByteCount);
  361. uint32_t quarterCount = totalIndexBufferByteCount / 4;
  362. createMesh(rtSector.m_meshGroups[1], quarterCount * 0, quarterCount);
  363. createMesh(rtSector.m_meshGroups[2], quarterCount * 1, quarterCount);
  364. createMesh(rtSector.m_meshGroups[3], quarterCount * 2, quarterCount);
  365. createMesh(rtSector.m_meshGroups[4], quarterCount * 3, quarterCount);
  366. }
  367. void TerrainMeshManager::RebuildSectors()
  368. {
  369. const float gridMeters = m_gridSize * m_sampleSpacing;
  370. const auto& materialAsset = m_materialInstance->GetAsset();
  371. const auto& shaderAsset = materialAsset->GetMaterialTypeAsset()->GetShaderAssetForObjectSrg();
  372. // Calculate the largest potential number of sectors needed per dimension at any lod level.
  373. const float firstLodDiameter = m_config.m_firstLodDistance * 2.0f;
  374. m_1dSectorCount = aznumeric_cast<uint32_t>(AZStd::ceilf(firstLodDiameter / gridMeters));
  375. // If the sector grid doesn't line up perfectly with the camera, it will cover part of a sector
  376. // along each boundary, so we need an extra sector to cover in those cases.
  377. m_1dSectorCount += 1;
  378. // Add one sector of wiggle room so to avoid thrashing updates when going back and forth over a boundary.
  379. m_1dSectorCount += 1;
  380. ClearSectorBuffers();
  381. const uint8_t lodCount = aznumeric_cast<uint8_t>(AZStd::ceilf(log2f(AZStd::GetMax(1.0f, m_config.m_renderDistance / m_config.m_firstLodDistance)) + 1.0f));
  382. m_sectorLods.reserve(lodCount);
  383. // Create all the sectors with uninitialized SRGs. The SRGs will be updated later by CheckLodGridsForUpdate().
  384. m_indexBufferView =
  385. {
  386. *m_indexBuffer->GetRHIBuffer(),
  387. 0,
  388. aznumeric_cast<uint32_t>(m_indexBuffer->GetBufferSize()),
  389. AZ::RHI::IndexFormat::Uint16
  390. };
  391. for (uint8_t lodLevel = 0; lodLevel < lodCount; ++lodLevel)
  392. {
  393. m_sectorLods.push_back({});
  394. SectorLodGrid& lodGrid = m_sectorLods.back();
  395. lodGrid.m_sectors.resize(m_1dSectorCount * m_1dSectorCount);
  396. for (Sector& sector : lodGrid.m_sectors)
  397. {
  398. sector.m_srg = AZ::RPI::ShaderResourceGroup::Create(shaderAsset, materialAsset->GetObjectSrgLayout()->GetName());
  399. sector.m_heightsNormalsBuffer = CreateMeshBufferInstance(sizeof(HeightNormalVertex), m_gridVerts2D);
  400. sector.m_geometryView.ClearStreamBufferViews();
  401. AZStd::vector<AZ::RHI::StreamBufferView> streamBufferViews;
  402. streamBufferViews.resize(StreamIndex::Count);
  403. streamBufferViews[StreamIndex::XYPositions] = CreateStreamBufferView(m_xyPositionsBuffer);
  404. streamBufferViews[StreamIndex::Heights] = CreateStreamBufferView(sector.m_heightsNormalsBuffer);
  405. streamBufferViews[StreamIndex::Normals] = CreateStreamBufferView(sector.m_heightsNormalsBuffer, AZ::RHI::GetFormatSize(HeightFormat));
  406. if (m_config.m_clodEnabled)
  407. {
  408. sector.m_lodHeightsNormalsBuffer = CreateMeshBufferInstance(sizeof(HeightNormalVertex), m_gridVerts2D);
  409. streamBufferViews[StreamIndex::LodHeights] = CreateStreamBufferView(sector.m_lodHeightsNormalsBuffer);
  410. streamBufferViews[StreamIndex::LodNormals] = CreateStreamBufferView(sector.m_lodHeightsNormalsBuffer, AZ::RHI::GetFormatSize(HeightFormat));
  411. }
  412. else
  413. {
  414. streamBufferViews[StreamIndex::LodHeights] = CreateStreamBufferView(m_dummyLodHeightsNormalsBuffer);
  415. streamBufferViews[StreamIndex::LodNormals] = CreateStreamBufferView(m_dummyLodHeightsNormalsBuffer, AZ::RHI::GetFormatSize(HeightFormat));
  416. }
  417. sector.m_geometryView.SetStreamBufferViews(streamBufferViews);
  418. BuildDrawPacket(sector);
  419. if (m_rayTracingEnabled)
  420. {
  421. sector.m_rtData = AZStd::make_unique<RtSector>();
  422. BuildRtSector(sector, lodLevel);
  423. }
  424. }
  425. }
  426. }
  427. void TerrainMeshManager::DrawMeshes(const AZ::RPI::FeatureProcessor::RenderPacket& process, const AZ::RPI::ViewPtr mainView)
  428. {
  429. AZ::Vector3 mainCameraPosition = mainView->GetCameraTransform().GetTranslation();
  430. CheckLodGridsForUpdate(mainCameraPosition);
  431. for (Sector* sector : m_sectorsThatNeedSrgCompiled)
  432. {
  433. sector->m_srg->Compile();
  434. sector->m_isQueuedForSrgCompile = false;
  435. }
  436. m_sectorsThatNeedSrgCompiled.clear();
  437. // Only update candidate sectors if the camera has moved. This could probably be relaxed further, but is a good starting point.
  438. const float minMovedDistanceSq = m_sampleSpacing * m_sampleSpacing;
  439. bool terrainChanged = m_candidateSectors.empty(); // candidate sectors need to be recalculated any time the terrain changes
  440. if (terrainChanged || m_cameraPosition.GetDistanceSq(mainCameraPosition) > minMovedDistanceSq)
  441. {
  442. m_cameraPosition = mainCameraPosition;
  443. UpdateCandidateSectors();
  444. }
  445. const AZ::RPI::AuxGeomDrawPtr auxGeomPtr = r_debugTerrainAabbs ?
  446. AZ::RPI::AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(m_parentScene) :
  447. nullptr;
  448. // Compare view frustums against the list of candidate sectors and submit those sectors to draw.
  449. for (auto& view : process.m_views)
  450. {
  451. if (terrainChanged)
  452. {
  453. view->ApplyFlags(m_meshMovedFlag.GetIndex());
  454. }
  455. const AZ::Frustum viewFrustum = AZ::Frustum::CreateFromMatrixColumnMajor(view->GetWorldToClipMatrix());
  456. for (CandidateSector& candidateSector : m_candidateSectors)
  457. {
  458. if (candidateSector.m_rhiDrawPacket && AZ::ShapeIntersection::Overlaps(viewFrustum, candidateSector.m_aabb))
  459. {
  460. view->AddDrawPacket(candidateSector.m_rhiDrawPacket);
  461. if (auxGeomPtr && view == mainView)
  462. {
  463. auxGeomPtr->DrawAabb(candidateSector.m_aabb, AZ::Colors::Red, AZ::RPI::AuxGeomDraw::DrawStyle::Line);
  464. }
  465. }
  466. }
  467. }
  468. }
  469. void TerrainMeshManager::SetRebuildDrawPackets()
  470. {
  471. m_rebuildDrawPackets = true;
  472. }
  473. void TerrainMeshManager::RebuildDrawPackets()
  474. {
  475. m_materialInstance->ApplyGlobalShaderOptions();
  476. m_cachedDrawData.clear();
  477. m_candidateSectors.clear();
  478. // Rebuild common draw packet data
  479. m_materialInstance->ForAllShaderItems(
  480. [&](const AZ::Name& materialPipelineName, const AZ::RPI::ShaderCollection::Item& shaderItem)
  481. {
  482. if (!shaderItem.IsEnabled())
  483. {
  484. return true;
  485. }
  486. // Force load and cache shader instances.
  487. AZ::Data::Instance<AZ::RPI::Shader> shader = AZ::RPI::Shader::FindOrCreate(shaderItem.GetShaderAsset());
  488. if (!shader)
  489. {
  490. AZ_Error(
  491. TerrainMeshManagerName,
  492. false,
  493. "Shader '%s'. Failed to find or create instance",
  494. shaderItem.GetShaderAsset()->GetName().GetCStr());
  495. return true;
  496. }
  497. // Skip the shader item without creating the shader instance
  498. // if the mesh is not going to be rendered based on the draw tag
  499. AZ::RHI::RHISystemInterface* rhiSystem = AZ::RHI::RHISystemInterface::Get();
  500. AZ::RHI::DrawListTagRegistry* drawListTagRegistry = rhiSystem->GetDrawListTagRegistry();
  501. // Use the explicit draw list override if exists.
  502. AZ::RHI::DrawListTag drawListTag = shaderItem.GetDrawListTagOverride();
  503. if (drawListTag.IsNull())
  504. {
  505. drawListTag = drawListTagRegistry->FindTag(shaderItem.GetShaderAsset()->GetDrawListName());
  506. }
  507. if (!m_parentScene->HasOutputForPipelineState(drawListTag))
  508. {
  509. // drawListTag not found in this scene, so skip this item
  510. return true;
  511. }
  512. // Set all unspecified shader options to default values, so that we get the most specialized variant possible.
  513. // (because FindVariantStableId treats unspecified options as a request specifically for a variant that doesn't specify those
  514. // options) [GFX TODO][ATOM-3883] We should consider updating the FindVariantStableId algorithm to handle default values for us,
  515. // and remove this step here.
  516. AZ::RPI::ShaderOptionGroup shaderOptions = *shaderItem.GetShaderOptions();
  517. shaderOptions.SetUnspecifiedToDefaultValues();
  518. const AZ::RPI::ShaderVariantId finalVariantId = shaderOptions.GetShaderVariantId();
  519. const AZ::RPI::ShaderVariant& variant = shader->GetVariant(finalVariantId);
  520. AZ::RHI::PipelineStateDescriptorForDraw pipelineStateDescriptor;
  521. variant.ConfigurePipelineState(pipelineStateDescriptor, shaderOptions);
  522. AZ::RHI::InputStreamLayoutBuilder layoutBuilder;
  523. layoutBuilder.AddBuffer()->Channel(AZ::RHI::ShaderSemantic{ "POSITION", 0 }, XYPositionFormat);
  524. layoutBuilder.AddBuffer()->Channel(AZ::RHI::ShaderSemantic{ "POSITION", 1 }, HeightFormat)->Padding(2);
  525. layoutBuilder.AddBuffer()->Channel(AZ::RHI::ShaderSemantic{ "NORMAL", 0 }, NormalFormat)->Padding(2);
  526. layoutBuilder.AddBuffer()->Channel(AZ::RHI::ShaderSemantic{ "POSITION", 2 }, HeightFormat)->Padding(2);
  527. layoutBuilder.AddBuffer()->Channel(AZ::RHI::ShaderSemantic{ "NORMAL", 1 }, NormalFormat)->Padding(2);
  528. pipelineStateDescriptor.m_inputStreamLayout = layoutBuilder.End();
  529. m_parentScene->ConfigurePipelineState(drawListTag, pipelineStateDescriptor);
  530. const AZ::RHI::PipelineState* pipelineState = shader->AcquirePipelineState(pipelineStateDescriptor);
  531. if (!pipelineState)
  532. {
  533. AZ_Error(
  534. TerrainMeshManagerName,
  535. false,
  536. "Shader '%s'. Failed to acquire default pipeline state",
  537. shaderItem.GetShaderAsset()->GetName().GetCStr());
  538. return true;
  539. }
  540. auto drawSrgLayout = shader->GetAsset()->GetDrawSrgLayout(shader->GetSupervariantIndex());
  541. m_cachedDrawData.push_back({ shader, shaderOptions, pipelineState, drawListTag, drawSrgLayout, variant, materialPipelineName });
  542. return true;
  543. });
  544. // Rebuild the draw packets themselves
  545. for (auto& lodGrid : m_sectorLods)
  546. {
  547. for (auto& sector : lodGrid.m_sectors)
  548. {
  549. BuildDrawPacket(sector);
  550. }
  551. }
  552. }
  553. void TerrainMeshManager::OnTerrainDataCreateEnd()
  554. {
  555. OnTerrainDataChanged(AZ::Aabb::CreateNull(), TerrainDataChangedMask::HeightData);
  556. }
  557. void TerrainMeshManager::OnTerrainDataDestroyBegin()
  558. {
  559. ClearSectorBuffers();
  560. m_rebuildSectors = true;
  561. }
  562. void TerrainMeshManager::OnTerrainDataChanged([[maybe_unused]] const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask)
  563. {
  564. if ((dataChangedMask & (TerrainDataChangedMask::HeightData | TerrainDataChangedMask::Settings)) != TerrainDataChangedMask::None)
  565. {
  566. AzFramework::Terrain::FloatRange heightBounds = AzFramework::Terrain::FloatRange::CreateNull();
  567. AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
  568. heightBounds, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightBounds);
  569. float queryResolution = 1.0f;
  570. AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
  571. queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution);
  572. bool gridSizeChanged = UpdateGridSize(m_config.m_firstLodDistance);
  573. // Sectors need to be rebuilt when certain settings change.
  574. m_rebuildSectors = m_rebuildSectors || (m_sampleSpacing != queryResolution) || (heightBounds != m_worldHeightBounds) || gridSizeChanged;
  575. m_worldHeightBounds = heightBounds;
  576. m_sampleSpacing = queryResolution;
  577. if (dirtyRegion.IsValid())
  578. {
  579. if (!m_rebuildSectors)
  580. {
  581. // Rebuild any sectors in the dirty region if they aren't all being rebuilt
  582. AZStd::vector<AZStd::vector<Sector*>> sectorsToUpdate(m_sectorLods.size());
  583. ForOverlappingSectors(dirtyRegion,
  584. [&sectorsToUpdate](Sector& sectorData, uint32_t lodLevel)
  585. {
  586. sectorsToUpdate.at(lodLevel).push_back(&sectorData);
  587. }
  588. );
  589. if (!sectorsToUpdate.empty())
  590. {
  591. ProcessSectorUpdates(sectorsToUpdate);
  592. }
  593. }
  594. }
  595. }
  596. }
  597. void TerrainMeshManager::CreateCommonBuffers()
  598. {
  599. // This function initializes positions and indices that are common to all terrain sectors. The indices are laid out
  600. // using a z-order curve (Morton code) which helps triangles which are close in space to also be close in the index
  601. // buffer. This in turn increases the probability that previously processed vertices will be in the vertex cache.
  602. // Generate x and y coordinates using Moser-de Bruijn sequences, so the final z-order position can be found quickly by interleaving.
  603. AZ_Assert(m_gridSize < AZStd::numeric_limits<uint8_t>::max(),
  604. "The following equation to generate z-order indices requires the number to be 8 or fewer bits.");
  605. AZStd::vector<uint16_t> zOrderX(m_gridSize);
  606. AZStd::vector<uint16_t> zOrderY(m_gridSize);
  607. for (uint16_t i = 0; i < m_gridSize; ++i)
  608. {
  609. // This will take any 8 bit number and put 0's in between each bit. For instance 0b1011 becomes 0b1000101.
  610. uint16_t value = ((i * 0x0101010101010101ULL & 0x8040201008040201ULL) * 0x0102040810204081ULL >> 49) & 0x5555;
  611. zOrderX.at(i) = value;
  612. zOrderY.at(i) = value << 1;
  613. }
  614. AZStd::vector<uint16_t> indices;
  615. indices.resize_no_construct(m_gridSize * m_gridSize * 6); // total number of quads, 2 triangles with 6 indices per quad.
  616. // Create the indices for a mesh patch in z-order for vertex cache optimization.
  617. for (uint16_t y = 0; y < m_gridSize; ++y)
  618. {
  619. for (uint16_t x = 0; x < m_gridSize; ++x)
  620. {
  621. uint32_t quadOrder = (zOrderX[x] | zOrderY[y]); // Interleave the x and y arrays from above for a final z-order index.
  622. quadOrder *= 6; // 6 indices per quad (2 triangles, 3 vertices each)
  623. const uint16_t topLeft = y * m_gridVerts1D + x;
  624. const uint16_t topRight = topLeft + 1;
  625. const uint16_t bottomLeft = topLeft + m_gridVerts1D;
  626. const uint16_t bottomRight = bottomLeft + 1;
  627. indices.at(quadOrder + 0) = topLeft;
  628. indices.at(quadOrder + 1) = topRight;
  629. indices.at(quadOrder + 2) = bottomLeft;
  630. indices.at(quadOrder + 3) = bottomLeft;
  631. indices.at(quadOrder + 4) = topRight;
  632. indices.at(quadOrder + 5) = bottomRight;
  633. }
  634. }
  635. // Infer the vertex order from the indices for cache efficient vertex buffer reads. Create a table that
  636. // can quickly map from a linear order (y * m_gridVerts1D + x) to the order dictated by the indices. Update
  637. // the index buffer to point directly to these new indices.
  638. constexpr uint16_t VertexNotSet = 0xFFFF;
  639. m_vertexOrder = AZStd::vector<uint16_t>(m_gridVerts2D, VertexNotSet);
  640. uint16_t vertex = 0;
  641. for (uint16_t& index : indices)
  642. {
  643. if (m_vertexOrder.at(index) == VertexNotSet)
  644. {
  645. // This is the first time this vertex has been seen in the index buffer, add it to the vertex order mapper.
  646. m_vertexOrder.at(index) = vertex;
  647. index = vertex;
  648. ++vertex;
  649. }
  650. else
  651. {
  652. // This vertex has already been added, so just update the index buffer to point to it.
  653. index = m_vertexOrder.at(index);
  654. }
  655. }
  656. m_indexBuffer = CreateMeshBufferInstance(
  657. AZ::RHI::GetFormatSize(AZ::RHI::Format::R16_UINT),
  658. aznumeric_cast<uint32_t>(indices.size()),
  659. indices.data());
  660. if (m_rayTracingEnabled)
  661. {
  662. // Generate a 32 bit index buffer for ray tracing by copying and transforming the 16 bit index buffer.
  663. AZStd::vector<uint32_t> rtIndices;
  664. rtIndices.resize_no_construct(indices.size());
  665. AZStd::transform(indices.begin(), indices.end(), rtIndices.begin(),
  666. [](uint16_t value)
  667. {
  668. return static_cast<uint32_t>(value);
  669. }
  670. );
  671. m_rtIndexBuffer = CreateMeshBufferInstance(
  672. AZ::RHI::GetFormatSize(AZ::RHI::Format::R32_UINT),
  673. aznumeric_cast<uint32_t>(rtIndices.size()),
  674. rtIndices.data());
  675. }
  676. // Create x/y positions. These are the same for all sectors since they're in local space.
  677. m_xyPositions.resize_no_construct(m_gridVerts2D);
  678. for (uint8_t y = 0; y < m_gridVerts1D; ++y)
  679. {
  680. for (uint8_t x = 0; x < m_gridVerts1D; ++x)
  681. {
  682. uint16_t zOrderCoord = m_vertexOrder.at(y * m_gridVerts1D + x);
  683. m_xyPositions.at(zOrderCoord) = { x, y };
  684. }
  685. }
  686. m_xyPositionsBuffer = CreateMeshBufferInstance(
  687. AZ::RHI::GetFormatSize(XYPositionFormat),
  688. aznumeric_cast<uint32_t>(m_xyPositions.size()),
  689. m_xyPositions.data());
  690. m_dummyLodHeightsNormalsBuffer = CreateMeshBufferInstance(sizeof(HeightNormalVertex), m_gridVerts2D, nullptr);
  691. }
  692. void TerrainMeshManager::UpdateSectorBuffers(Sector& sector, const AZStd::span<const HeightNormalVertex> heightsNormals)
  693. {
  694. sector.m_heightsNormalsBuffer->UpdateData(heightsNormals.data(), heightsNormals.size_bytes());
  695. if (sector.m_rtData)
  696. {
  697. // While heightsNormals is in the exact format the terrain shader expects for optimum efficiency, for
  698. // ray tracing it needs to be a more conventional layout. So here we generate more traditional R32G32B32
  699. // data from the highly compressed HeightNormalVertex.
  700. struct RtVert
  701. {
  702. float x;
  703. float y;
  704. float z;
  705. };
  706. AZStd::vector<RtVert> rtPositions(heightsNormals.size());
  707. AZStd::vector<RtVert> rtNormals(heightsNormals.size());
  708. AZ_Assert(heightsNormals.size() == m_gridVerts2D, "Unexpected number of vertices.");
  709. constexpr float maxHeight = static_cast<float>(AZStd::numeric_limits<HeightDataType>::max());
  710. constexpr float maxNormal = static_cast<float>(AZStd::numeric_limits<NormalDataType>::max());
  711. for (uint32_t i = 0; i < heightsNormals.size(); ++i)
  712. {
  713. const HeightNormalVertex& heightNormal = heightsNormals[i];
  714. XYPosition xyPosition = m_xyPositions.at(i);
  715. float xyPositionMax = static_cast<float>(m_gridSize);
  716. rtPositions.at(i) =
  717. {
  718. xyPosition.m_posx / xyPositionMax,
  719. xyPosition.m_posy / xyPositionMax,
  720. heightNormal.m_height == NoTerrainVertexHeight ?
  721. 0.0f :
  722. heightNormal.m_height / maxHeight,
  723. };
  724. float normalX = heightNormal.m_normal.first / maxNormal;
  725. float normalY = heightNormal.m_normal.second / maxNormal;
  726. // It's a little unfortunate to use a sqrt to decode a normal which used a sqrt to encode in the
  727. // first place, but this avoids branching around ray tracing in GatherMeshData(). It also helps ensure
  728. // the ray traced normal lines up with the compressed one used in forward pass.
  729. float normalZ = sqrt(AZStd::GetMax(0.0f, 1.0f - normalX * normalX - normalY * normalY));
  730. rtNormals.at(i) = { normalX, normalY, normalZ };
  731. }
  732. sector.m_rtData->m_positionsBuffer->UpdateData(rtPositions.data(), rtPositions.size() * sizeof(RtVert));
  733. sector.m_rtData->m_normalsBuffer->UpdateData(rtNormals.data(), rtNormals.size() * sizeof(RtVert));
  734. // If the mesh is currently visible, it must be removed and re-added to update its data.
  735. for (RtSector::MeshGroup& meshGroup : sector.m_rtData->m_meshGroups)
  736. {
  737. if (meshGroup.m_isVisible)
  738. {
  739. AZ::Vector3 translation = sector.m_aabb.GetMin();
  740. translation.SetZ(m_worldHeightBounds.m_min);
  741. meshGroup.m_mesh.m_transform = AZ::Transform::CreateTranslation(translation);
  742. m_rayTracingFeatureProcessor->RemoveMesh(meshGroup.m_id);
  743. m_rayTracingFeatureProcessor->AddMesh(meshGroup.m_id, meshGroup.m_mesh, meshGroup.m_submeshVector);
  744. }
  745. }
  746. }
  747. }
  748. void TerrainMeshManager::UpdateSectorLodBuffers(Sector& sector,
  749. const AZStd::span<const HeightNormalVertex> originalHeightsNormals,
  750. const AZStd::span<const HeightNormalVertex> lodHeightsNormals)
  751. {
  752. // Store the height and normal information for the next lod level in each vertex for continuous LOD.
  753. AZStd::vector<HeightNormalVertex> clodHeightNormals;
  754. clodHeightNormals.resize_no_construct(m_gridVerts2D);
  755. const uint16_t lodGridVerts1D = (m_gridVerts1D >> 1) + 1;
  756. for (uint16_t yPos = 0; yPos < m_gridVerts1D; ++yPos)
  757. {
  758. for (uint16_t xPos = 0; xPos < m_gridVerts1D; ++xPos)
  759. {
  760. uint16_t index = yPos * m_gridVerts1D + xPos;
  761. uint16_t lodIndex1 = (yPos / 2) * lodGridVerts1D + (xPos / 2);
  762. uint16_t lodIndex2 = lodIndex1;
  763. if (xPos % 2 == 1)
  764. {
  765. // x position is between two vertices in the row
  766. ++lodIndex1;
  767. }
  768. if (yPos % 2 == 1)
  769. {
  770. // y position is between two vertices in the column
  771. lodIndex2 += lodGridVerts1D;
  772. }
  773. const uint16_t zOrderIndex = m_vertexOrder.at(index);
  774. if (lodHeightsNormals[lodIndex1].m_height == NoTerrainVertexHeight || lodHeightsNormals[lodIndex2].m_height == NoTerrainVertexHeight)
  775. {
  776. // One of the neighboring vertices has no data, so use the original height and normal
  777. clodHeightNormals[zOrderIndex] = originalHeightsNormals[zOrderIndex];
  778. }
  779. else
  780. {
  781. clodHeightNormals[zOrderIndex] =
  782. {
  783. HeightDataType((lodHeightsNormals[lodIndex1].m_height + lodHeightsNormals[lodIndex2].m_height) / 2),
  784. NormalXYDataType(
  785. {
  786. int8_t((lodHeightsNormals[lodIndex1].m_normal.first + lodHeightsNormals[lodIndex2].m_normal.first) / 2),
  787. int8_t((lodHeightsNormals[lodIndex1].m_normal.second + lodHeightsNormals[lodIndex2].m_normal.second) / 2)
  788. })
  789. };
  790. }
  791. }
  792. }
  793. sector.m_lodHeightsNormalsBuffer->UpdateData(clodHeightNormals.data(), clodHeightNormals.size() * sizeof(HeightNormalVertex), 0);
  794. }
  795. void TerrainMeshManager::GatherMeshData(SectorDataRequest request, AZStd::vector<HeightNormalVertex>& meshHeightsNormals, AZ::Aabb& meshAabb, bool& terrainExistsAnywhere)
  796. {
  797. const AZ::Vector2 stepSize(request.m_vertexSpacing);
  798. const uint16_t querySamplesX = request.m_samplesX + 2; // extra row / column on each side for normals.
  799. const uint16_t querySamplesY = request.m_samplesY + 2; // extra row / column on each side for normals.
  800. const uint16_t querySamplesCount = querySamplesX * querySamplesY;
  801. const uint16_t outputSamplesCount = request.m_samplesX * request.m_samplesY;
  802. AZStd::vector<float> heights;
  803. heights.resize_no_construct(querySamplesCount);
  804. meshHeightsNormals.resize_no_construct(outputSamplesCount);
  805. auto perPositionCallback = [this, &heights, querySamplesX, &terrainExistsAnywhere]
  806. (size_t xIndex, size_t yIndex, const AzFramework::SurfaceData::SurfacePoint& surfacePoint, bool terrainExists)
  807. {
  808. static constexpr float HeightDoesNotExistValue = -1.0f;
  809. const float height = surfacePoint.m_position.GetZ() - m_worldHeightBounds.m_min;
  810. heights.at(yIndex * querySamplesX + xIndex) = terrainExists ? height : HeightDoesNotExistValue;
  811. terrainExistsAnywhere = terrainExistsAnywhere || terrainExists;
  812. };
  813. AzFramework::Terrain::TerrainQueryRegion queryRegion(
  814. request.m_worldStartPosition - stepSize, querySamplesX, querySamplesY, stepSize);
  815. AzFramework::Terrain::TerrainDataRequestBus::Broadcast(
  816. &AzFramework::Terrain::TerrainDataRequests::QueryRegion,
  817. queryRegion,
  818. AzFramework::Terrain::TerrainDataRequests::TerrainDataMask::Heights,
  819. perPositionCallback,
  820. request.m_samplerType);
  821. if (!terrainExistsAnywhere)
  822. {
  823. // No height data, so just return
  824. return;
  825. }
  826. float zExtents = (m_worldHeightBounds.m_max - m_worldHeightBounds.m_min);
  827. const float rcpWorldZ = 1.0f / zExtents;
  828. const float vertexSpacing2 = request.m_vertexSpacing * 2.0f;
  829. // initialize min/max heights to the max/min possible values so they're immediately updated when a valid point is found.
  830. float minHeight = zExtents;
  831. float maxHeight = 0.0f;
  832. // float versions of int max to make sure a int->float conversion doesn't happen at each loop iteration.
  833. constexpr float MaxHeightHalf = float(AZStd::numeric_limits<HeightDataType>::max() / 2);
  834. constexpr float MaxNormal = AZStd::numeric_limits<NormalDataType>::max();
  835. for (uint16_t y = 0; y < request.m_samplesY; ++y)
  836. {
  837. const uint16_t queryY = y + 1;
  838. for (uint16_t x = 0; x < request.m_samplesX; ++x)
  839. {
  840. const uint16_t queryX = x + 1;
  841. const uint16_t queryCoord = queryY * querySamplesX + queryX;
  842. uint16_t coord = y * request.m_samplesX + x;
  843. coord = request.m_useVertexOrderRemap ? m_vertexOrder.at(coord) : coord;
  844. const float height = heights.at(queryCoord);
  845. if (height < 0.0f)
  846. {
  847. // Primary terrain height is limited to every-other bit, and clod heights can be in-between or the same
  848. // as any of the primary heights. This leaves the max value as the single value that is never used by a
  849. // legitimate height.
  850. meshHeightsNormals.at(coord).m_height = NoTerrainVertexHeight;
  851. continue;
  852. }
  853. const float clampedHeight = AZ::GetClamp(height * rcpWorldZ, 0.0f, 1.0f);
  854. // For continuous LOD, it needs to be possible to create a height that's exactly in between any other height, so scale
  855. // and quantize to half the height, then multiply by 2, ensuring there's always an in-between value available.
  856. const HeightDataType quantizedHeight = aznumeric_cast<HeightDataType>(clampedHeight * MaxHeightHalf + 0.5f); // always positive, so just add 0.5 to round.
  857. meshHeightsNormals.at(coord).m_height = quantizedHeight * 2;
  858. if (minHeight > height)
  859. {
  860. minHeight = height;
  861. }
  862. else if (maxHeight < height)
  863. {
  864. maxHeight = height;
  865. }
  866. auto getSlope = [&](float height1, float height2)
  867. {
  868. if (height1 < 0.0f)
  869. {
  870. if (height2 < 0.0f)
  871. {
  872. // Assume no slope if the left and right vertices both don't exist.
  873. return 0.0f;
  874. }
  875. else
  876. {
  877. return (height - height2) / request.m_vertexSpacing;
  878. }
  879. }
  880. else
  881. {
  882. if (height2 < 0.0f)
  883. {
  884. return (height1 - height) / request.m_vertexSpacing;
  885. }
  886. else
  887. {
  888. return (height1 - height2) / vertexSpacing2;
  889. }
  890. }
  891. };
  892. const float leftHeight = heights.at(queryCoord - 1);
  893. const float rightHeight = heights.at(queryCoord + 1);
  894. const float xSlope = getSlope(leftHeight, rightHeight);
  895. const float normalX = xSlope / sqrt(xSlope * xSlope + 1); // sin(arctan(xSlope)
  896. const float upHeight = heights.at(queryCoord - querySamplesX);
  897. const float downHeight = heights.at(queryCoord + querySamplesX);
  898. const float ySlope = getSlope(upHeight, downHeight);
  899. const float normalY = ySlope / sqrt(ySlope * ySlope + 1); // sin(arctan(ySlope)
  900. meshHeightsNormals.at(coord).m_normal =
  901. {
  902. aznumeric_cast<NormalDataType>(AZStd::lround(normalX * MaxNormal)),
  903. aznumeric_cast<NormalDataType>(AZStd::lround(normalY * MaxNormal)),
  904. };
  905. }
  906. }
  907. if (maxHeight < minHeight)
  908. {
  909. // All height samples were invalid, so set the aabb to null.
  910. meshAabb.SetNull();
  911. }
  912. else
  913. {
  914. float width = (request.m_samplesX - 1) * request.m_vertexSpacing;
  915. float height = (request.m_samplesY - 1) * request.m_vertexSpacing;
  916. AZ::Vector3 aabbMin = AZ::Vector3(request.m_worldStartPosition.GetX(), request.m_worldStartPosition.GetY(), m_worldHeightBounds.m_min + minHeight);
  917. AZ::Vector3 aabbMax = AZ::Vector3(aabbMin.GetX() + width, aabbMin.GetY() + height, m_worldHeightBounds.m_min + maxHeight);
  918. meshAabb.Set(aabbMin, aabbMax);
  919. }
  920. }
  921. void TerrainMeshManager::ProcessSectorUpdates(AZStd::vector<AZStd::vector<Sector*>>& sectorUpdates)
  922. {
  923. AZ::JobCompletion jobCompletion;
  924. for (uint32_t lodLevel = 0; lodLevel < sectorUpdates.size(); ++lodLevel)
  925. {
  926. auto& sectors = sectorUpdates.at(lodLevel);
  927. if (sectors.empty())
  928. {
  929. continue;
  930. }
  931. for (Sector* sector : sectors)
  932. {
  933. const float gridMeters = (m_gridSize * m_sampleSpacing) * (1 << lodLevel);
  934. const auto jobLambda = [this, sector, gridMeters]() -> void
  935. {
  936. AZStd::vector<HeightNormalVertex> meshHeightsNormals;
  937. {
  938. SectorDataRequest request;
  939. request.m_samplesX = m_gridVerts1D;
  940. request.m_samplesY = m_gridVerts1D;
  941. request.m_worldStartPosition = sector->m_worldCoord.ToVector2() * gridMeters;
  942. request.m_vertexSpacing = gridMeters / m_gridSize;
  943. request.m_useVertexOrderRemap = true;
  944. GatherMeshData(request, meshHeightsNormals, sector->m_aabb, sector->m_hasData);
  945. if (sector->m_hasData)
  946. {
  947. UpdateSectorBuffers(*sector, meshHeightsNormals);
  948. }
  949. // Create AABBs for each quadrant for cases where this LOD needs to fill in a gap in a lower LOD.
  950. CreateAabbQuadrants(sector->m_aabb, sector->m_quadrantAabbs);
  951. }
  952. if (m_config.m_clodEnabled && sector->m_hasData)
  953. {
  954. SectorDataRequest request;
  955. uint16_t m_gridSizeNextLod = (m_gridSize >> 1);
  956. request.m_samplesX = m_gridSizeNextLod + 1;
  957. request.m_samplesY = m_gridSizeNextLod + 1;
  958. request.m_worldStartPosition = sector->m_worldCoord.ToVector2() * gridMeters;
  959. request.m_vertexSpacing = gridMeters / m_gridSizeNextLod;
  960. AZ::Aabb dummyAabb = AZ::Aabb::CreateNull(); // Don't update the sector aabb based on only the clod vertices.
  961. bool terrainExists = false;
  962. AZStd::vector<HeightNormalVertex> meshLodHeightsNormals;
  963. GatherMeshData(request, meshLodHeightsNormals, dummyAabb, terrainExists);
  964. if (!terrainExists)
  965. {
  966. // It's unlikely but possible for the higher lod to have data and the lower lod to not. In that case
  967. // meshLodHeights will be empty, so fill it with values that represent "no data".
  968. HeightNormalVertex defaultValue = { NoTerrainVertexHeight, NormalXYDataType(NormalDataType(0), NormalDataType(0)) };
  969. AZStd::fill(meshLodHeightsNormals.begin(), meshLodHeightsNormals.end(), defaultValue);
  970. }
  971. UpdateSectorLodBuffers(*sector, meshHeightsNormals, meshLodHeightsNormals);
  972. }
  973. };
  974. ShaderObjectData objectSrgData;
  975. objectSrgData.m_xyTranslation = { sector->m_worldCoord.m_x * gridMeters, sector->m_worldCoord.m_y * gridMeters };
  976. objectSrgData.m_xyScale = gridMeters * (aznumeric_cast<float>(AZStd::numeric_limits<uint8_t>::max()) / m_gridSize);
  977. objectSrgData.m_lodLevel = lodLevel;
  978. objectSrgData.m_rcpLodLevel = 1.0f / (lodLevel + 1);
  979. sector->m_srg->SetConstant(m_patchDataIndex, objectSrgData);
  980. if (!sector->m_isQueuedForSrgCompile)
  981. {
  982. m_sectorsThatNeedSrgCompiled.push_back(sector);
  983. }
  984. sector->m_hasData = false; // mark the terrain as not having data for now. Once a job runs if it actually has data it'll flip to true.
  985. // Check against the area of terrain that could appear in this sector for any terrain areas. If none exist then skip updating the mesh.
  986. bool hasTerrain = false;
  987. AZ::Vector3 minAabb = AZ::Vector3(sector->m_worldCoord.m_x * gridMeters, sector->m_worldCoord.m_y * gridMeters, m_worldHeightBounds.m_min);
  988. AZ::Aabb sectorBounds = AZ::Aabb::CreateFromMinMax(minAabb,
  989. minAabb + AZ::Vector3(gridMeters, gridMeters, m_worldHeightBounds.m_max - m_worldHeightBounds.m_min));
  990. AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
  991. hasTerrain, &AzFramework::Terrain::TerrainDataRequests::TerrainAreaExistsInBounds, sectorBounds);
  992. if (hasTerrain)
  993. {
  994. AZ::Job* executeGroupJob = aznew AZ::JobFunction<decltype(jobLambda)>(jobLambda, true, nullptr); // Auto-deletes
  995. executeGroupJob->SetDependent(&jobCompletion);
  996. executeGroupJob->Start();
  997. }
  998. }
  999. }
  1000. jobCompletion.StartAndWaitForCompletion();
  1001. m_candidateSectors.clear(); // Force recalculation of candidate sectors since AABBs could have changed.
  1002. }
  1003. void TerrainMeshManager::UpdateCandidateSectors()
  1004. {
  1005. // Gather a list of all sectors that could render based on their status, lod, and camera position.
  1006. float maxDistanceSq = m_config.m_firstLodDistance * m_config.m_firstLodDistance;
  1007. uint32_t nextLodSectorCount = m_1dSectorCount * 2; // The number of this lod's sectors that would fit into the next lod's space.
  1008. AZStd::vector<bool> previousSelectedSectors;
  1009. m_candidateSectors.clear();
  1010. AZStd::vector<RayTracedItem> newRayTraceItems;
  1011. if (m_rayTracingEnabled)
  1012. {
  1013. newRayTraceItems.reserve(m_sectorLods.size() * m_1dSectorCount * m_1dSectorCount);
  1014. }
  1015. for (uint32_t lodLevel = 0; lodLevel < m_sectorLods.size(); ++lodLevel)
  1016. {
  1017. auto& lodGrid = m_sectorLods.at(lodLevel);
  1018. // Each sector in an LOD is half the size of a sector in the next LOD in each direction, so 4 sectors
  1019. // in this LOD equal one sector in the next LOD. Construct a grid of bools for each sector in this
  1020. // LOD that covers the entire space of the next LOD, and mark everything to false. As sectors in
  1021. // this LOD are drawn, mark appropriate locations in the grid as true. When processing the next
  1022. // LOD, each of the next LOD's sectors will look up the 4 entries in this that represent quadrants
  1023. // of that sector to determine whether to draw nothing, specific quadrants, or the entire sector.
  1024. AZStd::vector<bool> selectedSectors(nextLodSectorCount * nextLodSectorCount, false);
  1025. Vector2i selectedSectorStartCoord{ 0, 0 };
  1026. if (lodLevel == m_sectorLods.size() - 1)
  1027. {
  1028. // There is no next lod, so just use this one's start coord to avoid lots of checks in the for loop.
  1029. selectedSectorStartCoord = m_sectorLods.at(lodLevel).m_startCoord;
  1030. }
  1031. else
  1032. {
  1033. // This is the start coord of the next LOD in the current LOD's scale.
  1034. selectedSectorStartCoord = m_sectorLods.at(lodLevel + 1).m_startCoord * 2;
  1035. }
  1036. for (uint32_t sectorIndex = 0; sectorIndex < lodGrid.m_sectors.size(); ++sectorIndex)
  1037. {
  1038. Sector& sector = lodGrid.m_sectors.at(sectorIndex);
  1039. Vector2i selectedCoord = sector.m_worldCoord - selectedSectorStartCoord;
  1040. uint32_t selectedIndex = selectedCoord.m_y * nextLodSectorCount + selectedCoord.m_x;
  1041. if (!sector.m_hasData)
  1042. {
  1043. selectedSectors.at(selectedIndex) = true; // Terrain just doesn't exist here, so mark as "selected" so another LOD doesn't try to draw here.
  1044. continue;
  1045. }
  1046. const float aabbMinDistanceSq = sector.m_aabb.GetDistanceSq(m_cameraPosition);
  1047. if (aabbMinDistanceSq < maxDistanceSq)
  1048. {
  1049. selectedSectors.at(selectedIndex) = true;
  1050. if (lodLevel == 0)
  1051. {
  1052. // Since this is the first lod, no previous lod to check, so just draw.
  1053. m_candidateSectors.push_back({ sector.m_aabb, sector.m_rhiDrawPacket.get() });
  1054. if (sector.m_rtData)
  1055. {
  1056. newRayTraceItems.push_back({ &sector, 0, lodLevel });
  1057. }
  1058. continue;
  1059. }
  1060. Vector2i previousCoord = (sector.m_worldCoord - lodGrid.m_startCoord) * 2;
  1061. uint32_t previousDrawnIndex = previousCoord.m_y * nextLodSectorCount + previousCoord.m_x;
  1062. // Check the 4 sectors in the previous LOD that are covered by this sector.
  1063. uint8_t coveredByHigherLod =
  1064. (uint8_t(previousSelectedSectors.at(previousDrawnIndex)) << 0) | // Top left
  1065. (uint8_t(previousSelectedSectors.at(previousDrawnIndex + 1)) << 1) | // Top right
  1066. (uint8_t(previousSelectedSectors.at(previousDrawnIndex + nextLodSectorCount)) << 2) | // Bottom left
  1067. (uint8_t(previousSelectedSectors.at(previousDrawnIndex + nextLodSectorCount + 1)) << 3); // Bottom right
  1068. if (coveredByHigherLod == 0b1111)
  1069. {
  1070. continue; // Completely covered by previous LOD, so do nothing
  1071. }
  1072. if (coveredByHigherLod == 0b0000)
  1073. {
  1074. // Not covered at all by previous LOD, so the draw entire sector
  1075. m_candidateSectors.push_back({ sector.m_aabb, sector.m_rhiDrawPacket.get() });
  1076. if (sector.m_rtData)
  1077. {
  1078. newRayTraceItems.push_back({ &sector, 0, lodLevel });
  1079. }
  1080. }
  1081. else
  1082. {
  1083. // Partially covered by previous LOD. Draw only missing quadrants
  1084. for (uint8_t i = 0; i < 4; ++i)
  1085. {
  1086. if ((coveredByHigherLod & 0b0001) == 0b0000)
  1087. {
  1088. m_candidateSectors.push_back({ sector.m_quadrantAabbs.at(i), sector.m_rhiDrawPacketQuadrant.at(i).get() });
  1089. if (sector.m_rtData)
  1090. {
  1091. newRayTraceItems.push_back({ &sector, i + 1u, lodLevel });
  1092. }
  1093. }
  1094. coveredByHigherLod >>= 1;
  1095. }
  1096. }
  1097. }
  1098. }
  1099. maxDistanceSq = maxDistanceSq * 4.0f; // Double the distance with squared distances is * 2^2.
  1100. previousSelectedSectors = AZStd::move(selectedSectors);
  1101. }
  1102. if (m_rayTracingEnabled)
  1103. {
  1104. // Compare the sorted new list to the old list to figure out which ray traced sectors need to be
  1105. // added or removed.
  1106. auto getMeshGroup = [](auto& item) -> auto&
  1107. {
  1108. return item.m_sector->m_rtData->m_meshGroups[item.m_meshGroupIndex];
  1109. };
  1110. AZStd::sort(newRayTraceItems.begin(), newRayTraceItems.end(),
  1111. [&getMeshGroup](const RayTracedItem& value1, const RayTracedItem& value2) -> bool
  1112. {
  1113. return getMeshGroup(value1).m_id < getMeshGroup(value2).m_id;
  1114. }
  1115. );
  1116. auto prevIt = m_rayTracedItems.begin();
  1117. auto newIt = newRayTraceItems.begin();
  1118. auto addMesh = [&](RayTracedItem& item, RtSector::MeshGroup& meshGroup)
  1119. {
  1120. const float gridMeters = (m_gridSize * m_sampleSpacing) * (1 << item.m_lodLevel);
  1121. AZ::Vector3 translation = AZ::Vector3(item.m_sector->m_worldCoord.m_x * gridMeters, item.m_sector->m_worldCoord.m_y * gridMeters, m_worldHeightBounds.m_min);
  1122. meshGroup.m_mesh.m_transform = AZ::Transform::CreateTranslation(translation);
  1123. meshGroup.m_isVisible = true;
  1124. m_rayTracingFeatureProcessor->AddMesh(meshGroup.m_id, meshGroup.m_mesh, meshGroup.m_submeshVector);
  1125. };
  1126. auto removeMesh = [&](RtSector::MeshGroup& meshGroup)
  1127. {
  1128. meshGroup.m_isVisible = false;
  1129. m_rayTracingFeatureProcessor->RemoveMesh(meshGroup.m_id);
  1130. };
  1131. // Since the two lists are sorted, we can easily compare them and figure out which items need
  1132. // to be removed or added. If a uuid shows up in the old list first, then it must not be in the new
  1133. // list, so it needs to be removed, then only the old list iterator is incremented. Similarly if a
  1134. // uuid shows up in the new list first then it must not be in the old list, so it needs to be added.
  1135. // Finally if the uuids match, they're in both lists, and therefore both iterators can be incremented.
  1136. while (prevIt < m_rayTracedItems.end() && newIt < newRayTraceItems.end())
  1137. {
  1138. RtSector::MeshGroup& prevMeshGroup = getMeshGroup(*prevIt);
  1139. RtSector::MeshGroup& newMeshGroup = getMeshGroup(*newIt);
  1140. if (prevMeshGroup.m_id < newMeshGroup.m_id)
  1141. {
  1142. removeMesh(prevMeshGroup);
  1143. ++prevIt;
  1144. }
  1145. else if (prevMeshGroup.m_id > newMeshGroup.m_id)
  1146. {
  1147. addMesh(*newIt, newMeshGroup);
  1148. ++newIt;
  1149. }
  1150. else
  1151. {
  1152. ++prevIt;
  1153. ++newIt;
  1154. }
  1155. }
  1156. // Since the above loop stops when either iterator is done, remaining items in the other iterator need to be handled here.
  1157. while (prevIt < m_rayTracedItems.end())
  1158. {
  1159. removeMesh(getMeshGroup(*prevIt));
  1160. ++prevIt;
  1161. }
  1162. while (newIt < newRayTraceItems.end())
  1163. {
  1164. addMesh(*newIt, getMeshGroup(*newIt));
  1165. ++newIt;
  1166. }
  1167. m_rayTracedItems = AZStd::move(newRayTraceItems);
  1168. }
  1169. }
  1170. void TerrainMeshManager::CreateAabbQuadrants(const AZ::Aabb& aabb, AZStd::span<AZ::Aabb, 4> quadrantAabb)
  1171. {
  1172. // Create 4 AABBs for each quadrant on the xy plane.
  1173. if (aabb.IsValid())
  1174. {
  1175. float centerX = aabb.GetCenter().GetX();
  1176. float centerY = aabb.GetCenter().GetY();
  1177. quadrantAabb[0] = AZ::Aabb::CreateFromMinMax(
  1178. aabb.GetMin(),
  1179. AZ::Vector3(centerX, centerY, aabb.GetMax().GetZ())
  1180. );
  1181. float halfExtentX = aabb.GetXExtent() * 0.5f;
  1182. float halfExtentY = aabb.GetYExtent() * 0.5f;
  1183. quadrantAabb[1] = quadrantAabb[0].GetTranslated(AZ::Vector3(halfExtentX, 0.0f, 0.0f));
  1184. quadrantAabb[2] = quadrantAabb[0].GetTranslated(AZ::Vector3(0.0f, halfExtentY, 0.0f));
  1185. quadrantAabb[3] = quadrantAabb[0].GetTranslated(AZ::Vector3(halfExtentX, halfExtentY, 0.0f));
  1186. }
  1187. else
  1188. {
  1189. AZStd::fill(quadrantAabb.begin(), quadrantAabb.end(), AZ::Aabb::CreateNull());
  1190. }
  1191. }
  1192. template<typename Callback>
  1193. void TerrainMeshManager::ForOverlappingSectors(const AZ::Aabb& bounds, Callback callback)
  1194. {
  1195. const AZ::Vector2 boundsMin2d = AZ::Vector2(bounds.GetMin());
  1196. const AZ::Vector2 boundsMax2d = AZ::Vector2(bounds.GetMax());
  1197. for (uint32_t lodLevel = 0; lodLevel < m_sectorLods.size(); ++lodLevel)
  1198. {
  1199. // Expand the bounds by the spacing of the lod since vertex normals are affected by neighbors.
  1200. // The bounds needs to be 2x what's expected because clod also encodes information about the normals
  1201. // for the next lod level in the current lod level (which has vertices spaced 2x as far apart)
  1202. const AZ::Vector2 lodSpacing = AZ::Vector2(m_sampleSpacing * (1 << lodLevel) * 2.0f);
  1203. const AZ::Vector2 lodBoundsMin2d = boundsMin2d - lodSpacing;
  1204. const AZ::Vector2 lodBoundsMax2d = boundsMax2d + lodSpacing;
  1205. const float gridMeters = (m_gridSize * m_sampleSpacing) * (1 << lodLevel);
  1206. auto& lodGrid = m_sectorLods.at(lodLevel);
  1207. for (Sector& sector : lodGrid.m_sectors)
  1208. {
  1209. const AZ::Vector2 sectorAabbMin2D = sector.m_worldCoord.ToVector2() * gridMeters;
  1210. const AZ::Vector2 sectorAabbMax2D = sectorAabbMin2D + AZ::Vector2(gridMeters);
  1211. const bool overlaps = sectorAabbMin2D.IsLessEqualThan(lodBoundsMax2d) && sectorAabbMax2D.IsGreaterEqualThan(lodBoundsMin2d);
  1212. if (overlaps)
  1213. {
  1214. callback(sector, lodLevel);
  1215. }
  1216. }
  1217. }
  1218. }
  1219. AZ::Data::Instance<AZ::RPI::Buffer> TerrainMeshManager::CreateMeshBufferInstance(uint32_t elementSize, uint32_t elementCount, const void* initialData, const char* name)
  1220. {
  1221. AZ::RPI::CommonBufferDescriptor desc;
  1222. desc.m_poolType = AZ::RPI::CommonBufferPoolType::StaticInputAssembly;
  1223. desc.m_elementSize = elementSize;
  1224. desc.m_byteCount = desc.m_elementSize * elementCount;
  1225. desc.m_bufferData = initialData;
  1226. if (name != nullptr)
  1227. {
  1228. desc.m_bufferName = name;
  1229. }
  1230. return AZ::RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc);
  1231. }
  1232. AZ::Data::Instance<AZ::RPI::Buffer> TerrainMeshManager::CreateRayTracingMeshBufferInstance(AZ::RHI::Format elementFormat, uint32_t elementCount, const void* initialData, const char* name)
  1233. {
  1234. AZ::RPI::CommonBufferDescriptor desc;
  1235. desc.m_poolType = AZ::RPI::CommonBufferPoolType::DynamicInputAssembly;
  1236. desc.m_elementSize = AZ::RHI::GetFormatSize(elementFormat);
  1237. desc.m_byteCount = desc.m_elementSize * elementCount;
  1238. desc.m_bufferData = initialData;
  1239. desc.m_elementFormat = elementFormat;
  1240. if (name != nullptr)
  1241. {
  1242. desc.m_bufferName = name;
  1243. }
  1244. return AZ::RPI::BufferSystemInterface::Get()->CreateBufferFromCommonPool(desc);
  1245. }
  1246. }