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.
532 lines
29 KiB
C++
532 lines
29 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 <AzCore/UnitTest/TestTypes.h>
|
|
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
|
#include <AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.h>
|
|
#include <Tests/EditorTestUtilities.h>
|
|
#include <EditorColliderComponent.h>
|
|
#include <EditorShapeColliderComponent.h>
|
|
#include <EditorRigidBodyComponent.h>
|
|
#include <EditorForceRegionComponent.h>
|
|
#include <ShapeColliderComponent.h>
|
|
#include <RigidBodyComponent.h>
|
|
#include <StaticRigidBodyComponent.h>
|
|
#include <RigidBodyStatic.h>
|
|
#include <LmbrCentral/Shape/BoxShapeComponentBus.h>
|
|
#include <LmbrCentral/Shape/CylinderShapeComponentBus.h>
|
|
#include <LmbrCentral/Shape/PolygonPrismShapeComponentBus.h>
|
|
#include <LmbrCentral/Shape/SphereShapeComponentBus.h>
|
|
#include <LmbrCentral/Shape/CompoundShapeComponentBus.h>
|
|
#include <PhysX/ForceRegionComponentBus.h>
|
|
#include <PhysX/PhysXLocks.h>
|
|
#include <PhysX/SystemComponentBus.h>
|
|
#include <Tests/PhysXTestCommon.h>
|
|
|
|
namespace PhysXEditorTests
|
|
{
|
|
namespace
|
|
{
|
|
struct TestData
|
|
{
|
|
const AZStd::vector<AZ::Vector2> polygonHShape = {
|
|
AZ::Vector2(0.0f, 0.0f),
|
|
AZ::Vector2(0.0f, 3.0f),
|
|
AZ::Vector2(1.0f, 3.0f),
|
|
AZ::Vector2(1.0f, 2.0f),
|
|
AZ::Vector2(2.0f, 2.0f),
|
|
AZ::Vector2(2.0f, 3.0f),
|
|
AZ::Vector2(3.0f, 3.0f),
|
|
AZ::Vector2(3.0f, 0.0f),
|
|
AZ::Vector2(2.0f, 0.0f),
|
|
AZ::Vector2(2.0f, 1.0f),
|
|
AZ::Vector2(1.0f, 1.0f),
|
|
AZ::Vector2(1.0f, 0.0f)
|
|
};
|
|
};
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeDependencySatisfied_EntityIsValid)
|
|
{
|
|
EntityPtr entity = CreateInactiveEditorEntity("ShapeColliderComponentEditorEntity");
|
|
entity->CreateComponent<PhysX::EditorShapeColliderComponent>();
|
|
entity->CreateComponent(LmbrCentral::EditorBoxShapeComponentTypeId);
|
|
|
|
// the entity should be in a valid state because the shape component requirement is satisfied
|
|
AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails();
|
|
EXPECT_TRUE(sortOutcome.IsSuccess());
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeDependencyMissing_EntityIsInvalid)
|
|
{
|
|
EntityPtr entity = CreateInactiveEditorEntity("ShapeColliderComponentEditorEntity");
|
|
entity->CreateComponent<PhysX::EditorShapeColliderComponent>();
|
|
|
|
// the entity should not be in a valid state because the shape collider component requires a shape component
|
|
AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails();
|
|
EXPECT_FALSE(sortOutcome.IsSuccess());
|
|
EXPECT_TRUE(sortOutcome.GetError().m_code == AZ::Entity::DependencySortResult::MissingRequiredService);
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_MultipleShapeColliderComponents_EntityIsInvalid)
|
|
{
|
|
EntityPtr entity = CreateInactiveEditorEntity("ShapeColliderComponentEditorEntity");
|
|
entity->CreateComponent<PhysX::EditorShapeColliderComponent>();
|
|
entity->CreateComponent(LmbrCentral::EditorBoxShapeComponentTypeId);
|
|
|
|
// adding a second shape collider component should make the entity invalid
|
|
entity->CreateComponent<PhysX::EditorShapeColliderComponent>();
|
|
|
|
// the entity should be in a valid state because the shape component requirement is satisfied
|
|
AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails();
|
|
EXPECT_FALSE(sortOutcome.IsSuccess());
|
|
EXPECT_TRUE(sortOutcome.GetError().m_code == AZ::Entity::DependencySortResult::HasIncompatibleServices);
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderPlusMultipleColliderComponents_EntityIsValid)
|
|
{
|
|
EntityPtr entity = CreateInactiveEditorEntity("ShapeColliderComponentEditorEntity");
|
|
entity->CreateComponent<PhysX::EditorShapeColliderComponent>();
|
|
entity->CreateComponent(LmbrCentral::EditorBoxShapeComponentTypeId);
|
|
|
|
// the shape collider component should be compatible with multiple collider components
|
|
entity->CreateComponent<PhysX::EditorColliderComponent>();
|
|
entity->CreateComponent<PhysX::EditorColliderComponent>();
|
|
|
|
// the entity should be in a valid state because the shape component requirement is satisfied
|
|
AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails();
|
|
EXPECT_TRUE(sortOutcome.IsSuccess());
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithBox_CorrectRuntimeComponents)
|
|
{
|
|
// create an editor entity with a shape collider component and a box shape component
|
|
EntityPtr editorEntity = CreateInactiveEditorEntity("ShapeColliderComponentEditorEntity");
|
|
editorEntity->CreateComponent<PhysX::EditorShapeColliderComponent>();
|
|
editorEntity->CreateComponent(LmbrCentral::EditorBoxShapeComponentTypeId);
|
|
editorEntity->Activate();
|
|
|
|
EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get());
|
|
|
|
// check that the runtime entity has the expected components
|
|
EXPECT_TRUE(gameEntity->FindComponent<PhysX::ShapeColliderComponent>() != nullptr);
|
|
EXPECT_TRUE(gameEntity->FindComponent(LmbrCentral::BoxShapeComponentTypeId) != nullptr);
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithBox_CorrectRuntimeGeometry)
|
|
{
|
|
// create an editor entity with a shape collider component and a box shape component
|
|
EntityPtr editorEntity = CreateInactiveEditorEntity("ShapeColliderComponentEditorEntity");
|
|
editorEntity->CreateComponent<PhysX::EditorShapeColliderComponent>();
|
|
editorEntity->CreateComponent(LmbrCentral::EditorBoxShapeComponentTypeId);
|
|
editorEntity->Activate();
|
|
|
|
const AZ::Vector3 boxDimensions(2.0f, 3.0f, 4.0f);
|
|
LmbrCentral::BoxShapeComponentRequestsBus::Event(editorEntity->GetId(),
|
|
&LmbrCentral::BoxShapeComponentRequests::SetBoxDimensions, boxDimensions);
|
|
|
|
EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get());
|
|
|
|
// since there was no editor rigid body component, the runtime entity should have a static rigid body
|
|
const auto* staticBody = azdynamic_cast<PhysX::StaticRigidBody*>(gameEntity->FindComponent<PhysX::StaticRigidBodyComponent>()->GetSimulatedBody());
|
|
const auto* pxRigidStatic = static_cast<const physx::PxRigidStatic*>(staticBody->GetNativePointer());
|
|
|
|
PHYSX_SCENE_READ_LOCK(pxRigidStatic->getScene());
|
|
|
|
// there should be a single shape on the rigid body and it should be a box
|
|
EXPECT_EQ(pxRigidStatic->getNbShapes(), 1);
|
|
physx::PxShape* shape = nullptr;
|
|
pxRigidStatic->getShapes(&shape, 1, 0);
|
|
EXPECT_EQ(shape->getGeometryType(), physx::PxGeometryType::eBOX);
|
|
|
|
// the bounding box of the rigid body should reflect the dimensions of the box set above
|
|
AZ::Aabb aabb = staticBody->GetAabb();
|
|
EXPECT_TRUE(aabb.GetMax().IsClose(0.5f * boxDimensions));
|
|
EXPECT_TRUE(aabb.GetMin().IsClose(-0.5f * boxDimensions));
|
|
}
|
|
|
|
void SetPolygonPrismVertices(AZ::EntityId entityId, const AZStd::vector<AZ::Vector2>& vertices)
|
|
{
|
|
AZ::PolygonPrismPtr polygonPrism;
|
|
LmbrCentral::PolygonPrismShapeComponentRequestBus::EventResult(polygonPrism, entityId,
|
|
&LmbrCentral::PolygonPrismShapeComponentRequests::GetPolygonPrism);
|
|
if (polygonPrism)
|
|
{
|
|
polygonPrism->m_vertexContainer.SetVertices(vertices);
|
|
}
|
|
}
|
|
|
|
void SetPolygonPrismHeight(AZ::EntityId entityId, float height)
|
|
{
|
|
AZ::PolygonPrismPtr polygonPrism;
|
|
LmbrCentral::PolygonPrismShapeComponentRequestBus::EventResult(polygonPrism, entityId,
|
|
&LmbrCentral::PolygonPrismShapeComponentRequests::GetPolygonPrism);
|
|
if (polygonPrism)
|
|
{
|
|
polygonPrism->SetHeight(height);
|
|
}
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithPolygonPrism_CorrectRuntimeGeometry)
|
|
{
|
|
// create an editor entity with a shape collider component and a polygon prism shape component
|
|
EntityPtr editorEntity = CreateInactiveEditorEntity("ShapeColliderComponentEditorEntity");
|
|
editorEntity->CreateComponent<PhysX::EditorShapeColliderComponent>();
|
|
editorEntity->CreateComponent(LmbrCentral::EditorPolygonPrismShapeComponentTypeId);
|
|
|
|
// suppress the shape collider error that will be raised because the polygon prism vertices have not been set yet
|
|
UnitTest::ErrorHandler polygonPrismErrorHandler("Invalid polygon prism");
|
|
editorEntity->Activate();
|
|
|
|
// modify the geometry of the polygon prism
|
|
TestData testData;
|
|
SetPolygonPrismVertices(editorEntity->GetId(), testData.polygonHShape);
|
|
SetPolygonPrismHeight(editorEntity->GetId(), 2.0f);
|
|
|
|
EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get());
|
|
|
|
// since there was no editor rigid body component, the runtime entity should have a static rigid body
|
|
const auto* staticBody = azdynamic_cast<PhysX::StaticRigidBody*>(gameEntity->FindComponent<PhysX::StaticRigidBodyComponent>()->GetSimulatedBody());
|
|
const auto* pxRigidStatic = static_cast<const physx::PxRigidStatic*>(staticBody->GetNativePointer());
|
|
|
|
PHYSX_SCENE_READ_LOCK(pxRigidStatic->getScene());
|
|
|
|
// the input polygon prism was not convex, so should have been decomposed into multiple shapes
|
|
const int numShapes = pxRigidStatic->getNbShapes();
|
|
EXPECT_TRUE(numShapes > 1);
|
|
|
|
// the shapes should all be convex meshes
|
|
for (int shapeIndex = 0; shapeIndex < numShapes; shapeIndex++)
|
|
{
|
|
physx::PxShape* shape = nullptr;
|
|
pxRigidStatic->getShapes(&shape, 1, shapeIndex);
|
|
EXPECT_EQ(shape->getGeometryType(), physx::PxGeometryType::eCONVEXMESH);
|
|
}
|
|
|
|
// the vertices of the input polygon prism ranged from (0, 0) to (3, 3) and the height was set to 2
|
|
// the bounding box of the static rigid body should reflect those values
|
|
AZ::Aabb aabb = staticBody->GetAabb();
|
|
EXPECT_TRUE(aabb.GetMax().IsClose(AZ::Vector3(3.0f, 3.0f, 2.0f)));
|
|
EXPECT_TRUE(aabb.GetMin().IsClose(AZ::Vector3::CreateZero()));
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithPolygonPrismAndNonUniformScale_CorrectRunAabb)
|
|
{
|
|
// create an editor entity with a shape collider component and a polygon prism shape component
|
|
EntityPtr editorEntity = CreateInactiveEditorEntity("ShapeColliderComponentEditorEntity");
|
|
editorEntity->CreateComponent<PhysX::EditorShapeColliderComponent>();
|
|
editorEntity->CreateComponent(LmbrCentral::EditorPolygonPrismShapeComponentTypeId);
|
|
|
|
// add a non-uniform scale component
|
|
editorEntity->CreateComponent<AzToolsFramework::Components::EditorNonUniformScaleComponent>();
|
|
|
|
// suppress the shape collider error that will be raised because the polygon prism vertices have not been set yet
|
|
UnitTest::ErrorHandler polygonPrismErrorHandler("Invalid polygon prism");
|
|
editorEntity->Activate();
|
|
|
|
// modify the geometry of the polygon prism
|
|
TestData testData;
|
|
AZ::EntityId entityId = editorEntity->GetId();
|
|
SetPolygonPrismVertices(entityId, testData.polygonHShape);
|
|
SetPolygonPrismHeight(entityId, 2.0f);
|
|
|
|
// update the transform scale and non-uniform scale
|
|
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalUniformScale, 2.0f);
|
|
AZ::NonUniformScaleRequestBus::Event(entityId, &AZ::NonUniformScaleRequests::SetScale, AZ::Vector3(0.5f, 1.5f, 2.0f));
|
|
|
|
EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get());
|
|
|
|
// since there was no editor rigid body component, the runtime entity should have a static rigid body
|
|
const auto* staticBody = azdynamic_cast<PhysX::StaticRigidBody*>(gameEntity->FindComponent<PhysX::StaticRigidBodyComponent>()->GetSimulatedBody());
|
|
|
|
// the vertices of the input polygon prism ranged from (0, 0) to (3, 3) and the height was set to 2
|
|
// the bounding box of the static rigid body should reflect those values combined with the scale values above
|
|
AZ::Aabb aabb = staticBody->GetAabb();
|
|
EXPECT_TRUE(aabb.GetMax().IsClose(AZ::Vector3(3.0f, 9.0f, 8.0f)));
|
|
EXPECT_TRUE(aabb.GetMin().IsClose(AZ::Vector3::CreateZero()));
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithCylinder_CorrectRuntimeComponents)
|
|
{
|
|
// create an editor entity with a shape collider component and a cylinder shape component
|
|
EntityPtr editorEntity = CreateInactiveEditorEntity("ShapeColliderComponentEditorEntity");
|
|
editorEntity->CreateComponent<PhysX::EditorShapeColliderComponent>();
|
|
editorEntity->CreateComponent(LmbrCentral::EditorCylinderShapeComponentTypeId);
|
|
editorEntity->Activate();
|
|
|
|
EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get());
|
|
|
|
// check that the runtime entity has the expected components
|
|
EXPECT_TRUE(gameEntity->FindComponent<PhysX::ShapeColliderComponent>() != nullptr);
|
|
EXPECT_TRUE(gameEntity->FindComponent(LmbrCentral::CylinderShapeComponentTypeId) != nullptr);
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithCylinderWithValidRadiusAndValidHeight_CorrectRuntimeGeometry)
|
|
{
|
|
// create an editor entity with a shape collider component and a cylinder shape component
|
|
EntityPtr editorEntity = CreateInactiveEditorEntity("ShapeColliderComponentEditorEntity");
|
|
editorEntity->CreateComponent<PhysX::EditorShapeColliderComponent>();
|
|
editorEntity->CreateComponent(LmbrCentral::EditorCylinderShapeComponentTypeId);
|
|
editorEntity->Activate();
|
|
|
|
const float validRadius = 1.0f;
|
|
const float validHeight = 1.0f;
|
|
|
|
LmbrCentral::CylinderShapeComponentRequestsBus::Event(editorEntity->GetId(),
|
|
&LmbrCentral::CylinderShapeComponentRequests::SetRadius, validRadius);
|
|
|
|
LmbrCentral::CylinderShapeComponentRequestsBus::Event(editorEntity->GetId(),
|
|
&LmbrCentral::CylinderShapeComponentRequests::SetHeight, validHeight);
|
|
|
|
EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get());
|
|
|
|
// since there was no editor rigid body component, the runtime entity should have a static rigid body
|
|
const auto* staticBody = azdynamic_cast<PhysX::StaticRigidBody*>(gameEntity->FindComponent<PhysX::StaticRigidBodyComponent>()->GetSimulatedBody());
|
|
const auto* pxRigidStatic = static_cast<const physx::PxRigidStatic*>(staticBody->GetNativePointer());
|
|
|
|
PHYSX_SCENE_READ_LOCK(pxRigidStatic->getScene());
|
|
|
|
// there should be a single shape on the rigid body and it should be a convex mesh
|
|
EXPECT_EQ(pxRigidStatic->getNbShapes(), 1);
|
|
physx::PxShape* shape = nullptr;
|
|
pxRigidStatic->getShapes(&shape, 1, 0);
|
|
EXPECT_EQ(shape->getGeometryType(), physx::PxGeometryType::eCONVEXMESH);
|
|
|
|
// the bounding box of the rigid body should reflect the dimensions of the cylinder set above
|
|
AZ::Aabb aabb = staticBody->GetAabb();
|
|
|
|
// Check that the z positions of the bounding box match that of the cylinder
|
|
EXPECT_NEAR(aabb.GetMin().GetZ(), -0.5f * validHeight, AZ::Constants::Tolerance);
|
|
EXPECT_NEAR(aabb.GetMax().GetZ(), 0.5f * validHeight, AZ::Constants::Tolerance);
|
|
|
|
// check that the xy points are not outside the radius of the cylinder
|
|
AZ::Vector2 vecMin(aabb.GetMin().GetX(), aabb.GetMin().GetY());
|
|
AZ::Vector2 vecMax(aabb.GetMax().GetX(), aabb.GetMax().GetY());
|
|
EXPECT_TRUE(AZ::GetAbs(vecMin.GetX()) <= validRadius);
|
|
EXPECT_TRUE(AZ::GetAbs(vecMin.GetY()) <= validRadius);
|
|
EXPECT_TRUE(AZ::GetAbs(vecMax.GetX()) <= validRadius);
|
|
EXPECT_TRUE(AZ::GetAbs(vecMax.GetX()) <= validRadius);
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithCylinderWithNullRadius_HandledGracefully)
|
|
{
|
|
ValidateInvalidEditorShapeColliderComponentParams(0.f, 1.f);
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithCylinderWithNullHeight_HandledGracefully)
|
|
{
|
|
ValidateInvalidEditorShapeColliderComponentParams(0.f, 1.f);
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithCylinderWithNullRadiusAndNullHeight_HandledGracefully)
|
|
{
|
|
ValidateInvalidEditorShapeColliderComponentParams(0.f, 0.f);
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithCylinderWithNegativeRadiusAndNullHeight_HandledGracefully)
|
|
{
|
|
ValidateInvalidEditorShapeColliderComponentParams(-1.f, 0.f);
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithCylinderWithNullRadiusAndNegativeHeight_HandledGracefully)
|
|
{
|
|
ValidateInvalidEditorShapeColliderComponentParams(0.f, -1.f);
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithBoxAndRigidBody_CorrectRuntimeComponents)
|
|
{
|
|
// create an editor entity with a shape collider component and a box shape component
|
|
EntityPtr editorEntity = CreateInactiveEditorEntity("ShapeColliderComponentEditorEntity");
|
|
editorEntity->CreateComponent<PhysX::EditorShapeColliderComponent>();
|
|
editorEntity->CreateComponent<PhysX::EditorRigidBodyComponent>();
|
|
editorEntity->CreateComponent(LmbrCentral::EditorBoxShapeComponentTypeId);
|
|
editorEntity->Activate();
|
|
|
|
EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get());
|
|
|
|
// check that the runtime entity has the expected components
|
|
EXPECT_TRUE(gameEntity->FindComponent<PhysX::ShapeColliderComponent>() != nullptr);
|
|
EXPECT_TRUE(gameEntity->FindComponent(LmbrCentral::BoxShapeComponentTypeId) != nullptr);
|
|
EXPECT_TRUE(gameEntity->FindComponent<PhysX::RigidBodyComponent>() != nullptr);
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithBoxAndRigidBody_CorrectRuntimeEntity)
|
|
{
|
|
// create an editor entity with a shape collider component and a box shape component
|
|
EntityPtr editorEntity = CreateInactiveEditorEntity("ShapeColliderComponentEditorEntity");
|
|
editorEntity->CreateComponent<PhysX::EditorShapeColliderComponent>();
|
|
editorEntity->CreateComponent<PhysX::EditorRigidBodyComponent>();
|
|
editorEntity->CreateComponent(LmbrCentral::EditorBoxShapeComponentTypeId);
|
|
editorEntity->Activate();
|
|
|
|
const AZ::Vector3 boxDimensions(2.0f, 3.0f, 4.0f);
|
|
LmbrCentral::BoxShapeComponentRequestsBus::Event(editorEntity->GetId(),
|
|
&LmbrCentral::BoxShapeComponentRequests::SetBoxDimensions, boxDimensions);
|
|
|
|
EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get());
|
|
|
|
// since there was an editor rigid body component, the runtime entity should have a dynamic rigid body
|
|
const auto* rigidBody = gameEntity->FindComponent<PhysX::RigidBodyComponent>()->GetRigidBody();
|
|
const auto* pxRigidDynamic = static_cast<const physx::PxRigidDynamic*>(rigidBody->GetNativePointer());
|
|
|
|
PHYSX_SCENE_READ_LOCK(pxRigidDynamic->getScene());
|
|
|
|
// there should be a single shape on the rigid body and it should be a box
|
|
EXPECT_EQ(pxRigidDynamic->getNbShapes(), 1);
|
|
physx::PxShape* shape = nullptr;
|
|
pxRigidDynamic->getShapes(&shape, 1, 0);
|
|
EXPECT_EQ(shape->getGeometryType(), physx::PxGeometryType::eBOX);
|
|
|
|
// the bounding box of the rigid body should reflect the dimensions of the box set above
|
|
AZ::Aabb aabb = rigidBody->GetAabb();
|
|
EXPECT_TRUE(aabb.GetMax().IsClose(0.5f * boxDimensions));
|
|
EXPECT_TRUE(aabb.GetMin().IsClose(-0.5f * boxDimensions));
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_TransformChanged_ColliderUpdated)
|
|
{
|
|
// create an editor entity with a shape collider component and a box shape component
|
|
EntityPtr editorEntity = CreateInactiveEditorEntity("ShapeColliderComponentEditorEntity");
|
|
editorEntity->CreateComponent<PhysX::EditorShapeColliderComponent>();
|
|
editorEntity->CreateComponent(LmbrCentral::EditorBoxShapeComponentTypeId);
|
|
editorEntity->Activate();
|
|
AZ::EntityId editorEntityId = editorEntity->GetId();
|
|
AZ::Vector3 boxDimensions = AZ::Vector3::CreateOne();
|
|
LmbrCentral::BoxShapeComponentRequestsBus::EventResult(boxDimensions, editorEntityId,
|
|
&LmbrCentral::BoxShapeComponentRequests::GetBoxDimensions);
|
|
|
|
// update the transform
|
|
const float scale = 2.0f;
|
|
AZ::TransformBus::Event(editorEntityId, &AZ::TransformInterface::SetLocalUniformScale, scale);
|
|
const AZ::Vector3 translation(10.0f, 20.0f, 30.0f);
|
|
AZ::TransformBus::Event(editorEntityId, &AZ::TransformInterface::SetWorldTranslation, translation);
|
|
|
|
// make a game entity and check its bounding box is consistent with the changed transform
|
|
EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get());
|
|
const auto* staticBody = azdynamic_cast<PhysX::StaticRigidBody*>(gameEntity->FindComponent<PhysX::StaticRigidBodyComponent>()->GetSimulatedBody());
|
|
AZ::Aabb aabb = staticBody->GetAabb();
|
|
EXPECT_TRUE(aabb.GetMax().IsClose(translation + 0.5f * scale * boxDimensions));
|
|
EXPECT_TRUE(aabb.GetMin().IsClose(translation - 0.5f * scale * boxDimensions));
|
|
}
|
|
|
|
void SetTrigger(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent, bool isTrigger)
|
|
{
|
|
AZ::SerializeContext* serializeContext = nullptr;
|
|
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
|
AzToolsFramework::InstanceDataHierarchy instanceDataHierarchy;
|
|
instanceDataHierarchy.AddRootInstance(editorShapeColliderComponent);
|
|
instanceDataHierarchy.Build(serializeContext, AZ::SerializeContext::ENUM_ACCESS_FOR_WRITE);
|
|
AzToolsFramework::InstanceDataHierarchy::InstanceDataNode* instanceNode =
|
|
instanceDataHierarchy.FindNodeByPartialAddress({ AZ_CRC("Trigger", 0x1a6b0f5d) });
|
|
if (instanceNode)
|
|
{
|
|
instanceNode->Write<bool>(isTrigger);
|
|
}
|
|
}
|
|
|
|
EntityPtr CreateRigidBox(const AZ::Vector3& boxDimensions, const AZ::Vector3& position)
|
|
{
|
|
EntityPtr rigidBodyEditorEntity = CreateInactiveEditorEntity("RigidBodyEditorEntity");
|
|
rigidBodyEditorEntity->CreateComponent<PhysX::EditorShapeColliderComponent>();
|
|
rigidBodyEditorEntity->CreateComponent(LmbrCentral::EditorBoxShapeComponentTypeId);
|
|
rigidBodyEditorEntity->CreateComponent<PhysX::EditorRigidBodyComponent>();
|
|
rigidBodyEditorEntity->Activate();
|
|
LmbrCentral::BoxShapeComponentRequestsBus::Event(rigidBodyEditorEntity->GetId(),
|
|
&LmbrCentral::BoxShapeComponentRequests::SetBoxDimensions, boxDimensions);
|
|
AZ::TransformBus::Event(rigidBodyEditorEntity->GetId(), &AZ::TransformInterface::SetWorldTranslation, position);
|
|
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
|
|
&AzToolsFramework::ToolsApplicationRequests::RemoveDirtyEntity, rigidBodyEditorEntity->GetId());
|
|
|
|
return CreateActiveGameEntityFromEditorEntity(rigidBodyEditorEntity.get());
|
|
}
|
|
|
|
// LYN-1241 - Test disabled due to AZ_Error reports about MaterialLibrary being not found in the AssetCatalog
|
|
TEST_F(PhysXEditorFixture, DISABLED_EditorShapeColliderComponent_PolygonPrismForceRegion_AppliesForceAtRuntime)
|
|
{
|
|
// create an editor entity with shape collider, polygon prism shape and force region components
|
|
EntityPtr forceRegionEditorEntity = CreateInactiveEditorEntity("ShapeColliderComponentEditorEntity");
|
|
auto* shapeColliderComponent = forceRegionEditorEntity->CreateComponent<PhysX::EditorShapeColliderComponent>();
|
|
SetTrigger(shapeColliderComponent, true);
|
|
forceRegionEditorEntity->CreateComponent(LmbrCentral::EditorPolygonPrismShapeComponentTypeId);
|
|
forceRegionEditorEntity->CreateComponent<PhysX::EditorForceRegionComponent>();
|
|
|
|
// suppress the shape collider error that will be raised because the polygon prism vertices have not been set yet
|
|
UnitTest::ErrorHandler polygonPrismErrorHandler("Invalid polygon prism");
|
|
forceRegionEditorEntity->Activate();
|
|
|
|
// modify the geometry of the polygon prism
|
|
TestData testData;
|
|
SetPolygonPrismVertices(forceRegionEditorEntity->GetId(), testData.polygonHShape);
|
|
SetPolygonPrismHeight(forceRegionEditorEntity->GetId(), 2.0f);
|
|
|
|
EntityPtr forceRegionGameEntity = CreateActiveGameEntityFromEditorEntity(forceRegionEditorEntity.get());
|
|
|
|
// add a force to the force region
|
|
PhysX::ForceRegionRequestBus::Event(forceRegionGameEntity->GetId(),
|
|
&PhysX::ForceRegionRequests::AddForceWorldSpace, AZ::Vector3::CreateAxisX(), 100.0f);
|
|
|
|
const AZ::Vector3 boxDimensions(0.5f, 0.5f, 0.5f);
|
|
// create one box over the centre of the polygon prism
|
|
const AZ::Vector3 box1Position(1.5f, 1.5f, 3.0f);
|
|
// create another box over one of the notches in the H
|
|
const AZ::Vector3 box2Position(1.5f, 0.5f, 3.0f);
|
|
EntityPtr rigidBodyGameEntity1 = CreateRigidBox(boxDimensions, box1Position);
|
|
EntityPtr rigidBodyGameEntity2 = CreateRigidBox(boxDimensions, box2Position);
|
|
|
|
PhysX::TestUtils::UpdateScene(m_defaultScene, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 100);
|
|
|
|
// the first rigid body should have been moved in the positive x direction by the force region
|
|
EXPECT_TRUE(rigidBodyGameEntity1->GetTransform()->GetWorldTranslation().GetX() > box1Position.GetX() + AZ::Constants::FloatEpsilon);
|
|
// the second rigid body should not have entered the force region and so its X position should not have been affected
|
|
EXPECT_NEAR(rigidBodyGameEntity2->GetTransform()->GetWorldTranslation().GetX(), box2Position.GetX(), 1e-3f);
|
|
}
|
|
|
|
TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_ShapeColliderWithScaleSetToParentEntity_CorrectRuntimeScale)
|
|
{
|
|
// create an editor parent entity (empty, need transform component only)
|
|
EntityPtr editorParentEntity = CreateInactiveEditorEntity("ParentEntity");
|
|
editorParentEntity->Activate();
|
|
|
|
// set some scale to parent entity
|
|
const float parentScale = 2.0f;
|
|
AZ::TransformBus::Event(editorParentEntity->GetId(), &AZ::TransformInterface::SetLocalUniformScale, parentScale);
|
|
|
|
// create an editor child entity with a shape collider component and a box shape component
|
|
EntityPtr editorChildEntity = CreateInactiveEditorEntity("ChildEntity");
|
|
editorChildEntity->CreateComponent<PhysX::EditorShapeColliderComponent>();
|
|
editorChildEntity->CreateComponent<PhysX::EditorRigidBodyComponent>();
|
|
editorChildEntity->CreateComponent(LmbrCentral::EditorBoxShapeComponentTypeId);
|
|
editorChildEntity->Activate();
|
|
|
|
// set some dimensions to child entity box component
|
|
const AZ::Vector3 boxDimensions(2.0f, 3.0f, 4.0f);
|
|
LmbrCentral::BoxShapeComponentRequestsBus::Event(editorChildEntity->GetId(),
|
|
&LmbrCentral::BoxShapeComponentRequests::SetBoxDimensions,
|
|
boxDimensions);
|
|
|
|
// set one entity as parent of another
|
|
AZ::TransformBus::Event(editorChildEntity->GetId(),
|
|
&AZ::TransformBus::Events::SetParentRelative,
|
|
editorParentEntity->GetId());
|
|
|
|
// build child game entity (parent will be built implicitly)
|
|
EntityPtr gameChildEntity = CreateActiveGameEntityFromEditorEntity(editorChildEntity.get());
|
|
|
|
// since there was an editor rigid body component, the runtime entity should have a dynamic rigid body
|
|
const AzPhysics::RigidBody* rigidBody =
|
|
gameChildEntity->FindComponent<PhysX::RigidBodyComponent>()->GetRigidBody();
|
|
|
|
// the bounding box of the rigid body should reflect the dimensions of the box set above
|
|
// and parent entity scale
|
|
const AZ::Aabb aabb = rigidBody->GetAabb();
|
|
EXPECT_THAT(aabb.GetMax(), UnitTest::IsClose(0.5f * boxDimensions * parentScale));
|
|
EXPECT_THAT(aabb.GetMin(), UnitTest::IsClose(-0.5f * boxDimensions * parentScale));
|
|
}
|
|
|
|
} // namespace PhysXEditorTests
|