You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/PhysX/Code/Tests/PhysXComponentBusTests.cpp

945 lines
47 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzFramework/Physics/RigidBodyBus.h>
#include <AzFramework/Physics/Shape.h>
#include <AzFramework/Physics/SystemBus.h>
#include <AzFramework/Physics/Configuration/SystemConfiguration.h>
#include <AZTestShared/Utils/Utils.h>
#include <Tests/PhysXGenericTestFixture.h>
#include <Tests/PhysXTestCommon.h>
namespace PhysX
{
class PhysicsComponentBusTest
: public GenericPhysicsInterfaceTest
{
};
TEST_F(PhysicsComponentBusTest, SetLinearDamping_DynamicSphere_MoreDampedBodyFallsSlower)
{
EntityPtr sphereA = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, -5.0f, 0.0f), 0.5f);
EntityPtr sphereB = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, 5.0f, 0.0f), 0.5f);
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
&Physics::RigidBodyRequests::SetLinearDamping, 0.1f);
Physics::RigidBodyRequestBus::Event(sphereB->GetId(),
&Physics::RigidBodyRequests::SetLinearDamping, 0.2f);
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 60);
float dampingA, dampingB;
Physics::RigidBodyRequestBus::EventResult(dampingA, sphereA->GetId(),
&Physics::RigidBodyRequests::GetLinearDamping);
Physics::RigidBodyRequestBus::EventResult(dampingB, sphereB->GetId(),
&Physics::RigidBodyRequests::GetLinearDamping);
EXPECT_NEAR(dampingA, 0.1f, 1e-3f);
EXPECT_NEAR(dampingB, 0.2f, 1e-3f);
float zA = TestUtils::GetPositionElement(sphereA, 2);
float zB = TestUtils::GetPositionElement(sphereB, 2);
EXPECT_GT(zB, zA);
AZ::Vector3 vA = AZ::Vector3::CreateZero();
AZ::Vector3 vB = AZ::Vector3::CreateZero();
Physics::RigidBodyRequestBus::EventResult(vA, sphereA->GetId(),
&Physics::RigidBodyRequests::GetLinearVelocity);
Physics::RigidBodyRequestBus::EventResult(vB, sphereB->GetId(),
&Physics::RigidBodyRequests::GetLinearVelocity);
EXPECT_GT(static_cast<float>(vA.GetLength()), static_cast<float>(vB.GetLength()));
}
TEST_F(PhysicsComponentBusTest, SetLinearDampingNegative_DynamicSphere_NegativeValueRejected)
{
UnitTest::ErrorHandler errorHandler("Negative linear damping value");
EntityPtr sphere = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3::CreateZero(), 0.5f);
float damping = 0.0f, initialDamping = 0.0f;
Physics::RigidBodyRequestBus::EventResult(initialDamping, sphere->GetId(),
&Physics::RigidBodyRequests::GetLinearDamping);
// a negative damping value should be rejected and the damping should remain at its previous value
Physics::RigidBodyRequestBus::Event(sphere->GetId(),
&Physics::RigidBodyRequests::SetLinearDamping, -0.1f);
Physics::RigidBodyRequestBus::EventResult(damping, sphere->GetId(),
&Physics::RigidBodyRequests::GetLinearDamping);
EXPECT_NEAR(damping, initialDamping, 1e-3f);
EXPECT_TRUE(errorHandler.GetWarningCount() > 0);
}
TEST_F(PhysicsComponentBusTest, SetAngularDamping_DynamicSphere_MoreDampedBodyRotatesSlower)
{
EntityPtr sphereA = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, -5.0f, 1.0f), 0.5f);
EntityPtr sphereB = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, 5.0f, 1.0f), 0.5f);
EntityPtr floor = TestUtils::CreateStaticBoxEntity(m_testSceneHandle, AZ::Vector3::CreateZero(), AZ::Vector3(100.0f, 100.0f, 1.0f));
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
&Physics::RigidBodyRequests::SetAngularDamping, 0.1f);
Physics::RigidBodyRequestBus::Event(sphereB->GetId(),
&Physics::RigidBodyRequests::SetAngularDamping, 0.2f);
float dampingA, dampingB;
Physics::RigidBodyRequestBus::EventResult(dampingA, sphereA->GetId(),
&Physics::RigidBodyRequests::GetAngularDamping);
Physics::RigidBodyRequestBus::EventResult(dampingB, sphereB->GetId(),
&Physics::RigidBodyRequests::GetAngularDamping);
EXPECT_NEAR(dampingA, 0.1f, 1e-3f);
EXPECT_NEAR(dampingB, 0.2f, 1e-3f);
AZ::Vector3 impulse(10.0f, 0.0f, 0.0f);
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
&Physics::RigidBodyRequests::ApplyLinearImpulse, impulse);
Physics::RigidBodyRequestBus::Event(sphereB->GetId(),
&Physics::RigidBodyRequests::ApplyLinearImpulse, impulse);
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 10);
auto angularVelocityA = AZ::Vector3::CreateZero();
auto angularVelocityB = AZ::Vector3::CreateZero();
for (int timestep = 0; timestep < 10; timestep++)
{
Physics::RigidBodyRequestBus::EventResult(angularVelocityA, sphereA->GetId(),
&Physics::RigidBodyRequests::GetAngularVelocity);
Physics::RigidBodyRequestBus::EventResult(angularVelocityB, sphereB->GetId(),
&Physics::RigidBodyRequests::GetAngularVelocity);
EXPECT_GT(static_cast<float>(angularVelocityA.GetLength()), static_cast<float>(angularVelocityB.GetLength()));
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 1);
}
}
TEST_F(PhysicsComponentBusTest, SetAngularDampingNegative_DynamicSphere_NegativeValueRejected)
{
UnitTest::ErrorHandler errorHandler("Negative angular damping value");
EntityPtr sphere = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3::CreateZero(), 0.5f);
float damping = 0.0f, initialDamping = 0.0f;
Physics::RigidBodyRequestBus::EventResult(initialDamping, sphere->GetId(),
&Physics::RigidBodyRequests::GetAngularDamping);
// a negative damping value should be rejected and the damping should remain at its previous value
Physics::RigidBodyRequestBus::Event(sphere->GetId(),
&Physics::RigidBodyRequests::SetAngularDamping, -0.1f);
Physics::RigidBodyRequestBus::EventResult(damping, sphere->GetId(),
&Physics::RigidBodyRequests::GetAngularDamping);
EXPECT_NEAR(damping, initialDamping, 1e-3f);
EXPECT_TRUE(errorHandler.GetWarningCount() > 0);
}
TEST_F(PhysicsComponentBusTest, AddImpulse_DynamicSphere_AffectsTrajectory)
{
EntityPtr sphereA = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, -5.0f, 0.0f), 0.5f);
EntityPtr sphereB = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, 5.0f, 0.0f), 0.5f);
AZ::Vector3 impulse(10.0f, 0.0f, 0.0f);
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
&Physics::RigidBodyRequests::ApplyLinearImpulse, impulse);
for (int i = 1; i < 10; i++)
{
float xPreviousA = TestUtils::GetPositionElement(sphereA, 0);
float xPreviousB = TestUtils::GetPositionElement(sphereB, 0);
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 10);
EXPECT_GT(TestUtils::GetPositionElement(sphereA, 0), xPreviousA);
EXPECT_NEAR(TestUtils::GetPositionElement(sphereB, 0), xPreviousB, 1e-3f);
}
}
TEST_F(PhysicsComponentBusTest, SetLinearVelocity_DynamicSphere_AffectsTrajectory)
{
EntityPtr sphereA = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, -5.0f, 0.0f), 0.5f);
EntityPtr sphereB = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, 5.0f, 0.0f), 0.5f);
AZ::Vector3 velocity(10.0f, 0.0f, 0.0f);
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
&Physics::RigidBodyRequests::SetLinearVelocity, velocity);
for (int i = 1; i < 10; i++)
{
float xPreviousA = TestUtils::GetPositionElement(sphereA, 0);
float xPreviousB = TestUtils::GetPositionElement(sphereB, 0);
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 10);
EXPECT_GT(TestUtils::GetPositionElement(sphereA, 0), xPreviousA);
EXPECT_NEAR(TestUtils::GetPositionElement(sphereB, 0), xPreviousB, 1e-3f);
}
}
TEST_F(PhysicsComponentBusTest, AddImpulseAtWorldPoint_DynamicSphere_AffectsTrajectoryAndRotation)
{
EntityPtr sphereA = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, -5.0f, 0.0f), 0.5f);
EntityPtr sphereB = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, 5.0f, 0.0f), 0.5f);
AZ::Vector3 impulse(10.0f, 0.0f, 0.0f);
AZ::Vector3 worldPoint(0.0f, -5.0f, 0.25f);
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
&Physics::RigidBodyRequests::ApplyLinearImpulseAtWorldPoint, impulse, worldPoint);
AZ::Vector3 angularVelocityA = AZ::Vector3::CreateZero();
AZ::Vector3 angularVelocityB = AZ::Vector3::CreateZero();
for (int i = 1; i < 10; i++)
{
float xPreviousA = TestUtils::GetPositionElement(sphereA, 0);
float xPreviousB = TestUtils::GetPositionElement(sphereB, 0);
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 10);
EXPECT_GT(TestUtils::GetPositionElement(sphereA, 0), xPreviousA);
EXPECT_NEAR(TestUtils::GetPositionElement(sphereB, 0), xPreviousB, 1e-3f);
Physics::RigidBodyRequestBus::EventResult(angularVelocityA, sphereA->GetId(),
&Physics::RigidBodyRequests::GetAngularVelocity);
Physics::RigidBodyRequestBus::EventResult(angularVelocityB, sphereB->GetId(),
&Physics::RigidBodyRequests::GetAngularVelocity);
EXPECT_FALSE(angularVelocityA.IsClose(AZ::Vector3::CreateZero()));
EXPECT_NEAR(static_cast<float>(angularVelocityA.GetX()), 0.0f, 1e-3f);
EXPECT_NEAR(static_cast<float>(angularVelocityA.GetZ()), 0.0f, 1e-3f);
EXPECT_TRUE(angularVelocityB.IsClose(AZ::Vector3::CreateZero()));
}
}
TEST_F(PhysicsComponentBusTest, AddAngularImpulse_DynamicSphere_AffectsRotation)
{
EntityPtr sphereA = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, -5.0f, 0.0f), 0.5f);
EntityPtr sphereB = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, 5.0f, 0.0f), 0.5f);
AZ::Vector3 angularImpulse(0.0f, 10.0f, 0.0f);
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
&Physics::RigidBodyRequests::ApplyAngularImpulse, angularImpulse);
for (int i = 1; i < 10; i++)
{
float xPreviousA = TestUtils::GetPositionElement(sphereA, 0);
float xPreviousB = TestUtils::GetPositionElement(sphereB, 0);
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 10);
EXPECT_NEAR(TestUtils::GetPositionElement(sphereA, 0), xPreviousA, 1e-3f);
EXPECT_NEAR(TestUtils::GetPositionElement(sphereB, 0), xPreviousB, 1e-3f);
AZ::Vector3 angularVelocityA = AZ::Vector3::CreateZero();
AZ::Vector3 angularVelocityB = AZ::Vector3::CreateZero();
Physics::RigidBodyRequestBus::EventResult(angularVelocityA, sphereA->GetId(),
&Physics::RigidBodyRequests::GetAngularVelocity);
Physics::RigidBodyRequestBus::EventResult(angularVelocityB, sphereB->GetId(),
&Physics::RigidBodyRequests::GetAngularVelocity);
EXPECT_FALSE(angularVelocityA.IsClose(AZ::Vector3::CreateZero()));
EXPECT_NEAR(static_cast<float>(angularVelocityA.GetX()), 0.0f, 1e-3f);
EXPECT_NEAR(static_cast<float>(angularVelocityA.GetZ()), 0.0f, 1e-3f);
EXPECT_TRUE(angularVelocityB.IsClose(AZ::Vector3::CreateZero()));
}
}
TEST_F(PhysicsComponentBusTest, SetAngularVelocity_DynamicSphere_AffectsRotation)
{
EntityPtr sphereA = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, -5.0f, 0.0f), 0.5f);
EntityPtr sphereB = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, 5.0f, 0.0f), 0.5f);
AZ::Vector3 angularVelocity(0.0f, 10.0f, 0.0f);
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
&Physics::RigidBodyRequests::SetAngularVelocity, angularVelocity);
for (int i = 1; i < 10; i++)
{
float xPreviousA = TestUtils::GetPositionElement(sphereA, 0);
float xPreviousB = TestUtils::GetPositionElement(sphereB, 0);
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 10);
EXPECT_NEAR(TestUtils::GetPositionElement(sphereA, 0), xPreviousA, 1e-3f);
EXPECT_NEAR(TestUtils::GetPositionElement(sphereB, 0), xPreviousB, 1e-3f);
AZ::Vector3 angularVelocityA = AZ::Vector3::CreateZero();
AZ::Vector3 angularVelocityB = AZ::Vector3::CreateZero();
Physics::RigidBodyRequestBus::EventResult(angularVelocityA, sphereA->GetId(),
&Physics::RigidBodyRequests::GetAngularVelocity);
Physics::RigidBodyRequestBus::EventResult(angularVelocityB, sphereB->GetId(),
&Physics::RigidBodyRequests::GetAngularVelocity);
EXPECT_FALSE(angularVelocityA.IsClose(AZ::Vector3::CreateZero()));
EXPECT_NEAR(static_cast<float>(angularVelocityA.GetX()), 0.0f, 1e-3f);
EXPECT_NEAR(static_cast<float>(angularVelocityA.GetZ()), 0.0f, 1e-3f);
EXPECT_TRUE(angularVelocityB.IsClose(AZ::Vector3::CreateZero()));
}
}
TEST_F(PhysicsComponentBusTest, GetLinearVelocity_FallingSphere_VelocityIncreasesOverTime)
{
EntityPtr sphere = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, 0.0f, 0.0f), 0.5f);
Physics::RigidBodyRequestBus::Event(sphere->GetId(),
&Physics::RigidBodyRequests::SetLinearDamping, 0.0f);
float previousSpeed = 0.0f;
for (int timestep = 0; timestep < 60; timestep++)
{
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 1);
AZ::Vector3 velocity;
Physics::RigidBodyRequestBus::EventResult(velocity, sphere->GetId(),
&Physics::RigidBodyRequests::GetLinearVelocity);
float speed = velocity.GetLength();
EXPECT_GT(speed, previousSpeed);
previousSpeed = speed;
}
}
TEST_F(PhysicsComponentBusTest, SetSleepThreshold_RollingSpheres_LowerThresholdSphereTravelsFurther)
{
EntityPtr sphereA = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, -5.0f, 1.0f), 0.5f);
EntityPtr sphereB = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, 5.0f, 1.0f), 0.5f);
EntityPtr floor = TestUtils::CreateStaticBoxEntity(m_testSceneHandle, AZ::Vector3::CreateZero(), AZ::Vector3(100.0f, 100.0f, 1.0f));
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
&Physics::RigidBodyRequests::SetAngularDamping, 0.75f);
Physics::RigidBodyRequestBus::Event(sphereB->GetId(),
&Physics::RigidBodyRequests::SetAngularDamping, 0.75f);
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
&Physics::RigidBodyRequests::SetSleepThreshold, 1.0f);
Physics::RigidBodyRequestBus::Event(sphereB->GetId(),
&Physics::RigidBodyRequests::SetSleepThreshold, 0.5f);
float sleepThresholdA, sleepThresholdB;
Physics::RigidBodyRequestBus::EventResult(sleepThresholdA, sphereA->GetId(),
&Physics::RigidBodyRequests::GetSleepThreshold);
Physics::RigidBodyRequestBus::EventResult(sleepThresholdB, sphereB->GetId(),
&Physics::RigidBodyRequests::GetSleepThreshold);
EXPECT_NEAR(sleepThresholdA, 1.0f, 1e-3f);
EXPECT_NEAR(sleepThresholdB, 0.5f, 1e-3f);
AZ::Vector3 impulse(0.0f, 0.1f, 0.0f);
Physics::RigidBodyRequestBus::Event(sphereA->GetId(),
&Physics::RigidBodyRequests::ApplyAngularImpulse, impulse);
Physics::RigidBodyRequestBus::Event(sphereB->GetId(),
&Physics::RigidBodyRequests::ApplyAngularImpulse, impulse);
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 300);
EXPECT_GT(TestUtils::GetPositionElement(sphereB, 0), TestUtils::GetPositionElement(sphereA, 0));
}
TEST_F(PhysicsComponentBusTest, SetSleepThresholdNegative_DynamicSphere_NegativeValueRejected)
{
UnitTest::ErrorHandler errorHandler("Negative sleep threshold value");
EntityPtr sphere = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, -5.0f, 1.0f), 0.5f);
float threshold = 0.0f, initialThreshold = 0.0f;
Physics::RigidBodyRequestBus::EventResult(initialThreshold, sphere->GetId(),
&Physics::RigidBodyRequests::GetSleepThreshold);
Physics::RigidBodyRequestBus::Event(sphere->GetId(),
&Physics::RigidBodyRequests::SetSleepThreshold, -0.5f);
Physics::RigidBodyRequestBus::EventResult(threshold, sphere->GetId(),
&Physics::RigidBodyRequests::GetSleepThreshold);
EXPECT_NEAR(threshold, initialThreshold, 1e-3f);
EXPECT_TRUE(errorHandler.GetWarningCount() > 0);
}
TEST_F(PhysicsComponentBusTest, SetMass_Seesaw_TipsDownAtHeavierEnd)
{
EntityPtr floor = TestUtils::CreateStaticBoxEntity(m_testSceneHandle, AZ::Vector3::CreateZero(), AZ::Vector3(100.0f, 100.0f, 1.0f));
EntityPtr pivot = TestUtils::CreateStaticBoxEntity(m_testSceneHandle, AZ::Vector3(0.0f, 0.0f, 0.7f), AZ::Vector3(0.4f, 1.0f, 0.4f));
EntityPtr seesaw = TestUtils::CreateBoxEntity(m_testSceneHandle, AZ::Vector3(0.0f, 0.0f, 0.95f), AZ::Vector3(20.0f, 1.0f, 0.1f));
EntityPtr boxA = TestUtils::CreateBoxEntity(m_testSceneHandle, AZ::Vector3(-9.0f, 0.0f, 1.5f), AZ::Vector3::CreateOne());
EntityPtr boxB = TestUtils::CreateBoxEntity(m_testSceneHandle, AZ::Vector3(9.0f, 0.0f, 1.5f), AZ::Vector3::CreateOne());
Physics::RigidBodyRequestBus::Event(boxA->GetId(), &Physics::RigidBodyRequests::SetMass, 5.0f);
float mass = 0.0f;
Physics::RigidBodyRequestBus::EventResult(mass, boxA->GetId(),
&Physics::RigidBodyRequests::GetMass);
EXPECT_NEAR(mass, 5.0f, 1e-3f);
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 30);
EXPECT_GT(1.5f, TestUtils::GetPositionElement(boxA, 2));
EXPECT_LT(1.5f, TestUtils::GetPositionElement(boxB, 2));
Physics::RigidBodyRequestBus::Event(boxB->GetId(), &Physics::RigidBodyRequests::SetMass, 20.0f);
Physics::RigidBodyRequestBus::EventResult(mass, boxB->GetId(),
&Physics::RigidBodyRequests::GetMass);
EXPECT_NEAR(mass, 20.0f, 1e-3f);
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 60);
EXPECT_LT(1.5f, TestUtils::GetPositionElement(boxA, 2));
EXPECT_GT(1.5f, TestUtils::GetPositionElement(boxB, 2));
}
TEST_F(PhysicsComponentBusTest, GetAabb_Sphere_ValidExtents)
{
AZ::Vector3 spherePosition(2.0f, -3.0f, 1.0f);
EntityPtr sphere = TestUtils::CreateSphereEntity(m_testSceneHandle, spherePosition, 0.5f);
AZ::Aabb sphereAabb;
Physics::RigidBodyRequestBus::EventResult(sphereAabb, sphere->GetId(),
&Physics::RigidBodyRequests::GetAabb);
EXPECT_TRUE(sphereAabb.GetMin().IsClose(spherePosition - 0.5f * AZ::Vector3::CreateOne()));
EXPECT_TRUE(sphereAabb.GetMax().IsClose(spherePosition + 0.5f * AZ::Vector3::CreateOne()));
// rotate the sphere and check the bounding box is still correct
AZ::Quaternion quat = AZ::Quaternion::CreateRotationZ(0.25f * AZ::Constants::Pi);
AZ::TransformBus::Event(sphere->GetId(), &AZ::TransformInterface::SetWorldTM,
AZ::Transform::CreateFromQuaternionAndTranslation(quat, spherePosition));
sphere->Deactivate();
sphere->Activate();
Physics::RigidBodyRequestBus::EventResult(sphereAabb, sphere->GetId(),
&Physics::RigidBodyRequests::GetAabb);
EXPECT_TRUE(sphereAabb.GetMin().IsClose(spherePosition - 0.5f * AZ::Vector3::CreateOne()));
EXPECT_TRUE(sphereAabb.GetMax().IsClose(spherePosition + 0.5f * AZ::Vector3::CreateOne()));
}
TEST_F(PhysicsComponentBusTest, GetAabb_Box_ValidExtents)
{
AZ::Vector3 boxPosition(2.0f, -3.0f, 1.0f);
AZ::Vector3 boxDimensions(3.0f, 4.0f, 5.0f);
EntityPtr box = TestUtils::CreateBoxEntity(m_testSceneHandle, boxPosition, boxDimensions);
AZ::Aabb boxAabb;
Physics::RigidBodyRequestBus::EventResult(boxAabb, box->GetId(),
&Physics::RigidBodyRequests::GetAabb);
EXPECT_TRUE(boxAabb.GetMin().IsClose(boxPosition - 0.5f * boxDimensions));
EXPECT_TRUE(boxAabb.GetMax().IsClose(boxPosition + 0.5f * boxDimensions));
// rotate the box and check the bounding box is still correct
AZ::Quaternion quat = AZ::Quaternion::CreateRotationZ(0.25f * AZ::Constants::Pi);
AZ::TransformBus::Event(box->GetId(), &AZ::TransformInterface::SetWorldTM,
AZ::Transform::CreateFromQuaternionAndTranslation(quat, boxPosition));
box->Deactivate();
box->Activate();
Physics::RigidBodyRequestBus::EventResult(boxAabb, box->GetId(),
&Physics::RigidBodyRequests::GetAabb);
AZ::Vector3 expectedRotatedDimensions(3.5f * sqrtf(2.0f), 3.5f * sqrtf(2.0f), 5.0f);
EXPECT_TRUE(boxAabb.GetMin().IsClose(boxPosition - 0.5f * expectedRotatedDimensions));
EXPECT_TRUE(boxAabb.GetMax().IsClose(boxPosition + 0.5f * expectedRotatedDimensions));
}
TEST_F(PhysicsComponentBusTest, GetAabb_Capsule_ValidExtents)
{
AZ::Vector3 capsulePosition(1.0f, -3.0f, 5.0f);
float capsuleHeight = 2.0f;
float capsuleRadius = 0.3f;
EntityPtr capsule = TestUtils::CreateCapsuleEntity(m_testSceneHandle, capsulePosition, capsuleHeight, capsuleRadius);
AZ::Aabb capsuleAabb;
Physics::RigidBodyRequestBus::EventResult(capsuleAabb, capsule->GetId(),
&Physics::RigidBodyRequests::GetAabb);
AZ::Vector3 expectedCapsuleHalfExtents(capsuleRadius, capsuleRadius, 0.5f * capsuleHeight);
EXPECT_TRUE(capsuleAabb.GetMin().IsClose(capsulePosition - expectedCapsuleHalfExtents));
EXPECT_TRUE(capsuleAabb.GetMax().IsClose(capsulePosition + expectedCapsuleHalfExtents));
// rotate the capsule and check the bounding box is still correct
AZ::Quaternion quat = AZ::Quaternion::CreateRotationY(0.25f * AZ::Constants::Pi);
AZ::TransformBus::Event(capsule->GetId(), &AZ::TransformInterface::SetWorldTM,
AZ::Transform::CreateFromQuaternionAndTranslation(quat, capsulePosition));
capsule->Deactivate();
capsule->Activate();
Physics::RigidBodyRequestBus::EventResult(capsuleAabb, capsule->GetId(),
&Physics::RigidBodyRequests::GetAabb);
float rotatedHalfHeight = 0.25f * sqrtf(2.0f) * capsuleHeight + (1.0f - 0.5f * sqrt(2.0f)) * capsuleRadius;
expectedCapsuleHalfExtents = AZ::Vector3(rotatedHalfHeight, capsuleRadius, rotatedHalfHeight);
EXPECT_TRUE(capsuleAabb.GetMin().IsClose(capsulePosition - expectedCapsuleHalfExtents));
EXPECT_TRUE(capsuleAabb.GetMax().IsClose(capsulePosition + expectedCapsuleHalfExtents));
}
TEST_F(PhysicsComponentBusTest, ForceAwakeForceAsleep_DynamicSphere_SleepStateCorrect)
{
EntityPtr floor = TestUtils::CreateStaticBoxEntity(m_testSceneHandle, AZ::Vector3::CreateZero(), AZ::Vector3(100.0f, 100.0f, 1.0f));
EntityPtr boxA = TestUtils::CreateBoxEntity(m_testSceneHandle, AZ::Vector3(-5.0f, 0.0f, 1.0f), AZ::Vector3::CreateOne());
EntityPtr boxB = TestUtils::CreateBoxEntity(m_testSceneHandle, AZ::Vector3(5.0f, 0.0f, 100.0f), AZ::Vector3::CreateOne());
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 60);
bool isAwakeA = false;
bool isAwakeB = false;
Physics::RigidBodyRequestBus::EventResult(isAwakeA, boxA->GetId(),
&Physics::RigidBodyRequests::IsAwake);
Physics::RigidBodyRequestBus::EventResult(isAwakeB, boxB->GetId(),
&Physics::RigidBodyRequests::IsAwake);
EXPECT_FALSE(isAwakeA);
EXPECT_TRUE(isAwakeB);
Physics::RigidBodyRequestBus::Event(boxA->GetId(), &Physics::RigidBodyRequests::ForceAwake);
Physics::RigidBodyRequestBus::Event(boxB->GetId(), &Physics::RigidBodyRequests::ForceAsleep);
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 1);
Physics::RigidBodyRequestBus::EventResult(isAwakeA, boxA->GetId(),
&Physics::RigidBodyRequests::IsAwake);
Physics::RigidBodyRequestBus::EventResult(isAwakeB, boxB->GetId(),
&Physics::RigidBodyRequests::IsAwake);
EXPECT_TRUE(isAwakeA);
EXPECT_FALSE(isAwakeB);
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 60);
Physics::RigidBodyRequestBus::EventResult(isAwakeA, boxA->GetId(),
&Physics::RigidBodyRequests::IsAwake);
Physics::RigidBodyRequestBus::EventResult(isAwakeB, boxB->GetId(),
&Physics::RigidBodyRequests::IsAwake);
EXPECT_FALSE(isAwakeA);
EXPECT_FALSE(isAwakeB);
}
TEST_F(PhysicsComponentBusTest, DisableEnablePhysics_DynamicSphere)
{
using namespace AzFramework;
EntityPtr sphere = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f, 0.0f, 0.0f), 0.5f);
Physics::RigidBodyRequestBus::Event(sphere->GetId(), &Physics::RigidBodyRequests::SetLinearDamping, 0.0f);
AZ::Vector3 velocity;
float previousSpeed = 0.0f;
for (int timestep = 0; timestep < 30; timestep++)
{
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 1);
Physics::RigidBodyRequestBus::EventResult(velocity, sphere->GetId(), &Physics::RigidBodyRequests::GetLinearVelocity);
previousSpeed = velocity.GetLength();
}
// Disable physics
Physics::RigidBodyRequestBus::Event(sphere->GetId(), &Physics::RigidBodyRequests::DisablePhysics);
// Check speed is not changing
for (int timestep = 0; timestep < 60; timestep++)
{
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 1);
Physics::RigidBodyRequestBus::EventResult(velocity, sphere->GetId(), &Physics::RigidBodyRequests::GetLinearVelocity);
float speed = velocity.GetLength();
EXPECT_FLOAT_EQ(speed, previousSpeed);
previousSpeed = speed;
}
// Check physics is disabled
bool physicsEnabled = true;
Physics::RigidBodyRequestBus::EventResult(physicsEnabled, sphere->GetId(), &Physics::RigidBodyRequests::IsPhysicsEnabled);
EXPECT_FALSE(physicsEnabled);
// Enable physics
Physics::RigidBodyRequestBus::Event(sphere->GetId(), &Physics::RigidBodyRequests::EnablePhysics);
// Check speed is increasing
for (int timestep = 0; timestep < 60; timestep++)
{
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 1);
Physics::RigidBodyRequestBus::EventResult(velocity, sphere->GetId(), &Physics::RigidBodyRequests::GetLinearVelocity);
float speed = velocity.GetLength();
EXPECT_GT(speed, previousSpeed);
previousSpeed = speed;
}
}
TEST_F(PhysicsComponentBusTest, Shape_Box_GetAabbIsCorrect)
{
Physics::ColliderConfiguration colliderConfig;
Physics::BoxShapeConfiguration shapeConfiguration;
shapeConfiguration.m_dimensions = AZ::Vector3(20.f, 20.f, 20.f);
AZStd::shared_ptr<Physics::Shape> shape;
Physics::SystemRequestBus::BroadcastResult(shape, &Physics::SystemRequests::CreateShape, colliderConfig, shapeConfiguration);
const AZ::Aabb localAabb = shape->GetAabbLocal();
EXPECT_TRUE(localAabb.GetMin().IsClose(-shapeConfiguration.m_dimensions / 2.f)
&& localAabb.GetMax().IsClose(shapeConfiguration.m_dimensions / 2.f));
AZ::Vector3 worldOffset = AZ::Vector3(0, 0, 40.f);
AZ::Transform worldTransform = AZ::Transform::Identity();
worldTransform.SetTranslation(worldOffset);
const AZ::Aabb worldAabb = shape->GetAabb(worldTransform);
EXPECT_TRUE(worldAabb.GetMin().IsClose((-shapeConfiguration.m_dimensions / 2.f) + worldOffset)
&& worldAabb.GetMax().IsClose((shapeConfiguration.m_dimensions / 2.f) + worldOffset));
}
TEST_F(PhysicsComponentBusTest, Shape_Sphere_GetAabbIsCorrect)
{
const float radius = 20.f;
Physics::ColliderConfiguration colliderConfig;
Physics::SphereShapeConfiguration shapeConfiguration;
shapeConfiguration.m_radius = radius;
AZStd::shared_ptr<Physics::Shape> shape;
Physics::SystemRequestBus::BroadcastResult(shape, &Physics::SystemRequests::CreateShape, colliderConfig, shapeConfiguration);
const AZ::Aabb localAabb = shape->GetAabbLocal();
EXPECT_TRUE(localAabb.GetMin().IsClose(AZ::Vector3(-radius, -radius, -radius))
&& localAabb.GetMax().IsClose(AZ::Vector3(radius, radius, radius)));
AZ::Vector3 worldOffset = AZ::Vector3(0, 0, 40.f);
AZ::Transform worldTransform = AZ::Transform::Identity();
worldTransform.SetTranslation(worldOffset);
const AZ::Aabb worldAabb = shape->GetAabb(worldTransform);
EXPECT_TRUE(worldAabb.GetMin().IsClose(AZ::Vector3(-radius, -radius, -radius) + worldOffset)
&& worldAabb.GetMax().IsClose(AZ::Vector3(radius, radius, radius) + worldOffset));
}
TEST_F(PhysicsComponentBusTest, Shape_Capsule_GetAabbIsCorrect)
{
const float radius = 20.f;
const float height = 80.f;
Physics::ColliderConfiguration colliderConfig;
Physics::CapsuleShapeConfiguration shapeConfiguration;
shapeConfiguration.m_radius = radius;
shapeConfiguration.m_height = height;
AZStd::shared_ptr<Physics::Shape> shape;
Physics::SystemRequestBus::BroadcastResult(shape, &Physics::SystemRequests::CreateShape, colliderConfig, shapeConfiguration);
const AZ::Aabb localAabb = shape->GetAabbLocal();
EXPECT_TRUE(localAabb.GetMin().IsClose(AZ::Vector3(-radius, -radius, -height / 2.f))
&& localAabb.GetMax().IsClose(AZ::Vector3(radius, radius, height / 2.f)));
AZ::Vector3 worldOffset = AZ::Vector3(0, 0, 40.f);
AZ::Transform worldTransform = AZ::Transform::Identity();
worldTransform.SetTranslation(worldOffset);
const AZ::Aabb worldAabb = shape->GetAabb(worldTransform);
EXPECT_TRUE(worldAabb.GetMin().IsClose(AZ::Vector3(-radius, -radius, -height / 2.f) + worldOffset)
&& worldAabb.GetMax().IsClose(AZ::Vector3(radius, radius, height / 2.f) + worldOffset));
}
TEST_F(PhysicsComponentBusTest, WorldBodyBus_RigidBodyColliders_AABBAreCorrect)
{
// Create 3 colliders, one of each type and check that the AABB of their body is the expected
EntityPtr box = TestUtils::CreateBoxEntity(m_testSceneHandle, AZ::Vector3(0, 0, 0), AZ::Vector3(32, 32, 32));
AZ::Aabb boxAABB;
AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(boxAABB, box->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetAabb);
EXPECT_TRUE(boxAABB.GetMin().IsClose(AZ::Vector3(-16, -16, -16)) && boxAABB.GetMax().IsClose(AZ::Vector3(16, 16, 16)));
EntityPtr sphere = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(-100, 0, 0), 16);
AZ::Aabb sphereAABB;
AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(sphereAABB, sphere->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetAabb);
EXPECT_TRUE(sphereAABB.GetMin().IsClose(AZ::Vector3(-16 -100, -16, -16)) && sphereAABB.GetMax().IsClose(AZ::Vector3(16 -100, 16, 16)));
EntityPtr capsule = TestUtils::CreateCapsuleEntity(m_testSceneHandle, AZ::Vector3(100, 0, 0), 128, 16);
AZ::Aabb capsuleAABB;
AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(capsuleAABB, capsule->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetAabb);
EXPECT_TRUE(capsuleAABB.GetMin().IsClose(AZ::Vector3(-16 +100, -16, -64)) && capsuleAABB.GetMax().IsClose(AZ::Vector3(16 +100, 16, 64)));
}
TEST_F(PhysicsComponentBusTest, WorldBodyBus_StaticRigidBodyColliders_AABBAreCorrect)
{
// Create 3 colliders, one of each type and check that the AABB of their body is the expected
EntityPtr box = TestUtils::CreateStaticBoxEntity(m_testSceneHandle, AZ::Vector3(0, 0, 0), AZ::Vector3(32, 32, 32));
AZ::Aabb boxAABB;
AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(boxAABB, box->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetAabb);
EXPECT_TRUE(boxAABB.GetMin().IsClose(AZ::Vector3(-16, -16, -16)) && boxAABB.GetMax().IsClose(AZ::Vector3(16, 16, 16)));
EntityPtr sphere = TestUtils::CreateStaticSphereEntity(m_testSceneHandle, AZ::Vector3(-100, 0, 0), 16);
AZ::Aabb sphereAABB;
AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(sphereAABB, sphere->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetAabb);
EXPECT_TRUE(sphereAABB.GetMin().IsClose(AZ::Vector3(-16 -100, -16, -16)) && sphereAABB.GetMax().IsClose(AZ::Vector3(16 -100, 16, 16)));
EntityPtr capsule = TestUtils::CreateStaticCapsuleEntity(m_testSceneHandle, AZ::Vector3(100, 0, 0), 128, 16);
AZ::Aabb capsuleAABB;
AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(capsuleAABB, capsule->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetAabb);
EXPECT_TRUE(capsuleAABB.GetMin().IsClose(AZ::Vector3(-16 +100, -16, -64)) && capsuleAABB.GetMax().IsClose(AZ::Vector3(16 +100, 16, 64)));
}
using CreateEntityFunc = AZStd::function<EntityPtr(const AZ::Vector3&)>;
void CheckDisableEnablePhysics(AZStd::vector<CreateEntityFunc> entityCreations, AzPhysics::SceneHandle sceneHandle)
{
auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
int i = 0;
for (CreateEntityFunc& entityCreation : entityCreations)
{
AZ::Vector3 entityPos = AZ::Vector3(128.f * i, 0, 0);
EntityPtr entity = entityCreation(entityPos);
AzPhysics::RayCastRequest request;
request.m_start = entityPos + AZ::Vector3(0, 0, 100);
request.m_direction = AZ::Vector3(0, 0, -1);
request.m_distance = 200.f;
AzPhysics::SimulatedBodyComponentRequestsBus::Event(entity->GetId(), &AzPhysics::SimulatedBodyComponentRequests::DisablePhysics);
bool enabled = true;
AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(enabled, entity->GetId(), &AzPhysics::SimulatedBodyComponentRequests::IsPhysicsEnabled);
EXPECT_FALSE(enabled);
AzPhysics::SceneQueryHits result = sceneInterface->QueryScene(sceneHandle, &request);
EXPECT_FALSE(result);
AzPhysics::SimulatedBodyComponentRequestsBus::Event(entity->GetId(), &AzPhysics::SimulatedBodyComponentRequests::EnablePhysics);
enabled = false;
AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(enabled, entity->GetId(), &AzPhysics::SimulatedBodyComponentRequests::IsPhysicsEnabled);
EXPECT_TRUE(enabled);
result = sceneInterface->QueryScene(sceneHandle, &request);
EXPECT_TRUE(result);
EXPECT_TRUE(result.m_hits.size() == 1);
EXPECT_TRUE(result.m_hits[0].m_entityId == entity->GetId());
++i;
}
}
TEST_F(PhysicsComponentBusTest, WorldBodyBus_EnableDisablePhysics_StaticRigidBody)
{
AZStd::vector<CreateEntityFunc> entityCreations =
{
[this](const AZ::Vector3& position) { return TestUtils::CreateStaticBoxEntity(m_testSceneHandle, position, AZ::Vector3(32, 32, 32)); },
[this](const AZ::Vector3& position) { return TestUtils::CreateStaticSphereEntity(m_testSceneHandle, position, 16); },
[this](const AZ::Vector3& position) { return TestUtils::CreateStaticCapsuleEntity(m_testSceneHandle, position, 16, 16); }
};
CheckDisableEnablePhysics(entityCreations, m_testSceneHandle);
}
TEST_F(PhysicsComponentBusTest, WorldBodyBus_EnableDisablePhysics_RigidBody)
{
AZStd::vector<CreateEntityFunc> entityCreations =
{
[this](const AZ::Vector3& position) { return TestUtils::CreateBoxEntity(m_testSceneHandle, position, AZ::Vector3(32, 32, 32)); },
[this](const AZ::Vector3& position) { return TestUtils::CreateSphereEntity(m_testSceneHandle, position, 16); },
[this](const AZ::Vector3& position) { return TestUtils::CreateCapsuleEntity(m_testSceneHandle, position, 16, 16); }
};
CheckDisableEnablePhysics(entityCreations, m_testSceneHandle);
}
TEST_F(PhysicsComponentBusTest, WorldBodyRayCast_CastAgainstStaticBox_ReturnsHit)
{
EntityPtr staticBoxEntity = TestUtils::CreateStaticBoxEntity(m_testSceneHandle, AZ::Vector3(0.0f), AZ::Vector3(10.f, 10.f, 10.f));
AzPhysics::RayCastRequest request;
request.m_start = AZ::Vector3(-100.0f, 0.0f, 0.0f);
request.m_direction = AZ::Vector3(1.0f, 0.0f, 0.0f);
request.m_distance = 200.0f;
AzPhysics::SceneQueryHit hit;
AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(hit, staticBoxEntity->GetId(), &AzPhysics::SimulatedBodyComponentRequests::RayCast, request);
EXPECT_TRUE(hit);
bool hitIncludeSphereEntity = (hit.m_entityId == staticBoxEntity->GetId());
EXPECT_TRUE(hitIncludeSphereEntity);
}
using RayCastFunc = AZStd::function<AzPhysics::SceneQueryHit(AZ::EntityId, const AzPhysics::RayCastRequest&)>;
class PhysicsRigidBodyRayBusTest
: public GenericPhysicsInterfaceTest
, public ::testing::WithParamInterface<RayCastFunc>
{
};
TEST_P(PhysicsRigidBodyRayBusTest, ComponentRayCast_CastAgainstNothing_ReturnsNoHit)
{
AzPhysics::RayCastRequest request;
request.m_start = AZ::Vector3(-100.0f, 0.0f, 0.0f);
request.m_direction = AZ::Vector3(1.0f, 0.0f, 0.0f);
request.m_distance = 200.0f;
auto rayCastFunction = GetParam();
AzPhysics::SceneQueryHit hit = rayCastFunction(AZ::EntityId(), request);
EXPECT_FALSE(hit);
}
TEST_P(PhysicsRigidBodyRayBusTest, ComponentRayCast_CastAgainstSphere_ReturnsHit)
{
EntityPtr sphereEntity = TestUtils::CreateSphereEntity(m_testSceneHandle, AZ::Vector3(0.0f), 10.0f);
AzPhysics::RayCastRequest request;
request.m_start = AZ::Vector3(-100.0f, 0.0f, 0.0f);
request.m_direction = AZ::Vector3(1.0f, 0.0f, 0.0f);
request.m_distance = 200.0f;
auto rayCastFunction = GetParam();
AzPhysics::SceneQueryHit hit = rayCastFunction(sphereEntity->GetId(), request);
EXPECT_TRUE(hit);
bool hitsIncludeSphereEntity = (hit.m_entityId == sphereEntity->GetId());
EXPECT_TRUE(hitsIncludeSphereEntity);
}
TEST_P(PhysicsRigidBodyRayBusTest, ComponentRayCast_CastAgainstBoxEntityWithLocalOffset_ReturnsHit)
{
const AZ::Vector3 boxExtent = AZ::Vector3(10.0f, 10.0f, 10.0f);
const AZ::Vector3 box1Offset(0.0f, 0.0f, 30.0f);
const AZ::Vector3 box2Offset(0.0f, 0.0f, -30.0f);
MultiShapeConfig config;
config.m_position = AZ::Vector3(0.0f, 100.0f, 20.0f);
config.m_shapes.AddBox(boxExtent, box1Offset);
config.m_shapes.AddBox(boxExtent, box2Offset);
auto shapeWithTwoBoxes = AddMultiShapeEntity(config);
AzPhysics::RayCastRequest request;
request.m_start = AZ::Vector3(-100.0f, 100.0f, 50.0f);
request.m_direction = AZ::Vector3(1.0f, 0.0f, 0.0f);
request.m_distance = 200.0f;
auto rayCastFunction = GetParam();
AzPhysics::SceneQueryHit result = rayCastFunction(shapeWithTwoBoxes->GetId(), request);
EXPECT_TRUE(result);
bool hitIncludeEntity = (result.m_entityId == shapeWithTwoBoxes->GetId());
EXPECT_TRUE(hitIncludeEntity);
}
TEST_P(PhysicsRigidBodyRayBusTest, ComponentRayCast_CastAgainstBoxEntityWithMultipleShapesLocalOffset_ReturnsHits)
{
// Entity at (0, 100, 20) with two box childs with offsets +30 and -30 in Z.
// Child boxes world position centers are at (0, 100, 50) and (0, 100, -10)
// 4 rays tests that should retrieves the correct boxes
const AZ::Vector3 boxExtent = AZ::Vector3(10.0f, 10.0f, 10.0f);
const AZ::Vector3 box1Offset(0.0f, 0.0f, 30.0f);
const AZ::Vector3 box2Offset(0.0f, 0.0f, -30.0f);
MultiShapeConfig config;
config.m_position = AZ::Vector3(0.0f, 100.0f, 20.0f);
config.m_shapes.AddBox(boxExtent, box1Offset);
config.m_shapes.AddBox(boxExtent, box2Offset);
AZStd::unique_ptr<AZ::Entity> shapeWithTwoBoxes(AddMultiShapeEntity(config));
AZStd::vector<AZStd::shared_ptr<Physics::Shape>> shapes;
PhysX::ColliderComponentRequestBus::EventResult(shapes, shapeWithTwoBoxes->GetId(), &PhysX::ColliderComponentRequests::GetShapes);
// Upper box part z=50 (-x to +x)
{
AzPhysics::RayCastRequest request;
request.m_start = AZ::Vector3(-100.0f, 100.0f, 50.0f);
request.m_direction = AZ::Vector3(1.0f, 0.0f, 0.0f);
request.m_distance = 200.0f;
auto rayCastFunction = GetParam();
AzPhysics::SceneQueryHit result = rayCastFunction(shapeWithTwoBoxes->GetId(), request);
EXPECT_TRUE(result);
bool hitIncludesEntity = (result.m_entityId == shapeWithTwoBoxes->GetId());
EXPECT_TRUE(hitIncludesEntity);
bool hitIncludesShape = (result.m_shape == shapes[0].get());
EXPECT_TRUE(hitIncludesShape);
}
// Lower box part z=-10 (-x to +x)
{
AzPhysics::RayCastRequest request;
request.m_start = AZ::Vector3(-100.0f, 100.0f, -10.0f);
request.m_direction = AZ::Vector3(1.0f, 0.0f, 0.0f);
request.m_distance = 200.0f;
auto rayCastFunction = GetParam();
AzPhysics::SceneQueryHit result = rayCastFunction(shapeWithTwoBoxes->GetId(), request);
EXPECT_TRUE(result);
bool hitIncludesEntity = (result.m_entityId == shapeWithTwoBoxes->GetId());
EXPECT_TRUE(hitIncludesEntity);
bool hitIncludesShape = (result.m_shape == shapes[1].get());
EXPECT_TRUE(hitIncludesShape);
}
// Trace Vertically from top, it should retrieve the upper box shape
{
AzPhysics::RayCastRequest request;
request.m_start = AZ::Vector3(0.0f, 100.0f, 80.0f);
request.m_direction = AZ::Vector3(0.0f, 0.0f, -1.0f);
request.m_distance = 200.0f;
auto rayCastFunction = GetParam();
AzPhysics::SceneQueryHit result = rayCastFunction(shapeWithTwoBoxes->GetId(), request);
EXPECT_TRUE(result);
bool hitIncludesEntity = (result.m_entityId == shapeWithTwoBoxes->GetId());
EXPECT_TRUE(hitIncludesEntity);
bool hitIncludesShape = (result.m_shape == shapes[0].get());
EXPECT_TRUE(hitIncludesShape);
}
// Trace Vertically from bottom, it should retrieve the lower box shape
{
AzPhysics::RayCastRequest request;
request.m_start = AZ::Vector3(0.0f, 100.0f, -80.0f);
request.m_direction = AZ::Vector3(0.0f, 0.0f, 1.0f);
request.m_distance = 200.0f;
auto rayCastFunction = GetParam();
AzPhysics::SceneQueryHit result = rayCastFunction(shapeWithTwoBoxes->GetId(), request);
EXPECT_TRUE(result);
bool hitIncludesEntity = (result.m_entityId == shapeWithTwoBoxes->GetId());
EXPECT_TRUE(hitIncludesEntity);
bool hitIncludesShape = (result.m_shape == shapes[1].get());
EXPECT_TRUE(hitIncludesShape);
}
}
TEST_P(PhysicsRigidBodyRayBusTest, ComponentRayCast_CastAgainstBoxEntityLocalOffsetAndRotation_ReturnsHits)
{
// Entity at (0,0,0) rotated by 90 degrees and child box offset (0,100,0).
// World position of the child should be (-100, 0, 0).
// This tests raycasts from (0, 0, 0) to (-200, 0 ,0) checking that collides with the box
const AZ::Vector3 boxExtent = AZ::Vector3(10.0f, 10.0f, 10.0f);
const AZ::Vector3 boxOffset(0.0f, 100.0f, 0.0f);
MultiShapeConfig config;
config.m_position = AZ::Vector3(0.0f, 0.0f, 0.0f);
config.m_rotation = AZ::Vector3(0, 0, AZ::Constants::HalfPi);
config.m_shapes.AddBox(boxExtent, boxOffset);
AZStd::unique_ptr<AZ::Entity> shapeWithOneBox(AddMultiShapeEntity(config));
AZStd::vector<AZStd::shared_ptr<Physics::Shape>> shapes;
PhysX::ColliderComponentRequestBus::EventResult(shapes, shapeWithOneBox->GetId(), &PhysX::ColliderComponentRequests::GetShapes);
AzPhysics::RayCastRequest request;
request.m_start = AZ::Vector3(0.0f, 0.0f, 0.0f);
request.m_direction = AZ::Vector3(-1.0f, 0.0f, 0.0f);
request.m_distance = 200.0f;
auto rayCastFunction = GetParam();
AzPhysics::SceneQueryHit result = rayCastFunction(shapeWithOneBox->GetId(), request);
EXPECT_TRUE(result);
bool hitIncludesEntity = (result.m_entityId == shapeWithOneBox->GetId());
EXPECT_TRUE(hitIncludesEntity);
bool hitIncludesShape = (result.m_shape == shapes[0].get());
EXPECT_TRUE(hitIncludesShape);
}
static const RayCastFunc RigidBodyRaycastEBusCall = []([[maybe_unused]] AZ::EntityId entityId, [[maybe_unused]] const AzPhysics::RayCastRequest& request)
{
AzPhysics::SceneQueryHit ret;
Physics::RigidBodyRequestBus::EventResult(ret, entityId, &Physics::RigidBodyRequests::RayCast, request);
return ret;
};
static const RayCastFunc WorldBodyRaycastEBusCall = []([[maybe_unused]] AZ::EntityId entityId, [[maybe_unused]] const AzPhysics::RayCastRequest& request)
{
AzPhysics::SceneQueryHit ret;
AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(ret, entityId, &AzPhysics::SimulatedBodyComponentRequests::RayCast, request);
return ret;
};
INSTANTIATE_TEST_CASE_P(, PhysicsRigidBodyRayBusTest,
::testing::Values(RigidBodyRaycastEBusCall, WorldBodyRaycastEBusCall),
// Provide nice names for the tests runs
[](const testing::TestParamInfo<PhysicsRigidBodyRayBusTest::ParamType>& info)
{
const char* name = "";
switch (info.index)
{
case 0:
name = "RigidBodyRequestBus";
break;
case 1:
name = "WorldBodyRequestBus";
break;
}
return name;
});
} // namespace Physics