Files
o3de/Gems/PhysX/Code/Tests/PhysXJointsTest.cpp
T
Esteban Papp 1f9b284de2 Merge branch 'development' into cmake/SPEC-7179
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>

# Conflicts:
#	Code/Editor/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin_precompiled.h
#	Code/Editor/Plugins/EditorCommon/EditorCommon_precompiled.h
#	Code/Editor/Plugins/EditorCommon/stdafx.cpp
#	Code/Editor/Plugins/FFMPEGPlugin/FFMPEGPlugin_precompiled.h
#	Code/Editor/Plugins/PerforcePlugin/PerforcePlugin_precompiled.h
#	Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsTool_precompiled.h
#	Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFramework_precompiled.h
#	Code/Tools/AssetProcessor/native/precompiled.h
#	Code/Tools/Standalone/StandaloneTools_precompiled.h
#	Gems/AssetMemoryAnalyzer/Code/Source/AssetMemoryAnalyzer_precompiled.h
#	Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageProcessing_precompiled.h
#	Gems/Atom/RHI/DX12/Code/Source/RHI/Atom_RHI_DX12_precompiled.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/Mac/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/iOS/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Source/Atom_RHI_Metal_precompiled.h
#	Gems/Atom/RHI/Metal/Code/atom_rhi_metal_common_files.cmake
#	Gems/Atom/RHI/Null/Code/Source/Atom_RHI_Null_precompiled.h
#	Gems/Atom/RHI/Null/Code/atom_rhi_null_common_files.cmake
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Android/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Linux/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Mac/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Windows/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Source/Atom_RHI_Vulkan_precompiled.h
#	Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp
#	Gems/Atom/RHI/Vulkan/Code/atom_rhi_vulkan_common_files.cmake
#	Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont_precompiled.h
#	Gems/Blast/Code/Source/StdAfx.cpp
#	Gems/Camera/Code/Source/Camera_precompiled.h
#	Gems/EMotionFX/Code/Source/EMotionFX_precompiled.h
#	Gems/FastNoise/Code/Source/FastNoise_precompiled.h
#	Gems/Gestures/Code/Source/Gestures_precompiled.h
#	Gems/GradientSignal/Code/Source/GradientSignal_precompiled.h
#	Gems/GraphCanvas/Code/precompiled.h
#	Gems/ImGui/Code/Source/ImGui_precompiled.h
#	Gems/InAppPurchases/Code/Source/InAppPurchases_precompiled.h
#	Gems/LmbrCentral/Code/Source/LmbrCentral_precompiled.h
#	Gems/LmbrCentral/Code/Tests/ShapeGeometryUtilTest.cpp
#	Gems/LyShine/Code/Editor/UiCanvasEditor_precompiled.h
#	Gems/LyShine/Code/Source/Animation/LyShine_precompiled.h
#	Gems/LyShine/Code/Source/LyShine_precompiled.h
#	Gems/LyShineExamples/Code/Source/LyShineExamples_precompiled.h
#	Gems/Maestro/Code/Source/Cinematics/Maestro_precompiled.h
#	Gems/Maestro/Code/Source/Maestro_precompiled.h
#	Gems/MessagePopup/Code/Source/MessagePopup_precompiled.h
#	Gems/Metastream/Code/Source/Metastream_precompiled.h
#	Gems/Microphone/Code/Source/Microphone_precompiled.h
#	Gems/Multiplayer/Code/Source/Multiplayer_precompiled.h
#	Gems/PhysX/Code/NumericalMethods/Source/NumericalMethods_precompiled.h
#	Gems/PhysX/Code/Source/PhysXUnsupported_precompiled.h
#	Gems/PhysX/Code/Source/PhysX_precompiled.h
#	Gems/PhysX/Code/physx_unsupported_files.cmake
#	Gems/PhysXDebug/Code/Source/PhysXDebugUnsupported_precompiled.h
#	Gems/PhysXDebug/Code/Source/PhysXDebug_precompiled.h
#	Gems/ScriptCanvas/Code/Editor/precompiled.h
#	Gems/ScriptCanvas/Code/Source/precompiled.h
#	Gems/ScriptCanvasDeveloper/Code/Source/precompiled.h
#	Gems/ScriptCanvasPhysics/Code/Source/ScriptCanvasPhysics_precompiled.h
#	Gems/ScriptEvents/Code/Source/precompiled.h
#	Gems/ScriptEvents/Code/Tests/Editor/EditorTests.cpp
#	Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweener_precompiled.h
#	Gems/SliceFavorites/Code/Source/SliceFavorites_precompiled.h
#	Gems/StartingPointCamera/Code/Source/StartingPointCamera_precompiled.h
#	Gems/StartingPointInput/Code/Source/StartingPointInput_precompiled.h
#	Gems/StartingPointMovement/Code/Source/StartingPointMovement_precompiled.h
#	Gems/SurfaceData/Code/Source/SurfaceData_precompiled.h
#	Gems/TextureAtlas/Code/Source/TextureAtlas_precompiled.h
#	Gems/TickBusOrderViewer/Code/Source/TickBusOrderViewer_precompiled.h
#	Gems/Twitch/Code/Source/Twitch_precompiled.h
#	Gems/VirtualGamepad/Code/Source/VirtualGamepad_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBoxUnsupported_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBox_precompiled.h
2021-07-16 15:42:37 -07:00

294 lines
12 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 <AzTest/AzTest.h>
#include <Tests/PhysXGenericTestFixture.h>
#include <Tests/PhysXTestCommon.h>
#include <BoxColliderComponent.h>
#include <RigidBodyComponent.h>
#include <JointComponent.h>
#include <BallJointComponent.h>
#include <FixedJointComponent.h>
#include <HingeJointComponent.h>
#include <PhysX/Joint/Configuration/PhysXJointConfiguration.h>
#include <AzCore/Component/TransformBus.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Physics/ShapeConfiguration.h>
#include <AzFramework/Physics/SystemBus.h>
#include <AzFramework/Physics/Configuration/RigidBodyConfiguration.h>
#include <AzFramework/Physics/PhysicsSystem.h>
namespace PhysX
{
using PhysXJointsTest = PhysX::GenericPhysicsInterfaceTest;
template<typename JointComponentType>
AZStd::unique_ptr<AZ::Entity> AddBodyColliderEntity( AzPhysics::SceneHandle sceneHandle,
const AZ::Vector3& position,
const AZ::Vector3& initialLinearVelocity,
AZStd::shared_ptr<JointComponentConfiguration> jointConfig = nullptr,
AZStd::shared_ptr<JointGenericProperties> jointGenericProperties = nullptr,
AZStd::shared_ptr<JointLimitProperties> jointLimitProperties = nullptr)
{
const char* entityName = "testEntity";
auto entity = AZStd::make_unique<AZ::Entity>(entityName);
AZ::TransformConfig transformConfig;
transformConfig.m_worldTransform = AZ::Transform::CreateTranslation(position);
entity->CreateComponent<AzFramework::TransformComponent>()->SetConfiguration(transformConfig);
auto colliderConfiguration = AZStd::make_shared<Physics::ColliderConfiguration>();
auto boxShapeConfiguration = AZStd::make_shared<Physics::BoxShapeConfiguration>();
auto boxColliderComponent = entity->CreateComponent<BoxColliderComponent>();
boxColliderComponent->SetShapeConfigurationList({ AZStd::make_pair(colliderConfiguration, boxShapeConfiguration) });
AzPhysics::RigidBodyConfiguration rigidBodyConfig;
rigidBodyConfig.m_initialLinearVelocity = initialLinearVelocity;
rigidBodyConfig.m_gravityEnabled = false;
// Make lead body very heavy
if (!jointConfig)
{
rigidBodyConfig.m_mass = 9999.0f;
}
entity->CreateComponent<PhysX::RigidBodyComponent>(rigidBodyConfig, sceneHandle);
if (jointConfig)
{
jointConfig->m_followerEntity = entity->GetId();
JointGenericProperties defaultJointGenericProperties;
JointLimitProperties defaultJointLimitProperties;
entity->CreateComponent<JointComponentType>(
*jointConfig,
(jointGenericProperties)? *jointGenericProperties : defaultJointGenericProperties,
(jointLimitProperties)? *jointLimitProperties : defaultJointLimitProperties);
}
entity->Init();
entity->Activate();
return entity;
}
AZ::Vector3 RunJointTest(AzPhysics::Scene* scene, AZ::EntityId followerEntityId)
{
AZ::Vector3 followerEndPosition(0.0f, 0.0f, 0.0f);
//Run simulation for a while - bounces box once on force volume
const float deltaTime = AzPhysics::SystemConfiguration::DefaultFixedTimestep;
const AZ::u32 numSteps = 240;
PhysX::TestUtils::UpdateScene(scene, deltaTime, numSteps);
AZ::TransformBus::EventResult(followerEndPosition,
followerEntityId,
&AZ::TransformBus::Events::GetWorldTranslation);
return followerEndPosition;
}
TEST_F(PhysXJointsTest, Joints_FixedJoint_BodiesAreConstrainedAndMoveTogether)
{
// Place lead on the right of follower, tie them together with fixed joint and send the lead moving to the right.
// The follower should be pulled along if the fixed joint works.
const AZ::Vector3 followerPosition(-1.0f, 0.0f, 0.0f);
const AZ::Vector3 followerInitialLinearVelocity(0.0f, 0.0f, 0.0f);
const AZ::Vector3 leadPosition(1.0f, 0.0f, 0.0f);
const AZ::Vector3 leadInitialLinearVelocity(10.0f, 0.0f, 0.0f);
const AZ::Transform jointLocalTransform = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 0.0f, 0.0f));
// Templated joint component type is irrelevant since joint component is not created for this invocation.
auto leadEntity = AddBodyColliderEntity<JointComponent>(m_testSceneHandle,
leadPosition,
leadInitialLinearVelocity);
auto jointConfig = AZStd::make_shared<JointComponentConfiguration>();
jointConfig->m_leadEntity = leadEntity->GetId();
jointConfig->m_localTransformFromFollower = jointLocalTransform;
auto followerEntity = AddBodyColliderEntity<FixedJointComponent>(m_testSceneHandle,
followerPosition,
followerInitialLinearVelocity,
jointConfig);
const AZ::Vector3 followerEndPosition = RunJointTest(m_defaultScene, followerEntity->GetId());
EXPECT_TRUE(followerEndPosition.GetX() > followerPosition.GetX());
}
TEST_F(PhysXJointsTest, Joint_HingeJoint_FollowerSwingsAroundLead)
{
// Place lead on the right of follower, tie them together with hinge joint and send the follower moving up.
// The follower should swing around the lead.
const AZ::Vector3 followerPosition(-1.0f, 0.0f, 0.0f);
const AZ::Vector3 followerInitialLinearVelocity(0.0f, 0.0f, 10.0f);
const AZ::Vector3 leadPosition(1.0f, 0.0f, 0.0f);
const AZ::Vector3 leadInitialLinearVelocity(0.0f, 0.0f, 0.0f);
const AZ::Vector3 jointLocalPosition(1.0f, 0.0f, 0.0f);
const AZ::Quaternion jointLocalRotation = AZ::Quaternion::CreateRotationZ(90.0f);
const AZ::Transform jointLocalTransform = AZ::Transform::CreateFromQuaternionAndTranslation(
jointLocalRotation,
jointLocalPosition);
// Templated joint component type is irrelevant since joint component is not created for this invocation.
auto leadEntity = AddBodyColliderEntity<JointComponent>(m_testSceneHandle,
leadPosition,
leadInitialLinearVelocity);
auto jointConfig = AZStd::make_shared<JointComponentConfiguration>();
jointConfig->m_leadEntity = leadEntity->GetId();
jointConfig->m_localTransformFromFollower = jointLocalTransform;
auto jointLimits = AZStd::make_shared <JointLimitProperties>();
jointLimits->m_isLimited = false;
auto followerEntity = AddBodyColliderEntity<HingeJointComponent>(m_testSceneHandle,
followerPosition,
followerInitialLinearVelocity,
jointConfig,
nullptr,
jointLimits);
const AZ::Vector3 followerEndPosition = RunJointTest(m_defaultScene, followerEntity->GetId());
EXPECT_TRUE(followerEndPosition.GetX() > followerPosition.GetX());
EXPECT_TRUE(abs(followerEndPosition.GetZ()) > FLT_EPSILON);
}
TEST_F(PhysXJointsTest, Joint_BallJoint_FollowerSwingsUpAboutLead)
{
// Place lead on top of follower, tie them together with ball joint and send the follower moving sideways in the X and Y directions.
// The follower should swing up about lead.
const AZ::Vector3 followerPosition(0.0f, 0.0f, -1.0f);
const AZ::Vector3 followerInitialLinearVelocity(10.0f, 10.0f, 0.0f);
const AZ::Vector3 leadPosition(0.0f, 0.0f, 1.0f);
const AZ::Vector3 leadInitialLinearVelocity(0.0f, 0.0f, 0.0f);
const AZ::Vector3 jointLocalPosition(0.0f, 0.0f, 2.0f);
const AZ::Quaternion jointLocalRotation = AZ::Quaternion::CreateRotationY(90.0f);
const AZ::Transform jointLocalTransform = AZ::Transform::CreateFromQuaternionAndTranslation(
jointLocalRotation,
jointLocalPosition);
// Templated joint component type is irrelevant since joint component is not created for this invocation.
auto leadEntity = AddBodyColliderEntity<JointComponent>(m_testSceneHandle,
leadPosition,
leadInitialLinearVelocity);
auto jointConfig = AZStd::make_shared<JointComponentConfiguration>();
jointConfig->m_leadEntity = leadEntity->GetId();
jointConfig->m_localTransformFromFollower = jointLocalTransform;
auto jointLimits = AZStd::make_shared <JointLimitProperties>();
jointLimits->m_isLimited = false;
auto followerEntity = AddBodyColliderEntity<BallJointComponent>(m_testSceneHandle,
followerPosition,
followerInitialLinearVelocity,
jointConfig,
nullptr,
jointLimits);
const AZ::Vector3 followerEndPosition = RunJointTest(m_defaultScene, followerEntity->GetId());
EXPECT_TRUE(followerEndPosition.GetZ() > followerPosition.GetZ());
}
// for some reason TYPED_TEST_CASE with the fixture is not working on Android + Linux
#ifdef ENABLE_JOINTS_TYPED_TEST_CASE
template<class JointConfigurationType>
class PhysXJointsApiTest : public PhysX::GenericPhysicsInterfaceTest
{
public:
void SetUp() override
{
PhysX::GenericPhysicsInterfaceTest::SetUp();
if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
{
AzPhysics::RigidBodyConfiguration parentConfiguration;
AzPhysics::RigidBodyConfiguration childConfiguration;
auto colliderConfig = AZStd::make_shared<Physics::ColliderConfiguration>();
auto shapeConfiguration = AZStd::make_shared<Physics::BoxShapeConfiguration>(AZ::Vector3(1.0f, 1.0f, 1.0f));
parentConfiguration.m_colliderAndShapeData = AzPhysics::ShapeColliderPair(colliderConfig, shapeConfiguration);
childConfiguration.m_colliderAndShapeData = AzPhysics::ShapeColliderPair(colliderConfig, shapeConfiguration);
// Put the child body a bit to the lower side of X to avoid it colliding with parent
childConfiguration.m_position.SetX(childConfiguration.m_position.GetX() - 2.0f);
m_childInitialPos = childConfiguration.m_position;
parentConfiguration.m_initialLinearVelocity.SetX(10.0f);
m_parentBodyHandle = sceneInterface->AddSimulatedBody(m_testSceneHandle, &parentConfiguration);
m_childBodyHandle = sceneInterface->AddSimulatedBody(m_testSceneHandle, &childConfiguration);
}
}
void TearDown() override
{
if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
{
sceneInterface->RemoveSimulatedBody(m_testSceneHandle, m_parentBodyHandle);
sceneInterface->RemoveSimulatedBody(m_testSceneHandle, m_childBodyHandle);
}
PhysX::GenericPhysicsInterfaceTest::TearDown();
}
AzPhysics::SimulatedBodyHandle m_parentBodyHandle = AzPhysics::InvalidJointHandle;
AzPhysics::SimulatedBodyHandle m_childBodyHandle = AzPhysics::InvalidJointHandle;
AZ::Vector3 m_childInitialPos;
};
using JointTypes = testing::Types<
D6JointLimitConfiguration,
FixedJointConfiguration,
BallJointConfiguration,
HingeJointConfiguration>;
TYPED_TEST_CASE(PhysXJointsApiTest, JointTypes);
TYPED_TEST(PhysXJointsApiTest, Joint_ChildFollowsParent)
{
TypeParam jointConfiguration;
AzPhysics::JointHandle jointHandle = AzPhysics::InvalidJointHandle;
if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
{
jointHandle = sceneInterface->AddJoint(m_testSceneHandle, &jointConfiguration, m_parentBodyHandle, m_childBodyHandle);
}
EXPECT_NE(jointHandle, AzPhysics::InvalidJointHandle);
// run physics to trigger the the move of parent body
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 1);
AZ::Vector3 childCurrentPos;
if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
{
auto* childBody = sceneInterface->GetSimulatedBodyFromHandle(m_testSceneHandle, m_childBodyHandle);
childCurrentPos = childBody->GetPosition();
}
EXPECT_GT(childCurrentPos.GetX(), m_childInitialPos.GetX());
}
#endif // ENABLE_JOINTS_TYPED_TEST_CASE
}