diff --git a/Code/Framework/AzCore/AzCore/Math/Aabb.cpp b/Code/Framework/AzCore/AzCore/Math/Aabb.cpp index 94e138d88d..3f7cb4ecf5 100644 --- a/Code/Framework/AzCore/AzCore/Math/Aabb.cpp +++ b/Code/Framework/AzCore/AzCore/Math/Aabb.cpp @@ -146,8 +146,8 @@ namespace AZ ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) ->Method("GetTranslated", &Aabb::GetTranslated) ->Method("GetSurfaceArea", &Aabb::GetSurfaceArea) - ->Method("GetTransformedObb", &Aabb::GetTransformedObb) - ->Method("GetTransformedAabb", &Aabb::GetTransformedAabb) + ->Method("GetTransformedObb", static_cast(&Aabb::GetTransformedObb)) + ->Method("GetTransformedAabb", static_cast(&Aabb::GetTransformedAabb)) ->Method("ApplyTransform", &Aabb::ApplyTransform) ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All) ->Method("Clone", [](const Aabb& rhs) -> Aabb { return rhs; }) @@ -195,6 +195,20 @@ namespace AZ } + Obb Aabb::GetTransformedObb(const Matrix3x4& matrix3x4) const + { + Matrix3x4 matrixNoScale = matrix3x4; + const AZ::Vector3 scale = matrixNoScale.ExtractScale(); + const AZ::Quaternion rotation = AZ::Quaternion::CreateFromMatrix3x4(matrixNoScale); + + return Obb::CreateFromPositionRotationAndHalfLengths( + matrix3x4 * GetCenter(), + rotation, + 0.5f * scale * GetExtents() + ); + } + + void Aabb::ApplyTransform(const Transform& transform) { Vector3 a, b, axisCoeffs; @@ -224,4 +238,17 @@ namespace AZ m_min = newMin; m_max = newMax; } + + + void Aabb::ApplyMatrix3x4(const Matrix3x4& matrix3x4) + { + const AZ::Vector3 extents = GetExtents(); + const AZ::Vector3 center = matrix3x4 * GetCenter(); + AZ::Vector3 newHalfExtents( + 0.5f * matrix3x4.GetRowAsVector3(0).GetAbs().Dot(extents), + 0.5f * matrix3x4.GetRowAsVector3(1).GetAbs().Dot(extents), + 0.5f * matrix3x4.GetRowAsVector3(2).GetAbs().Dot(extents)); + m_min = center - newHalfExtents; + m_max = center + newHalfExtents; + } } diff --git a/Code/Framework/AzCore/AzCore/Math/Aabb.h b/Code/Framework/AzCore/AzCore/Math/Aabb.h index 0aebd099f4..ef7704533e 100644 --- a/Code/Framework/AzCore/AzCore/Math/Aabb.h +++ b/Code/Framework/AzCore/AzCore/Math/Aabb.h @@ -129,11 +129,21 @@ namespace AZ void ApplyTransform(const Transform& transform); + void ApplyMatrix3x4(const Matrix3x4& matrix3x4); + + void MultiplyByScale(const Vector3& scale); + //! Transforms an Aabb and returns the resulting Obb. - class Obb GetTransformedObb(const Transform& transform) const; + [[nodiscard]] Obb GetTransformedObb(const Transform& transform) const; + + //! Transforms an Aabb and returns the resulting Obb. + [[nodiscard]] Obb GetTransformedObb(const Matrix3x4& matrix3x4) const; //! Returns a new AABB containing the transformed AABB. - Aabb GetTransformedAabb(const Transform& transform) const; + [[nodiscard]] Aabb GetTransformedAabb(const Transform& transform) const; + + //! Returns a new AABB containing the transformed AABB. + [[nodiscard]] Aabb GetTransformedAabb(const Matrix3x4& matrix3x4) const; //! Checks if this aabb is equal to another within a floating point tolerance. bool IsClose(const Aabb& rhs, float tolerance = Constants::Tolerance) const; diff --git a/Code/Framework/AzCore/AzCore/Math/Aabb.inl b/Code/Framework/AzCore/AzCore/Math/Aabb.inl index 5e6a5ae188..13549a30f6 100644 --- a/Code/Framework/AzCore/AzCore/Math/Aabb.inl +++ b/Code/Framework/AzCore/AzCore/Math/Aabb.inl @@ -292,6 +292,14 @@ namespace AZ } + AZ_MATH_INLINE void Aabb::MultiplyByScale(const Vector3& scale) + { + m_min *= scale; + m_max *= scale; + AZ_MATH_ASSERT(IsValid(), "Min must be less than Max"); + } + + AZ_MATH_INLINE Aabb Aabb::GetTransformedAabb(const Transform& transform) const { Aabb aabb = Aabb::CreateFromMinMax(m_min, m_max); @@ -300,6 +308,14 @@ namespace AZ } + AZ_MATH_INLINE Aabb Aabb::GetTransformedAabb(const Matrix3x4& matrix3x4) const + { + Aabb aabb = Aabb::CreateFromMinMax(m_min, m_max); + aabb.ApplyMatrix3x4(matrix3x4); + return aabb; + } + + AZ_MATH_INLINE bool Aabb::IsClose(const Aabb& rhs, float tolerance) const { return m_min.IsClose(rhs.m_min, tolerance) && m_max.IsClose(rhs.m_max, tolerance); diff --git a/Code/Framework/AzCore/Tests/Math/AabbTests.cpp b/Code/Framework/AzCore/Tests/Math/AabbTests.cpp index 20a4d2ed85..44cc800616 100644 --- a/Code/Framework/AzCore/Tests/Math/AabbTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/AabbTests.cpp @@ -15,6 +15,7 @@ #include #include #include +#include using namespace AZ; @@ -385,4 +386,77 @@ namespace UnitTest EXPECT_TRUE(aabb.GetMin().IsClose(transAabb.GetMin())); EXPECT_TRUE(aabb.GetMax().IsClose(transAabb.GetMax())); } + + TEST(MATH_AabbTransform, GetTransformedObbMatrix3x4) + { + Vector3 min(-1.0f, -2.0f, -3.0f); + Vector3 max(4.0f, 3.0f, 2.0f); + Aabb aabb = Aabb::CreateFromMinMax(min, max); + + Quaternion rotation(0.46f, 0.26f, 0.58f, 0.62f); + Vector3 translation(5.0f, 7.0f, 9.0f); + + Matrix3x4 matrix3x4 = Matrix3x4::CreateFromQuaternionAndTranslation(rotation, translation); + + matrix3x4.MultiplyByScale(Vector3(0.5f, 1.5f, 2.0f)); + + Obb obb = aabb.GetTransformedObb(matrix3x4); + + EXPECT_THAT(obb.GetRotation(), IsClose(rotation)); + EXPECT_THAT(obb.GetHalfLengths(), IsClose(Vector3(1.25f, 3.75f, 5.0f))); + EXPECT_THAT(obb.GetPosition(), IsClose(Vector3(3.928f, 7.9156f, 9.3708f))); + } + + TEST(MATH_AabbTransform, GetTransformedAabbMatrix3x4) + { + Vector3 min(2.0f, 3.0f, 5.0f); + Vector3 max(6.0f, 5.0f, 11.0f); + Aabb aabb = Aabb::CreateFromMinMax(min, max); + + Quaternion rotation(0.34f, 0.46f, 0.58f, 0.58f); + Vector3 translation(-3.0f, -4.0f, -5.0f); + + Matrix3x4 matrix3x4 = Matrix3x4::CreateFromQuaternionAndTranslation(rotation, translation); + + matrix3x4.MultiplyByScale(Vector3(1.2f, 0.8f, 2.0f)); + + Aabb transformedAabb = aabb.GetTransformedAabb(matrix3x4); + + EXPECT_THAT(transformedAabb.GetMin(), IsClose(Vector3(4.1488f, -0.01216f, -0.31904f))); + EXPECT_THAT(transformedAabb.GetMax(), IsClose(Vector3(16.3216f, 6.54272f, 5.98112f))); + } + + TEST(MATH_AabbTransform, GetTransformedObbFitsInsideTransformedAabb) + { + Vector3 min(4.0f, 3.0f, 1.0f); + Vector3 max(7.0f, 6.0f, 8.0f); + Aabb aabb = Aabb::CreateFromMinMax(min, max); + + Quaternion rotation(0.40f, 0.40f, 0.64f, 0.52f); + Vector3 translation(-2.0f, 4.0f, -3.0f); + + Matrix3x4 matrix3x4 = Matrix3x4::CreateFromQuaternionAndTranslation(rotation, translation); + + matrix3x4.MultiplyByScale(Vector3(2.2f, 0.6f, 1.4f)); + + Aabb transformedAabb = aabb.GetTransformedAabb(matrix3x4); + Obb transformedObb = aabb.GetTransformedObb(matrix3x4); + Aabb aabbContainingTransformedObb = Aabb::CreateFromObb(transformedObb); + + EXPECT_THAT(transformedAabb.GetMin(), IsClose(aabbContainingTransformedObb.GetMin())); + EXPECT_THAT(transformedAabb.GetMax(), IsClose(aabbContainingTransformedObb.GetMax())); + } + + TEST(MATH_AabbTransform, MultiplyByScale) + { + Vector3 min(2.0f, 6.0f, 8.0f); + Vector3 max(6.0f, 9.0f, 10.0f); + Aabb aabb = Aabb::CreateFromMinMax(min, max); + + Vector3 scale(0.5f, 2.0f, 1.5f); + aabb.MultiplyByScale(scale); + + EXPECT_THAT(aabb.GetMin(), IsClose(Vector3(1.0f, 12.0f, 12.0f))); + EXPECT_THAT(aabb.GetMax(), IsClose(Vector3(3.0f, 18.0f, 15.0f))); + } } diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/temp/128x128_RGBA8.tga.streamingimage b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/temp/128x128_RGBA8.tga.streamingimage new file mode 100644 index 0000000000..b867b7cfb1 Binary files /dev/null and b/Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/TestAssets/temp/128x128_RGBA8.tga.streamingimage differ diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h index 7ad8bd8283..eb49859141 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h @@ -145,8 +145,10 @@ namespace AZ const MaterialAssignmentMap& GetMaterialAssignmentMap(const MeshHandle& meshHandle) const override; void ConnectModelChangeEventHandler(const MeshHandle& meshHandle, ModelChangedEvent::Handler& handler) override; - void SetTransform(const MeshHandle& meshHandle, const AZ::Transform& transform) override; + void SetTransform(const MeshHandle& meshHandle, const AZ::Transform& transform, + const AZ::Vector3& nonUniformScale = AZ::Vector3::CreateOne()) override; Transform GetTransform(const MeshHandle& meshHandle) override; + Vector3 GetNonUniformScale(const MeshHandle& meshHandle) override; void SetSortKey(const MeshHandle& meshHandle, RHI::DrawItemSortKey sortKey) override; RHI::DrawItemSortKey GetSortKey(const MeshHandle& meshHandle) override; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h index 77e3467fb9..05f2a408b8 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h @@ -62,9 +62,12 @@ namespace AZ //! Connects a handler to any changes to an RPI::Model. Changes include loading and reloading. virtual void ConnectModelChangeEventHandler(const MeshHandle& meshHandle, ModelChangedEvent::Handler& handler) = 0; //! Sets the transform for a given mesh handle. - virtual void SetTransform(const MeshHandle& meshHandle, const AZ::Transform& transform) = 0; + virtual void SetTransform(const MeshHandle& meshHandle, const Transform& transform, + const Vector3& nonUniformScale = Vector3::CreateOne()) = 0; //! Gets the transform for a given mesh handle. virtual Transform GetTransform(const MeshHandle& meshHandle) = 0; + //! Gets the non-uniform scale for a given mesh handle. + virtual Vector3 GetNonUniformScale(const MeshHandle& meshHandle) = 0; //! Sets the sort key for a given mesh handle. virtual void SetSortKey(const MeshHandle& meshHandle, RHI::DrawItemSortKey sortKey) = 0; //! Gets the sort key for a given mesh handle. diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/TransformService/TransformServiceFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/TransformService/TransformServiceFeatureProcessor.h index 2344f745f8..ddec9a6a1d 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/TransformService/TransformServiceFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/TransformService/TransformServiceFeatureProcessor.h @@ -50,8 +50,10 @@ namespace AZ // TransformServiceFeatureProcessorInterface overrides ... ObjectId ReserveObjectId() override; void ReleaseObjectId(ObjectId& id) override; - void SetTransformForId(ObjectId id, const AZ::Transform& transform) override; + void SetTransformForId(ObjectId id, const AZ::Transform& transform, + const AZ::Vector3& nonUniformScale = AZ::Vector3::CreateOne()) override; AZ::Transform GetTransformForId(ObjectId id) const override; + AZ::Vector3 GetNonUniformScaleForId(ObjectId id) const override; private: diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/TransformService/TransformServiceFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/TransformService/TransformServiceFeatureProcessorInterface.h index 0b17a9a5a0..bb3606b12b 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/TransformService/TransformServiceFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/TransformService/TransformServiceFeatureProcessorInterface.h @@ -13,6 +13,7 @@ #pragma once #include +#include #include namespace AZ @@ -34,11 +35,13 @@ namespace AZ //! Releases an object ID to be used by others. The passed in handle is invalidated. virtual void ReleaseObjectId(ObjectId& id) = 0; - //! Sets the transform for a given id. Id must be one reserved earlier. - virtual void SetTransformForId(ObjectId id, const AZ::Transform& transform) = 0; + //! Sets the transform (and optionally non-uniform scale) for a given id. Id must be one reserved earlier. + virtual void SetTransformForId(ObjectId id, const AZ::Transform& transform, + const AZ::Vector3& nonUniformScale = AZ::Vector3::CreateOne()) = 0; //! Gets the transform for a given id. Id must be one reserved earlier. virtual AZ::Transform GetTransformForId(ObjectId) const = 0; - + //! Gets the non-uniform scale for a given id. Id must be one reserved earlier. + virtual AZ::Vector3 GetNonUniformScaleForId(ObjectId id) const = 0; }; } } diff --git a/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h index 6fc1ac0e30..a1a7e94cb0 100644 --- a/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h @@ -31,11 +31,12 @@ namespace UnitTest MOCK_CONST_METHOD1(GetModel, AZStd::intrusive_ptr(const MeshHandle&)); MOCK_CONST_METHOD1(GetMaterialAssignmentMap, const AZ::Render::MaterialAssignmentMap&(const MeshHandle&)); MOCK_METHOD2(ConnectModelChangeEventHandler, void(const MeshHandle&, ModelChangedEvent::Handler&)); - MOCK_METHOD2(SetTransform, void(const MeshHandle&, const AZ::Transform&)); + MOCK_METHOD3(SetTransform, void(const MeshHandle&, const AZ::Transform&, const AZ::Vector3&)); MOCK_METHOD2(SetExcludeFromReflectionCubeMaps, void(const MeshHandle&, bool)); MOCK_METHOD2(SetMaterialAssignmentMap, void(const MeshHandle&, const AZ::Data::Instance&)); MOCK_METHOD2(SetMaterialAssignmentMap, void(const MeshHandle&, const AZ::Render::MaterialAssignmentMap&)); - MOCK_METHOD1(GetTransform, AZ::Transform (const MeshHandle&)); + MOCK_METHOD1(GetTransform, AZ::Transform(const MeshHandle&)); + MOCK_METHOD1(GetNonUniformScale, AZ::Vector3(const MeshHandle&)); MOCK_METHOD2(SetSortKey, void (const MeshHandle&, AZ::RHI::DrawItemSortKey)); MOCK_METHOD1(GetSortKey, AZ::RHI::DrawItemSortKey(const MeshHandle&)); MOCK_METHOD2(SetLodOverride, void(const MeshHandle&, AZ::RPI::Cullable::LodOverride)); diff --git a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp index dc53498c8b..7845c8f402 100644 --- a/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/AuxGeom/AuxGeomDrawQueue.cpp @@ -504,7 +504,7 @@ namespace AZ box.m_faceCullMode = ConvertRPIFaceCullFlag(faceCull); box.m_color = color; box.m_scale = localMatrix3x4.ExtractScale() * extents; - box.m_position = localMatrix3x4.GetTranslation() + center; + box.m_position = matrix3x4 * center; box.m_rotationMatrix = Matrix3x3::CreateFromMatrix3x4(localMatrix3x4); box.m_pointSize = m_pointSize; box.m_viewProjOverrideIndex = viewProjOverrideIndex; diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index 5f39c6bc38..37f9360f5d 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -256,7 +256,7 @@ namespace AZ } } - void MeshFeatureProcessor::SetTransform(const MeshHandle& meshHandle, const AZ::Transform& transform) + void MeshFeatureProcessor::SetTransform(const MeshHandle& meshHandle, const AZ::Transform& transform, const AZ::Vector3& nonUniformScale) { if (meshHandle.IsValid()) { @@ -264,12 +264,12 @@ namespace AZ meshData.m_cullBoundsNeedsUpdate = true; meshData.m_objectSrgNeedsUpdate = true; - m_transformService->SetTransformForId(meshHandle->m_objectId, transform); + m_transformService->SetTransformForId(meshHandle->m_objectId, transform, nonUniformScale); // ray tracing data needs to be updated with the new transform if (m_rayTracingFeatureProcessor) { - m_rayTracingFeatureProcessor->SetMeshTransform(meshHandle->m_objectId, transform); + m_rayTracingFeatureProcessor->SetMeshTransform(meshHandle->m_objectId, transform, nonUniformScale); } } } @@ -287,6 +287,19 @@ namespace AZ } } + Vector3 MeshFeatureProcessor::GetNonUniformScale(const MeshHandle& meshHandle) + { + if (meshHandle.IsValid()) + { + return m_transformService->GetNonUniformScaleForId(meshHandle->m_objectId); + } + else + { + AZ_Assert(false, "Invalid mesh handle"); + return Vector3::CreateOne(); + } + } + void MeshFeatureProcessor::SetSortKey(const MeshHandle& meshHandle, RHI::DrawItemSortKey sortKey) { if (meshHandle.IsValid()) @@ -845,10 +858,13 @@ namespace AZ AZ_Assert(m_model, "The model has not finished loading yet"); Transform localToWorld = transformService->GetTransformForId(m_objectId); + Vector3 nonUniformScale = transformService->GetNonUniformScaleForId(m_objectId); Vector3 center; float radius; Aabb localAabb = m_model->GetAabb(); + localAabb.MultiplyByScale(nonUniformScale); + localAabb.GetTransformedAabb(localToWorld).GetAsSphere(center, radius); m_cullable.m_cullData.m_boundingSphere = Sphere(center, radius); diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp index 4aff31c552..2bb2fa2ac2 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingAccelerationStructurePass.cpp @@ -81,6 +81,7 @@ namespace AZ ->HitGroupIndex(blasIndex) ->Blas(rayTracingSubMesh.m_blas) ->Transform(rayTracingMesh.second.m_transform) + ->NonUniformScale(rayTracingMesh.second.m_nonUniformScale) ; } diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp index bb8f7ff54d..337d111130 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.cpp @@ -143,7 +143,7 @@ namespace AZ m_meshInfoBufferNeedsUpdate = true; } - void RayTracingFeatureProcessor::SetMeshTransform(const ObjectId objectId, AZ::Transform transform) + void RayTracingFeatureProcessor::SetMeshTransform(const ObjectId objectId, const AZ::Transform transform, const AZ::Vector3 nonUniformScale) { if (!m_rayTracingEnabled) { @@ -154,6 +154,7 @@ namespace AZ if (itMesh != m_meshes.end()) { itMesh->second.m_transform = transform; + itMesh->second.m_nonUniformScale = nonUniformScale; m_revision++; } diff --git a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h index 9dee35716b..f317f1c096 100644 --- a/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/RayTracing/RayTracingFeatureProcessor.h @@ -68,6 +68,9 @@ namespace AZ // mesh transform AZ::Transform m_transform = AZ::Transform::CreateIdentity(); + // mesh non-uniform scale + AZ::Vector3 m_nonUniformScale = AZ::Vector3::CreateOne(); + // flag indicating if the Blas objects in the sub-meshes are built bool m_blasBuilt = false; }; @@ -85,7 +88,8 @@ namespace AZ //! Sets the ray tracing mesh transform //! This will cause an update to the RayTracing acceleration structure on the next frame - void SetMeshTransform(const ObjectId objectId, const AZ::Transform transform); + void SetMeshTransform(const ObjectId objectId, const AZ::Transform transform, + const AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne()); //! Retrieves ray tracing data for all meshes in the scene const MeshMap& GetMeshes() const { return m_meshes; } diff --git a/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp index a4f255185a..acb6e4a287 100644 --- a/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/TransformService/TransformServiceFeatureProcessor.cpp @@ -210,14 +210,14 @@ namespace AZ } } - void TransformServiceFeatureProcessor::SetTransformForId(ObjectId id, const AZ::Transform& transform) + void TransformServiceFeatureProcessor::SetTransformForId(ObjectId id, const AZ::Transform& transform, const AZ::Vector3& nonUniformScale) { AZ_Error("TransformServiceFeatureProcessor", m_isWriteable, "Transform data cannot be written to during this phase"); AZ_Error("TransformServiceFeatureProcessor", id.IsValid(), "Attempting to set the transform for an invalid handle."); if (id.IsValid()) { AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromTransform(transform); - + matrix3x4.MultiplyByScale(nonUniformScale); matrix3x4.StoreToRowMajorFloat12(m_objectToWorldTransforms.at(id.GetIndex()).m_transform); // Inverse transpose to take the non-uniform scale out of the transform for usage with normals. @@ -228,8 +228,18 @@ namespace AZ AZ::Transform TransformServiceFeatureProcessor::GetTransformForId(ObjectId id) const { - AZ_Error("TransformServiceFeatureProcessor", id.IsValid(), "Attempting to set the transform for an invalid handle."); - return AZ::Transform::CreateFromMatrix3x4( Matrix3x4::CreateFromRowMajorFloat12(m_objectToWorldTransforms.at(id.GetIndex()).m_transform) ); + AZ_Error("TransformServiceFeatureProcessor", id.IsValid(), "Attempting to get the transform for an invalid handle."); + AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromRowMajorFloat12(m_objectToWorldTransforms.at(id.GetIndex()).m_transform); + AZ::Transform transform = AZ::Transform::CreateFromMatrix3x4(matrix3x4); + transform.ExtractScale(); + return transform; + } + + AZ::Vector3 TransformServiceFeatureProcessor::GetNonUniformScaleForId(ObjectId id) const + { + AZ_Error("TransformServiceFeatureProcessor", id.IsValid(), "Attempting to get the non-uniform scale for an invalid handle."); + AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromRowMajorFloat12(m_objectToWorldTransforms.at(id.GetIndex()).m_transform); + return matrix3x4.RetrieveScale(); } } } diff --git a/Gems/Atom/RHI/Code/Include/Atom/RHI/RayTracingAccelerationStructure.h b/Gems/Atom/RHI/Code/Include/Atom/RHI/RayTracingAccelerationStructure.h index 09ba2b1d59..5fde518cb2 100644 --- a/Gems/Atom/RHI/Code/Include/Atom/RHI/RayTracingAccelerationStructure.h +++ b/Gems/Atom/RHI/Code/Include/Atom/RHI/RayTracingAccelerationStructure.h @@ -111,6 +111,7 @@ namespace AZ uint32_t m_instanceID = 0; uint32_t m_hitGroupIndex = 0; AZ::Transform m_transform = AZ::Transform::CreateIdentity(); + AZ::Vector3 m_nonUniformScale = AZ::Vector3::CreateOne(); RHI::Ptr m_blas; }; using RayTracingTlasInstanceVector = AZStd::vector; @@ -154,6 +155,7 @@ namespace AZ RayTracingTlasDescriptor* InstanceID(uint32_t instanceID); RayTracingTlasDescriptor* HitGroupIndex(uint32_t hitGroupIndex); RayTracingTlasDescriptor* Transform(const AZ::Transform& transform); + RayTracingTlasDescriptor* NonUniformScale(const AZ::Vector3& nonUniformScale); RayTracingTlasDescriptor* Blas(RHI::Ptr& blas); RayTracingTlasDescriptor* InstancesBuffer(RHI::Ptr& tlasInstances); RayTracingTlasDescriptor* NumInstances(uint32_t numInstancesInBuffer); diff --git a/Gems/Atom/RHI/Code/Source/RHI/RayTracingAccelerationStructure.cpp b/Gems/Atom/RHI/Code/Source/RHI/RayTracingAccelerationStructure.cpp index 37e5b316d6..4349761eb5 100644 --- a/Gems/Atom/RHI/Code/Source/RHI/RayTracingAccelerationStructure.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI/RayTracingAccelerationStructure.cpp @@ -85,6 +85,13 @@ namespace AZ return this; } + RayTracingTlasDescriptor* RayTracingTlasDescriptor::NonUniformScale(const AZ::Vector3& nonUniformScale) + { + AZ_Assert(m_buildContext, "NonUniformSCale property can only be added to an Instance entry"); + m_buildContext->m_nonUniformScale = nonUniformScale; + return this; + } + RayTracingTlasDescriptor* RayTracingTlasDescriptor::Blas(RHI::Ptr& blas) { AZ_Assert(m_buildContext, "Blas property can only be added to an Instance entry"); diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/RayTracingTlas.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/RayTracingTlas.cpp index 77d8d38e42..1342db5690 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/RayTracingTlas.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/RayTracingTlas.cpp @@ -89,8 +89,9 @@ namespace AZ mappedData[i].InstanceID = instance.m_instanceID; mappedData[i].InstanceContributionToHitGroupIndex = instance.m_hitGroupIndex; // convert transform to row-major 3x4 - AZ::Matrix3x4 matrix34 = AZ::Matrix3x4::CreateFromTransform(instance.m_transform); - matrix34.StoreToRowMajorFloat12(&mappedData[i].Transform[0][0]); + AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromTransform(instance.m_transform); + matrix3x4.MultiplyByScale(instance.m_nonUniformScale); + matrix3x4.StoreToRowMajorFloat12(&mappedData[i].Transform[0][0]); mappedData[i].AccelerationStructure = static_cast(blas->GetBuffers().m_blasBuffer.get())->GetMemoryView().GetGpuAddress(); // [GFX TODO][ATOM-5270] Add ray tracing TLAS instance mask support mappedData[i].InstanceMask = 0x1; diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingTlas.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingTlas.cpp index 9720fecb6d..a1346612c9 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingTlas.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/RayTracingTlas.cpp @@ -92,9 +92,9 @@ namespace AZ mappedData[i].instanceCustomIndex = instance.m_instanceID; mappedData[i].instanceShaderBindingTableRecordOffset = instance.m_hitGroupIndex; - // convert transform to row-major 3x4 - AZ::Matrix3x4 matrix34 = AZ::Matrix3x4::CreateFromTransform(instance.m_transform); - matrix34.StoreToRowMajorFloat12(&mappedData[i].transform.matrix[0][0]); + AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromTransform(instance.m_transform); + matrix3x4.MultiplyByScale(instance.m_nonUniformScale); + matrix3x4.StoreToRowMajorFloat12(&mappedData[i].transform.matrix[0][0]); RayTracingBlas* blas = static_cast(instance.m_blas.get()); VkAccelerationStructureDeviceAddressInfoKHR addressInfo = {}; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h index 48c33fb0d6..35af200759 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Model/Model.h @@ -73,12 +73,14 @@ namespace AZ //! [GFX TODO][ATOM-4343 Bake mesh spatial during AP processing] //! //! @param modelTransform a transform that puts the model into the ray's coordinate space + //! @param nonUniformScale Non-uniform scale applied in the model's local frame. //! @param rayStart position where the ray starts //! @param dir direction where the ray ends (does not have to be unit length) //! @param distanceFactor if an intersection is detected, this will be set such that distanceFactor * dir.length == distance to intersection //! @param normal if an intersection is detected, this will be set to the normal at the point of intersection //! @return true if the ray intersects the mesh - bool RayIntersection(const AZ::Transform& modelTransform, const AZ::Vector3& rayStart, const AZ::Vector3& dir, float& distanceFactor, AZ::Vector3& normal) const; + bool RayIntersection(const AZ::Transform& modelTransform, const AZ::Vector3& nonUniformScale, const AZ::Vector3& rayStart, + const AZ::Vector3& dir, float& distanceFactor, AZ::Vector3& normal) const; //! Get available UV names from the model and its lods. const AZStd::unordered_set& GetUvNames() const; 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 8b49448fb1..15c8aaf528 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Model/Model.cpp @@ -164,18 +164,22 @@ namespace AZ return false; } - bool Model::RayIntersection(const AZ::Transform& modelTransform, const AZ::Vector3& rayStart, const AZ::Vector3& dir, float& distanceFactor, AZ::Vector3& normal) const + bool Model::RayIntersection(const AZ::Transform& modelTransform, const AZ::Vector3& nonUniformScale, const AZ::Vector3& rayStart, const AZ::Vector3& dir, float& distanceFactor, AZ::Vector3& normal) const { AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender); + const AZ::Vector3 clampedScale = nonUniformScale.GetMax(AZ::Vector3(AZ::MinTransformScale)); + const AZ::Transform inverseTM = modelTransform.GetInverse(); - const AZ::Vector3 raySrcLocal = inverseTM.TransformPoint(rayStart); + const AZ::Vector3 raySrcLocal = inverseTM.TransformPoint(rayStart) / clampedScale; // Instead of just rotating 'dir' we need it to be scaled too, so that 'distanceFactor' will be in the target units rather than object local units. const AZ::Vector3 rayDest = rayStart + dir; - const AZ::Vector3 rayDestLocal = inverseTM.TransformPoint(rayDest); + const AZ::Vector3 rayDestLocal = inverseTM.TransformPoint(rayDest) / clampedScale; const AZ::Vector3 rayDirLocal = rayDestLocal - raySrcLocal; - return LocalRayIntersection(raySrcLocal, rayDirLocal, distanceFactor, normal); + bool result = LocalRayIntersection(raySrcLocal, rayDirLocal, distanceFactor, normal); + normal = (normal * clampedScale).GetNormalized(); + return result; } const AZStd::unordered_set& Model::GetUvNames() const diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.cpp index b9b0759dda..c9aa3a6d5e 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/EditorMeshComponent.cpp @@ -137,9 +137,12 @@ namespace AZ AZ::Transform transform = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult(transform, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); + AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + AZ::Vector3 ignoreNormal; - return m_controller.GetModel()->RayIntersection(transform, src, dir, distance, ignoreNormal); + return m_controller.GetModel()->RayIntersection(transform, nonUniformScale, src, dir, distance, ignoreNormal); } bool EditorMeshComponent::SupportsEditorRayIntersect() diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp index 88b0e812d2..b0315fdf9c 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp @@ -188,6 +188,11 @@ namespace AZ m_meshFeatureProcessor = RPI::Scene::GetFeatureProcessorForEntity(m_entityId); AZ_Error("MeshComponentController", m_meshFeatureProcessor, "Unable to find a MeshFeatureProcessorInterface on the entityId."); + m_cachedNonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(m_cachedNonUniformScale, m_entityId, &AZ::NonUniformScaleRequests::GetScale); + AZ::NonUniformScaleRequestBus::Event(m_entityId, &AZ::NonUniformScaleRequests::RegisterScaleChangedEvent, + m_nonUniformScaleChangedHandler); + MeshComponentRequestBus::Handler::BusConnect(m_entityId); TransformNotificationBus::Handler::BusConnect(m_entityId); MaterialReceiverRequestBus::Handler::BusConnect(m_entityId); @@ -209,6 +214,8 @@ namespace AZ MaterialReceiverRequestBus::Handler::BusDisconnect(); MaterialComponentNotificationBus::Handler::BusDisconnect(); + m_nonUniformScaleChangedHandler.Disconnect(); + m_meshFeatureProcessor = nullptr; m_transformInterface = nullptr; m_entityId = AZ::EntityId(AZ::EntityId::InvalidEntityId); @@ -229,7 +236,16 @@ namespace AZ { if (m_meshFeatureProcessor) { - m_meshFeatureProcessor->SetTransform(m_meshHandle, world); + m_meshFeatureProcessor->SetTransform(m_meshHandle, world, m_cachedNonUniformScale); + } + } + + void MeshComponentController::HandleNonUniformScaleChange(const AZ::Vector3& nonUniformScale) + { + m_cachedNonUniformScale = nonUniformScale; + if (m_meshFeatureProcessor) + { + m_meshFeatureProcessor->SetTransform(m_meshHandle, m_transformInterface->GetWorldTM(), m_cachedNonUniformScale); } } @@ -275,8 +291,9 @@ namespace AZ m_meshHandle = m_meshFeatureProcessor->AcquireMesh(m_configuration.m_modelAsset, materials); m_meshFeatureProcessor->ConnectModelChangeEventHandler(m_meshHandle, m_changeEventHandler); - const AZ::Transform& transform = m_transformInterface ? m_transformInterface->GetWorldTM() : Transform::Identity(); - m_meshFeatureProcessor->SetTransform(m_meshHandle, transform); + const AZ::Transform& transform = m_transformInterface ? m_transformInterface->GetWorldTM() : AZ::Transform::CreateIdentity(); + + m_meshFeatureProcessor->SetTransform(m_meshHandle, transform, m_cachedNonUniformScale); m_meshFeatureProcessor->SetSortKey(m_meshHandle, m_configuration.m_sortKey); m_meshFeatureProcessor->SetLodOverride(m_meshHandle, m_configuration.m_lodOverride); m_meshFeatureProcessor->SetExcludeFromReflectionCubeMaps(m_meshHandle, m_configuration.m_excludeFromReflectionCubeMaps); @@ -413,7 +430,16 @@ namespace AZ Aabb MeshComponentController::GetLocalBounds() { const Data::Instance model = GetModel(); - return model ? model->GetAabb() : Aabb::CreateNull(); + if (model) + { + Aabb aabb = model->GetAabb(); + aabb.MultiplyByScale(m_cachedNonUniformScale); + return aabb; + } + else + { + return Aabb::CreateNull(); + } } } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h index 357f1ded6c..afdcfa25c7 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h @@ -14,6 +14,7 @@ #include #include +#include #include @@ -122,17 +123,25 @@ namespace AZ void UnregisterModel(); void RefreshModelRegistration(); + void HandleNonUniformScaleChange(const AZ::Vector3& nonUniformScale); + Render::MeshFeatureProcessorInterface* m_meshFeatureProcessor = nullptr; Render::MeshFeatureProcessorInterface::MeshHandle m_meshHandle; TransformInterface* m_transformInterface = nullptr; AZ::EntityId m_entityId; bool m_isVisible = true; MeshComponentConfig m_configuration; + AZ::Vector3 m_cachedNonUniformScale = AZ::Vector3::CreateOne(); MeshFeatureProcessorInterface::ModelChangedEvent::Handler m_changeEventHandler { [&](Data::Instance model) { HandleModelChange(model); } }; + + AZ::NonUniformScaleChangedEvent::Handler m_nonUniformScaleChangedHandler + { + [&](const AZ::Vector3& nonUniformScale) { HandleNonUniformScaleChange(nonUniformScale); } + }; }; } // namespace Render diff --git a/Gems/Blast/Code/Source/Family/ActorRenderManager.cpp b/Gems/Blast/Code/Source/Family/ActorRenderManager.cpp index deb6362ee3..74bba48d3c 100644 --- a/Gems/Blast/Code/Source/Family/ActorRenderManager.cpp +++ b/Gems/Blast/Code/Source/Family/ActorRenderManager.cpp @@ -74,10 +74,7 @@ namespace Blast { if (m_chunkActors[chunkId]) { - auto transform = m_chunkActors[chunkId]->GetWorldBody()->GetTransform(); - // Multiply by scale because the transform on the world body does not store scale - transform.MultiplyByScale(m_scale); - m_meshFeatureProcessor->SetTransform(m_chunkMeshHandles[chunkId], transform); + m_meshFeatureProcessor->SetTransform(m_chunkMeshHandles[chunkId], m_chunkActors[chunkId]->GetWorldBody()->GetTransform(), m_scale); } } } diff --git a/Gems/Blast/Code/Tests/ActorRenderManagerTest.cpp b/Gems/Blast/Code/Tests/ActorRenderManagerTest.cpp index 4036023397..114721a095 100644 --- a/Gems/Blast/Code/Tests/ActorRenderManagerTest.cpp +++ b/Gems/Blast/Code/Tests/ActorRenderManagerTest.cpp @@ -114,7 +114,7 @@ namespace Blast // ActorRenderManager::SyncMeshes { - EXPECT_CALL(*m_mockMeshFeatureProcessor, SetTransform(_, _)) + EXPECT_CALL(*m_mockMeshFeatureProcessor, SetTransform(_, _, _)) .Times(aznumeric_cast(m_actorFactory->m_mockActors[0]->GetChunkIndices().size())); actorRenderManager->SyncMeshes(); }