merge stabilization/2110 to development - 2021/11/09
Signed-off-by: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com>
This commit is contained in:
@@ -151,24 +151,32 @@ namespace Terrain
|
||||
{
|
||||
float maxSample = 0.0f;
|
||||
terrainExists = false;
|
||||
|
||||
GradientSignal::GradientSampleParams params(AZ::Vector3(inPosition.GetX(), inPosition.GetY(), 0.0f));
|
||||
|
||||
// Right now, when the list contains multiple entries, we will use the highest point from each gradient.
|
||||
// This is needed in part because gradients don't really have world bounds, so they exist everywhere but generally have a value
|
||||
// of 0 outside their data bounds if they're using bounded data. We should examine the possibility of extending the gradient API
|
||||
// to provide actual bounds so that it's possible to detect if the gradient even 'exists' in an area, at which point we could just
|
||||
// make this list a prioritized list from top to bottom for any points that overlap.
|
||||
for (auto& gradientId : m_configuration.m_gradientEntities)
|
||||
AZ_WarningOnce("Terrain", !m_isRequestInProgress, "Detected cyclic dependences with terrain height entity references");
|
||||
if (!m_isRequestInProgress)
|
||||
{
|
||||
// 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;
|
||||
m_isRequestInProgress = true;
|
||||
GradientSignal::GradientSampleParams params(AZ::Vector3(inPosition.GetX(), inPosition.GetY(), 0.0f));
|
||||
|
||||
float sample = 0.0f;
|
||||
GradientSignal::GradientRequestBus::EventResult(
|
||||
sample, gradientId, &GradientSignal::GradientRequestBus::Events::GetValue, params);
|
||||
maxSample = AZ::GetMax(maxSample, sample);
|
||||
// Right now, when the list contains multiple entries, we will use the highest point from each gradient.
|
||||
// This is needed in part because gradients don't really have world bounds, so they exist everywhere but generally have a value
|
||||
// of 0 outside their data bounds if they're using bounded data. We should examine the possibility of extending the gradient
|
||||
// API to provide actual bounds so that it's possible to detect if the gradient even 'exists' in an area, at which point we
|
||||
// could just make this list a prioritized list from top to bottom for any points that overlap.
|
||||
for (auto& gradientId : m_configuration.m_gradientEntities)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
m_isRequestInProgress = false;
|
||||
}
|
||||
|
||||
const float height = AZ::Lerp(m_cachedShapeBounds.GetMin().GetZ(), m_cachedShapeBounds.GetMax().GetZ(), maxSample);
|
||||
|
||||
@@ -91,6 +91,9 @@ namespace Terrain
|
||||
AZ::Vector2 m_cachedHeightQueryResolution{ 1.0f, 1.0f };
|
||||
AZ::Aabb m_cachedShapeBounds;
|
||||
|
||||
// prevent recursion in case user attaches cyclic dependences
|
||||
mutable bool m_isRequestInProgress{ false };
|
||||
|
||||
LmbrCentral::DependencyMonitor m_dependencyMonitor;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -218,6 +218,12 @@ namespace Terrain
|
||||
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
|
||||
queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution);
|
||||
|
||||
// Take the dirty region and adjust the Z values to the world min/max so that even if the dirty region falls outside the current
|
||||
// world bounds, we still update the wireframe accordingly.
|
||||
AZ::Aabb dirtyRegion2D = AZ::Aabb::CreateFromMinMaxValues(
|
||||
dirtyRegion.GetMin().GetX(), dirtyRegion.GetMin().GetY(), worldBounds.GetMin().GetZ(),
|
||||
dirtyRegion.GetMax().GetX(), dirtyRegion.GetMax().GetY(), worldBounds.GetMax().GetZ());
|
||||
|
||||
// Calculate the world size of each sector. Note that this size actually ends at the last point, not the last square.
|
||||
// So for example, the sector size for 3 points will go from (*--*--*) even though it will be used to draw (*--*--*--).
|
||||
const float xSectorSize = (queryResolution.GetX() * SectorSizeInGridPoints);
|
||||
@@ -230,7 +236,7 @@ namespace Terrain
|
||||
|
||||
// If we haven't cached anything before, or if the world bounds has changed, clear our cache structure and repopulate it
|
||||
// with WireframeSector entries with the proper AABB sizes.
|
||||
if (!m_wireframeBounds.IsValid() || !dirtyRegion.IsValid() || !m_wireframeBounds.IsClose(worldBounds))
|
||||
if (!m_wireframeBounds.IsValid() || !dirtyRegion2D.IsValid() || !m_wireframeBounds.IsClose(worldBounds))
|
||||
{
|
||||
m_wireframeBounds = worldBounds;
|
||||
|
||||
@@ -266,7 +272,7 @@ namespace Terrain
|
||||
// For each sector, if it overlaps with the dirty region, clear it out and recache the wireframe line data.
|
||||
for (auto& sector : m_wireframeSectors)
|
||||
{
|
||||
if (dirtyRegion.IsValid() && !dirtyRegion.Overlaps(sector.m_aabb))
|
||||
if (dirtyRegion2D.IsValid() && !dirtyRegion2D.Overlaps(sector.m_aabb))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace Terrain
|
||||
->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_4096Meters, "4 Kilometers")
|
||||
->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_8192Meters, "8 Kilometers")
|
||||
->EnumAttribute(TerrainWorldRendererConfig::WorldSize::_16384Meters, "16 Kilometers")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, false) // Keeping invisible until it's hooked up under the hood
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -972,10 +972,10 @@ namespace Terrain
|
||||
m_areaData.m_rebuildSectors = false;
|
||||
|
||||
m_sectorData.clear();
|
||||
const float xFirstPatchStart = terrainBounds.GetMin().GetX() - fmod(terrainBounds.GetMin().GetX(), GridMeters);
|
||||
const float xLastPatchStart = terrainBounds.GetMax().GetX() - fmod(terrainBounds.GetMax().GetX(), GridMeters);
|
||||
const float yFirstPatchStart = terrainBounds.GetMin().GetY() - fmod(terrainBounds.GetMin().GetY(), GridMeters);
|
||||
const float yLastPatchStart = terrainBounds.GetMax().GetY() - fmod(terrainBounds.GetMax().GetY(), GridMeters);
|
||||
const float xFirstPatchStart = AZStd::floorf(terrainBounds.GetMin().GetX() / GridMeters) * GridMeters;
|
||||
const float xLastPatchStart = AZStd::floorf(terrainBounds.GetMax().GetX() / GridMeters) * GridMeters;
|
||||
const float yFirstPatchStart = AZStd::floorf(terrainBounds.GetMin().GetY() / GridMeters) * GridMeters;
|
||||
const float yLastPatchStart = AZStd::floorf(terrainBounds.GetMax().GetY() / GridMeters) * GridMeters;
|
||||
|
||||
const auto& materialAsset = m_materialInstance->GetAsset();
|
||||
const auto& shaderAsset = materialAsset->GetMaterialTypeAsset()->GetShaderAssetForObjectSrg();
|
||||
@@ -1217,7 +1217,7 @@ namespace Terrain
|
||||
|
||||
// For every distance doubling beyond a minDistanceForLod0, we only need half the mesh density. Each LOD
|
||||
// is exactly half the resolution of the last.
|
||||
const float lodForCamera = floorf(AZ::GetMax(0.0f, log2f(sectorDistance / minDistanceForLod0)));
|
||||
const float lodForCamera = AZStd::floorf(AZ::GetMax(0.0f, log2f(sectorDistance / minDistanceForLod0)));
|
||||
|
||||
// All cameras should render the same LOD so effects like shadows are consistent.
|
||||
lodChoice = AZ::GetMin(lodChoice, aznumeric_cast<uint8_t>(lodForCamera));
|
||||
|
||||
@@ -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<AZStd::shared_mutex> 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<AZStd::shared_mutex> 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<AZStd::shared_mutex> 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);
|
||||
|
||||
@@ -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<AZ::EntityId, AZ::Aabb, TerrainLayerPriorityComparator> m_registeredAreas;
|
||||
AZStd::map<AZ::EntityId, TerrainAreaData, TerrainLayerPriorityComparator> m_registeredAreas;
|
||||
};
|
||||
} // namespace Terrain
|
||||
|
||||
Reference in New Issue
Block a user