diff --git a/Code/Framework/AzCore/AzCore/Math/ShapeIntersection.inl b/Code/Framework/AzCore/AzCore/Math/ShapeIntersection.inl index b35906a434..0638e070fc 100644 --- a/Code/Framework/AzCore/AzCore/Math/ShapeIntersection.inl +++ b/Code/Framework/AzCore/AzCore/Math/ShapeIntersection.inl @@ -130,7 +130,11 @@ namespace AZ //So for each plane, we can test compare the center-to-plane distance to this interval to see which side of the plane the AABB is on. //The AABB is not overlapping if it is fully behind any of the planes, otherwise it is overlapping. const Vector3 center = aabb.GetCenter(); - const Vector3 extents = 0.5f * aabb.GetExtents(); + + //If the AABB contains FLT_MAX at either (or both) extremes, it would be easy to overflow here by using "0.5f * GetExtents()" + //or "0.5f * (GetMax() - GetMin())". By separating into two separate multiplies before the subtraction, we can ensure + //that we don't overflow. + const Vector3 extents = (0.5f * aabb.GetMax()) - (0.5f * aabb.GetMin()); for (Frustum::PlaneId planeId = Frustum::PlaneId::Near; planeId < Frustum::PlaneId::MAX; ++planeId) { diff --git a/Code/Framework/AzCore/Tests/Math/ShapeIntersectionTests.cpp b/Code/Framework/AzCore/Tests/Math/ShapeIntersectionTests.cpp index a28e8a9dbf..659febf564 100644 --- a/Code/Framework/AzCore/Tests/Math/ShapeIntersectionTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/ShapeIntersectionTests.cpp @@ -42,6 +42,7 @@ namespace UnitTest AZ::Aabb unitBox = AZ::Aabb::CreateCenterHalfExtents(AZ::Vector3::CreateZero(), AZ::Vector3(1.f, 1.f, 1.f)); AZ::Aabb aabb = AZ::Aabb::CreateCenterHalfExtents(AZ::Vector3(10.f, 10.f, 10.f), AZ::Vector3(1.f, 1.f, 1.f)); AZ::Aabb aabb1 = AZ::Aabb::CreateCenterHalfExtents(AZ::Vector3(10.f, 10.f, 10.f), AZ::Vector3(100.f, 100.f, 100.f)); + AZ::Aabb maxSizeAabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(-AZ::Constants::FloatMax), AZ::Vector3(AZ::Constants::FloatMax)); AZ::Vector3 point(0.f, 0.f, 0.f); AZ::Vector3 point1(10.f, 10.f, 10.f); @@ -73,6 +74,10 @@ namespace UnitTest EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(frustum, aabb1)); EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(sphere1, far_value)); + // Verify that an AABB that covers the max floating point range successfully overlaps with a frustum and doesn't hit any + // floating-point math overflows. + EXPECT_TRUE(AZ::ShapeIntersection::Overlaps(frustum, maxSizeAabb)); + EXPECT_FALSE(AZ::ShapeIntersection::Overlaps(frustum, aabb)); EXPECT_FALSE(AZ::ShapeIntersection::Overlaps(unitSphere, aabb)); EXPECT_FALSE(AZ::ShapeIntersection::Overlaps(unitSphere, sphere2)); diff --git a/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp b/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp index ec50b6f052..d98a3628a8 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp +++ b/Gems/AtomLyIntegration/AtomFont/Code/Source/FFont.cpp @@ -1732,6 +1732,14 @@ void AZ::FFont::DrawScreenAlignedText3d( currentView->GetWorldToViewMatrix(), currentView->GetViewToClipMatrix() ); + + // Text behind the camera shouldn't get rendered. WorldToScreenNDC returns values in the range 0 - 1, so Z < 0.5 is behind the screen + // and >= 0.5 is in front of the screen. + if (positionNDC.GetZ() < 0.5f) + { + return; + } + internalParams.m_ctx.m_sizeIn800x600 = false; DrawStringUInternal( diff --git a/Gems/SurfaceData/Code/Source/SurfaceDataUtility.cpp b/Gems/SurfaceData/Code/Source/SurfaceDataUtility.cpp index bcadbe7c7d..884ee8e840 100644 --- a/Gems/SurfaceData/Code/Source/SurfaceDataUtility.cpp +++ b/Gems/SurfaceData/Code/Source/SurfaceDataUtility.cpp @@ -34,7 +34,7 @@ namespace SurfaceData { // Transform everything back to world space outPosition = meshTransform.TransformPoint((rayStartLocal + (rayDirectionLocal * distance)) * clampedScale); - outNormal = meshTransform.TransformVector(normalLocal * clampedScale); + outNormal = meshTransform.TransformVector(normalLocal).GetNormalized(); return true; } diff --git a/Gems/Vegetation/Code/Source/Debugger/DebugComponent.cpp b/Gems/Vegetation/Code/Source/Debugger/DebugComponent.cpp index 527bb26f7d..bfe74a8b28 100644 --- a/Gems/Vegetation/Code/Source/Debugger/DebugComponent.cpp +++ b/Gems/Vegetation/Code/Source/Debugger/DebugComponent.cpp @@ -112,6 +112,7 @@ void DebugComponent::Activate() DebugNotificationBus::Handler::BusConnect(); DebugNotificationBus::AllowFunctionQueuing(true); AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId()); + AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId()); SystemConfigurationRequestBus::Handler::BusConnect(); VEG_PROFILE_METHOD(DebugSystemDataBus::BroadcastResult(m_debugData, &DebugSystemDataBus::Events::GetDebugData)); @@ -120,6 +121,7 @@ void DebugComponent::Activate() void DebugComponent::Deactivate() { SystemConfigurationRequestBus::Handler::BusDisconnect(); + AzFramework::BoundsRequestBus::Handler::BusDisconnect(); AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect(); DebugRequestBus::Handler::BusDisconnect(); DebugNotificationBus::Handler::BusDisconnect(); diff --git a/Gems/Vegetation/Code/Source/Debugger/DebugComponent.h b/Gems/Vegetation/Code/Source/Debugger/DebugComponent.h index 346f0918f9..4b927b63b0 100644 --- a/Gems/Vegetation/Code/Source/Debugger/DebugComponent.h +++ b/Gems/Vegetation/Code/Source/Debugger/DebugComponent.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,7 @@ namespace Vegetation class DebugComponent : public AZ::Component , private AzFramework::EntityDebugDisplayEventBus::Handler + , private AzFramework::BoundsRequestBus::Handler , private DebugRequestBus::Handler , private DebugNotificationBus::Handler , private SystemConfigurationRequestBus::Handler @@ -80,6 +82,9 @@ namespace Vegetation ////////////////////////////////////////////////////////////////////////// // EntityDebugDisplayEventBus + + // Ideally this would use ViewportDebugDisplayEventBus::DisplayViewport, but that doesn't currently work in game mode, + // so instead we use this plus the BoundsRequestBus with a large AABB to get ourselves rendered. void DisplayEntityViewport( const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; @@ -110,6 +115,20 @@ namespace Vegetation void UpdateSystemConfig(const AZ::ComponentConfig* config) override; void GetSystemConfig([[maybe_unused]] AZ::ComponentConfig* config) const override {}; // ignore this call + ////////////////////////////////////////////////////////////////////////// + // BoundsRequestBus + AZ::Aabb GetWorldBounds() override + { + // DisplayEntityViewport relies on the BoundsRequestBus to get the entity bounds to determine when to call debug drawing + // for that entity. Since this is a level component that can draw infinitely far in every direction, we return an + // effectively infinite AABB so that it always draws. + return AZ::Aabb::CreateFromMinMax(AZ::Vector3(-AZ::Constants::FloatMax), AZ::Vector3(AZ::Constants::FloatMax)); + } + AZ::Aabb GetLocalBounds() override + { + // The local and world bounds will be the same for this component. + return GetWorldBounds(); + } protected: void PrepareNextReport();