From 97b0eddcb4f139dcac4c9f716ab1301c2a76cd00 Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Thu, 4 Nov 2021 17:56:55 -0500 Subject: [PATCH] Hook up the "Use Ground Plane" toggle. The "Use Ground Plane" toggle is now functional. When disabled, the terrain layer spawner will say "terrain exists = false" for any point in its bounds unless there's also a Terrain Height Gradient List component with a valid entry. When enabled, it will always say "terrain exists = true", and it will return the min height of the spawner box as the ground plane if there's no valid Terrain Height Gradient List height provider. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../TerrainHeightGradientListComponent.cpp | 19 ++++---- .../Source/TerrainSystem/TerrainSystem.cpp | 44 +++++++++++++------ .../Code/Source/TerrainSystem/TerrainSystem.h | 9 +++- 3 files changed, 49 insertions(+), 23 deletions(-) diff --git a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp index 231d5abc28..c4415007b5 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainHeightGradientListComponent.cpp @@ -161,14 +161,17 @@ namespace Terrain // make this list a prioritized list from top to bottom for any points that overlap. for (auto& gradientId : m_configuration.m_gradientEntities) { - // If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain - // to *not* exist at a specific point. - terrainExists = true; - - float sample = 0.0f; - GradientSignal::GradientRequestBus::EventResult( - sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params); - maxSample = AZ::GetMax(maxSample, sample); + if (gradientId.IsValid()) + { + // If gradients ever provide bounds, or if we add a value threshold in this component, it would be possible for terrain + // to *not* exist at a specific point. + terrainExists = true; + + float sample = 0.0f; + GradientSignal::GradientRequestBus::EventResult( + sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params); + maxSample = AZ::GetMax(maxSample, sample); + } } const float height = AZ::Lerp(m_cachedShapeBounds.GetMin().GetZ(), m_cachedShapeBounds.GetMax().GetZ(), maxSample); diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp index 8d39340b06..20041e2fc8 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp @@ -222,20 +222,31 @@ float TerrainSystem::GetHeightSynchronous(float x, float y, Sampler sampler, boo float TerrainSystem::GetTerrainAreaHeight(float x, float y, bool& terrainExists) const { - AZ::Vector3 inPosition((float)x, (float)y, m_currentSettings.m_worldBounds.GetMin().GetZ()); - float height = m_currentSettings.m_worldBounds.GetMin().GetZ(); + const float worldMin = m_currentSettings.m_worldBounds.GetMin().GetZ(); + AZ::Vector3 inPosition(x, y, worldMin); + float height = worldMin; + terrainExists = false; AZStd::shared_lock lock(m_areaMutex); - for (auto& [areaId, areaBounds] : m_registeredAreas) + for (auto& [areaId, areaData] : m_registeredAreas) { - inPosition.SetZ(areaBounds.GetMin().GetZ()); - if (areaBounds.Contains(inPosition)) + const float areaMin = areaData.m_areaBounds.GetMin().GetZ(); + inPosition.SetZ(areaMin); + if (areaData.m_areaBounds.Contains(inPosition)) { AZ::Vector3 outPosition; Terrain::TerrainAreaHeightRequestBus::Event( areaId, &Terrain::TerrainAreaHeightRequestBus::Events::GetHeight, inPosition, outPosition, terrainExists); height = outPosition.GetZ(); + if (!terrainExists) + { + // If the terrain height provider doesn't have any data, then check the area's "use ground plane" setting. + // If it's set, then create a default ground plane by saying terrain exists at the minimum height for the area. + // Otherwise, we'll set the height at the terrain world minimum and say it doesn't exist. + terrainExists = areaData.m_useGroundPlane; + height = areaData.m_useGroundPlane ? areaMin : worldMin; + } break; } } @@ -395,12 +406,12 @@ AZ::EntityId TerrainSystem::FindBestAreaEntityAtPosition(float x, float y, AZ::A AZStd::shared_lock lock(m_areaMutex); // The areas are sorted into priority order: the first area that contains inPosition is the most suitable. - for (const auto& [areaId, areaBounds] : m_registeredAreas) + for (const auto& [areaId, areaData] : m_registeredAreas) { - inPosition.SetZ(areaBounds.GetMin().GetZ()); - if (areaBounds.Contains(inPosition)) + inPosition.SetZ(areaData.m_areaBounds.GetMin().GetZ()); + if (areaData.m_areaBounds.Contains(inPosition)) { - bounds = areaBounds; + bounds = areaData.m_areaBounds; return areaId; } } @@ -548,7 +559,12 @@ void TerrainSystem::RegisterArea(AZ::EntityId areaId) AZStd::unique_lock lock(m_areaMutex); AZ::Aabb aabb = AZ::Aabb::CreateNull(); LmbrCentral::ShapeComponentRequestsBus::EventResult(aabb, areaId, &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); - m_registeredAreas[areaId] = aabb; + + // Cache off whether or not this layer spawner should have a default ground plane when no other terrain height data exists. + bool useGroundPlane = false; + Terrain::TerrainSpawnerRequestBus::EventResult(useGroundPlane, areaId, &Terrain::TerrainSpawnerRequestBus::Events::GetUseGroundPlane); + + m_registeredAreas[areaId] = { aabb, useGroundPlane }; m_dirtyRegion.AddAabb(aabb); m_terrainHeightDirty = true; m_terrainSurfacesDirty = true; @@ -565,10 +581,10 @@ void TerrainSystem::UnregisterArea(AZ::EntityId areaId) m_registeredAreas, [areaId, this](const auto& item) { - auto const& [entityId, aabb] = item; + auto const& [entityId, areaData] = item; if (areaId == entityId) { - m_dirtyRegion.AddAabb(aabb); + m_dirtyRegion.AddAabb(areaData.m_areaBounds); m_terrainHeightDirty = true; m_terrainSurfacesDirty = true; return true; @@ -585,10 +601,10 @@ void TerrainSystem::RefreshArea(AZ::EntityId areaId, AzFramework::Terrain::Terra auto areaAabb = m_registeredAreas.find(areaId); - AZ::Aabb oldAabb = (areaAabb != m_registeredAreas.end()) ? areaAabb->second : AZ::Aabb::CreateNull(); + AZ::Aabb oldAabb = (areaAabb != m_registeredAreas.end()) ? areaAabb->second.m_areaBounds : AZ::Aabb::CreateNull(); AZ::Aabb newAabb = AZ::Aabb::CreateNull(); LmbrCentral::ShapeComponentRequestsBus::EventResult(newAabb, areaId, &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); - m_registeredAreas[areaId] = newAabb; + m_registeredAreas[areaId].m_areaBounds = newAabb; AZ::Aabb expandedAabb = oldAabb; expandedAabb.AddAabb(newAabb); diff --git a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h index 022cd218cc..c6bd19fdda 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.h @@ -168,7 +168,14 @@ namespace Terrain bool m_terrainSurfacesDirty = false; AZ::Aabb m_dirtyRegion; + // Cached data for each terrain area to use when looking up terrain data. + struct TerrainAreaData + { + AZ::Aabb m_areaBounds{ AZ::Aabb::CreateNull() }; + bool m_useGroundPlane{ false }; + }; + mutable AZStd::shared_mutex m_areaMutex; - AZStd::map m_registeredAreas; + AZStd::map m_registeredAreas; }; } // namespace Terrain