From cc50a18fe997eeccc342d63f913942581d2fc9ae Mon Sep 17 00:00:00 2001 From: greerdv Date: Thu, 20 Jan 2022 11:38:22 +0000 Subject: [PATCH 1/5] add support for quad shapes in shape collider component Signed-off-by: greerdv --- .../Source/EditorShapeColliderComponent.cpp | 133 +++++++++++++++++- .../Source/EditorShapeColliderComponent.h | 11 ++ .../Code/Source/ShapeColliderComponent.h | 1 + 3 files changed, 144 insertions(+), 1 deletion(-) diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index 59b659a0bf..f61b2fce74 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,17 @@ namespace PhysX return AZ::Edit::PropertyVisibility::Hide; } + AZ::Crc32 EditorShapeColliderComponent::SingleSidedVisibility() + { + if ((m_shapeType == ShapeType::QuadSingleSided || m_shapeType == ShapeType::QuadDoubleSided) + && GetEntity()->FindComponent() == nullptr) + { + return AZ::Edit::PropertyVisibility::Show; + } + + return AZ::Edit::PropertyVisibility::Hide; + } + void EditorShapeColliderComponent::Reflect(AZ::ReflectContext* context) { if (auto serializeContext = azrtti_cast(context)) @@ -74,6 +86,7 @@ namespace PhysX ->Field("DebugDrawSettings", &EditorShapeColliderComponent::m_colliderDebugDraw) ->Field("ShapeConfigs", &EditorShapeColliderComponent::m_shapeConfigs) ->Field("SubdivisionCount", &EditorShapeColliderComponent::m_subdivisionCount) + ->Field("SingleSided", &EditorShapeColliderComponent::m_singleSided) ; if (auto editContext = serializeContext->GetEditContext()) @@ -100,6 +113,10 @@ namespace PhysX ->Attribute(AZ::Edit::Attributes::Max, Utils::MaxFrustumSubdivisions) ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorShapeColliderComponent::OnSubdivisionCountChange) ->Attribute(AZ::Edit::Attributes::Visibility, &EditorShapeColliderComponent::SubdivisionCountVisibility) + ->DataElement(AZ::Edit::UIHandlers::Default, &EditorShapeColliderComponent::m_singleSided, "Single sided", + "If enabled, planar shapes will only collide from one direction (not valid for shapes attached to dynamic rigid bodies)") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorShapeColliderComponent::OnSingleSidedChange) + ->Attribute(AZ::Edit::Attributes::Visibility, &EditorShapeColliderComponent::SingleSidedVisibility) ; } } @@ -126,7 +143,6 @@ namespace PhysX incompatible.push_back(AZ_CRC_CE("AxisAlignedBoxShapeService")); incompatible.push_back(AZ_CRC_CE("CompoundShapeService")); incompatible.push_back(AZ_CRC_CE("DiskShapeService")); - incompatible.push_back(AZ_CRC_CE("QuadShapeService")); incompatible.push_back(AZ_CRC_CE("TubeShapeService")); incompatible.push_back(AZ_CRC_CE("ReferenceShapeService")); } @@ -206,6 +222,12 @@ namespace PhysX break; } + case ShapeType::QuadSingleSided: + case ShapeType::QuadDoubleSided: + { + // a quad shape has no interior, just return an empty vector of sample points + break; + } default: AZ_WarningOnce("PhysX Shape Collider Component", false, "Unsupported shape type in UpdateCachedSamplePoints"); break; @@ -331,6 +353,11 @@ namespace PhysX UpdateCylinderConfig(uniformScale); } + else if (shapeCrc == ShapeConstants::Quad) + { + UpdateQuadConfig(overallScale); + } + else { m_shapeType = !shapeCrc ? ShapeType::None : ShapeType::Unsupported; @@ -354,6 +381,60 @@ namespace PhysX m_geometryCache.m_boxDimensions = scale * boxDimensions; } + void EditorShapeColliderComponent::UpdateQuadConfig(const AZ::Vector3& scale) + { + LmbrCentral::QuadShapeConfig quadShapeConfig; + LmbrCentral::QuadShapeComponentRequestBus::EventResult(quadShapeConfig, GetEntityId(), + &LmbrCentral::QuadShapeComponentRequests::GetQuadConfiguration); + + const float minDimension = 1e-3f; // used to prevent the dimensions being 0 in any direction + const float xDim = AZ::GetMax(minDimension, quadShapeConfig.m_width); + const float yDim = AZ::GetMax(minDimension, quadShapeConfig.m_height); + + if (m_singleSided) + { + AZStd::vector cookedData; + + constexpr AZ::u32 vertexCount = 4; + const AZ::Vector3 vertices[vertexCount] = + { + AZ::Vector3(-0.5f * xDim, -0.5f * yDim, 0.0f), + AZ::Vector3(-0.5f * xDim, 0.5f * yDim, 0.0f), + AZ::Vector3(0.5f * xDim, 0.5f * yDim, 0.0f), + AZ::Vector3(0.5f * xDim, -0.5f * yDim, 0.0f), + }; + + constexpr AZ::u32 indexCount = 6; + const AZ::u32 indices[indexCount] = + { + 0, 1, 2, + 0, 2, 3 + }; + + bool cookingResult = false; + Physics::SystemRequestBus::BroadcastResult(cookingResult, &Physics::SystemRequests::CookTriangleMeshToMemory, + vertices, vertexCount, indices, indexCount, cookedData); + + Physics::CookedMeshShapeConfiguration shapeConfig; + shapeConfig.SetCookedMeshData(cookedData.data(), cookedData.size(), + Physics::CookedMeshShapeConfiguration::MeshType::TriangleMesh); + shapeConfig.m_scale = scale; + + SetShapeConfig(ShapeType::QuadSingleSided, shapeConfig); + } + + else + { + // it's not possible to create a perfectly 2d convex in PhysX, so the best we can do is a very thin box + const float zDim = AZ::GetMax(minDimension, 1e-3f * AZ::GetMin(xDim, yDim)); + const AZ::Vector3 boxDimensions(xDim, yDim, zDim); + + SetShapeConfig(ShapeType::QuadDoubleSided, Physics::BoxShapeConfiguration(boxDimensions)); + + m_shapeConfigs.back()->m_scale = scale; + } + } + void EditorShapeColliderComponent::UpdateCapsuleConfig(const AZ::Vector3& scale) { LmbrCentral::CapsuleShapeConfig lmbrCentralCapsuleShapeConfig; @@ -425,6 +506,12 @@ namespace PhysX } } + void EditorShapeColliderComponent::OnSingleSidedChange() + { + UpdateShapeConfigs(); + CreateStaticEditorCollider(); + } + AZ::u32 EditorShapeColliderComponent::OnSubdivisionCountChange() { const AZ::Vector3 uniformScale = Utils::GetUniformScale(GetEntityId()); @@ -615,6 +702,8 @@ namespace PhysX m_editorSceneHandle = m_sceneInterface->GetSceneHandle(AzPhysics::EditorPhysicsSceneName); } + UpdateTriggerSettings(); + UpdateSingleSidedSettings(); UpdateShapeConfigs(); // Debug drawing @@ -767,6 +856,7 @@ namespace PhysX if (changeReason == LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::ShapeChanged) { UpdateShapeConfigs(); + UpdateTriggerSettings(); CreateStaticEditorCollider(); Physics::ColliderComponentEventBus::Event(GetEntityId(), &Physics::ColliderComponentEvents::OnColliderChanged); @@ -849,4 +939,45 @@ namespace PhysX { return m_colliderConfig.m_isTrigger; } + + void EditorShapeColliderComponent::UpdateTriggerSettings() + { + if (m_shapeType == ShapeType::QuadSingleSided || m_shapeType == ShapeType::QuadDoubleSided) + { + if (!m_previousIsTrigger.has_value()) + { + m_previousIsTrigger = m_colliderConfig.m_isTrigger; + } + m_colliderConfig.SetPropertyVisibility(Physics::ColliderConfiguration::PropertyVisibility::IsTrigger, false); + } + else + { + if (m_previousIsTrigger.has_value()) + { + m_colliderConfig.m_isTrigger = m_previousIsTrigger.value(); + m_previousIsTrigger.reset(); + } + m_colliderConfig.SetPropertyVisibility(Physics::ColliderConfiguration::PropertyVisibility::IsTrigger, true); + } + } + + void EditorShapeColliderComponent::UpdateSingleSidedSettings() + { + if (GetEntity()->FindComponent()) + { + if (!m_previousSingleSided.has_value()) + { + m_previousSingleSided = m_singleSided; + } + m_singleSided = false; + } + else + { + if (m_previousSingleSided.has_value()) + { + m_singleSided = m_previousSingleSided.value(); + m_previousSingleSided.reset(); + } + } + } } // namespace PhysX diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h index 3422d4959f..0f5350b781 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h @@ -41,6 +41,8 @@ namespace PhysX Sphere, PolygonPrism, Cylinder, + QuadDoubleSided, + QuadSingleSided, Unsupported }; @@ -89,6 +91,7 @@ namespace PhysX AZ::u32 OnConfigurationChanged(); void UpdateShapeConfigs(); void UpdateBoxConfig(const AZ::Vector3& scale); + void UpdateQuadConfig(const AZ::Vector3& scale); void UpdateCapsuleConfig(const AZ::Vector3& scale); void UpdateSphereConfig(const AZ::Vector3& scale); void UpdateCylinderConfig(const AZ::Vector3& scale); @@ -103,6 +106,8 @@ namespace PhysX AZ::u32 OnSubdivisionCountChange(); AZ::Crc32 SubdivisionCountVisibility(); + void OnSingleSidedChange(); + AZ::Crc32 SingleSidedVisibility(); // AZ::Component void Activate() override; @@ -137,6 +142,9 @@ namespace PhysX AZ::Aabb GetColliderShapeAabb() override; bool IsTrigger() override; + void UpdateTriggerSettings(); + void UpdateSingleSidedSettings(); + Physics::ColliderConfiguration m_colliderConfig; //!< Stores collision layers, whether the collider is a trigger, etc. DebugDraw::Collider m_colliderDebugDraw; //!< Handles drawing the collider based on global and local AzPhysics::SceneInterface* m_sceneInterface = nullptr; @@ -151,6 +159,9 @@ namespace PhysX //! @note 16 is the number of subdivisions in the debug cylinder that is loaded as a mesh (not generated procedurally) AZ::u8 m_subdivisionCount = 16; mutable GeometryCache m_geometryCache; //!< Cached data for generating sample points inside the attached shape. + AZStd::optional m_previousIsTrigger; //!< Stores the previous trigger setting if the shape is changed to one which does not support triggers. + bool m_singleSided = false; //!< Used for 2d shapes like quad which may be treated as either single or doubled sided. + AZStd::optional m_previousSingleSided; //!< Stores the previous single sided setting when unable to support single-sided shapes (such as when used with a dynamic rigid body). AzPhysics::SystemEvents::OnConfigurationChangedEvent::Handler m_physXConfigChangedHandler; AzPhysics::SystemEvents::OnMaterialLibraryChangedEvent::Handler m_onMaterialLibraryChangedEventHandler; diff --git a/Gems/PhysX/Code/Source/ShapeColliderComponent.h b/Gems/PhysX/Code/Source/ShapeColliderComponent.h index 2065f17dcf..cfc51f08ed 100644 --- a/Gems/PhysX/Code/Source/ShapeColliderComponent.h +++ b/Gems/PhysX/Code/Source/ShapeColliderComponent.h @@ -30,6 +30,7 @@ namespace PhysX static const AZ::Crc32 Sphere = AZ_CRC("Sphere", 0x55f96687); static const AZ::Crc32 PolygonPrism = AZ_CRC("PolygonPrism", 0xd6b50036); static const AZ::Crc32 Cylinder = AZ_CRC("Cylinder", 0x9b045bea); + static const AZ::Crc32 Quad = AZ_CRC("QuadShape", 0x40d75e14); } // namespace ShapeConstants /// Component that provides a collider based on geometry from a shape component. From a8eb5be2e6f3952ce1fdff147104dd1e1e049d7a Mon Sep 17 00:00:00 2001 From: greerdv Date: Thu, 20 Jan 2022 13:24:01 +0000 Subject: [PATCH 2/5] fix order of triangle indices for single-sided quad collider Signed-off-by: greerdv --- Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index f61b2fce74..b935ff2536 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -407,8 +407,8 @@ namespace PhysX constexpr AZ::u32 indexCount = 6; const AZ::u32 indices[indexCount] = { - 0, 1, 2, - 0, 2, 3 + 0, 2, 1, + 0, 3, 2 }; bool cookingResult = false; From f61011f5ac8382c7db145538f873933a64e76b51 Mon Sep 17 00:00:00 2001 From: greerdv Date: Thu, 20 Jan 2022 18:14:33 +0000 Subject: [PATCH 3/5] add editor side tests for quad colliders Signed-off-by: greerdv --- .../Source/EditorShapeColliderComponent.cpp | 3 +- .../Tests/ShapeColliderComponentTests.cpp | 150 +++++++++++++++++- 2 files changed, 148 insertions(+), 5 deletions(-) diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index b935ff2536..bdf70fc227 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -702,9 +702,9 @@ namespace PhysX m_editorSceneHandle = m_sceneInterface->GetSceneHandle(AzPhysics::EditorPhysicsSceneName); } - UpdateTriggerSettings(); UpdateSingleSidedSettings(); UpdateShapeConfigs(); + UpdateTriggerSettings(); // Debug drawing m_colliderDebugDraw.Connect(GetEntityId()); @@ -947,6 +947,7 @@ namespace PhysX if (!m_previousIsTrigger.has_value()) { m_previousIsTrigger = m_colliderConfig.m_isTrigger; + m_colliderConfig.m_isTrigger = false; } m_colliderConfig.SetPropertyVisibility(Physics::ColliderConfiguration::PropertyVisibility::IsTrigger, false); } diff --git a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp index 06ba4f3e9c..21d381a259 100644 --- a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp +++ b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -452,21 +453,55 @@ namespace PhysXEditorTests EXPECT_TRUE(aabb.GetMin().IsClose(translation - 0.5f * scale * boxDimensions)); } - void SetTrigger(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent, bool isTrigger) + void SetBoolValueOnComponent(AZ::Component* component, AZ::Crc32 name, bool value) { AZ::SerializeContext* serializeContext = nullptr; AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext); AzToolsFramework::InstanceDataHierarchy instanceDataHierarchy; - instanceDataHierarchy.AddRootInstance(editorShapeColliderComponent); + instanceDataHierarchy.AddRootInstance(component); instanceDataHierarchy.Build(serializeContext, AZ::SerializeContext::ENUM_ACCESS_FOR_WRITE); AzToolsFramework::InstanceDataHierarchy::InstanceDataNode* instanceNode = - instanceDataHierarchy.FindNodeByPartialAddress({ AZ_CRC("Trigger", 0x1a6b0f5d) }); + instanceDataHierarchy.FindNodeByPartialAddress({ name }); if (instanceNode) { - instanceNode->Write(isTrigger); + instanceNode->Write(value); } } + void SetTrigger(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent, bool isTrigger) + { + SetBoolValueOnComponent(editorShapeColliderComponent, AZ_CRC("Trigger", 0x1a6b0f5d), isTrigger); + } + + bool GetBoolValueFromComponent(AZ::Component* component, AZ::Crc32 name) + { + AZ::SerializeContext* serializeContext = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext); + AzToolsFramework::InstanceDataHierarchy instanceDataHierarchy; + instanceDataHierarchy.AddRootInstance(component); + instanceDataHierarchy.Build(serializeContext, AZ::SerializeContext::ENUM_ACCESS_FOR_READ); + AzToolsFramework::InstanceDataHierarchy::InstanceDataNode* instanceNode = + instanceDataHierarchy.FindNodeByPartialAddress({ name }); + bool value = false; + instanceNode->Read(value); + return value; + } + + bool IsTrigger(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent) + { + return GetBoolValueFromComponent(editorShapeColliderComponent, AZ_CRC("Trigger")); + } + + void SetSingleSided(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent, bool singleSided) + { + SetBoolValueOnComponent(editorShapeColliderComponent, AZ_CRC("SingleSided"), singleSided); + } + + bool IsSingleSided(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent) + { + return GetBoolValueFromComponent(editorShapeColliderComponent, AZ_CRC("SingleSided")); + } + EntityPtr CreateRigidBox(const AZ::Vector3& boxDimensions, const AZ::Vector3& position) { EntityPtr rigidBodyEditorEntity = CreateInactiveEditorEntity("RigidBodyEditorEntity"); @@ -566,4 +601,111 @@ namespace PhysXEditorTests EXPECT_THAT(aabb.GetMin(), UnitTest::IsClose(-0.5f * boxDimensions * parentScale)); } + class PhysXEditorParamBoolFixture + : public ::testing::WithParamInterface + , public PhysXEditorFixture + { + }; + + TEST_P(PhysXEditorParamBoolFixture, EditorShapeColliderComponent_ShapeColliderWithQuadShapeNonUniformlyScalesCorrectly) + { + // test both single and double-sided quad colliders + bool singleSided = GetParam(); + + EntityPtr editorEntity = CreateInactiveEditorEntity("QuadEntity"); + editorEntity->CreateComponent(LmbrCentral::EditorQuadShapeComponentTypeId); + auto* shapeColliderComponent = editorEntity->CreateComponent(); + SetSingleSided(shapeColliderComponent, singleSided); + editorEntity->CreateComponent(); + const auto entityId = editorEntity->GetId(); + + editorEntity->Activate(); + + LmbrCentral::QuadShapeComponentRequestBus::Event(entityId, &LmbrCentral::QuadShapeComponentRequests::SetQuadWidth, 1.2f); + LmbrCentral::QuadShapeComponentRequestBus::Event(entityId, &LmbrCentral::QuadShapeComponentRequests::SetQuadHeight, 0.8f); + + // update the transform scale and non-uniform scale + AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalUniformScale, 3.0f); + AZ::NonUniformScaleRequestBus::Event(entityId, &AZ::NonUniformScaleRequests::SetScale, AZ::Vector3(1.5f, 0.5f, 1.0f)); + + // make a game entity and check that its AABB is as expected + EntityPtr gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get()); + AZ::Aabb aabb = gameEntity->FindComponent()->GetAabb(); + + EXPECT_NEAR(aabb.GetMin().GetX(), -2.7f, 1e-3f); + EXPECT_NEAR(aabb.GetMin().GetY(), -0.6f, 1e-3f); + EXPECT_NEAR(aabb.GetMax().GetX(), 2.7f, 1e-3f); + EXPECT_NEAR(aabb.GetMax().GetY(), 0.6f, 1e-3f); + EXPECT_TRUE(true); + } + + TEST_P(PhysXEditorParamBoolFixture, EditorShapeColliderComponent_TriggerSettingIsRememberedWhenSwitchingToQuadAndBack) + { + bool initialTriggerSetting = GetParam(); + + // create an editor entity with a box component (which does support trigger) + EntityPtr editorEntity = CreateInactiveEditorEntity("QuadEntity"); + auto* boxShapeComponent = editorEntity->CreateComponent(LmbrCentral::EditorBoxShapeComponentTypeId); + auto* shapeColliderComponent = editorEntity->CreateComponent(); + SetTrigger(shapeColliderComponent, initialTriggerSetting); + editorEntity->Activate(); + + // the trigger setting should be what it was set to + EXPECT_EQ(IsTrigger(shapeColliderComponent), initialTriggerSetting); + + // deactivate the entity and swap the box for a quad (which does not support trigger) + editorEntity->Deactivate(); + editorEntity->RemoveComponent(boxShapeComponent); + auto* quadShapeComponent = editorEntity->CreateComponent(LmbrCentral::EditorQuadShapeComponentTypeId); + editorEntity->Activate(); + + // the trigger setting should now be false, because quad shape does not support triggers + EXPECT_FALSE(IsTrigger(shapeColliderComponent)); + + // swap back to a box shape + editorEntity->Deactivate(); + editorEntity->RemoveComponent(quadShapeComponent); + editorEntity->AddComponent(boxShapeComponent); + editorEntity->Activate(); + + // the original trigger setting should have been remembered + EXPECT_EQ(IsTrigger(shapeColliderComponent), initialTriggerSetting); + + // the quad shape component is no longer attached to the entity so won't be automatically cleared up + delete quadShapeComponent; + } + + TEST_P(PhysXEditorParamBoolFixture, EditorShapeColliderComponent_SingleSidedSettingIsRememberedWhenAddingAndRemovingRigidBody) + { + bool initialSingleSidedSetting = GetParam(); + + // create an editor entity without a rigid body (that means both single-sided and double-sided quads are valid) + EntityPtr editorEntity = CreateInactiveEditorEntity("QuadEntity"); + editorEntity->CreateComponent(LmbrCentral::EditorQuadShapeComponentTypeId); + auto* shapeColliderComponent = editorEntity->CreateComponent(); + SetSingleSided(shapeColliderComponent, initialSingleSidedSetting); + editorEntity->Activate(); + + // verify that the single sided setting matches the initial value + EXPECT_EQ(IsSingleSided(shapeColliderComponent), initialSingleSidedSetting); + + // add an editor rigid body component (this should mean single-sided quads are not supported) + editorEntity->Deactivate(); + auto rigidBodyComponent = editorEntity->CreateComponent(); + editorEntity->Activate(); + + EXPECT_FALSE(IsSingleSided(shapeColliderComponent)); + + // remove the editor rigid body component (the previous single-sided setting should be restored) + editorEntity->Deactivate(); + editorEntity->RemoveComponent(rigidBodyComponent); + editorEntity->Activate(); + + EXPECT_EQ(IsSingleSided(shapeColliderComponent), initialSingleSidedSetting); + + // the rigid body component is no longer attached to the entity so won't be automatically cleared up + delete rigidBodyComponent; + } + + INSTANTIATE_TEST_CASE_P(PhysXEditorTests, PhysXEditorParamBoolFixture, ::testing::Bool()); } // namespace PhysXEditorTests From 6a700e6b955d784b814cf7d39aa1ed65244236fa Mon Sep 17 00:00:00 2001 From: greerdv Date: Thu, 20 Jan 2022 19:10:56 +0000 Subject: [PATCH 4/5] add test for runtime behaviour of single sided quad collider Signed-off-by: greerdv --- .../Tests/ShapeColliderComponentTests.cpp | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp index 21d381a259..c164e5de63 100644 --- a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp +++ b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp @@ -707,5 +707,39 @@ namespace PhysXEditorTests delete rigidBodyComponent; } - INSTANTIATE_TEST_CASE_P(PhysXEditorTests, PhysXEditorParamBoolFixture, ::testing::Bool()); + INSTANTIATE_TEST_CASE_P(PhysXEditorTest, PhysXEditorParamBoolFixture, ::testing::Bool()); + + TEST_F(PhysXEditorFixture, EditorShapeColliderComponent_SingleSidedQuadDoesNotCollideFromBelow) + { + // create an editor entity without a rigid body (that means both single-sided and double-sided quads are valid), positioned at the origin + EntityPtr editorQuadEntity = CreateInactiveEditorEntity("QuadEntity"); + editorQuadEntity->CreateComponent(LmbrCentral::EditorQuadShapeComponentTypeId); + auto* shapeColliderComponent = editorQuadEntity->CreateComponent(); + SetSingleSided(shapeColliderComponent, true); + editorQuadEntity->Activate(); + LmbrCentral::QuadShapeComponentRequestBus::Event(editorQuadEntity->GetId(), &LmbrCentral::QuadShapeComponentRequests::SetQuadHeight, 10.0f); + LmbrCentral::QuadShapeComponentRequestBus::Event(editorQuadEntity->GetId(), &LmbrCentral::QuadShapeComponentRequests::SetQuadWidth, 10.0f); + + // add a second entity with a box collider and a rigid body, positioned below the quad + EntityPtr editorBoxEntity = CreateInactiveEditorEntity("BoxEntity"); + editorBoxEntity->CreateComponent(LmbrCentral::BoxShapeComponentTypeId); + editorBoxEntity->CreateComponent(); + editorBoxEntity->CreateComponent(); + editorBoxEntity->Activate(); + AZ::TransformBus::Event(editorBoxEntity->GetId(), &AZ::TransformBus::Events::SetWorldTranslation, -AZ::Vector3::CreateAxisZ()); + + EntityPtr gameQuadEntity = CreateActiveGameEntityFromEditorEntity(editorQuadEntity.get()); + EntityPtr gameBoxEntity = CreateActiveGameEntityFromEditorEntity(editorBoxEntity.get()); + + // give the box enough upward velocity to rise above the level of the quad and simulate + Physics::RigidBodyRequestBus::Event(gameBoxEntity->GetId(), &Physics::RigidBodyRequests::SetLinearVelocity, AZ::Vector3::CreateAxisZ(6.0f)); + PhysX::TestUtils::UpdateScene(m_defaultScene, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 200); + + // the box should travel through the base of the quad because it has no collision from that direction + // and land on the top surface of the quad, which does have collision + float finalHeight = 0.0f; + AZ::TransformBus::EventResult(finalHeight, gameBoxEntity->GetId(), &AZ::TransformBus::Events::GetWorldZ); + + EXPECT_GT(finalHeight, 0.0f); + } } // namespace PhysXEditorTests From d412f3cc502d598464422444ea991cd0cfb0ab84 Mon Sep 17 00:00:00 2001 From: greerdv Date: Mon, 24 Jan 2022 12:38:15 +0000 Subject: [PATCH 5/5] update with feedback from PR Signed-off-by: greerdv --- .../Source/EditorShapeColliderComponent.cpp | 17 ++++++++--------- .../Code/Source/EditorShapeColliderComponent.h | 4 ++-- .../Code/Tests/ShapeColliderComponentTests.cpp | 16 +++++++++------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp index bdf70fc227..d99a76f306 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.cpp @@ -55,7 +55,7 @@ namespace PhysX m_colliderConfig.SetPropertyVisibility(Physics::ColliderConfiguration::Offset, false); } - AZ::Crc32 EditorShapeColliderComponent::SubdivisionCountVisibility() + AZ::Crc32 EditorShapeColliderComponent::SubdivisionCountVisibility() const { if (m_shapeType == ShapeType::Cylinder) { @@ -65,7 +65,7 @@ namespace PhysX return AZ::Edit::PropertyVisibility::Hide; } - AZ::Crc32 EditorShapeColliderComponent::SingleSidedVisibility() + AZ::Crc32 EditorShapeColliderComponent::SingleSidedVisibility() const { if ((m_shapeType == ShapeType::QuadSingleSided || m_shapeType == ShapeType::QuadDoubleSided) && GetEntity()->FindComponent() == nullptr) @@ -124,16 +124,16 @@ namespace PhysX void EditorShapeColliderComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { - provided.push_back(AZ_CRC("PhysicsWorldBodyService", 0x944da0cc)); - provided.push_back(AZ_CRC("PhysXColliderService", 0x4ff43f7c)); - provided.push_back(AZ_CRC("PhysXTriggerService", 0x3a117d7b)); - provided.push_back(AZ_CRC("PhysXShapeColliderService", 0x98a7e779)); + provided.push_back(AZ_CRC_CE("PhysicsWorldBodyService")); + provided.push_back(AZ_CRC_CE("PhysXColliderService")); + provided.push_back(AZ_CRC_CE("PhysXTriggerService")); + provided.push_back(AZ_CRC_CE("PhysXShapeColliderService")); } void EditorShapeColliderComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) { - required.push_back(AZ_CRC("TransformService", 0x8ee22c50)); - required.push_back(AZ_CRC("ShapeService", 0xe86aa5fe)); + required.push_back(AZ_CRC_CE("TransformService")); + required.push_back(AZ_CRC_CE("ShapeService")); } void EditorShapeColliderComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) @@ -422,7 +422,6 @@ namespace PhysX SetShapeConfig(ShapeType::QuadSingleSided, shapeConfig); } - else { // it's not possible to create a perfectly 2d convex in PhysX, so the best we can do is a very thin box diff --git a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h index 0f5350b781..09d5f803fc 100644 --- a/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h +++ b/Gems/PhysX/Code/Source/EditorShapeColliderComponent.h @@ -105,9 +105,9 @@ namespace PhysX void RefreshUiProperties(); AZ::u32 OnSubdivisionCountChange(); - AZ::Crc32 SubdivisionCountVisibility(); + AZ::Crc32 SubdivisionCountVisibility() const; void OnSingleSidedChange(); - AZ::Crc32 SingleSidedVisibility(); + AZ::Crc32 SingleSidedVisibility() const; // AZ::Component void Activate() override; diff --git a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp index c164e5de63..c9dcbe64d1 100644 --- a/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp +++ b/Gems/PhysX/Code/Tests/ShapeColliderComponentTests.cpp @@ -470,7 +470,7 @@ namespace PhysXEditorTests void SetTrigger(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent, bool isTrigger) { - SetBoolValueOnComponent(editorShapeColliderComponent, AZ_CRC("Trigger", 0x1a6b0f5d), isTrigger); + SetBoolValueOnComponent(editorShapeColliderComponent, AZ_CRC_CE("Trigger"), isTrigger); } bool GetBoolValueFromComponent(AZ::Component* component, AZ::Crc32 name) @@ -489,17 +489,17 @@ namespace PhysXEditorTests bool IsTrigger(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent) { - return GetBoolValueFromComponent(editorShapeColliderComponent, AZ_CRC("Trigger")); + return GetBoolValueFromComponent(editorShapeColliderComponent, AZ_CRC_CE("Trigger")); } void SetSingleSided(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent, bool singleSided) { - SetBoolValueOnComponent(editorShapeColliderComponent, AZ_CRC("SingleSided"), singleSided); + SetBoolValueOnComponent(editorShapeColliderComponent, AZ_CRC_CE("SingleSided"), singleSided); } bool IsSingleSided(PhysX::EditorShapeColliderComponent* editorShapeColliderComponent) { - return GetBoolValueFromComponent(editorShapeColliderComponent, AZ_CRC("SingleSided")); + return GetBoolValueFromComponent(editorShapeColliderComponent, AZ_CRC_CE("SingleSided")); } EntityPtr CreateRigidBox(const AZ::Vector3& boxDimensions, const AZ::Vector3& position) @@ -636,7 +636,6 @@ namespace PhysXEditorTests EXPECT_NEAR(aabb.GetMin().GetY(), -0.6f, 1e-3f); EXPECT_NEAR(aabb.GetMax().GetX(), 2.7f, 1e-3f); EXPECT_NEAR(aabb.GetMax().GetY(), 0.6f, 1e-3f); - EXPECT_TRUE(true); } TEST_P(PhysXEditorParamBoolFixture, EditorShapeColliderComponent_TriggerSettingIsRememberedWhenSwitchingToQuadAndBack) @@ -731,9 +730,12 @@ namespace PhysXEditorTests EntityPtr gameQuadEntity = CreateActiveGameEntityFromEditorEntity(editorQuadEntity.get()); EntityPtr gameBoxEntity = CreateActiveGameEntityFromEditorEntity(editorBoxEntity.get()); - // give the box enough upward velocity to rise above the level of the quad and simulate + // give the box enough upward velocity to rise above the level of the quad + // simulate for enough time that the box would have reached the top of its trajectory and fallen back past the starting point if + // it hadn't collided with the top of the quad + const int numTimesteps = 100; Physics::RigidBodyRequestBus::Event(gameBoxEntity->GetId(), &Physics::RigidBodyRequests::SetLinearVelocity, AZ::Vector3::CreateAxisZ(6.0f)); - PhysX::TestUtils::UpdateScene(m_defaultScene, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 200); + PhysX::TestUtils::UpdateScene(m_defaultScene, AzPhysics::SystemConfiguration::DefaultFixedTimestep, numTimesteps); // the box should travel through the base of the quad because it has no collision from that direction // and land on the top surface of the quad, which does have collision