From c873ddac45e5721dd07eea96848b43c737cd4f5d Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Thu, 24 Jun 2021 12:54:48 -0500 Subject: [PATCH] [LYN-3099] Fixed some issues with vegetation planting on surfaces (#1554) * [LYN-3099] Fix vegetation raycasts to use bounded ray queries instead of FLT_MAX. Raycasts with a distance of FLT_MAX sometimes overflowed deep in IntersectSegmentTriangleCCW, so it's better to have strict start/end positional queries. We have specific starts and ends anyways, so it's a safer approach anyways. This also adds support for Non-Uniform Scale for meshes, since it was clearly not working correctly in vegetation when testing various scaled meshes. * Addressed PR feedback --- .../SurfaceData/SurfaceDataMeshComponent.cpp | 20 ++++++++++++--- .../SurfaceData/SurfaceDataMeshComponent.h | 6 ++++- .../SurfaceData/Utility/SurfaceDataUtility.h | 6 ++--- .../Code/Source/SurfaceDataUtility.cpp | 21 ++++++++++------ .../Components/MeshBlockerComponent.cpp | 25 +++++++++++++++---- .../Source/Components/MeshBlockerComponent.h | 8 +++++- 6 files changed, 65 insertions(+), 21 deletions(-) diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp index 04c818beb2..4daedacdab 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.cpp @@ -73,8 +73,14 @@ namespace SurfaceData } } + SurfaceDataMeshComponent::SurfaceDataMeshComponent() + : m_nonUniformScaleChangedHandler([this]([[maybe_unused]] const AZ::Vector3& scale) { this->OnCompositionChanged(); }) + { + } + SurfaceDataMeshComponent::SurfaceDataMeshComponent(const SurfaceDataMeshConfig& configuration) : m_configuration(configuration) + , m_nonUniformScaleChangedHandler([this]([[maybe_unused]] const AZ::Vector3& scale) { this->OnCompositionChanged(); }) { } @@ -83,6 +89,9 @@ namespace SurfaceData AZ::TransformNotificationBus::Handler::BusConnect(GetEntityId()); AZ::Render::MeshComponentNotificationBus::Handler::BusConnect(GetEntityId()); + AZ::NonUniformScaleRequestBus::Event( + GetEntityId(), &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent, m_nonUniformScaleChangedHandler); + m_providerHandle = InvalidSurfaceDataRegistryHandle; m_refresh = false; @@ -98,6 +107,7 @@ namespace SurfaceData m_providerHandle = InvalidSurfaceDataRegistryHandle; } + m_nonUniformScaleChangedHandler.Disconnect(); SurfaceDataProviderRequestBus::Handler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect(); AZ::TransformNotificationBus::Handler::BusDisconnect(); @@ -157,9 +167,10 @@ namespace SurfaceData return false; } - const AZ::Vector3 rayOrigin = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_meshBounds.GetMax().GetZ() + s_rayAABBHeightPadding); - const AZ::Vector3 rayDirection = -AZ::Vector3::CreateAxisZ(); - return GetMeshRayIntersection(*mesh, m_meshWorldTM, m_meshWorldTMInverse, rayOrigin, rayDirection, outPosition, outNormal); + const AZ::Vector3 rayStart = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_meshBounds.GetMax().GetZ() + s_rayAABBHeightPadding); + const AZ::Vector3 rayEnd = AZ::Vector3(inPosition.GetX(), inPosition.GetY(), m_meshBounds.GetMin().GetZ() - s_rayAABBHeightPadding); + return GetMeshRayIntersection( + *mesh, m_meshWorldTM, m_meshWorldTMInverse, m_meshNonUniformScale, rayStart, rayEnd, outPosition, outNormal); } @@ -241,6 +252,9 @@ namespace SurfaceData AZ::TransformBus::EventResult(m_meshWorldTM, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); m_meshWorldTMInverse = m_meshWorldTM.GetInverse(); + m_meshNonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(m_meshNonUniformScale, GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + meshValidAfterUpdate = (m_meshAssetData.GetAs() != nullptr) && (m_meshBounds.IsValid()); } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.h index cb5c499384..2e0cdcb460 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/SurfaceData/SurfaceDataMeshComponent.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -52,7 +53,7 @@ namespace SurfaceData static void Reflect(AZ::ReflectContext* context); SurfaceDataMeshComponent(const SurfaceDataMeshConfig& configuration); - SurfaceDataMeshComponent() = default; + SurfaceDataMeshComponent(); ~SurfaceDataMeshComponent() = default; ////////////////////////////////////////////////////////////////////////// @@ -86,6 +87,8 @@ namespace SurfaceData AZ::Aabb GetSurfaceAabb() const; SurfaceTagVector GetSurfaceTags() const; + AZ::NonUniformScaleChangedEvent::Handler m_nonUniformScaleChangedHandler; ///< Responds to changes in non-uniform scale. + SurfaceDataMeshConfig m_configuration; SurfaceDataRegistryHandle m_providerHandle = InvalidSurfaceDataRegistryHandle; @@ -96,6 +99,7 @@ namespace SurfaceData AZ::Data::Asset m_meshAssetData; AZ::Transform m_meshWorldTM = AZ::Transform::CreateIdentity(); AZ::Transform m_meshWorldTMInverse = AZ::Transform::CreateIdentity(); + AZ::Vector3 m_meshNonUniformScale = AZ::Vector3::CreateOne(); AZ::Aabb m_meshBounds = AZ::Aabb::CreateNull(); }; } diff --git a/Gems/SurfaceData/Code/Include/SurfaceData/Utility/SurfaceDataUtility.h b/Gems/SurfaceData/Code/Include/SurfaceData/Utility/SurfaceDataUtility.h index 7b8e86c641..9df55803f9 100644 --- a/Gems/SurfaceData/Code/Include/SurfaceData/Utility/SurfaceDataUtility.h +++ b/Gems/SurfaceData/Code/Include/SurfaceData/Utility/SurfaceDataUtility.h @@ -83,9 +83,9 @@ namespace SurfaceData bool GetMeshRayIntersection( const AZ::RPI::ModelAsset& meshAsset, const AZ::Transform& meshTransform, - const AZ::Transform& meshTransformInverse, const AZ::Vector3& rayOrigin, - const AZ::Vector3& rayDirection, AZ::Vector3& outPosition, - AZ::Vector3& outNormal); + const AZ::Transform& meshTransformInverse, const AZ::Vector3& nonUniformScale, + const AZ::Vector3& rayStart, const AZ::Vector3& rayEnd, + AZ::Vector3& outPosition, AZ::Vector3& outNormal); AZ_INLINE void AddMaxValueForMasks(SurfaceTagWeightMap& masks, const AZ::Crc32 tag, const float value) { diff --git a/Gems/SurfaceData/Code/Source/SurfaceDataUtility.cpp b/Gems/SurfaceData/Code/Source/SurfaceDataUtility.cpp index 5862fe3b33..72dee012c2 100644 --- a/Gems/SurfaceData/Code/Source/SurfaceDataUtility.cpp +++ b/Gems/SurfaceData/Code/Source/SurfaceDataUtility.cpp @@ -11,23 +11,28 @@ namespace SurfaceData { bool GetMeshRayIntersection( - const AZ::RPI::ModelAsset& meshAsset, const AZ::Transform& meshTransform, const AZ::Transform& meshTransformInverse, - const AZ::Vector3& rayOrigin, const AZ::Vector3& rayDirection, AZ::Vector3& outPosition, AZ::Vector3& outNormal) + const AZ::RPI::ModelAsset& meshAsset, const AZ::Transform& meshTransform, + const AZ::Transform& meshTransformInverse, const AZ::Vector3& nonUniformScale, + const AZ::Vector3& rayStart, const AZ::Vector3& rayEnd, + AZ::Vector3& outPosition, AZ::Vector3& outNormal) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Entity); + const AZ::Vector3 clampedScale = nonUniformScale.GetMax(AZ::Vector3(AZ::MinTransformScale)); + // Transform everything into model space - const AZ::Vector3 rayOriginLocal = meshTransformInverse.TransformPoint(rayOrigin); - const AZ::Vector3 rayDirectionLocal = meshTransformInverse.TransformVector(rayDirection).GetNormalized(); - float distance = FLT_MAX; + const AZ::Vector3 rayStartLocal = meshTransformInverse.TransformPoint(rayStart) / clampedScale; + const AZ::Vector3 rayEndLocal = meshTransformInverse.TransformPoint(rayEnd) / clampedScale; + const AZ::Vector3 rayDirectionLocal = (rayEndLocal - rayStartLocal).GetNormalized(); + float distance = rayEndLocal.GetDistance(rayStartLocal); AZ::Vector3 normalLocal; - if (meshAsset.LocalRayIntersectionAgainstModel(rayOriginLocal, rayDirectionLocal, distance, normalLocal)) + if (meshAsset.LocalRayIntersectionAgainstModel(rayStartLocal, rayDirectionLocal, distance, normalLocal)) { // Transform everything back to world space - outPosition = meshTransform.TransformPoint(rayOriginLocal + (rayDirectionLocal * distance)); - outNormal = meshTransform.TransformVector(normalLocal); + outPosition = meshTransform.TransformPoint((rayStartLocal + (rayDirectionLocal * distance)) * clampedScale); + outNormal = meshTransform.TransformVector(normalLocal * clampedScale); return true; } diff --git a/Gems/Vegetation/Code/Source/Components/MeshBlockerComponent.cpp b/Gems/Vegetation/Code/Source/Components/MeshBlockerComponent.cpp index b5b503ed4a..097c03a4fd 100644 --- a/Gems/Vegetation/Code/Source/Components/MeshBlockerComponent.cpp +++ b/Gems/Vegetation/Code/Source/Components/MeshBlockerComponent.cpp @@ -129,9 +129,16 @@ namespace Vegetation } } + MeshBlockerComponent::MeshBlockerComponent() + : AreaComponentBase() + , m_nonUniformScaleChangedHandler([this]([[maybe_unused]] const AZ::Vector3& scale) { this->OnCompositionChanged(); }) + { + } + MeshBlockerComponent::MeshBlockerComponent(const MeshBlockerConfig& configuration) : AreaComponentBase(configuration) , m_configuration(configuration) + , m_nonUniformScaleChangedHandler([this]([[maybe_unused]] const AZ::Vector3& scale) { this->OnCompositionChanged(); }) { } @@ -139,6 +146,9 @@ namespace Vegetation { AZ::Render::MeshComponentNotificationBus::Handler::BusConnect(GetEntityId()); + AZ::NonUniformScaleRequestBus::Event( + GetEntityId(), &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent, m_nonUniformScaleChangedHandler); + UpdateMeshData(); m_refresh = false; @@ -153,6 +163,7 @@ namespace Vegetation { AreaComponentBase::Deactivate(); //must deactivate base first to ensure AreaRequestBus disconnect waits for other threads + m_nonUniformScaleChangedHandler.Disconnect(); SurfaceData::SurfaceDataSystemNotificationBus::Handler::BusDisconnect(); m_refresh = false; @@ -260,9 +271,10 @@ namespace Vegetation AZ::Vector3 outPosition; AZ::Vector3 outNormal; - const AZ::Vector3 rayOrigin(point.m_position.GetX(), point.m_position.GetY(), m_meshBoundsForIntersection.GetMax().GetZ()); - const AZ::Vector3 rayDirection = -AZ::Vector3::CreateAxisZ(); - bool intersected = SurfaceData::GetMeshRayIntersection(*mesh, m_meshWorldTM, m_meshWorldTMInverse, rayOrigin, rayDirection, outPosition, outNormal) && + const AZ::Vector3 rayStart(point.m_position.GetX(), point.m_position.GetY(), m_meshBoundsForIntersection.GetMax().GetZ()); + const AZ::Vector3 rayEnd(point.m_position.GetX(), point.m_position.GetY(), m_meshBoundsForIntersection.GetMin().GetZ()); + bool intersected = SurfaceData::GetMeshRayIntersection( + *mesh, m_meshWorldTM, m_meshWorldTMInverse, m_meshNonUniformScale, rayStart, rayEnd, outPosition, outNormal) && m_meshBoundsForIntersection.Contains(outPosition); m_cachedRayHits[point.m_handle] = intersected; return intersected; @@ -377,10 +389,10 @@ namespace Vegetation m_meshBoundsForIntersection.GetMin().GetZ() + m_meshBoundsForIntersection.GetExtents().GetZ() * m_configuration.m_meshHeightPercentMax); AZ::Vector3 cornerMin = m_meshBoundsForIntersection.GetMin(); - cornerMin.SetZ(heights.first); + cornerMin.SetZ(heights.first - s_rayAABBHeightPadding); AZ::Vector3 cornerMax = m_meshBoundsForIntersection.GetMax(); - cornerMax.SetZ(heights.second); + cornerMax.SetZ(heights.second + s_rayAABBHeightPadding); m_meshBoundsForIntersection.Set(cornerMin, cornerMax); } @@ -392,6 +404,9 @@ namespace Vegetation AZ::TransformBus::EventResult(m_meshWorldTM, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); m_meshWorldTMInverse = m_meshWorldTM.GetInverse(); + m_meshNonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(m_meshNonUniformScale, GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + AreaComponentBase::OnCompositionChanged(); } diff --git a/Gems/Vegetation/Code/Source/Components/MeshBlockerComponent.h b/Gems/Vegetation/Code/Source/Components/MeshBlockerComponent.h index 56a14ae1f6..bfafa2a321 100644 --- a/Gems/Vegetation/Code/Source/Components/MeshBlockerComponent.h +++ b/Gems/Vegetation/Code/Source/Components/MeshBlockerComponent.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -62,7 +63,7 @@ namespace Vegetation static void Reflect(AZ::ReflectContext* context); MeshBlockerComponent(const MeshBlockerConfig& configuration); - MeshBlockerComponent() = default; + MeshBlockerComponent(); ~MeshBlockerComponent() = default; ////////////////////////////////////////////////////////////////////////// @@ -122,6 +123,8 @@ namespace Vegetation MeshBlockerConfig m_configuration; AZStd::atomic_bool m_refresh{ false }; + AZ::NonUniformScaleChangedEvent::Handler m_nonUniformScaleChangedHandler; ///< Responds to changes in non-uniform scale. + // cached data mutable AZStd::recursive_mutex m_cacheMutex; AZ::Data::Asset m_meshAssetData; @@ -129,9 +132,12 @@ namespace Vegetation AZ::Transform m_meshWorldTMInverse = AZ::Transform::CreateIdentity(); AZ::Aabb m_meshBounds = AZ::Aabb::CreateNull(); AZ::Aabb m_meshBoundsForIntersection = AZ::Aabb::CreateNull(); + AZ::Vector3 m_meshNonUniformScale = AZ::Vector3::CreateOne(); bool m_meshVisible = false; using CachedRayHits = AZStd::unordered_map; CachedRayHits m_cachedRayHits; + + static constexpr float s_rayAABBHeightPadding = 0.1f; }; }