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/PhysXSceneTests.cpp

589 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 <AzTest/AzTest.h>
#include <Tests/PhysXTestCommon.h>
#include <AzFramework/Physics/PhysicsSystem.h>
#include <AzFramework/Physics/Configuration/StaticRigidBodyConfiguration.h>
#include <AzFramework/Physics/PhysicsScene.h>
namespace PhysX
{
//setup a test fixture with a scene named 'TestScene'
class PhysXSceneFixture
: public testing::Test
{
public:
void SetUp() override
{
if (auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get())
{
AzPhysics::SceneConfiguration newSceneConfig;
newSceneConfig.m_sceneName = "TestScene";
m_testSceneHandle = physicsSystem->AddScene(newSceneConfig);
}
}
void TearDown() override
{
//Cleanup any created scenes
if (auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get())
{
physicsSystem->RemoveScene(m_testSceneHandle);
}
m_testSceneHandle = AzPhysics::InvalidSceneHandle;
}
AzPhysics::SceneHandle m_testSceneHandle;
};
TEST_F(PhysXSceneFixture, ChangesToConfiguration_TriggersEvents)
{
auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get();
//get the test scene, and the config.
AzPhysics::Scene* scene = physicsSystem->GetScene(m_testSceneHandle);
AzPhysics::SceneConfiguration newConfig = scene->GetConfiguration();
//make a modification
newConfig.m_gravity.SetX(42);
//setup the handler
bool eventTriggered = false;
AzPhysics::SceneEvents::OnSceneConfigurationChanged::Handler onConfigChanged(
[&eventTriggered, newConfig, this](AzPhysics::SceneHandle sceneHandle, const AzPhysics::SceneConfiguration& config)
{
eventTriggered = true;
//the event should come from the test scene
EXPECT_EQ(sceneHandle, m_testSceneHandle);
//the config should match the one provided
EXPECT_EQ(config, newConfig);
});
scene->RegisterSceneConfigurationChangedEventHandler(onConfigChanged);
//apply the config
scene->UpdateConfiguration(newConfig);
//The event should be triggered.
EXPECT_TRUE(eventTriggered);
}
TEST_F(PhysXSceneFixture, CreateSimulatedBodies_WithSceneInterface)
{
auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
//add a static rigid body
AzPhysics::StaticRigidBodyConfiguration config;
config.m_colliderAndShapeData = AzPhysics::ShapeColliderPair(
AZStd::make_shared<Physics::ColliderConfiguration>(),
AZStd::make_shared<Physics::BoxShapeConfiguration>(AZ::Vector3::CreateOne()));
AzPhysics::SimulatedBodyHandle simBodyHandle = sceneInterface->AddSimulatedBody(m_testSceneHandle, &config);
EXPECT_FALSE(simBodyHandle == AzPhysics::InvalidSimulatedBodyHandle);
}
TEST_F(PhysXSceneFixture, AddSimulatedBodies_returnsExpected)
{
auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
AzPhysics::SimulatedBodyConfigurationList configs;
//invalid scene handle returns empty
AzPhysics::SimulatedBodyHandleList emptyBodies = sceneInterface->AddSimulatedBodies(AzPhysics::InvalidSceneHandle, configs);
EXPECT_TRUE(emptyBodies.empty());
emptyBodies = sceneInterface->AddSimulatedBodies(AzPhysics::SceneHandle(static_cast<AZ::u32>(2347892347890), AzPhysics::SceneIndex(7)), configs);
EXPECT_TRUE(emptyBodies.empty());
//add some rigid bodies
AzPhysics::ShapeColliderPair shapeColliderData(
AZStd::make_shared<Physics::ColliderConfiguration>(),
AZStd::make_shared<Physics::BoxShapeConfiguration>(AZ::Vector3::CreateOne()));
constexpr const int numberOfBodies = 100;
for (int i = 0; i < numberOfBodies; i++)
{
const float xpos = 2.0f * static_cast<float>(i);
AzPhysics::RigidBodyConfiguration* config = aznew AzPhysics::RigidBodyConfiguration();
config->m_colliderAndShapeData = shapeColliderData;
config->m_position = AZ::Vector3::CreateAxisX(xpos);
configs.emplace_back(config);
}
//add 1 null entry into the list
const int nullIndx = numberOfBodies / 3;
configs.insert(configs.begin() + nullIndx, nullptr);
//add the bodies
AzPhysics::SimulatedBodyHandleList newBodies = sceneInterface->AddSimulatedBodies(m_testSceneHandle, configs);
//should have a list of valid handles of the same size.
EXPECT_TRUE(newBodies.size() == configs.size());
for (int i = 0; i < numberOfBodies; i++)
{
if (i != nullIndx) //all other indexes should have valid handles.
{
EXPECT_FALSE(newBodies[i] == AzPhysics::InvalidSimulatedBodyHandle);
}
else //this is the index where a null config was sent, this should be invalid.
{
EXPECT_TRUE(newBodies[i] == AzPhysics::InvalidSimulatedBodyHandle);
}
}
//cleanup
for (auto* config : configs)
{
delete config;
}
configs.clear();
}
TEST_F(PhysXSceneFixture, GetSimulatedBodies_returnsExpected)
{
auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
AzPhysics::SimulatedBodyConfigurationList configs;
//add some rigid bodies
AzPhysics::ShapeColliderPair shapeColliderData(
AZStd::make_shared<Physics::ColliderConfiguration>(),
AZStd::make_shared<Physics::BoxShapeConfiguration>(AZ::Vector3::CreateOne()));
constexpr const int numberOfBodies = 100;
for (int i = 0; i < numberOfBodies; i++)
{
const float xpos = 2.0f * static_cast<float>(i);
AzPhysics::RigidBodyConfiguration* config = aznew AzPhysics::RigidBodyConfiguration();
config->m_colliderAndShapeData = shapeColliderData;
config->m_position = AZ::Vector3::CreateAxisX(xpos);
configs.emplace_back(config);
}
//add the bodies
AzPhysics::SimulatedBodyHandleList newBodies = sceneInterface->AddSimulatedBodies(m_testSceneHandle, configs);
//invalid scene handle returns null
AzPhysics::SimulatedBody* nullBody = sceneInterface->GetSimulatedBodyFromHandle(AzPhysics::InvalidSceneHandle, newBodies[0]);
EXPECT_TRUE(nullBody == nullptr);
nullBody = sceneInterface->GetSimulatedBodyFromHandle(AzPhysics::SceneHandle(static_cast<AZ::u32>(2347892347890), AzPhysics::SceneIndex(7)), newBodies[0]);
EXPECT_TRUE(nullBody == nullptr);
//invalid simulated body handle returns null
nullBody = sceneInterface->GetSimulatedBodyFromHandle(AzPhysics::InvalidSceneHandle, AzPhysics::InvalidSimulatedBodyHandle);
EXPECT_TRUE(nullBody == nullptr);
nullBody = sceneInterface->GetSimulatedBodyFromHandle(m_testSceneHandle, AzPhysics::SimulatedBodyHandle(1347892348, 9));
EXPECT_TRUE(nullBody == nullptr);
//get 1 simulated body, should not be null.
AzPhysics::SimulatedBody* bodyPtr = sceneInterface->GetSimulatedBodyFromHandle(m_testSceneHandle, newBodies[0]);
EXPECT_TRUE(bodyPtr != nullptr);
//request by list
AzPhysics::SimulatedBodyList bodies = sceneInterface->GetSimulatedBodiesFromHandle(m_testSceneHandle, newBodies);
//lists should be the same size
EXPECT_EQ(bodies.size(), newBodies.size());
//none should be null
for (auto* body : bodies)
{
EXPECT_TRUE(body != nullptr);
}
//cleanup
for (auto* config : configs)
{
delete config;
}
configs.clear();
}
TEST_F(PhysXSceneFixture, RemovedSimulatedBody_IsRemoved)
{
auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
//add a simulated body
AzPhysics::ShapeColliderPair shapeColliderData(
AZStd::make_shared<Physics::ColliderConfiguration>(),
AZStd::make_shared<Physics::BoxShapeConfiguration>(AZ::Vector3::CreateOne()));
AzPhysics::StaticRigidBodyConfiguration config;
config.m_colliderAndShapeData = shapeColliderData;
AzPhysics::SimulatedBodyHandle simBodyHandle = sceneInterface->AddSimulatedBody(m_testSceneHandle, &config);
//remove the body
sceneInterface->RemoveSimulatedBody(m_testSceneHandle, simBodyHandle);
//GetSimulatedBodyFromHandle should return nullptr
EXPECT_EQ(sceneInterface->GetSimulatedBodyFromHandle(m_testSceneHandle, simBodyHandle), nullptr);
}
TEST_F(PhysXSceneFixture, RemovedSimulatedBody_FreesSimulatedBodyIndex_ForNextCreated)
{
auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
//add a few simulated body
AzPhysics::ShapeColliderPair shapeColliderData(
AZStd::make_shared<Physics::ColliderConfiguration>(),
AZStd::make_shared<Physics::BoxShapeConfiguration>(AZ::Vector3::CreateOne()));
AzPhysics::StaticRigidBodyConfiguration config;
config.m_colliderAndShapeData = shapeColliderData;
AzPhysics::SimulatedBodyHandleList simBodyHandles;
constexpr const int numBodies = 10;
for (int i = 0; i < numBodies; i++)
{
simBodyHandles.emplace_back(sceneInterface->AddSimulatedBody(m_testSceneHandle, &config));
}
//select 1 to remove
AzPhysics::SimulatedBodyHandle removedSelection = simBodyHandles[simBodyHandles.size() / 2];
const AzPhysics::SimulatedBodyIndex removedIndex = AZStd::get<AzPhysics::HandleTypeIndex::Index>(removedSelection);
sceneInterface->RemoveSimulatedBody(m_testSceneHandle, removedSelection);
// The removedSelection handle should be set to invalid in RemoveSimulatedBody
EXPECT_EQ(removedSelection, AzPhysics::InvalidSimulatedBodyHandle);
//add a new one.
AzPhysics::SimulatedBodyHandle newSimBodyHandle = sceneInterface->AddSimulatedBody(m_testSceneHandle, &config);
//The old and new handle should share an index as the freed slot will be used
EXPECT_EQ(removedIndex,
AZStd::get<AzPhysics::HandleTypeIndex::Index>(newSimBodyHandle));
}
TEST_F(PhysXSceneFixture, AddRemoveSimulatedBodies_SendEvents)
{
auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
//setup the add/remove handlers
bool addTriggered = false;
AzPhysics::SimulatedBodyHandle addEventSimBodyHandle = AzPhysics::InvalidSimulatedBodyHandle;
AzPhysics::SceneEvents::OnSimulationBodyAdded::Handler addedEvent(
[&addEventSimBodyHandle, &addTriggered, this](AzPhysics::SceneHandle sceneHandle, AzPhysics::SimulatedBodyHandle simBodyHandle)
{
addTriggered = true;
addEventSimBodyHandle = simBodyHandle;
EXPECT_EQ(sceneHandle, m_testSceneHandle);
});
bool removedTriggered = false;
AzPhysics::SimulatedBodyHandle removeEventSimBodyHandle = AzPhysics::InvalidSimulatedBodyHandle;
AzPhysics::SceneEvents::OnSimulationBodyRemoved::Handler removedEvent(
[&removeEventSimBodyHandle, &removedTriggered, this](AzPhysics::SceneHandle sceneHandle, AzPhysics::SimulatedBodyHandle simBodyHandle)
{
removedTriggered = true;
removeEventSimBodyHandle = simBodyHandle;
EXPECT_EQ(sceneHandle, m_testSceneHandle);
});
//register the handlers
sceneInterface->RegisterSimulationBodyAddedHandler(m_testSceneHandle, addedEvent);
sceneInterface->RegisterSimulationBodyRemovedHandler(m_testSceneHandle, removedEvent);
//add a simulated body
AzPhysics::ShapeColliderPair shapeColliderData(
AZStd::make_shared<Physics::ColliderConfiguration>(),
AZStd::make_shared<Physics::BoxShapeConfiguration>(AZ::Vector3::CreateOne()));
AzPhysics::StaticRigidBodyConfiguration config;
config.m_colliderAndShapeData = shapeColliderData;
AzPhysics::SimulatedBodyHandle simBodyHandle = sceneInterface->AddSimulatedBody(m_testSceneHandle, &config);
EXPECT_TRUE(addTriggered);
EXPECT_EQ(simBodyHandle, addEventSimBodyHandle);
//remove the body
const AzPhysics::SimulatedBodyHandle removedHandle = simBodyHandle; //copy the handle as RemoveSimulatedBody will mark it invalid.
sceneInterface->RemoveSimulatedBody(m_testSceneHandle, simBodyHandle);
EXPECT_TRUE(removedTriggered);
EXPECT_EQ(removedHandle, removeEventSimBodyHandle);
}
TEST_F(PhysXSceneFixture, StartFinishSimulationEvents_triggerAsExpected)
{
auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get();
auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
//add another scene for testing that only the registered scene sends the event + able to reuse 1 event handler for both scenes.
AzPhysics::SceneConfiguration newSceneConfig;
newSceneConfig.m_sceneName = "SecondTestScene";
AzPhysics::SceneHandle secondTestSceneHandle = physicsSystem->AddScene(newSceneConfig);
//struct to hold data for handlers
struct EventTriggerData
{
int startCount = 0;
int finishCount = 0;
AzPhysics::SceneHandle m_sceneHandle = AzPhysics::InvalidSceneHandle;
};
// setup start/finish handlers that will connect to the test scene.
EventTriggerData testScenedata;
testScenedata.m_sceneHandle = m_testSceneHandle;
AzPhysics::SceneEvents::OnSceneSimulationStartHandler startTestSceneHandler(
[&testScenedata](AzPhysics::SceneHandle sceneHandle, float fixedDeltaTime)
{
testScenedata.startCount++;
EXPECT_EQ(sceneHandle, testScenedata.m_sceneHandle);
EXPECT_NEAR(AzPhysics::SystemConfiguration::DefaultFixedTimestep, fixedDeltaTime, 0.001f);
});
AzPhysics::SceneEvents::OnSceneSimulationFinishHandler finishTestSceneHandler(
[&testScenedata](AzPhysics::SceneHandle sceneHandle, [[maybe_unused]] float fixedDeltatime)
{
testScenedata.finishCount++;
EXPECT_EQ(sceneHandle, testScenedata.m_sceneHandle);
});
sceneInterface->RegisterSceneSimulationStartHandler(m_testSceneHandle, startTestSceneHandler);
sceneInterface->RegisterSceneSimulationFinishHandler(m_testSceneHandle, finishTestSceneHandler);
// setup start/finish handlers that will connect to the second test scene.
EventTriggerData secondTestScenedata;
secondTestScenedata.m_sceneHandle = secondTestSceneHandle;
AzPhysics::SceneEvents::OnSceneSimulationStartHandler startSecondTestSceneHandler(
[&secondTestScenedata](AzPhysics::SceneHandle sceneHandle, float fixedDeltaTime)
{
secondTestScenedata.startCount++;
EXPECT_EQ(sceneHandle, secondTestScenedata.m_sceneHandle);
EXPECT_NEAR(AzPhysics::SystemConfiguration::DefaultFixedTimestep, fixedDeltaTime, 0.001f);
});
AzPhysics::SceneEvents::OnSceneSimulationFinishHandler finishSecondTestSceneHandler(
[&secondTestScenedata](AzPhysics::SceneHandle sceneHandle, [[maybe_unused]] float fixedDeltatime)
{
secondTestScenedata.finishCount++;
EXPECT_EQ(sceneHandle, secondTestScenedata.m_sceneHandle);
});
sceneInterface->RegisterSceneSimulationStartHandler(secondTestSceneHandle, startSecondTestSceneHandler);
sceneInterface->RegisterSceneSimulationFinishHandler(secondTestSceneHandle, finishSecondTestSceneHandler);
//update the system to trigger the events
physicsSystem->Simulate(AzPhysics::SystemConfiguration::DefaultFixedTimestep);
//handlers should trigger once
EXPECT_EQ(testScenedata.startCount, 1);
EXPECT_EQ(testScenedata.finishCount, 1);
EXPECT_EQ(secondTestScenedata.startCount, 1);
EXPECT_EQ(secondTestScenedata.finishCount, 1);
//clean up
physicsSystem->RemoveScene(secondTestSceneHandle);
}
TEST_F(PhysXSceneFixture, StartFinishSimulationEvents_triggerInCorrectOrder)
{
auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
AZStd::vector<AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority> orderedEventTriggersStart;
AZStd::vector<AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority> orderedEventTriggersFinish;
//setup the start simulate handlers
AzPhysics::SceneEvents::OnSceneSimulationStartHandler physicsStartEvent(
[&orderedEventTriggersStart](
[[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
[[maybe_unused]] float fixedDeltaTime )
{
orderedEventTriggersStart.push_back(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Physics);
}, aznumeric_cast<int32_t>(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Physics));
AzPhysics::SceneEvents::OnSceneSimulationStartHandler animationStartEvent(
[&orderedEventTriggersStart](
[[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
[[maybe_unused]] float fixedDeltaTime)
{
orderedEventTriggersStart.push_back(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Animation);
}, aznumeric_cast<int32_t>(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Animation));
AzPhysics::SceneEvents::OnSceneSimulationStartHandler componentsStartEvent(
[&orderedEventTriggersStart](
[[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
[[maybe_unused]] float fixedDeltaTime)
{
orderedEventTriggersStart.push_back(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Components);
}, aznumeric_cast<int32_t>(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Components));
AzPhysics::SceneEvents::OnSceneSimulationStartHandler scriptingStartEvent(
[&orderedEventTriggersStart](
[[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
[[maybe_unused]] float fixedDeltaTime)
{
orderedEventTriggersStart.push_back(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Scripting);
}, aznumeric_cast<int32_t>(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Scripting));
AzPhysics::SceneEvents::OnSceneSimulationStartHandler audioStartEvent(
[&orderedEventTriggersStart](
[[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
[[maybe_unused]] float fixedDeltaTime)
{
orderedEventTriggersStart.push_back(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Audio);
}, aznumeric_cast<int32_t>(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Audio));
AzPhysics::SceneEvents::OnSceneSimulationStartHandler defaultStartEvent(
[&orderedEventTriggersStart](
[[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
[[maybe_unused]] float fixedDeltaTime)
{
orderedEventTriggersStart.push_back(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Default);
}, aznumeric_cast<int32_t>(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Default));
sceneInterface->RegisterSceneSimulationStartHandler(m_testSceneHandle, physicsStartEvent);
sceneInterface->RegisterSceneSimulationStartHandler(m_testSceneHandle, animationStartEvent);
sceneInterface->RegisterSceneSimulationStartHandler(m_testSceneHandle, componentsStartEvent);
sceneInterface->RegisterSceneSimulationStartHandler(m_testSceneHandle, scriptingStartEvent);
sceneInterface->RegisterSceneSimulationStartHandler(m_testSceneHandle, audioStartEvent);
sceneInterface->RegisterSceneSimulationStartHandler(m_testSceneHandle, defaultStartEvent);
AzPhysics::SceneEvents::OnSceneSimulationFinishHandler physicsFinishEvent(
[&orderedEventTriggersFinish](
[[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
[[maybe_unused]] float fixedDeltatime)
{
orderedEventTriggersFinish.push_back(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Physics);
}, aznumeric_cast<int32_t>(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Physics));
AzPhysics::SceneEvents::OnSceneSimulationFinishHandler animationFinishEvent(
[&orderedEventTriggersFinish](
[[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
[[maybe_unused]] float fixedDeltatime)
{
orderedEventTriggersFinish.push_back(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Animation);
}, aznumeric_cast<int32_t>(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Animation));
AzPhysics::SceneEvents::OnSceneSimulationFinishHandler componentsFinishEvent(
[&orderedEventTriggersFinish](
[[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
[[maybe_unused]] float fixedDeltatime)
{
orderedEventTriggersFinish.push_back(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Components);
}, aznumeric_cast<int32_t>(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Components));
AzPhysics::SceneEvents::OnSceneSimulationFinishHandler scriptingFinishEvent(
[&orderedEventTriggersFinish](
[[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
[[maybe_unused]] float fixedDeltatime)
{
orderedEventTriggersFinish.push_back(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Scripting);
}, aznumeric_cast<int32_t>(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Scripting));
AzPhysics::SceneEvents::OnSceneSimulationFinishHandler audioFinishEvent(
[&orderedEventTriggersFinish](
[[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
[[maybe_unused]] float fixedDeltatime)
{
orderedEventTriggersFinish.push_back(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Audio);
}, aznumeric_cast<int32_t>(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Audio));
AzPhysics::SceneEvents::OnSceneSimulationFinishHandler defaultFinishEvent(
[&orderedEventTriggersFinish](
[[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
[[maybe_unused]] float fixedDeltatime)
{
orderedEventTriggersFinish.push_back(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Default);
}, aznumeric_cast<int32_t>(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Default));
sceneInterface->RegisterSceneSimulationFinishHandler(m_testSceneHandle, physicsFinishEvent);
sceneInterface->RegisterSceneSimulationFinishHandler(m_testSceneHandle, animationFinishEvent);
sceneInterface->RegisterSceneSimulationFinishHandler(m_testSceneHandle, componentsFinishEvent);
sceneInterface->RegisterSceneSimulationFinishHandler(m_testSceneHandle, scriptingFinishEvent);
sceneInterface->RegisterSceneSimulationFinishHandler(m_testSceneHandle, audioFinishEvent);
sceneInterface->RegisterSceneSimulationFinishHandler(m_testSceneHandle, defaultFinishEvent);
//trigger the events
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 1);
//all events should trigger
EXPECT_EQ(6, orderedEventTriggersStart.size());
EXPECT_EQ(6, orderedEventTriggersFinish.size());
//is in order (sorted from Largest to smallest)
auto compareOp = [](const AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority& lhs,
const AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority& rhs) -> bool
{
return lhs > rhs;
};
EXPECT_TRUE(AZStd::is_sorted(orderedEventTriggersStart.begin(), orderedEventTriggersStart.end(), compareOp));
EXPECT_TRUE(AZStd::is_sorted(orderedEventTriggersFinish.begin(), orderedEventTriggersFinish.end(), compareOp));
}
TEST_F(PhysXSceneFixture, ChangeGravity_sendsNotification)
{
auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
const AZ::Vector3 changedGravity(9.81f, 0.0f, 0.0f);
bool eventTriggered = false;
//setup handler
AzPhysics::SceneEvents::OnSceneGravityChangedEvent::Handler onGravityChangedHandler(
[this, changedGravity, &eventTriggered](AzPhysics::SceneHandle sceneHandle, const AZ::Vector3& newGravity)
{
eventTriggered = true;
// scene handles should match
EXPECT_TRUE(sceneHandle == m_testSceneHandle);
// the new gravity value should be close to the requested gravity
EXPECT_TRUE(changedGravity.IsClose(newGravity));
});
sceneInterface->RegisterSceneGravityChangedEvent(m_testSceneHandle, onGravityChangedHandler);
//update the gravity
sceneInterface->SetGravity(m_testSceneHandle, changedGravity);
EXPECT_TRUE(eventTriggered);
}
class PhysXSceneActiveSimulatedBodiesFixture
: public testing::Test
{
public:
void SetUp() override
{
if (auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get())
{
AzPhysics::SceneConfiguration newSceneConfig;
newSceneConfig.m_sceneName = "TestScene";
newSceneConfig.m_enableActiveActors = true;
m_testSceneHandle = physicsSystem->AddScene(newSceneConfig);
}
}
void TearDown() override
{
//Cleanup any created scenes
if (auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get())
{
physicsSystem->RemoveScene(m_testSceneHandle);
}
m_testSceneHandle = AzPhysics::InvalidSceneHandle;
}
AzPhysics::SceneHandle m_testSceneHandle;
};
TEST_F(PhysXSceneActiveSimulatedBodiesFixture, SceneActiveSimulatedBodies_CorrectlyReported)
{
auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
// setup shape config
AzPhysics::ShapeColliderPair shapeColliderData(
AZStd::make_shared<Physics::ColliderConfiguration>(),
AZStd::make_shared<Physics::BoxShapeConfiguration>(AZ::Vector3::CreateOne()));
// add a static simulated body - this is not expected to be reported as an active actor
AzPhysics::StaticRigidBodyConfiguration staticConfig;
staticConfig.m_colliderAndShapeData = shapeColliderData;
sceneInterface->AddSimulatedBody(m_testSceneHandle, &staticConfig);
// add a rigid body - this is expect to be reported as an active actor
AzPhysics::RigidBodyConfiguration rigidConfig;
rigidConfig.m_colliderAndShapeData = shapeColliderData;
AzPhysics::SimulatedBodyHandle rigidSphereHandle = sceneInterface->AddSimulatedBody(m_testSceneHandle, &rigidConfig);
// create + register the active handler
bool handlerTriggered = false;
AzPhysics::SceneEvents::OnSceneActiveSimulatedBodiesEvent::Handler activeActorsHandler(
[rigidSphereHandle, &handlerTriggered, this](AzPhysics::SceneHandle sceneHandle, const AzPhysics::SimulatedBodyHandleList& activeBodyList)
{
handlerTriggered = true;
//the scene handles should match
EXPECT_TRUE(m_testSceneHandle == sceneHandle);
//there should only be 1 entry and it should be the rigidSphereHandler
EXPECT_TRUE(activeBodyList.size() == 1);
EXPECT_TRUE(activeBodyList[0] == rigidSphereHandle);
});
sceneInterface->RegisterSceneActiveSimulatedBodiesHandler(m_testSceneHandle, activeActorsHandler);
// run physics to trigger the event
TestUtils::UpdateScene(m_testSceneHandle, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 1);
EXPECT_TRUE(handlerTriggered);
}
}