diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/RigidBodyConfiguration.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/RigidBodyConfiguration.cpp index a61f6a4845..cfea76f1d7 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/RigidBodyConfiguration.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/RigidBodyConfiguration.cpp @@ -123,6 +123,12 @@ namespace AzPhysics ->Field("Kinematic", &RigidBodyConfiguration::m_kinematic) ->Field("CCD Enabled", &RigidBodyConfiguration::m_ccdEnabled) ->Field("Compute Mass", &RigidBodyConfiguration::m_computeMass) + ->Field("Lock Linear X", &RigidBodyConfiguration::m_lockLinearX) + ->Field("Lock Linear Y", &RigidBodyConfiguration::m_lockLinearY) + ->Field("Lock Linear Z", &RigidBodyConfiguration::m_lockLinearZ) + ->Field("Lock Angular X", &RigidBodyConfiguration::m_lockAngularX) + ->Field("Lock Angular Y", &RigidBodyConfiguration::m_lockAngularY) + ->Field("Lock Angular Z", &RigidBodyConfiguration::m_lockAngularZ) ->Field("Mass", &RigidBodyConfiguration::m_mass) ->Field("Compute COM", &RigidBodyConfiguration::m_computeCenterOfMass) ->Field("Centre of mass offset", &RigidBodyConfiguration::m_centerOfMassOffset) diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/RigidBodyConfiguration.h b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/RigidBodyConfiguration.h index 59829e740c..46f2620938 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/RigidBodyConfiguration.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/RigidBodyConfiguration.h @@ -62,6 +62,16 @@ namespace AzPhysics bool m_computeInertiaTensor = true; bool m_computeMass = true; + //! Flags to restrict motion along specific world-space axes. + bool m_lockLinearX = false; + bool m_lockLinearY = false; + bool m_lockLinearZ = false; + + //! Flags to restrict rotation around specific world-space axes. + bool m_lockAngularX = false; + bool m_lockAngularY = false; + bool m_lockAngularZ = false; + //! If set, non-simulated shapes will also be included in the mass properties calculation. bool m_includeAllShapesInMassCalculation = false; diff --git a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp index d27874f030..c94cc46ba4 100644 --- a/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorRigidBodyComponent.cpp @@ -156,6 +156,33 @@ namespace PhysX ->DataElement(AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_kinematic, "Kinematic", "Rigid body is kinematic") ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetKinematicVisibility) + + // Linear axis locking properties + ->ClassElement(AZ::Edit::ClassElements::Group, "Linear Axis Locking") + ->Attribute(AZ::Edit::Attributes::AutoExpand, false) + ->DataElement( + AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockLinearX, "Lock X", + "Lock linear momentum in X direction") + ->DataElement( + AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockLinearY, "Lock Y", + "Lock linear momentum in Y direction") + ->DataElement( + AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockLinearZ, "Lock Z", + "Lock linear momentum in Z direction") + + // Angular axis locking properties + ->ClassElement(AZ::Edit::ClassElements::Group, "Angular Axis Locking") + ->Attribute(AZ::Edit::Attributes::AutoExpand, false) + ->DataElement( + AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockAngularX, "Lock X", + "Lock angular momentum in X direction") + ->DataElement( + AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockAngularY, "Lock Y", + "Lock angular momentum in Y direction") + ->DataElement( + AZ::Edit::UIHandlers::Default, &AzPhysics::RigidBodyConfiguration::m_lockAngularZ, "Lock Z", + "Lock angular momentum in Z direction") + ->ClassElement(AZ::Edit::ClassElements::Group, "Continuous Collision Detection") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::Visibility, &AzPhysics::RigidBodyConfiguration::GetCCDVisibility) diff --git a/Gems/PhysX/Code/Source/Utils.cpp b/Gems/PhysX/Code/Source/Utils.cpp index 4e5695e3c6..0b7ab9436b 100644 --- a/Gems/PhysX/Code/Source/Utils.cpp +++ b/Gems/PhysX/Code/Source/Utils.cpp @@ -1466,6 +1466,14 @@ namespace PhysX rigidDynamic->setRigidBodyFlag(physx::PxRigidBodyFlag::eKINEMATIC, configuration.m_kinematic); rigidDynamic->setMaxAngularVelocity(configuration.m_maxAngularVelocity); + // Set axis locks. + rigidDynamic->setRigidDynamicLockFlag(physx::PxRigidDynamicLockFlag::eLOCK_LINEAR_X, configuration.m_lockLinearX); + rigidDynamic->setRigidDynamicLockFlag(physx::PxRigidDynamicLockFlag::eLOCK_LINEAR_Y, configuration.m_lockLinearY); + rigidDynamic->setRigidDynamicLockFlag(physx::PxRigidDynamicLockFlag::eLOCK_LINEAR_Z, configuration.m_lockLinearZ); + rigidDynamic->setRigidDynamicLockFlag(physx::PxRigidDynamicLockFlag::eLOCK_ANGULAR_X, configuration.m_lockAngularX); + rigidDynamic->setRigidDynamicLockFlag(physx::PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y, configuration.m_lockAngularY); + rigidDynamic->setRigidDynamicLockFlag(physx::PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z, configuration.m_lockAngularZ); + return rigidDynamic; } diff --git a/Gems/PhysX/Code/Tests/PhysXSpecificTest.cpp b/Gems/PhysX/Code/Tests/PhysXSpecificTest.cpp index 770e47d477..76ee035b70 100644 --- a/Gems/PhysX/Code/Tests/PhysXSpecificTest.cpp +++ b/Gems/PhysX/Code/Tests/PhysXSpecificTest.cpp @@ -1102,6 +1102,60 @@ namespace PhysX SanityCheckValidFrustumParams(points.value(), validHeight, validBottomRadius, validTopRadius, validSubdivisions); } + TEST_F(PhysXSpecificTest, RigidBody_RigidBodyWithAxisLockFlagsCreated_InternalPhysXFlagsSetAccordingly) + { + // Helper function wrapping creation logic + auto CreateRigidBody = [this](bool linearX, bool linearY, bool linearZ, bool angularX, bool angularY, bool angularZ) -> AzPhysics::RigidBody* + { + AzPhysics::RigidBodyConfiguration rigidBodyConfig; + + rigidBodyConfig.m_lockLinearX = linearX; + rigidBodyConfig.m_lockLinearY = linearY; + rigidBodyConfig.m_lockLinearZ = linearZ; + + rigidBodyConfig.m_lockAngularX = angularX; + rigidBodyConfig.m_lockAngularY = angularY; + rigidBodyConfig.m_lockAngularZ = angularZ; + + if (auto* sceneInterface = AZ::Interface::Get()) + { + AzPhysics::SimulatedBodyHandle simBodyHandle = sceneInterface->AddSimulatedBody(m_testSceneHandle, &rigidBodyConfig); + return azdynamic_cast(sceneInterface->GetSimulatedBodyFromHandle(m_testSceneHandle, simBodyHandle)); + } + + return nullptr; + }; + + auto RemoveRigidBody = [this](AzPhysics::RigidBody*& rigidBody) + { + auto* sceneInterface = AZ::Interface::Get(); + if (rigidBody && sceneInterface) + { + sceneInterface->RemoveSimulatedBody(rigidBody->m_sceneOwner, rigidBody->m_bodyHandle); + } + rigidBody = nullptr; + }; + + auto TestLockFlags = [&CreateRigidBody, &RemoveRigidBody](bool linearX, bool linearY, bool linearZ, + bool angularX, bool angularY, bool angularZ, + physx::PxRigidDynamicLockFlags expectedFlags) + { + auto* rigidBody = CreateRigidBody(linearX, linearY, linearZ, angularX, angularY, angularZ); + ASSERT_TRUE(rigidBody != nullptr); + + physx::PxRigidDynamic* pxRigidBody = static_cast(rigidBody->GetNativePointer()); + EXPECT_EQ(pxRigidBody->getRigidDynamicLockFlags(), expectedFlags); + + RemoveRigidBody(rigidBody); + }; + + TestLockFlags(false, false, false, false, false, false, physx::PxRigidDynamicLockFlags(0)); + TestLockFlags(true, false, false, false, false, false, physx::PxRigidDynamicLockFlags(physx::PxRigidDynamicLockFlag::eLOCK_LINEAR_X)); + TestLockFlags(false, false, false, false, true, false, physx::PxRigidDynamicLockFlags(physx::PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y)); + TestLockFlags(false, true, false, false, false, true, + physx::PxRigidDynamicLockFlags(physx::PxRigidDynamicLockFlag::eLOCK_LINEAR_Y | physx::PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z)); + } + TEST_F(PhysXSpecificTest, RigidBody_RigidBodyWithSimulatedFlagsHitsPlane_OnlySimulatedShapeCollidesWithPlane) { // Helper function wrapping creation logic