diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Collision/CollisionGroups.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Collision/CollisionGroups.cpp index 36bd7178c1..f8cc37156e 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Collision/CollisionGroups.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Collision/CollisionGroups.cpp @@ -34,6 +34,28 @@ namespace AzPhysics const CollisionGroup CollisionGroup::All_NoTouchBend = CollisionGroup::All.GetMask() & ~CollisionLayer::TouchBend.GetMask(); #endif + void CollisionGroupScriptConstructor(CollisionGroup* thisPtr, AZ::ScriptDataContext& scriptDataContext) + { + int numArgs = scriptDataContext.GetNumArguments(); + if (numArgs != 1) + { + scriptDataContext.GetScriptContext()->Error(AZ::ScriptContext::ErrorType::Error, true, + "CollisionGroup() accepts only 1 argument, not %d", numArgs); + return; + } + + if (!scriptDataContext.IsString(0)) + { + scriptDataContext.GetScriptContext()->Error(AZ::ScriptContext::ErrorType::Error, true, + "Argument to CollisionGroup() should be string"); + return; + } + + AZStd::string groupName; + scriptDataContext.ReadArg(0, groupName); + *thisPtr = CollisionGroup(groupName); + } + void CollisionGroup::Reflect(AZ::ReflectContext* context) { if (auto* serializeContext = azrtti_cast(context)) @@ -50,7 +72,9 @@ namespace AzPhysics ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) ->Attribute(AZ::Script::Attributes::Module, "physics") ->Attribute(AZ::Script::Attributes::Category, "AzPhysics") - ->Constructor() + ->Constructor() + ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value) + ->Attribute(AZ::Script::Attributes::ConstructorOverride, &CollisionGroupScriptConstructor) ; } } diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSceneQueries.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSceneQueries.cpp index 92cd89a801..30e0cde9df 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSceneQueries.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Common/PhysicsSceneQueries.cpp @@ -88,6 +88,19 @@ namespace AzPhysics ; } } + + if (auto* behaviorContext = azdynamic_cast(context)) + { + behaviorContext->Class("SceneQueryRequest") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Module, "physics") + ->Attribute(AZ::Script::Attributes::Category, "PhysX") + ->Property("Collision", BehaviorValueProperty(&SceneQueryRequest::m_collisionGroup)) + // Until enum class support for behavior context is done, expose this as an int + ->Property("QueryType", [](const SceneQueryRequest& self) { return static_cast(self.m_queryType); }, + [](SceneQueryRequest& self, int newQueryType) { self.m_queryType = SceneQuery::QueryType(newQueryType); }) + ; + } } /*static*/ void RayCastRequest::Reflect(AZ::ReflectContext* context) @@ -123,10 +136,6 @@ namespace AzPhysics ->Property("Distance", BehaviorValueProperty(&RayCastRequest::m_distance)) ->Property("Start", BehaviorValueProperty(&RayCastRequest::m_start)) ->Property("Direction", BehaviorValueProperty(&RayCastRequest::m_direction)) - ->Property("Collision", BehaviorValueProperty(&RayCastRequest::m_collisionGroup)) - // Until enum class support for behavior context is done, expose this as an int - ->Property("QueryType", [](const RayCastRequest& self) { return static_cast(self.m_queryType); }, - [](RayCastRequest& self, int newQueryType) { self.m_queryType = SceneQuery::QueryType(newQueryType); }) ; } } diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp index 809b4a2b0f..6498b6afce 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Utils.cpp @@ -73,21 +73,22 @@ namespace Physics { if (auto behaviorContext = azrtti_cast(context)) { - behaviorContext->EBus("CharacterControllerRequestBus", "Character Controller") - ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::RuntimeOwn) + behaviorContext->EBus("CharacterControllerRequestBus") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Module, "physics") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") - ->Event("GetBasePosition", &Physics::CharacterRequests::GetBasePosition, "Get Base Position") - ->Event("SetBasePosition", &Physics::CharacterRequests::SetBasePosition, "Set Base Position") - ->Event("GetCenterPosition", &Physics::CharacterRequests::GetCenterPosition, "Get Center Position") - ->Event("GetStepHeight", &Physics::CharacterRequests::GetStepHeight, "Get Step Height") - ->Event("SetStepHeight", &Physics::CharacterRequests::SetStepHeight, "Set Step Height") - ->Event("GetUpDirection", &Physics::CharacterRequests::GetUpDirection, "Get Up Direction") - ->Event("GetSlopeLimitDegrees", &Physics::CharacterRequests::GetSlopeLimitDegrees, "Get Slope Limit (Degrees)") - ->Event("SetSlopeLimitDegrees", &Physics::CharacterRequests::SetSlopeLimitDegrees, "Set Slope Limit (Degrees)") - ->Event("GetMaximumSpeed", &Physics::CharacterRequests::GetMaximumSpeed, "Get Maximum Speed") - ->Event("SetMaximumSpeed", &Physics::CharacterRequests::SetMaximumSpeed, "Set Maximum Speed") - ->Event("GetVelocity", &Physics::CharacterRequests::GetVelocity, "Get Velocity") - ->Event("AddVelocity", &Physics::CharacterRequests::AddVelocity, "Add Velocity") + ->Event("GetBasePosition", &CharacterRequests::GetBasePosition, "Get Base Position") + ->Event("SetBasePosition", &CharacterRequests::SetBasePosition, "Set Base Position") + ->Event("GetCenterPosition", &CharacterRequests::GetCenterPosition, "Get Center Position") + ->Event("GetStepHeight", &CharacterRequests::GetStepHeight, "Get Step Height") + ->Event("SetStepHeight", &CharacterRequests::SetStepHeight, "Set Step Height") + ->Event("GetUpDirection", &CharacterRequests::GetUpDirection, "Get Up Direction") + ->Event("GetSlopeLimitDegrees", &CharacterRequests::GetSlopeLimitDegrees, "Get Slope Limit (Degrees)") + ->Event("SetSlopeLimitDegrees", &CharacterRequests::SetSlopeLimitDegrees, "Set Slope Limit (Degrees)") + ->Event("GetMaximumSpeed", &CharacterRequests::GetMaximumSpeed, "Get Maximum Speed") + ->Event("SetMaximumSpeed", &CharacterRequests::SetMaximumSpeed, "Set Maximum Speed") + ->Event("GetVelocity", &CharacterRequests::GetVelocity, "Get Velocity") + ->Event("AddVelocity", &CharacterRequests::AddVelocity, "Add Velocity") ; } } diff --git a/Gems/PhysX/Code/Source/Shape.cpp b/Gems/PhysX/Code/Source/Shape.cpp index b29e407e0c..129bfa9774 100644 --- a/Gems/PhysX/Code/Source/Shape.cpp +++ b/Gems/PhysX/Code/Source/Shape.cpp @@ -326,6 +326,12 @@ namespace PhysX AzPhysics::SceneQueryHit Shape::RayCastInternal(const AzPhysics::RayCastRequest& worldSpaceRequest, const physx::PxTransform& pose) { + if (const bool shouldCollide = worldSpaceRequest.m_collisionGroup.GetMask() & m_collisionLayer.GetMask(); + !shouldCollide) + { + return AzPhysics::SceneQueryHit(); + } + const physx::PxVec3 start = PxMathConvert(worldSpaceRequest.m_start); const physx::PxVec3 unitDir = PxMathConvert(worldSpaceRequest.m_direction); const physx::PxU32 maxHits = 1; diff --git a/Gems/PhysX/Code/Tests/PhysXScriptTest.cpp b/Gems/PhysX/Code/Tests/PhysXScriptTest.cpp new file mode 100644 index 0000000000..ac9a731d54 --- /dev/null +++ b/Gems/PhysX/Code/Tests/PhysXScriptTest.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include "PhysXTestFixtures.h" +#include "PhysXTestUtil.h" +#include "PhysXTestCommon.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace PhysX +{ + static AZStd::map s_testEntities; + + AZ::EntityId GetTestEntityId(const char* name) + { + if (auto it = s_testEntities.find(name); + it != s_testEntities.end()) + { + return it->second->GetId(); + } + + return AZ::EntityId(); + } + + void ExpectTrue(bool check) + { + EXPECT_TRUE(check); + } + + class PhysXScriptTest + : public PhysXDefaultWorldTest + { + public: + AZ_TYPE_INFO(PhysXScriptTest, "{337A9DB4-ACF7-42A7-92E5-48A9FF14B49C}"); + + void SetUp() override + { + PhysXDefaultWorldTest::SetUp(); + + m_behaviorContext = AZStd::make_unique(); + AZ::Entity::Reflect(m_behaviorContext.get()); + AZ::MathReflect(m_behaviorContext.get()); + AzFramework::EntityContext::Reflect(m_behaviorContext.get()); + Physics::ReflectionUtils::ReflectPhysicsApi(m_behaviorContext.get()); + m_behaviorContext->Method("ExpectTrue", &ExpectTrue); + m_behaviorContext->Method("GetTestEntityId", &GetTestEntityId); + m_scriptContext = AZStd::make_unique(); + m_scriptContext->BindTo(m_behaviorContext.get()); + } + + void TearDown() override + { + s_testEntities.clear(); + + m_scriptContext.reset(); + m_behaviorContext.reset(); + + PhysXDefaultWorldTest::TearDown(); + } + + AZ::BehaviorContext* GetBehaviorContext() + { + return m_behaviorContext.get(); + } + + AZ::ScriptContext* GetScriptContext() + { + return m_scriptContext.get(); + } + + private: + AZStd::unique_ptr m_behaviorContext; + AZStd::unique_ptr m_scriptContext; + }; + + TEST_F(PhysXScriptTest, ScriptedRaycast_RaycastNotIntersectingBox_ReturnsNoHits) + { + s_testEntities.insert( + { + "Box", + TestUtils::AddStaticUnitTestObject(GetDefaultSceneHandle(), AZ::Vector3::CreateZero(), "Box") + }); + + const char luaCode[] = + R"( + boxId = GetTestEntityId("Box") + request = RayCastRequest() + request.Start = Vector3(5.0, 0.0, 5.0) + request.Direction = Vector3(0.0, 0.0, -1.0) + request.Distance = 10.0 + hit = SimulatedBodyComponentRequestBus.Event.RayCast(boxId, request) + ExpectTrue(hit.EntityId == EntityId()) + )"; + + EXPECT_TRUE(GetScriptContext()->Execute(luaCode)); + } + + TEST_F(PhysXScriptTest, ScriptedRaycast_RaycastIntersectingBox_ReturnsHitOnBox) + { + s_testEntities.insert( + { + "Box", + TestUtils::AddStaticUnitTestObject(GetDefaultSceneHandle(), AZ::Vector3::CreateZero(), "Box") + }); + + const char luaCode[] = + R"( + boxId = GetTestEntityId("Box") + request = RayCastRequest() + request.Start = Vector3(0.0, 0.0, 5.0) + request.Direction = Vector3(0.0, 0.0, -1.0) + request.Distance = 10.0 + hit = SimulatedBodyComponentRequestBus.Event.RayCast(boxId, request) + ExpectTrue(hit.EntityId == boxId) + )"; + + EXPECT_TRUE(GetScriptContext()->Execute(luaCode)); + } + + TEST_F(PhysXScriptTest, ScriptedRaycast_RaycastNotInteractingCollisionFilters_ReturnsNoHit) + { + s_testEntities.insert( + { + "Box", + TestUtils::AddStaticUnitTestObject(GetDefaultSceneHandle(), AZ::Vector3::CreateZero(), "Box") + }); + + const char luaCode[] = + R"( + boxId = GetTestEntityId("Box") + request = RayCastRequest() + request.Start = Vector3(0.0, 0.0, 5.0) + request.Direction = Vector3(0.0, 0.0, -1.0) + request.Distance = 10.0 + request.Collision = CollisionGroup("None") + hit = SimulatedBodyComponentRequestBus.Event.RayCast(boxId, request) + ExpectTrue(hit.EntityId == EntityId()) + )"; + + EXPECT_TRUE(GetScriptContext()->Execute(luaCode)); + } +} // namespace PhysX diff --git a/Gems/PhysX/Code/physx_tests_files.cmake b/Gems/PhysX/Code/physx_tests_files.cmake index 09ecf19de5..d42e872d5d 100644 --- a/Gems/PhysX/Code/physx_tests_files.cmake +++ b/Gems/PhysX/Code/physx_tests_files.cmake @@ -29,6 +29,7 @@ set(FILES Tests/PhysXTestUtil.h Tests/PhysXTestUtil.cpp Tests/PhysXMultithreadingTest.cpp + Tests/PhysXScriptTest.cpp Tests/CharacterControllerTests.cpp Tests/RagdollConfiguration.xml Tests/RagdollTestData.h