From 567156b85a15c30f0721e66fd8b9bb9a8abdb639 Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Thu, 24 Jun 2021 17:30:24 -0500 Subject: [PATCH] [ATOM-4343] Temporary fix for vegetation raycasts until full solution is implemented. (#1572) Currently, the first time a raycast is attempted for a model, the raycast will fail and the model's kdtree will asynchronously get built. This breaks the vegetation system, which expects the queries to always work. This adds in a brute-force fallback for use while the kdtree is building. However, other use cases like the Editor mouse cursor selection raycast still should get the current "silent failure" behavior, because otherwise the Editor will lock up for several seconds the first time the mouse moves over an extremely complex model. --- .../RPI/Code/Include/Atom/RPI.Reflect/Model/ModelAsset.h | 4 +++- Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp | 4 +++- .../RPI/Code/Source/RPI.Reflect/Model/ModelAsset.cpp | 5 +++-- Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp | 9 ++++++--- Gems/SurfaceData/Code/Source/SurfaceDataUtility.cpp | 3 ++- Gems/Vegetation/Code/Tests/VegetationMocks.h | 3 ++- 6 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Model/ModelAsset.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Model/ModelAsset.h index 465dae960d..c30116eea0 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Model/ModelAsset.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Model/ModelAsset.h @@ -60,12 +60,14 @@ namespace AZ //! //! @param rayStart The starting point of the ray. //! @param rayDir The direction and length of the ray (magnitude is encoded in the direction). + //! @param allowBruteForce Allow for brute force queries while the mesh is baking (remove when ATOM-4343 is complete) //! @param[out] distanceNormalized If an intersection is found, will be set to the normalized distance of the intersection //! (in the range 0.0-1.0) - to calculate the actual distance, multiply distanceNormalized by the magnitude of rayDir. //! @param[out] normal If an intersection is found, will be set to the normal at the point of collision. //! @return True if the ray intersects the mesh. virtual bool LocalRayIntersectionAgainstModel( - const AZ::Vector3& rayStart, const AZ::Vector3& rayDir, float& distanceNormalized, AZ::Vector3& normal) const; + const AZ::Vector3& rayStart, const AZ::Vector3& rayDir, bool allowBruteForce, + float& distanceNormalized, AZ::Vector3& normal) const; private: void SetReady(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp index a0743f97d3..7c869c7910 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp @@ -146,7 +146,9 @@ namespace AZ AZ::Debug::Timer timer; timer.Stamp(); #endif - const bool hit = modelAssetPtr->LocalRayIntersectionAgainstModel(rayStart, rayDir, distanceNormalized, normal); + constexpr bool AllowBruteForce = false; + const bool hit = modelAssetPtr->LocalRayIntersectionAgainstModel( + rayStart, rayDir, AllowBruteForce, distanceNormalized, normal); #if defined(AZ_RPI_PROFILE_RAYCASTING_AGAINST_MODELS) if (hit) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelAsset.cpp index 99e97b0300..bf0e4eb584 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/ModelAsset.cpp @@ -72,7 +72,8 @@ namespace AZ } bool ModelAsset::LocalRayIntersectionAgainstModel( - const AZ::Vector3& rayStart, const AZ::Vector3& rayDir, float& distanceNormalized, AZ::Vector3& normal) const + const AZ::Vector3& rayStart, const AZ::Vector3& rayDir, bool allowBruteForce, + float& distanceNormalized, AZ::Vector3& normal) const { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzRender); @@ -90,7 +91,7 @@ namespace AZ BuildKdTree(); AZ_WarningOnce("Model", false, "ray intersection against a model that is still creating spatial information"); - return false; + return allowBruteForce ? BruteForceRayIntersect(rayStart, rayDir, distanceNormalized, normal) : false; } else { diff --git a/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp b/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp index 6329d11a55..7bc9ed5d11 100644 --- a/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Model/ModelTests.cpp @@ -1214,11 +1214,12 @@ namespace UnitTest float distance = AZStd::numeric_limits::max(); AZ::Vector3 normal; + constexpr bool AllowBruteForce = false; EXPECT_THAT( mesh.GetModel()->LocalRayIntersectionAgainstModel( AZ::Vector3(GetParam().xpos, GetParam().ypos, GetParam().zpos), - AZ::Vector3(GetParam().xdir, GetParam().ydir, GetParam().zdir), distance, normal), + AZ::Vector3(GetParam().xdir, GetParam().ydir, GetParam().zdir), AllowBruteForce, distance, normal), testing::Eq(GetParam().expectedShouldIntersect)); EXPECT_THAT(distance, testing::FloatEq(GetParam().expectedDistance)); } @@ -1262,9 +1263,10 @@ namespace UnitTest // firing down the negative z axis, positioned 5 units from cube (cube is 2x2x2 so intersection // happens at 1 in z) + constexpr bool AllowBruteForce = false; EXPECT_THAT( m_mesh->GetModel()->LocalRayIntersectionAgainstModel( - AZ::Vector3::CreateAxisZ(5.0f), -AZ::Vector3::CreateAxisZ(10.0f), t, normal), + AZ::Vector3::CreateAxisZ(5.0f), -AZ::Vector3::CreateAxisZ(10.0f), AllowBruteForce, t, normal), testing::Eq(true)); EXPECT_THAT(t, testing::FloatEq(0.4f)); } @@ -1275,9 +1277,10 @@ namespace UnitTest AZ::Vector3 normal = AZ::Vector3::CreateOne(); // invalid starting normal // ensure the intersection happens right at the end of the ray + constexpr bool AllowBruteForce = false; EXPECT_THAT( m_mesh->GetModel()->LocalRayIntersectionAgainstModel( - AZ::Vector3::CreateAxisY(10.0f), -AZ::Vector3::CreateAxisY(9.0f), t, normal), + AZ::Vector3::CreateAxisY(10.0f), -AZ::Vector3::CreateAxisY(9.0f), AllowBruteForce, t, normal), testing::Eq(true)); EXPECT_THAT(t, testing::FloatEq(1.0f)); EXPECT_THAT(normal, IsClose(AZ::Vector3::CreateAxisY())); diff --git a/Gems/SurfaceData/Code/Source/SurfaceDataUtility.cpp b/Gems/SurfaceData/Code/Source/SurfaceDataUtility.cpp index 72dee012c2..439bc35071 100644 --- a/Gems/SurfaceData/Code/Source/SurfaceDataUtility.cpp +++ b/Gems/SurfaceData/Code/Source/SurfaceDataUtility.cpp @@ -28,7 +28,8 @@ namespace SurfaceData AZ::Vector3 normalLocal; - if (meshAsset.LocalRayIntersectionAgainstModel(rayStartLocal, rayDirectionLocal, distance, normalLocal)) + constexpr bool AllowBruteForce = true; + if (meshAsset.LocalRayIntersectionAgainstModel(rayStartLocal, rayDirectionLocal, AllowBruteForce, distance, normalLocal)) { // Transform everything back to world space outPosition = meshTransform.TransformPoint((rayStartLocal + (rayDirectionLocal * distance)) * clampedScale); diff --git a/Gems/Vegetation/Code/Tests/VegetationMocks.h b/Gems/Vegetation/Code/Tests/VegetationMocks.h index 1b95975e67..e2e135a3f3 100644 --- a/Gems/Vegetation/Code/Tests/VegetationMocks.h +++ b/Gems/Vegetation/Code/Tests/VegetationMocks.h @@ -475,7 +475,8 @@ namespace UnitTest ~MockMeshAsset() override = default; bool LocalRayIntersectionAgainstModel( - [[maybe_unused]] const AZ::Vector3& rayStart, [[maybe_unused]] const AZ::Vector3& dir, [[maybe_unused]] float& distance, [[maybe_unused]] AZ::Vector3& normal) const override + [[maybe_unused]] const AZ::Vector3& rayStart, [[maybe_unused]] const AZ::Vector3& dir, [[maybe_unused]] bool allowBruteForce, + [[maybe_unused]] float& distance, [[maybe_unused]] AZ::Vector3& normal) const override { distance = 0.1f; return true;