diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentModes/BoxViewportEdit.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentModes/BoxViewportEdit.cpp index 7e496390c8..4c08ef6855 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentModes/BoxViewportEdit.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentModes/BoxViewportEdit.cpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include namespace AzToolsFramework { @@ -37,20 +39,20 @@ namespace AzToolsFramework void BoxViewportEdit::UpdateManipulators() { AZ::Transform boxWorldFromLocal = AZ::Transform::CreateIdentity(); - BoxManipulatorRequestBus::EventResult( - boxWorldFromLocal, m_entityComponentIdPair, &BoxManipulatorRequests::GetCurrentTransform); + AZ::TransformBus::EventResult( + boxWorldFromLocal, m_entityComponentIdPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); - AZ::Vector3 boxScale = AZ::Vector3::CreateOne(); - BoxManipulatorRequestBus::EventResult( - boxScale, m_entityComponentIdPair, &BoxManipulatorRequests::GetBoxScale); + AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult( + nonUniformScale, m_entityComponentIdPair.GetEntityId(), &AZ::NonUniformScaleRequestBus::Events::GetScale); AZ::Vector3 boxDimensions = AZ::Vector3::CreateZero(); BoxManipulatorRequestBus::EventResult( - boxDimensions, m_entityComponentIdPair, &BoxManipulatorRequests::GetDimensions); + boxDimensions, m_entityComponentIdPair, &BoxManipulatorRequestBus::Events::GetDimensions); - // ensure we apply the entity scale to the box dimensions so - // the manipulators appear in the correct location - boxDimensions *= boxScale; + AZ::Transform boxLocalTransform = AZ::Transform::CreateIdentity(); + BoxManipulatorRequestBus::EventResult( + boxLocalTransform, m_entityComponentIdPair, &BoxManipulatorRequestBus::Events::GetCurrentLocalTransform); for (size_t manipulatorIndex = 0; manipulatorIndex < m_linearManipulators.size(); ++manipulatorIndex) { @@ -58,7 +60,8 @@ namespace AzToolsFramework { linearManipulator->SetSpace(boxWorldFromLocal); linearManipulator->SetLocalTransform( - AZ::Transform::CreateTranslation(s_boxAxes[manipulatorIndex] * 0.5f * boxDimensions)); + boxLocalTransform * AZ::Transform::CreateTranslation(s_boxAxes[manipulatorIndex] * 0.5f * boxDimensions)); + linearManipulator->SetNonUniformScale(nonUniformScale); linearManipulator->SetBoundsDirty(); } } @@ -69,8 +72,8 @@ namespace AzToolsFramework m_entityComponentIdPair = entityComponentIdPair; AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); - BoxManipulatorRequestBus::EventResult( - worldFromLocal, entityComponentIdPair, &BoxManipulatorRequests::GetCurrentTransform); + AZ::TransformBus::EventResult( + worldFromLocal, entityComponentIdPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); for (size_t manipulatorIndex = 0; manipulatorIndex < m_linearManipulators.size(); ++manipulatorIndex) { @@ -85,36 +88,35 @@ namespace AzToolsFramework ManipulatorViews views; views.emplace_back(CreateManipulatorViewQuadBillboard( - AzFramework::ViewportColors::DefaultManipulatorHandleColor, AzFramework::ViewportConstants::DefaultManipulatorHandleSize)); + AzFramework::ViewportColors::DefaultManipulatorHandleColor, + AzFramework::ViewportConstants::DefaultManipulatorHandleSize)); linearManipulator->SetViews(AZStd::move(views)); linearManipulator->InstallMouseMoveCallback( - [this, entityComponentIdPair]( - const LinearManipulator::Action& action) + [this, entityComponentIdPair, + transformScale{ linearManipulator->GetSpace().GetUniformScale() }](const LinearManipulator::Action& action) { + AZ::Transform boxLocalTransform = AZ::Transform::CreateIdentity(); + BoxManipulatorRequestBus::EventResult( + boxLocalTransform, entityComponentIdPair, &BoxManipulatorRequestBus::Events::GetCurrentLocalTransform); + + const AZ::Vector3 manipulatorPosition = GetPositionInManipulatorFrame(transformScale, boxLocalTransform, action); + // calculate the amount of displacement along an axis this manipulator has moved // clamp movement so it cannot go negative based on axis direction const AZ::Vector3 axisDisplacement = - action.LocalPosition().GetAbs() * 2.0f - * AZ::GetMax(0.0f, action.LocalPosition().GetNormalized().Dot(action.m_fixed.m_axis)); - - AZ::Vector3 boxScale = AZ::Vector3::CreateOne(); - BoxManipulatorRequestBus::EventResult( - boxScale, entityComponentIdPair, &BoxManipulatorRequests::GetBoxScale); + manipulatorPosition.GetAbs() * 2.0f + * AZ::GetMax(0.0f, manipulatorPosition.GetNormalized().Dot(action.m_fixed.m_axis)); AZ::Vector3 boxDimensions = AZ::Vector3::CreateZero(); BoxManipulatorRequestBus::EventResult( - boxDimensions, entityComponentIdPair, &BoxManipulatorRequests::GetDimensions); - - // ensure we take into account the entity scale using the axis displacement - const AZ::Vector3 scaledAxisDisplacement = - axisDisplacement / boxScale; + boxDimensions, entityComponentIdPair, &BoxManipulatorRequestBus::Events::GetDimensions); // update dimensions - preserve dimensions not effected by this // axis, and update current axis displacement BoxManipulatorRequestBus::Event( - entityComponentIdPair, &BoxManipulatorRequests::SetDimensions, - (NotAxis(action.m_fixed.m_axis) * boxDimensions).GetMax(scaledAxisDisplacement)); + entityComponentIdPair, &BoxManipulatorRequestBus::Events::SetDimensions, + (NotAxis(action.m_fixed.m_axis) * boxDimensions).GetMax(axisDisplacement)); UpdateManipulators(); }); @@ -137,4 +139,12 @@ namespace AzToolsFramework } } } + + AZ::Vector3 GetPositionInManipulatorFrame(float worldUniformScale, const AZ::Transform& manipulatorLocalTransform, + const LinearManipulator::Action& action) + { + return manipulatorLocalTransform.GetInverse().TransformPoint( + action.m_start.m_localPosition + + action.m_current.m_localPositionOffset / AZ::GetClamp(worldUniformScale, AZ::MinTransformScale, AZ::MaxTransformScale)); + } } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentModes/BoxViewportEdit.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentModes/BoxViewportEdit.h index 98ef17e547..66ea626e12 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentModes/BoxViewportEdit.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ComponentModes/BoxViewportEdit.h @@ -31,4 +31,10 @@ namespace AzToolsFramework using BoxManipulators = AZStd::array, 6>; BoxManipulators m_linearManipulators; ///< Manipulators for editing box size. }; + + /// Calculates the position of the manipulator in its own reference frame. + /// Removes the effects of the manipulator local transform, and accounts for world transform scale in + /// the action local offset. + AZ::Vector3 GetPositionInManipulatorFrame(float worldUniformScale, const AZ::Transform& manipulatorLocalTransform, + const LinearManipulator::Action& action); } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BoxManipulatorRequestBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BoxManipulatorRequestBus.h index b0bcdf47f2..0d6299f5d4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BoxManipulatorRequestBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Manipulators/BoxManipulatorRequestBus.h @@ -26,11 +26,21 @@ namespace AzToolsFramework virtual AZ::Vector3 GetDimensions() = 0; //! Set the X/Y/Z dimensions of the box shape/collider. virtual void SetDimensions(const AZ::Vector3& dimensions) = 0; + // O3DE_DEPRECATION_NOTICE(GHI-7572) + //! @deprecated Because non-uniform scale effects can be complex, it is recommended to separately use + //! AZ::TransformBus::Events::GetWorldTM, AZ::NonUniformScaleRequests::GetScale and GetCurrentLocalTransform + //! and combine their effects. //! Get the transform of the box shape/collider. //! This is used by \ref BoxComponentMode instead of the \ref \AZ::TransformBus //! because a collider may have an additional translation/orientation offset from //! the Entity transform. virtual AZ::Transform GetCurrentTransform() = 0; + //! Get the transform of the box relative to the entity. + virtual AZ::Transform GetCurrentLocalTransform() = 0; + // O3DE_DEPRECATION_NOTICE(GHI-7572) + //! @deprecated Because non-uniform scale effects can be complex, it is recommended to separately use + //! AZ::TransformBus::Events::GetWorldTM, AZ::NonUniformScaleRequests::GetScale and GetCurrentLocalTransform + //! and combine their effects. //! Get the scale currently applied to the box. //! With the Box Shape, the largest x/y/z component is taken //! so scale is always uniform, with colliders the scale may diff --git a/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.cpp b/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.cpp index f78f2f048d..953bc9d349 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.cpp @@ -161,6 +161,11 @@ namespace LmbrCentral return AzToolsFramework::TransformNormalizedScale(m_aaboxShape.GetCurrentTransform()); } + AZ::Transform EditorAxisAlignedBoxShapeComponent::GetCurrentLocalTransform() + { + return AZ::Transform::CreateIdentity(); + } + AZ::Vector3 EditorAxisAlignedBoxShapeComponent::GetBoxScale() { return AZ::Vector3(m_aaboxShape.GetCurrentTransform().GetUniformScale() * m_aaboxShape.GetCurrentNonUniformScale()); diff --git a/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.h b/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.h index 8bff4ea7e1..a906b10129 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.h +++ b/Gems/LmbrCentral/Code/Source/Shape/EditorAxisAlignedBoxShapeComponent.h @@ -58,6 +58,7 @@ namespace LmbrCentral AZ::Vector3 GetDimensions() override; void SetDimensions(const AZ::Vector3& dimensions) override; AZ::Transform GetCurrentTransform() override; + AZ::Transform GetCurrentLocalTransform() override; AZ::Vector3 GetBoxScale() override; void ConfigurationChanged(); diff --git a/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.cpp b/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.cpp index 2983ca7753..777e2f728b 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.cpp +++ b/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.cpp @@ -167,6 +167,11 @@ namespace LmbrCentral return AzToolsFramework::TransformNormalizedScale(m_boxShape.GetCurrentTransform()); } + AZ::Transform EditorBoxShapeComponent::GetCurrentLocalTransform() + { + return AZ::Transform::CreateIdentity(); + } + AZ::Vector3 EditorBoxShapeComponent::GetBoxScale() { return AZ::Vector3(m_boxShape.GetCurrentTransform().GetUniformScale() * m_boxShape.GetCurrentNonUniformScale()); diff --git a/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.h b/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.h index aa24bda5c4..499a20497c 100644 --- a/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.h +++ b/Gems/LmbrCentral/Code/Source/Shape/EditorBoxShapeComponent.h @@ -57,6 +57,7 @@ namespace LmbrCentral AZ::Vector3 GetDimensions() override; void SetDimensions(const AZ::Vector3& dimensions) override; AZ::Transform GetCurrentTransform() override; + AZ::Transform GetCurrentLocalTransform() override; AZ::Vector3 GetBoxScale() override; void ConfigurationChanged(); diff --git a/Gems/LmbrCentral/Code/Tests/EditorBoxShapeComponentTests.cpp b/Gems/LmbrCentral/Code/Tests/EditorBoxShapeComponentTests.cpp index 4b72a4fb58..82a7f57937 100644 --- a/Gems/LmbrCentral/Code/Tests/EditorBoxShapeComponentTests.cpp +++ b/Gems/LmbrCentral/Code/Tests/EditorBoxShapeComponentTests.cpp @@ -7,6 +7,17 @@ */ #include "LmbrCentralReflectionTest.h" #include "Shape/EditorBoxShapeComponent.h" +#include "Shape/EditorSphereShapeComponent.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace LmbrCentral { @@ -56,5 +67,110 @@ namespace LmbrCentral EXPECT_EQ(dimensions, AZ::Vector3(0.37f, 0.57f, 0.66f)); } + + class EditorBoxShapeComponentFixture : public UnitTest::ToolsApplicationFixture + { + public: + void SetUpEditorFixtureImpl() override; + void TearDownEditorFixtureImpl() override; + + AZStd::unique_ptr m_editorBoxShapeComponentDescriptor; + AZStd::unique_ptr m_editorSphereShapeComponentDescriptor; + + AZ::Entity* m_entity = nullptr; + }; + + void EditorBoxShapeComponentFixture::SetUpEditorFixtureImpl() + { + AZ::SerializeContext* serializeContext = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + + // need to reflect EditorSphereShapeComponent in order for EditorBaseShapeComponent to be reflected + m_editorSphereShapeComponentDescriptor = AZStd::unique_ptr(EditorSphereShapeComponent::CreateDescriptor()); + + m_editorBoxShapeComponentDescriptor = + AZStd::unique_ptr(EditorBoxShapeComponent::CreateDescriptor()); + + ShapeComponentConfig::Reflect(serializeContext); + BoxShape::Reflect(serializeContext); + m_editorSphereShapeComponentDescriptor->Reflect(serializeContext); + m_editorBoxShapeComponentDescriptor->Reflect(serializeContext); + + UnitTest::CreateDefaultEditorEntity("BoxShapeComponentEntity", &m_entity); + m_entity->Deactivate(); + m_entity->CreateComponent(AzToolsFramework::Components::EditorNonUniformScaleComponent::RTTI_Type()); + m_entity->CreateComponent(EditorBoxShapeComponentTypeId); + m_entity->Activate(); + } + + void EditorBoxShapeComponentFixture::TearDownEditorFixtureImpl() + { + AzToolsFramework::EditorEntityContextRequestBus::Broadcast( + &AzToolsFramework::EditorEntityContextRequestBus::Events::DestroyEditorEntity, m_entity->GetId()); + m_entity = nullptr; + + m_editorBoxShapeComponentDescriptor.reset(); + m_editorSphereShapeComponentDescriptor.reset(); + } + + using EditorBoxShapeComponentManipulatorFixture = + UnitTest::IndirectCallManipulatorViewportInteractionFixtureMixin; + + TEST_F(EditorBoxShapeComponentManipulatorFixture, BoxShapeNonUniformScaleManipulatorsScaleCorrectly) + { + // a rotation which rotates the x-axis to (0.8, 0.6, 0) + const AZ::Quaternion boxRotation(0.0f, 0.0f, 0.316228f, 0.948683f); + AZ::Transform boxTransform = AZ::Transform::CreateFromQuaternionAndTranslation(boxRotation, AZ::Vector3(2.0f, 3.0f, 4.0f)); + boxTransform.SetUniformScale(1.5f); + AZ::TransformBus::Event(m_entity->GetId(), &AZ::TransformBus::Events::SetWorldTM, boxTransform); + + const AZ::Vector3 nonUniformScale(4.0f, 1.5f, 2.0f); + AZ::NonUniformScaleRequestBus::Event(m_entity->GetId(), &AZ::NonUniformScaleRequests::SetScale, nonUniformScale); + + const AZ::Vector3 boxDimensions(1.0f, 2.0f, 2.5f); + BoxShapeComponentRequestsBus::Event(m_entity->GetId(), &BoxShapeComponentRequests::SetBoxDimensions, boxDimensions); + + // enter the box shape component's component mode + AzToolsFramework::SelectEntity(m_entity->GetId()); + AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Broadcast( + &AzToolsFramework::ComponentModeFramework::ComponentModeSystemRequestBus::Events::AddSelectedComponentModesOfType, + EditorBoxShapeComponentTypeId); + + // position the camera so it is looking down at the box + AzFramework::SetCameraTransform( + m_cameraState, + AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(-AZ::Constants::HalfPi), AZ::Vector3(2.0f, 3.0f, 20.0f))); + + // position in world space which should allow grabbing the box's x scale manipulator + // the unscaled position of the x scale manipulator in the box's local frame should be (0.5f, 0.0f, 0.0f) + // after non-uniform scale, the manipulator position should be (2.0f, 0.0f, 0.0f) + // after the scale of the entity transform, the manipulator position should be (3.0f, 0.0f, 0.0f) + // after the rotation of the entity transform, the manipulator position should be (2.4f, 1.8f, 0.0f) + // after the translation of the entity transform, the manipulator position should be (4.4f, 4.8f, 4.0f) + const AZ::Vector3 worldStart(4.4f, 4.8f, 4.0f); + + // position in world space to move to + const AZ::Vector3 worldEnd(6.8f, 6.6f, 4.0f); + + const auto screenStart = AzFramework::WorldToScreen(worldStart, m_cameraState); + const auto screenEnd = AzFramework::WorldToScreen(worldEnd, m_cameraState); + + m_actionDispatcher + ->CameraState(m_cameraState) + // move the mouse to the position of the x scale manipulator + ->MousePosition(screenStart) + // drag to move the manipulator + ->MouseLButtonDown() + ->MousePosition(screenEnd) + ->MouseLButtonUp(); + + AZ::Vector3 newBoxDimensions = AZ::Vector3::CreateZero(); + BoxShapeComponentRequestsBus::EventResult(newBoxDimensions, m_entity->GetId(), &BoxShapeComponentRequests::GetBoxDimensions); + + const AZ::Vector3 expectedBoxDimensions(2.0f, 2.0f, 2.5f); + // allow a reasonably high tolerance because we can't get better accuracy than the resolution of the viewport + EXPECT_THAT(newBoxDimensions, UnitTest::IsCloseTolerance(expectedBoxDimensions, 1e-2f)); + } } diff --git a/Gems/PhysX/Code/Editor/ColliderCapsuleMode.cpp b/Gems/PhysX/Code/Editor/ColliderCapsuleMode.cpp index 0bc941b082..b704f8feec 100644 --- a/Gems/PhysX/Code/Editor/ColliderCapsuleMode.cpp +++ b/Gems/PhysX/Code/Editor/ColliderCapsuleMode.cpp @@ -8,11 +8,14 @@ #include "ColliderCapsuleMode.h" #include +#include #include #include #include +#include #include +#include #include #include @@ -34,10 +37,15 @@ namespace PhysX void ColliderCapsuleMode::Setup(const AZ::EntityComponentIdPair& idPair) { AZ::Transform colliderWorldTransform = AZ::Transform::Identity(); - PhysX::EditorColliderComponentRequestBus::EventResult(colliderWorldTransform, idPair, &PhysX::EditorColliderComponentRequests::GetColliderWorldTransform); + AZ::TransformBus::EventResult(colliderWorldTransform, idPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); - SetupRadiusManipulator(idPair, colliderWorldTransform); - SetupHeightManipulator(idPair, colliderWorldTransform); + AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, idPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + + SetupRadiusManipulator(idPair, colliderWorldTransform, colliderLocalTransform, nonUniformScale); + SetupHeightManipulator(idPair, colliderWorldTransform, colliderLocalTransform, nonUniformScale); AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(idPair.GetEntityId()); } @@ -45,7 +53,12 @@ namespace PhysX void ColliderCapsuleMode::Refresh(const AZ::EntityComponentIdPair& idPair) { AZ::Transform colliderWorldTransform = AZ::Transform::Identity(); - PhysX::EditorColliderComponentRequestBus::EventResult(colliderWorldTransform, idPair, &PhysX::EditorColliderComponentRequests::GetColliderWorldTransform); + AZ::TransformBus::EventResult(colliderWorldTransform, idPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); + + AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, idPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + + AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); // Read the state of the capsule into manipulators to support undo/redo float capsuleHeight = 0.0f; @@ -55,9 +68,13 @@ namespace PhysX PhysX::EditorColliderComponentRequestBus::EventResult(capsuleRadius, idPair, &PhysX::EditorColliderComponentRequests::GetCapsuleRadius); m_radiusManipulator->SetSpace(colliderWorldTransform); - m_radiusManipulator->SetLocalPosition(m_radiusManipulator->GetAxis() * capsuleRadius); + m_radiusManipulator->SetLocalTransform( + colliderLocalTransform * AZ::Transform::CreateTranslation(m_radiusManipulator->GetAxis() * capsuleRadius)); + m_radiusManipulator->SetNonUniformScale(nonUniformScale); m_heightManipulator->SetSpace(colliderWorldTransform); - m_heightManipulator->SetLocalPosition(m_heightManipulator->GetAxis() * capsuleHeight * HalfHeight); + m_heightManipulator->SetLocalTransform( + colliderLocalTransform * AZ::Transform::CreateTranslation(m_heightManipulator->GetAxis() * capsuleHeight * HalfHeight)); + m_heightManipulator->SetNonUniformScale(nonUniformScale); } void ColliderCapsuleMode::Teardown(const AZ::EntityComponentIdPair& idPair) @@ -83,13 +100,23 @@ namespace PhysX AZ_UNUSED(debugDisplay); const AzFramework::CameraState cameraState = AzToolsFramework::GetCameraState(viewportInfo.m_viewportId); - float radius = m_radiusManipulator->GetLocalPosition().GetLength(); - m_radiusManipulator->SetAxis(cameraState.m_side); - m_radiusManipulator->SetLocalPosition(cameraState.m_side * radius); + if (!m_radiusManipulator->EntityComponentIdPairs().empty()) + { + const AZ::EntityComponentIdPair idPair = *m_radiusManipulator->EntityComponentIdPairs().begin(); + float radius = 0.0f; + PhysX::EditorColliderComponentRequestBus::EventResult(radius, idPair, &PhysX::EditorColliderComponentRequests::GetCapsuleRadius); + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + m_radiusManipulator->SetAxis(cameraState.m_side); + m_radiusManipulator->SetLocalTransform(colliderLocalTransform * AZ::Transform::CreateTranslation(cameraState.m_side * radius)); + } } - void ColliderCapsuleMode::SetupRadiusManipulator(const AZ::EntityComponentIdPair& idPair, const AZ::Transform& worldTransform) + void ColliderCapsuleMode::SetupRadiusManipulator( + const AZ::EntityComponentIdPair& idPair, + const AZ::Transform& worldTransform, + const AZ::Transform& localTransform, + const AZ::Vector3& nonUniformScale) { // Radius manipulator float capsuleRadius = 0.0f; @@ -99,7 +126,8 @@ namespace PhysX m_radiusManipulator->AddEntityComponentIdPair(idPair); m_radiusManipulator->SetAxis(RadiusManipulatorAxis); m_radiusManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); - m_radiusManipulator->SetLocalPosition(RadiusManipulatorAxis * capsuleRadius); + m_radiusManipulator->SetLocalTransform(localTransform * AZ::Transform::CreateTranslation(RadiusManipulatorAxis * capsuleRadius)); + m_radiusManipulator->SetNonUniformScale(nonUniformScale); { AzToolsFramework::ManipulatorViews views; @@ -114,7 +142,11 @@ namespace PhysX }); } - void ColliderCapsuleMode::SetupHeightManipulator(const AZ::EntityComponentIdPair& idPair, const AZ::Transform& worldTransform) + void ColliderCapsuleMode::SetupHeightManipulator( + const AZ::EntityComponentIdPair& idPair, + const AZ::Transform& worldTransform, + const AZ::Transform& localTransform, + const AZ::Vector3& nonUniformScale) { // Height manipulator float capsuleHeight = 0.0f; @@ -124,7 +156,9 @@ namespace PhysX m_heightManipulator->AddEntityComponentIdPair(idPair); m_heightManipulator->SetAxis(HeightManipulatorAxis); m_heightManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); - m_heightManipulator->SetLocalPosition(HeightManipulatorAxis * capsuleHeight * HalfHeight); // Manipulator positioned at half the capsules height. + m_heightManipulator->SetLocalTransform( + localTransform * AZ::Transform::CreateTranslation(HeightManipulatorAxis * capsuleHeight * HalfHeight)); + m_heightManipulator->SetNonUniformScale(nonUniformScale); { AzToolsFramework::ManipulatorViews views; @@ -137,19 +171,23 @@ namespace PhysX { OnHeightManipulatorMoved(action, idPair); }); - } void ColliderCapsuleMode::OnRadiusManipulatorMoved(const AzToolsFramework::LinearManipulator::Action& action, const AZ::EntityComponentIdPair& idPair) { + // manipulator action offsets do not take entity transform scale into account, so need to apply it here + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + const AZ::Vector3 manipulatorPosition = AzToolsFramework::GetPositionInManipulatorFrame( + m_radiusManipulator->GetSpace().GetUniformScale(), colliderLocalTransform, action); + // Get the distance the manipulator has moved along the axis. - float extent = action.LocalPosition().Dot(action.m_fixed.m_axis); + float extent = manipulatorPosition.Dot(action.m_fixed.m_axis); // Clamp radius to a small value. extent = AZ::GetMax(extent, MinCapsuleRadius); // Update the manipulator and capsule radius. - m_radiusManipulator->SetLocalPosition(extent * action.m_fixed.m_axis); + m_radiusManipulator->SetLocalTransform(colliderLocalTransform * AZ::Transform::CreateTranslation(extent * action.m_fixed.m_axis)); // Adjust the height manipulator so it is always clamped to twice the radius. AdjustHeightManipulator(idPair, static_cast(extent)); @@ -160,14 +198,19 @@ namespace PhysX void ColliderCapsuleMode::OnHeightManipulatorMoved(const AzToolsFramework::LinearManipulator::Action& action, const AZ::EntityComponentIdPair& idPair) { + // manipulator action offsets do not take entity transform scale into account, so need to apply it here + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + const AZ::Vector3 manipulatorPosition = AzToolsFramework::GetPositionInManipulatorFrame( + m_heightManipulator->GetSpace().GetUniformScale(), colliderLocalTransform, action); + // Get the distance the manipulator has moved along the axis. - float extent = action.LocalPosition().Dot(action.m_fixed.m_axis); + float extent = manipulatorPosition.Dot(action.m_fixed.m_axis); // Ensure capsule's half height is always greater than the radius. extent = AZ::GetMax(extent, MinCapsuleHeight); // Update the manipulator and capsule height. - m_heightManipulator->SetLocalPosition(extent * action.m_fixed.m_axis); + m_heightManipulator->SetLocalTransform(colliderLocalTransform * AZ::Transform::CreateTranslation(extent * action.m_fixed.m_axis)); // The final height of the capsule is twice the manipulator's extent. float capsuleHeight = extent / HalfHeight; @@ -188,7 +231,9 @@ namespace PhysX capsuleRadius = AZ::GetMin(capsuleRadius, capsuleHeight * HalfHeight); // Update manipulator and the capsule radius. - m_radiusManipulator->SetLocalPosition(capsuleRadius * m_radiusManipulator->GetAxis()); + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + m_radiusManipulator->SetLocalTransform( + colliderLocalTransform * AZ::Transform::CreateTranslation(capsuleRadius * m_radiusManipulator->GetAxis())); PhysX::EditorColliderComponentRequestBus::Event(idPair, &PhysX::EditorColliderComponentRequests::SetCapsuleRadius, capsuleRadius); } @@ -201,7 +246,9 @@ namespace PhysX capsuleHeight = AZ::GetMax(capsuleHeight, capsuleRadius / HalfHeight); // Update the manipulator and capsule height. - m_heightManipulator->SetLocalPosition(capsuleHeight * HalfHeight * m_heightManipulator->GetAxis()); + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + m_heightManipulator->SetLocalTransform( + colliderLocalTransform * AZ::Transform::CreateTranslation(capsuleHeight * HalfHeight * m_heightManipulator->GetAxis())); PhysX::EditorColliderComponentRequestBus::Event(idPair, &PhysX::EditorColliderComponentRequests::SetCapsuleHeight, capsuleHeight); } } diff --git a/Gems/PhysX/Code/Editor/ColliderCapsuleMode.h b/Gems/PhysX/Code/Editor/ColliderCapsuleMode.h index ceedd84a35..5284b3d1ed 100644 --- a/Gems/PhysX/Code/Editor/ColliderCapsuleMode.h +++ b/Gems/PhysX/Code/Editor/ColliderCapsuleMode.h @@ -34,8 +34,16 @@ namespace PhysX const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override; - void SetupRadiusManipulator(const AZ::EntityComponentIdPair& idPair, const AZ::Transform& worldTransform); - void SetupHeightManipulator(const AZ::EntityComponentIdPair& idPair, const AZ::Transform& worldTransform); + void SetupRadiusManipulator( + const AZ::EntityComponentIdPair& idPair, + const AZ::Transform& worldTransform, + const AZ::Transform& localTransform, + const AZ::Vector3& nonUniformScale); + void SetupHeightManipulator( + const AZ::EntityComponentIdPair& idPair, + const AZ::Transform& worldTransform, + const AZ::Transform& localTransform, + const AZ::Vector3& nonUniformScale); void OnRadiusManipulatorMoved(const AzToolsFramework::LinearManipulator::Action& action, const AZ::EntityComponentIdPair& idPair); void OnHeightManipulatorMoved(const AzToolsFramework::LinearManipulator::Action& action, const AZ::EntityComponentIdPair& idPair); void AdjustRadiusManipulator(const AZ::EntityComponentIdPair& idPair, const float capsuleHeight); diff --git a/Gems/PhysX/Code/Editor/ColliderOffsetMode.cpp b/Gems/PhysX/Code/Editor/ColliderOffsetMode.cpp index 21f27e4622..2c9b90d5b3 100644 --- a/Gems/PhysX/Code/Editor/ColliderOffsetMode.cpp +++ b/Gems/PhysX/Code/Editor/ColliderOffsetMode.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -28,10 +29,14 @@ namespace PhysX AZ::Transform worldTransform; AZ::TransformBus::EventResult(worldTransform, idPair.GetEntityId(), &AZ::TransformInterface::GetWorldTM); + AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, idPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + AZ::Vector3 colliderOffset; PhysX::EditorColliderComponentRequestBus::EventResult(colliderOffset, idPair, &PhysX::EditorColliderComponentRequests::GetColliderOffset); m_translationManipulators.SetSpace(worldTransform); + m_translationManipulators.SetNonUniformScale(nonUniformScale); m_translationManipulators.SetLocalPosition(colliderOffset); m_translationManipulators.AddEntityComponentIdPair(idPair); m_translationManipulators.Register(AzToolsFramework::g_mainManipulatorManagerId); @@ -40,19 +45,19 @@ namespace PhysX m_translationManipulators.InstallLinearManipulatorMouseMoveCallback([this, idPair]( const AzToolsFramework::LinearManipulator::Action& action) { - OnManipulatorMoved(action.LocalPosition(), idPair); + OnManipulatorMoved(action.m_start.m_localPosition, action.m_current.m_localPositionOffset, idPair); }); m_translationManipulators.InstallPlanarManipulatorMouseMoveCallback([this, idPair]( const AzToolsFramework::PlanarManipulator::Action& action) { - OnManipulatorMoved(action.LocalPosition(), idPair); + OnManipulatorMoved(action.m_start.m_localPosition, action.m_current.m_localOffset, idPair); }); m_translationManipulators.InstallSurfaceManipulatorMouseMoveCallback([this, idPair]( const AzToolsFramework::SurfaceManipulator::Action& action) { - OnManipulatorMoved(action.LocalPosition(), idPair); + OnManipulatorMoved(action.m_start.m_localPosition, action.m_current.m_localOffset, idPair); }); } @@ -69,10 +74,14 @@ namespace PhysX m_translationManipulators.Unregister(); } - void ColliderOffsetMode::OnManipulatorMoved(const AZ::Vector3& position, const AZ::EntityComponentIdPair& idPair) + void ColliderOffsetMode::OnManipulatorMoved(const AZ::Vector3& startPosition, const AZ::Vector3& offset, const AZ::EntityComponentIdPair& idPair) { - m_translationManipulators.SetLocalPosition(position); - PhysX::EditorColliderComponentRequestBus::Event(idPair, &PhysX::EditorColliderComponentRequests::SetColliderOffset, position); + AZ::Transform worldTransform = AZ::Transform::CreateIdentity(); + AZ::TransformBus::EventResult(worldTransform, idPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); + const float scale = AZ::GetMax(AZ::MinTransformScale, worldTransform.GetUniformScale()); + const AZ::Vector3 newPosition = startPosition + offset / scale; + m_translationManipulators.SetLocalPosition(newPosition); + PhysX::EditorColliderComponentRequestBus::Event(idPair, &PhysX::EditorColliderComponentRequests::SetColliderOffset, newPosition); } void ColliderOffsetMode::ResetValues(const AZ::EntityComponentIdPair& idPair) diff --git a/Gems/PhysX/Code/Editor/ColliderOffsetMode.h b/Gems/PhysX/Code/Editor/ColliderOffsetMode.h index 6a2e6a8a12..525000a1b1 100644 --- a/Gems/PhysX/Code/Editor/ColliderOffsetMode.h +++ b/Gems/PhysX/Code/Editor/ColliderOffsetMode.h @@ -28,7 +28,8 @@ namespace PhysX void ResetValues(const AZ::EntityComponentIdPair& idPair) override; private: - void OnManipulatorMoved(const AZ::Vector3& position, const AZ::EntityComponentIdPair& idPair); + void OnManipulatorMoved( + const AZ::Vector3& startPosition, const AZ::Vector3& offset, const AZ::EntityComponentIdPair& idPair); AzToolsFramework::TranslationManipulators m_translationManipulators; }; diff --git a/Gems/PhysX/Code/Editor/ColliderRotationMode.cpp b/Gems/PhysX/Code/Editor/ColliderRotationMode.cpp index 5f3c98bcee..d39fd001e4 100644 --- a/Gems/PhysX/Code/Editor/ColliderRotationMode.cpp +++ b/Gems/PhysX/Code/Editor/ColliderRotationMode.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,9 @@ namespace PhysX AZ::Transform worldTransform; AZ::TransformBus::EventResult(worldTransform, idPair.GetEntityId(), &AZ::TransformInterface::GetWorldTM); + AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, idPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + AZ::Quaternion colliderRotation; PhysX::EditorColliderComponentRequestBus::EventResult(colliderRotation, idPair, &PhysX::EditorColliderComponentRequests::GetColliderRotation); @@ -36,7 +40,8 @@ namespace PhysX PhysX::EditorColliderComponentRequestBus::EventResult(colliderOffset, idPair, &PhysX::EditorColliderComponentRequests::GetColliderOffset); m_rotationManipulators.SetSpace(worldTransform); - m_rotationManipulators.SetLocalPosition(colliderOffset); + m_rotationManipulators.SetNonUniformScale(nonUniformScale); + m_rotationManipulators.SetLocalPosition(nonUniformScale * colliderOffset); m_rotationManipulators.SetLocalOrientation(colliderRotation); m_rotationManipulators.AddEntityComponentIdPair(idPair); m_rotationManipulators.Register(AzToolsFramework::g_mainManipulatorManagerId); @@ -70,7 +75,11 @@ namespace PhysX AZ::Vector3 colliderOffset = AZ::Vector3::CreateZero(); PhysX::EditorColliderComponentRequestBus::EventResult(colliderOffset, idPair, &PhysX::EditorColliderComponentRequests::GetColliderOffset); - m_rotationManipulators.SetLocalPosition(colliderOffset); + + AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, idPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + + m_rotationManipulators.SetLocalPosition(nonUniformScale * colliderOffset); } void ColliderRotationMode::Teardown(const AZ::EntityComponentIdPair& idPair) diff --git a/Gems/PhysX/Code/Editor/ColliderSphereMode.cpp b/Gems/PhysX/Code/Editor/ColliderSphereMode.cpp index ba0fd76ea9..8d69e28726 100644 --- a/Gems/PhysX/Code/Editor/ColliderSphereMode.cpp +++ b/Gems/PhysX/Code/Editor/ColliderSphereMode.cpp @@ -8,11 +8,14 @@ #include "ColliderSphereMode.h" #include +#include #include #include #include +#include #include +#include #include #include #include @@ -27,7 +30,12 @@ namespace PhysX void ColliderSphereMode::Setup(const AZ::EntityComponentIdPair& idPair) { AZ::Transform colliderWorldTransform = AZ::Transform::Identity(); - PhysX::EditorColliderComponentRequestBus::EventResult(colliderWorldTransform, idPair, &PhysX::EditorColliderComponentRequests::GetColliderWorldTransform); + AZ::TransformBus::EventResult(colliderWorldTransform, idPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); + + AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, idPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); float sphereRadius = 0.0f; PhysX::EditorColliderComponentRequestBus::EventResult(sphereRadius, idPair, &PhysX::EditorColliderComponentRequests::GetSphereRadius); @@ -36,7 +44,9 @@ namespace PhysX m_radiusManipulator->AddEntityComponentIdPair(idPair); m_radiusManipulator->SetAxis(ManipulatorAxis); m_radiusManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); - m_radiusManipulator->SetLocalPosition(ManipulatorAxis * sphereRadius); + m_radiusManipulator->SetLocalTransform(colliderLocalTransform * AZ::Transform::CreateTranslation(ManipulatorAxis * sphereRadius)); + m_radiusManipulator->SetNonUniformScale(nonUniformScale); + m_radiusManipulator->SetBoundsDirty(); AzToolsFramework::ManipulatorViews views; views.emplace_back(AzToolsFramework::CreateManipulatorViewQuadBillboard(AzFramework::ViewportColors::DefaultManipulatorHandleColor, AzFramework::ViewportConstants::DefaultManipulatorHandleSize)); @@ -53,12 +63,18 @@ namespace PhysX void ColliderSphereMode::Refresh(const AZ::EntityComponentIdPair& idPair) { AZ::Transform colliderWorldTransform = AZ::Transform::Identity(); - PhysX::EditorColliderComponentRequestBus::EventResult(colliderWorldTransform, idPair, &PhysX::EditorColliderComponentRequests::GetColliderWorldTransform); + AZ::TransformBus::EventResult(colliderWorldTransform, idPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); m_radiusManipulator->SetSpace(colliderWorldTransform); + AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne(); + AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, idPair.GetEntityId(), &AZ::NonUniformScaleRequests::GetScale); + + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + float sphereRadius = 0.0f; PhysX::EditorColliderComponentRequestBus::EventResult(sphereRadius, idPair, &PhysX::EditorColliderComponentRequests::GetSphereRadius); - m_radiusManipulator->SetLocalPosition(ManipulatorAxis * sphereRadius); + m_radiusManipulator->SetLocalTransform(colliderLocalTransform * AZ::Transform::CreateTranslation(ManipulatorAxis * sphereRadius)); + m_radiusManipulator->SetBoundsDirty(); } void ColliderSphereMode::Teardown(const AZ::EntityComponentIdPair& idPair) @@ -80,22 +96,34 @@ namespace PhysX AZ_UNUSED(debugDisplay); const AzFramework::CameraState cameraState = AzToolsFramework::GetCameraState(viewportInfo.m_viewportId); - float radius = m_radiusManipulator->GetLocalPosition().GetLength(); - m_radiusManipulator->SetAxis(cameraState.m_side); - m_radiusManipulator->SetLocalPosition(cameraState.m_side * radius); + if (!m_radiusManipulator->EntityComponentIdPairs().empty()) + { + const AZ::EntityComponentIdPair idPair = *m_radiusManipulator->EntityComponentIdPairs().begin(); + float radius = 0.0f; + PhysX::EditorColliderComponentRequestBus::EventResult(radius, idPair, &PhysX::EditorColliderComponentRequests::GetSphereRadius); + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + m_radiusManipulator->SetAxis(cameraState.m_side); + m_radiusManipulator->SetLocalTransform(colliderLocalTransform * AZ::Transform::CreateTranslation(cameraState.m_side * radius)); + } } void ColliderSphereMode::OnManipulatorMoved(const AzToolsFramework::LinearManipulator::Action& action, const AZ::EntityComponentIdPair& idPair) { + // manipulator offsets do not take transform scale into account, need to handle it here + const AZ::Transform colliderLocalTransform = Utils::GetColliderLocalTransform(idPair); + const AZ::Vector3 manipulatorPosition = AzToolsFramework::GetPositionInManipulatorFrame( + m_radiusManipulator->GetSpace().GetUniformScale(), colliderLocalTransform, action); + // Get the distance the manipulator has moved along the axis. - float extent = action.LocalPosition().Dot(action.m_fixed.m_axis); + float extent = manipulatorPosition.Dot(action.m_fixed.m_axis); // Clamp the distance to a small value to prevent it going negative. extent = AZ::GetMax(extent, MinSphereRadius); // Update the manipulator and sphere radius - m_radiusManipulator->SetLocalPosition(extent * action.m_fixed.m_axis); + m_radiusManipulator->SetLocalTransform( + Utils::GetColliderLocalTransform(idPair) * AZ::Transform::CreateTranslation(extent * action.m_fixed.m_axis)); PhysX::EditorColliderComponentRequestBus::Event(idPair, &PhysX::EditorColliderComponentRequests::SetSphereRadius, extent); } } diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp index 69ae037e07..d432655e78 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.cpp @@ -582,9 +582,8 @@ namespace PhysX AZ::Transform EditorColliderComponent::GetColliderLocalTransform() const { - const AZ::Vector3 nonUniformScale = Utils::GetTransformScale(GetEntityId()); return AZ::Transform::CreateFromQuaternionAndTranslation( - m_configuration.m_rotation, m_configuration.m_position * nonUniformScale); + m_configuration.m_rotation, m_configuration.m_position); } void EditorColliderComponent::UpdateMeshAsset() @@ -970,8 +969,10 @@ namespace PhysX static_cast(shapeConfiguration); const AZ::Vector3 overallScale = Utils::GetTransformScale(GetEntityId()) * m_cachedNonUniformScale * assetScale; + Physics::ColliderConfiguration nonUniformScaledColliderConfiguration = *colliderConfiguration; + nonUniformScaledColliderConfiguration.m_position *= m_cachedNonUniformScale; - m_colliderDebugDraw.DrawMesh(debugDisplay, *colliderConfiguration, *cookedMeshShapeConfiguration, + m_colliderDebugDraw.DrawMesh(debugDisplay, nonUniformScaledColliderConfiguration, *cookedMeshShapeConfiguration, overallScale, static_cast(shapeIndex)); break; } @@ -1054,12 +1055,17 @@ namespace PhysX AZ::Transform EditorColliderComponent::GetCurrentTransform() { - return GetColliderWorldTransform(); + return GetWorldTM(); + } + + AZ::Transform EditorColliderComponent::GetCurrentLocalTransform() + { + return GetColliderLocalTransform(); } AZ::Vector3 EditorColliderComponent::GetBoxScale() { - return AZ::Vector3(GetWorldTM().GetUniformScale()); + return AZ::Vector3::CreateOne(); } void EditorColliderComponent::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& world) @@ -1203,7 +1209,7 @@ namespace PhysX AZ::Transform EditorColliderComponent::GetColliderWorldTransform() { - return AzToolsFramework::TransformNormalizedScale(GetWorldTM()) * GetColliderLocalTransform(); + return GetWorldTM() * GetColliderLocalTransform(); } bool EditorColliderComponent::ShouldUpdateCollisionMeshFromRender() const diff --git a/Gems/PhysX/Code/Source/EditorColliderComponent.h b/Gems/PhysX/Code/Source/EditorColliderComponent.h index 773dd0a9b8..bc3d4da6d9 100644 --- a/Gems/PhysX/Code/Source/EditorColliderComponent.h +++ b/Gems/PhysX/Code/Source/EditorColliderComponent.h @@ -171,6 +171,7 @@ namespace PhysX AZ::Vector3 GetDimensions() override; void SetDimensions(const AZ::Vector3& dimensions) override; AZ::Transform GetCurrentTransform() override; + AZ::Transform GetCurrentLocalTransform() override; AZ::Vector3 GetBoxScale() override; // AZ::Render::MeshComponentNotificationBus diff --git a/Gems/PhysX/Code/Source/Utils.cpp b/Gems/PhysX/Code/Source/Utils.cpp index cc4ef2a12c..9848ab0415 100644 --- a/Gems/PhysX/Code/Source/Utils.cpp +++ b/Gems/PhysX/Code/Source/Utils.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -832,6 +833,17 @@ namespace PhysX return AZ::Transform::CreateFromQuaternionAndTranslation(colliderRelativeRotation, colliderRelativePosition); } + AZ::Transform GetColliderLocalTransform(const AZ::EntityComponentIdPair& idPair) + { + AZ::Quaternion colliderRotation = AZ::Quaternion::CreateIdentity(); + PhysX::EditorColliderComponentRequestBus::EventResult(colliderRotation, idPair, &PhysX::EditorColliderComponentRequests::GetColliderRotation); + + AZ::Vector3 colliderOffset = AZ::Vector3::CreateZero(); + PhysX::EditorColliderComponentRequestBus::EventResult(colliderOffset, idPair, &PhysX::EditorColliderComponentRequests::GetColliderOffset); + + return AZ::Transform::CreateFromQuaternionAndTranslation(colliderRotation, colliderOffset); + } + AZ::Transform GetColliderWorldTransform(const AZ::Transform& worldTransform, const AZ::Vector3& colliderRelativePosition, const AZ::Quaternion& colliderRelativeRotation) diff --git a/Gems/PhysX/Code/Source/Utils.h b/Gems/PhysX/Code/Source/Utils.h index 5244926aa2..f9e25a59f2 100644 --- a/Gems/PhysX/Code/Source/Utils.h +++ b/Gems/PhysX/Code/Source/Utils.h @@ -142,6 +142,9 @@ namespace PhysX AZ::Transform GetColliderLocalTransform(const AZ::Vector3& colliderRelativePosition , const AZ::Quaternion& colliderRelativeRotation); + //! Gets the local transform for a collider (the position and rotation relative to its entity). + AZ::Transform GetColliderLocalTransform(const AZ::EntityComponentIdPair& idPair); + //! Combines collider position and orientation offsets and world transform to a transform. AZ::Transform GetColliderWorldTransform(const AZ::Transform& worldTransform , const AZ::Vector3& colliderRelativePosition diff --git a/Gems/PhysX/Code/Tests/PhysXColliderComponentModeTests.cpp b/Gems/PhysX/Code/Tests/PhysXColliderComponentModeTests.cpp index 5722d1a397..a9797d3d53 100644 --- a/Gems/PhysX/Code/Tests/PhysXColliderComponentModeTests.cpp +++ b/Gems/PhysX/Code/Tests/PhysXColliderComponentModeTests.cpp @@ -17,7 +17,9 @@ #include #include #include +#include #include +#include namespace UnitTest { @@ -437,4 +439,378 @@ namespace UnitTest EXPECT_NEAR(assetScale.GetY(), 1.0f, tolerance); EXPECT_NEAR(assetScale.GetZ(), 1.0f, tolerance); } + + class PhysXEditorColliderComponentFixture : public UnitTest::ToolsApplicationFixture + { + public: + void SetUpEditorFixtureImpl() override; + void TearDownEditorFixtureImpl() override; + void SetupTransform(const AZ::Quaternion& rotation, const AZ::Vector3& translation, float uniformScale); + void SetupCollider( + const Physics::ShapeConfiguration& shapeConfiguration, + const AZ::Quaternion& colliderRotation, + const AZ::Vector3& colliderOffset); + void SetupNonUniformScale(const AZ::Vector3& nonUniformScale); + void EnterColliderSubMode(PhysX::ColliderComponentModeRequests::SubMode subMode); + + AZ::Entity* m_entity = nullptr; + AZ::EntityComponentIdPair m_idPair; + }; + + void PhysXEditorColliderComponentFixture::SetUpEditorFixtureImpl() + { + AZ::SerializeContext* serializeContext = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + + UnitTest::CreateDefaultEditorEntity("EditorColliderComponentEntity", &m_entity); + } + + void PhysXEditorColliderComponentFixture::TearDownEditorFixtureImpl() + { + AzToolsFramework::EditorEntityContextRequestBus::Broadcast( + &AzToolsFramework::EditorEntityContextRequestBus::Events::DestroyEditorEntity, m_entity->GetId()); + m_entity = nullptr; + } + + void PhysXEditorColliderComponentFixture::SetupTransform( + const AZ::Quaternion& rotation, const AZ::Vector3& translation, float uniformScale) + { + const AZ::Transform transform = AZ::Transform::CreateFromQuaternionAndTranslation(rotation, translation); + AZ::TransformBus::Event(m_entity->GetId(), &AZ::TransformBus::Events::SetWorldTM, transform); + AZ::TransformBus::Event(m_entity->GetId(), &AZ::TransformBus::Events::SetLocalUniformScale, uniformScale); + } + + void PhysXEditorColliderComponentFixture::SetupCollider( + const Physics::ShapeConfiguration& shapeConfiguration, const AZ::Quaternion& colliderRotation, const AZ::Vector3& colliderOffset) + { + m_entity->Deactivate(); + auto* colliderComponent = + m_entity->CreateComponent(Physics::ColliderConfiguration(), shapeConfiguration); + m_entity->Activate(); + m_idPair = AZ::EntityComponentIdPair(m_entity->GetId(), colliderComponent->GetId()); + PhysX::EditorColliderComponentRequestBus::Event( + m_idPair, &PhysX::EditorColliderComponentRequests::SetColliderOffset, colliderOffset); + PhysX::EditorColliderComponentRequestBus::Event( + m_idPair, &PhysX::EditorColliderComponentRequests::SetColliderRotation, colliderRotation); + } + + void PhysXEditorColliderComponentFixture::SetupNonUniformScale(const AZ::Vector3& nonUniformScale) + { + m_entity->Deactivate(); + m_entity->CreateComponent(AzToolsFramework::Components::EditorNonUniformScaleComponent::RTTI_Type()); + m_entity->Activate(); + AZ::NonUniformScaleRequestBus::Event(m_entity->GetId(), &AZ::NonUniformScaleRequests::SetScale, nonUniformScale); + } + + void PhysXEditorColliderComponentFixture::EnterColliderSubMode(PhysX::ColliderComponentModeRequests::SubMode subMode) + { + AzToolsFramework::SelectEntity(m_entity->GetId()); + EnterComponentMode(); + PhysX::ColliderComponentModeRequestBus::Broadcast(&PhysX::ColliderComponentModeRequests::SetCurrentMode, subMode); + } + + using PhysXEditorColliderComponentManipulatorFixture = + UnitTest::IndirectCallManipulatorViewportInteractionFixtureMixin; + + // use a reasonably large tolerance because manipulator precision is limited by viewport resolution + static const float ManipulatorTolerance = 0.01f; + + TEST_F(PhysXEditorColliderComponentManipulatorFixture, OffsetManipulatorsCorrectlyLocatedRelativeToCollider) + { + const AZ::Vector3 boxDimensions(2.0f, 3.0f, 1.5f); + const AZ::Quaternion boxRotation(0.1f, 0.1f, 0.7f, 0.7f); + const AZ::Vector3 boxOffset(3.0f, 1.0f, 2.0f); + SetupCollider(Physics::BoxShapeConfiguration(boxDimensions), boxRotation, boxOffset); + const AZ::Quaternion entityRotation(0.8f, 0.2f, 0.4f, 0.4f); + const AZ::Vector3 entityTranslation(2.0f, -3.0f, 0.5f); + const float uniformScale = 2.0f; + SetupTransform(entityRotation, entityTranslation, uniformScale); + EnterColliderSubMode(PhysX::ColliderComponentModeRequests::SubMode::Offset); + + // the expected position of the collider centre based on the combination of entity transform and collider offset + const AZ::Vector3 expectedColliderPosition(8.8f, -2.28f, 3.54f); + + // the expected world space direction of the collider offset x-axis based on the entity transform + const AZ::Vector3 expectedXAxis(0.6f, 0.64f, 0.48f); + + // position the camera to look down at the collider from above + AzFramework::SetCameraTransform( + m_cameraState, + AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(-AZ::Constants::HalfPi), expectedColliderPosition + AZ::Vector3::CreateAxisZ(10.0f))); + + // position in world space, slightly moved along the x-axis in order to grab the x translation manipulator + const AZ::Vector3 worldStart = expectedColliderPosition + 0.5f * expectedXAxis; + + // position in world space to move to + const AZ::Vector3 worldEnd = worldStart + 2.0f * expectedXAxis; + + const auto screenStart = AzFramework::WorldToScreen(worldStart, m_cameraState); + const auto screenEnd = AzFramework::WorldToScreen(worldEnd, m_cameraState); + + m_actionDispatcher + ->CameraState(m_cameraState) + // move the mouse to the position of the x offset manipulator + ->MousePosition(screenStart) + // drag to move the manipulator + ->MouseLButtonDown() + ->MousePosition(screenEnd) + ->MouseLButtonUp(); + + AZ::Vector3 newColliderOffset = AZ::Vector3::CreateZero(); + PhysX::EditorColliderComponentRequestBus::EventResult( + newColliderOffset, m_idPair, &PhysX::EditorColliderComponentRequests::GetColliderOffset); + + EXPECT_THAT(newColliderOffset, IsCloseTolerance(AZ::Vector3(4.0f, 1.0f, 2.0f), ManipulatorTolerance)); + } + + TEST_F(PhysXEditorColliderComponentManipulatorFixture, OffsetManipulatorsCorrectlyLocatedRelativeToColliderWithNonUniformScale) + { + const float capsuleRadius = 0.5f; + const float capsuleHeight = 2.0f; + const AZ::Quaternion capsuleRotation(0.2f, -0.4f, 0.8f, 0.4f); + const AZ::Vector3 capsuleOffset(-2.0f, 3.0f, -1.0f); + SetupCollider(Physics::CapsuleShapeConfiguration(capsuleHeight, capsuleRadius), capsuleRotation, capsuleOffset); + const AZ::Quaternion entityRotation(-0.1f, 0.7f, -0.7f, 0.1f); + const AZ::Vector3 entityTranslation(-1.0f, 1.0f, -2.5f); + const float uniformScale = 1.5f; + SetupTransform(entityRotation, entityTranslation, uniformScale); + const AZ::Vector3 nonUniformScale(2.0f, 0.5f, 1.5f); + SetupNonUniformScale(nonUniformScale); + EnterColliderSubMode(PhysX::ColliderComponentModeRequests::SubMode::Offset); + + // the expected position of the collider centre based on the combination of entity transform, collider offset and non-uniform scale + const AZ::Vector3 expectedColliderPosition(4.13f, 4.84f, -4.75f); + + // the expected world space direction of the collider offset z-axis based on the entity transform + const AZ::Vector3 expectedZAxis(0.28f, -0.96f, 0.0f); + + // position the camera to look at the collider from underneath + AzFramework::SetCameraTransform( + m_cameraState, + AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(AZ::Constants::HalfPi), expectedColliderPosition - AZ::Vector3::CreateAxisZ(10.0f))); + + // position in world space, slightly moved along the z-axis in order to grab the z translation manipulator + // need to go in the negative z direction because the camera angle causes the manipulator to flip + const AZ::Vector3 worldStart = expectedColliderPosition - 0.5f * expectedZAxis; + + // position in world space to move to + const AZ::Vector3 worldEnd = worldStart - 2.25f * expectedZAxis; + + const auto screenStart = AzFramework::WorldToScreen(worldStart, m_cameraState); + const auto screenEnd = AzFramework::WorldToScreen(worldEnd, m_cameraState); + + m_actionDispatcher + ->CameraState(m_cameraState) + // move the mouse to the position of the z offset manipulator + ->MousePosition(screenStart) + // drag to move the manipulator + ->MouseLButtonDown() + ->MousePosition(screenEnd) + ->MouseLButtonUp(); + + AZ::Vector3 newColliderOffset = AZ::Vector3::CreateZero(); + PhysX::EditorColliderComponentRequestBus::EventResult( + newColliderOffset, m_idPair, &PhysX::EditorColliderComponentRequests::GetColliderOffset); + + EXPECT_THAT(newColliderOffset, IsCloseTolerance(AZ::Vector3(-2.0f, 3.0f, -2.0f), ManipulatorTolerance)); + } + + TEST_F(PhysXEditorColliderComponentManipulatorFixture, BoxColliderScaleManipulatorsCorrectlyLocatedRelativeToColliderWithNonUniformScale) + { + const AZ::Vector3 boxDimensions(2.0f, 2.0f, 3.0f); + const AZ::Quaternion boxRotation(0.7f, 0.7f, -0.1f, 0.1f); + const AZ::Vector3 boxOffset(0.5f, 1.5f, 2.0f); + SetupCollider(Physics::BoxShapeConfiguration(boxDimensions), boxRotation, boxOffset); + const AZ::Quaternion entityRotation(0.2f, 0.4f, -0.4f, 0.8f); + const AZ::Vector3 entityTranslation(2.0f, -3.0f, -2.0f); + const float uniformScale = 0.5f; + SetupTransform(entityRotation, entityTranslation, uniformScale); + const AZ::Vector3 nonUniformScale(3.0f, 1.5f, 2.5f); + SetupNonUniformScale(nonUniformScale); + EnterColliderSubMode(PhysX::ColliderComponentModeRequests::SubMode::Dimensions); + + // the expected position of the collider centre based on the combination of entity transform, collider offset and non-uniform scale + const AZ::Vector3 expectedColliderPosition(4.37f, -4.285f, -1.1f); + + // the expected position of the y scale manipulator relative to the centre of the collider, based on collider + // rotation, entity rotation and scale, and non-uniform scale + const AZ::Vector3 scaleManipulatorYDelta(0.54f, -0.72f, -1.2f); + + // position the camera to look at the collider along the x-y diagonal + AzFramework::SetCameraTransform( + m_cameraState, + AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationZ(-AZ::Constants::QuarterPi), expectedColliderPosition - AZ::Vector3(2.0f, 2.0f, 0.0f))); + + const AZ::Vector3 worldStart = expectedColliderPosition + scaleManipulatorYDelta; + const AZ::Vector3 worldEnd = worldStart + 0.1f * scaleManipulatorYDelta; + + const auto screenStart = AzFramework::WorldToScreen(worldStart, m_cameraState); + const auto screenEnd = AzFramework::WorldToScreen(worldEnd, m_cameraState); + + m_actionDispatcher + ->CameraState(m_cameraState) + // move the mouse to the position of the y scale manipulator + ->MousePosition(screenStart) + // drag to move the manipulator + ->MouseLButtonDown() + ->MousePosition(screenEnd) + ->MouseLButtonUp(); + + AZ::Vector3 newBoxDimensions = AZ::Vector3::CreateZero(); + AzToolsFramework::BoxManipulatorRequestBus::EventResult( + newBoxDimensions, m_idPair, &AzToolsFramework::BoxManipulatorRequests::GetDimensions); + + EXPECT_THAT(newBoxDimensions, IsCloseTolerance(AZ::Vector3(2.0f, 2.2f, 3.0f), ManipulatorTolerance)); + } + + TEST_F(PhysXEditorColliderComponentManipulatorFixture, SphereColliderScaleManipulatorsCorrectlyLocatedRelativeToColliderWithNonUniformScale) + { + const float sphereRadius = 1.0f; + const AZ::Quaternion sphereRotation(-0.1f, 0.7f, -0.7f, 0.1f); + const AZ::Vector3 sphereOffset(-2.0f, 1.0f, -3.0f); + SetupCollider(Physics::SphereShapeConfiguration(sphereRadius), sphereRotation, sphereOffset); + const AZ::Quaternion entityRotation(-0.4f, -0.2f, 0.4f, 0.8f); + const AZ::Vector3 entityTranslation(-1.0f, -3.0f, 3.0f); + const float uniformScale = 1.5f; + SetupTransform(entityRotation, entityTranslation, uniformScale); + const AZ::Vector3 nonUniformScale(1.5f, 0.5f, 2.0f); + SetupNonUniformScale(nonUniformScale); + EnterColliderSubMode(PhysX::ColliderComponentModeRequests::SubMode::Dimensions); + + // the expected position of the collider centre based on the combination of entity transform, collider offset and non-uniform scale + const AZ::Vector3 expectedColliderPosition(1.7f, -10.65f, -3.0f); + + // position the camera to look at the collider along the y-axis + AzFramework::SetCameraTransform( + m_cameraState, AZ::Transform::CreateTranslation(expectedColliderPosition - AZ::Vector3(0.0f, 5.0f, 0.0f))); + + // the expected position of the scale manipulator relative to the centre of the collider, based on collider + // rotation, entity scale, non-uniform scale and camera state + const AZ::Vector3 scaleManipulatorDelta(-1.1952f, -1.8036f, 0.168f); + + const AZ::Vector3 worldStart = expectedColliderPosition + scaleManipulatorDelta; + const AZ::Vector3 worldEnd = worldStart - 0.1f * scaleManipulatorDelta; + + const auto screenStart = AzFramework::WorldToScreen(worldStart, m_cameraState); + const auto screenEnd = AzFramework::WorldToScreen(worldEnd, m_cameraState); + + m_actionDispatcher + ->CameraState(m_cameraState) + // move the mouse to the position of the y scale manipulator + ->MousePosition(screenStart) + // drag to move the manipulator + ->MouseLButtonDown() + ->MousePosition(screenEnd) + ->MouseLButtonUp(); + + float newSphereRadius = 0.0f; + PhysX::EditorColliderComponentRequestBus::EventResult( + newSphereRadius, m_idPair, &PhysX::EditorColliderComponentRequests::GetSphereRadius); + + EXPECT_NEAR(newSphereRadius, 0.9f, ManipulatorTolerance); + } + + TEST_F(PhysXEditorColliderComponentManipulatorFixture, CapsuleColliderScaleManipulatorsCorrectlyLocatedRelativeToColliderWithNonUniformScale) + { + const float capsuleRadius = 0.2f; + const float capsuleHeight = 1.0f; + const AZ::Quaternion capsuleRotation(-0.2f, -0.8f, -0.4f, 0.4f); + const AZ::Vector3 capsuleOffset(1.0f, -2.0f, 1.0f); + SetupCollider(Physics::CapsuleShapeConfiguration(capsuleHeight, capsuleRadius), capsuleRotation, capsuleOffset); + const AZ::Quaternion entityRotation(0.7f, -0.1f, -0.1f, 0.7f); + const AZ::Vector3 entityTranslation(-2.0f, 1.0f, -3.0f); + const float uniformScale = 2.0f; + SetupTransform(entityRotation, entityTranslation, uniformScale); + const AZ::Vector3 nonUniformScale(1.0f, 0.5f, 1.5f); + SetupNonUniformScale(nonUniformScale); + EnterColliderSubMode(PhysX::ColliderComponentModeRequests::SubMode::Dimensions); + + // the expected position of the collider centre based on the combination of entity transform, collider offset and non-uniform scale + const AZ::Vector3 expectedColliderPosition(-0.92f, -2.44f, -5.0f); + + // the expected position of the height manipulator relative to the centre of the collider, based on collider + // rotation, entity scale and non-uniform scale + const AZ::Vector3 heightManipulatorDelta(-0.3096f, 0.6528f, 0.4f); + + // position the camera to look at the collider along the y-z diagonal + AzFramework::SetCameraTransform( + m_cameraState, + AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(-AZ::Constants::QuarterPi), expectedColliderPosition + AZ::Vector3(0.0f, -1.0f, 1.0f))); + + const AZ::Vector3 worldStart = expectedColliderPosition + heightManipulatorDelta; + const AZ::Vector3 worldEnd = worldStart + 0.2f * heightManipulatorDelta; + + const auto screenStart = AzFramework::WorldToScreen(worldStart, m_cameraState); + const auto screenEnd = AzFramework::WorldToScreen(worldEnd, m_cameraState); + + m_actionDispatcher + ->CameraState(m_cameraState) + // move the mouse to the position of the height manipulator + ->MousePosition(screenStart) + // drag to move the manipulator + ->MouseLButtonDown() + ->MousePosition(screenEnd) + ->MouseLButtonUp(); + + float newCapsuleHeight = 0.0f; + PhysX::EditorColliderComponentRequestBus::EventResult( + newCapsuleHeight, m_idPair, &PhysX::EditorColliderComponentRequests::GetCapsuleHeight); + + EXPECT_NEAR(newCapsuleHeight, 1.2f, ManipulatorTolerance); + } + + TEST_F(PhysXEditorColliderComponentManipulatorFixture, ColliderRotationManipulatorsCorrectlyLocatedRelativeToColliderWithNonUniformScale) + { + const float capsuleRadius = 1.2f; + const float capsuleHeight = 4.0f; + const AZ::Quaternion capsuleRotation(0.7f, 0.7f, -0.1f, 0.1f); + const AZ::Vector3 capsuleOffset(-2.0f, -2.0f, 1.0f); + SetupCollider(Physics::CapsuleShapeConfiguration(capsuleHeight, capsuleRadius), capsuleRotation, capsuleOffset); + const AZ::Quaternion entityRotation(0.8f, -0.4f, -0.4f, 0.2f); + const AZ::Vector3 entityTranslation(1.0f, -1.5f, 2.0f); + const float uniformScale = 1.5f; + SetupTransform(entityRotation, entityTranslation, uniformScale); + const AZ::Vector3 nonUniformScale(1.5f, 1.5f, 2.0f); + SetupNonUniformScale(nonUniformScale); + EnterColliderSubMode(PhysX::ColliderComponentModeRequests::SubMode::Rotation); + + // the expected position of the collider centre based on the combination of entity transform, collider offset and non-uniform scale + const AZ::Vector3 expectedColliderPosition(-0.86f, 4.8f, -0.52f); + + // the y and z axes of the collider's frame in world space, used to locate points on the x rotation manipulator arc to interact with + const AZ::Vector3 yDirection(0.36f, -0.8f, -0.48f); + const AZ::Vector3 zDirection(0.9024f, 0.168f, 0.3968f); + + // position the camera to look at the collider along the world y axis + AzFramework::SetCameraTransform( + m_cameraState, + AZ::Transform::CreateTranslation(expectedColliderPosition - AZ::Vector3(0.0f, 10.0f, 0.0f))); + + const float screenToWorldMultiplier = AzToolsFramework::CalculateScreenToWorldMultiplier(expectedColliderPosition, m_cameraState); + const float manipulatorViewRadius = 2.0f; + const AZ::Vector3 worldStart = expectedColliderPosition + screenToWorldMultiplier * manipulatorViewRadius * yDirection; + const AZ::Vector3 worldEnd = expectedColliderPosition + screenToWorldMultiplier * manipulatorViewRadius * zDirection; + + const auto screenStart = AzFramework::WorldToScreen(worldStart, m_cameraState); + const auto screenEnd = AzFramework::WorldToScreen(worldEnd, m_cameraState); + + m_actionDispatcher + ->CameraState(m_cameraState) + // move the mouse to a position on the angular manipulator + ->MousePosition(screenStart) + // drag to move the manipulator + ->MouseLButtonDown() + ->MousePosition(screenEnd) + ->MouseLButtonUp(); + + AZ::Quaternion newColliderRotation = AZ::Quaternion::CreateIdentity(); + PhysX::EditorColliderComponentRequestBus::EventResult( + newColliderRotation, m_idPair, &PhysX::EditorColliderComponentRequests::GetColliderRotation); + + EXPECT_THAT(newColliderRotation, testing::Not(IsCloseTolerance(capsuleRotation, ManipulatorTolerance))); + } } // namespace UnitTest