Files
o3de/Gems/PhysX/Code/Tests/PhysXWorldTest.cpp
T
2021-03-08 14:30:57 -08:00

227 lines
8.0 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <PhysX_precompiled.h>
#include <AzTest/AzTest.h>
#include <AzFramework/Physics/World.h>
#include <AzFramework/Physics/SystemBus.h>
#include <AzFramework/Physics/Shape.h>
#include <AzFramework/Physics/PhysicsSystem.h>
#include <Tests/PhysXTestCommon.h>
namespace PhysX
{
class WorldEventListener
: public Physics::WorldNotificationBus::Handler
{
public:
WorldEventListener(const char* worldId = "")
: WorldEventListener(worldId, WorldNotifications::Default)
{
}
WorldEventListener(const char* worldId, int priority)
: m_updateOrder(priority)
{
Physics::WorldNotificationBus::Handler::BusConnect(AZ::Crc32(worldId));
}
void Cleanup()
{
Physics::WorldNotificationBus::Handler::BusDisconnect();
}
void OnPrePhysicsSubtick(float fixedDeltaTime) override
{
if(m_onPreUpdate)
m_onPreUpdate(fixedDeltaTime);
m_preUpdates.push_back(fixedDeltaTime);
}
void OnPostPhysicsSubtick(float fixedDeltaTime) override
{
if (m_onPostUpdate)
m_onPostUpdate(fixedDeltaTime);
m_postUpdates.push_back(fixedDeltaTime);
}
int GetPhysicsTickOrder() override
{
return m_updateOrder;
}
AZStd::vector<float> m_preUpdates;
AZStd::vector<float> m_postUpdates;
AZStd::function<void(float)> m_onPostUpdate;
AZStd::function<void(float)> m_onPreUpdate;
int m_updateOrder = WorldNotifications::Default;
};
class PhysXWorldTest
: public::testing::Test
{
protected:
using ScenePtr = AzPhysics::Scene*;
AZStd::tuple<ScenePtr, WorldEventListener> CreateWorld(const char* worldId)
{
ScenePtr scene = nullptr;
if (auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get())
{
AzPhysics::SceneConfiguration sceneConfiguration = physicsSystem->GetDefaultSceneConfiguration();
sceneConfiguration.m_legacyId = AZ::Crc32(worldId);
AzPhysics::SceneHandle sceneHandle = physicsSystem->AddScene(sceneConfiguration);
if (scene = physicsSystem->GetScene(sceneHandle))
{
m_createdSceneHandles.emplace_back(sceneHandle);
}
}
return AZStd::make_tuple(scene, WorldEventListener(worldId));
}
void TearDown() override
{
//Ensure any Scenes used have been created are removed
if (auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get())
{
physicsSystem->RemoveScenes(m_createdSceneHandles);
}
m_createdSceneHandles.clear();
TestUtils::ResetPhysXSystem();
}
AzPhysics::SceneHandleList m_createdSceneHandles;
};
TEST_F(PhysXWorldTest, OnPostUpdateTriggeredPerWorld)
{
constexpr const float deltaTime = AzPhysics::SystemConfiguration::DefaultFixedTimestep;
auto* phsyicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get();
//Setup Scene 1
ScenePtr scene1;
WorldEventListener listener1;
std::tie(scene1, listener1) = CreateWorld("scene1");
// Tick the physics system, the fixed update should only run once.
phsyicsSystem->Simulate(deltaTime);
listener1.Cleanup(); //disconnect the handler
//setup scene 2
ScenePtr scene2;
WorldEventListener listener2;
std::tie(scene2, listener2) = CreateWorld("scene2");
// Tick the physics system, the fixed update should run twice.
phsyicsSystem->Simulate(deltaTime*2);
listener2.Cleanup();
// Then, we should receive correct amount of updates per world
EXPECT_EQ(1, listener1.m_postUpdates.size());
EXPECT_EQ(2, listener2.m_postUpdates.size());
}
TEST_F(PhysXWorldTest, OnPreUpdateTriggeredPerWorld)
{
constexpr const float deltaTime = AzPhysics::SystemConfiguration::DefaultFixedTimestep;
auto* phsyicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get();
//Setup Scene 1
ScenePtr scene1;
WorldEventListener listener1;
std::tie(scene1, listener1) = CreateWorld("scene1");
// Tick the physics system, the fixed update should only run once.
phsyicsSystem->Simulate(deltaTime);
listener1.Cleanup(); //disconnect the handler
//setup scene 2
ScenePtr scene2;
WorldEventListener listener2;
std::tie(scene2, listener2) = CreateWorld("scene2");
// Tick the physics system, the fixed update should run twice.
phsyicsSystem->Simulate(deltaTime * 2);
listener2.Cleanup();
// Then, we should receive correct amount of updates per world
EXPECT_EQ(1, listener1.m_preUpdates.size());
EXPECT_EQ(2, listener2.m_preUpdates.size());
}
TEST_F(PhysXWorldTest, WorldNotificationBusOrdered)
{
// GIVEN there is a world with multiple listeners
const char* worldId = "scene1";
ScenePtr scene = nullptr;
AzPhysics::SceneHandle sceneHandle = AzPhysics::InvalidSceneHandle;
auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get();
if (physicsSystem != nullptr)
{
AzPhysics::SceneConfiguration sceneConfiguration = physicsSystem->GetDefaultSceneConfiguration();
sceneConfiguration.m_legacyId = AZ::Crc32(worldId);
sceneHandle = physicsSystem->AddScene(sceneConfiguration);
scene = physicsSystem->GetScene(sceneHandle);
}
// Connect the buses in random-ish order.
WorldEventListener listener1(worldId, Physics::WorldNotifications::Physics);
WorldEventListener listener5(worldId, Physics::WorldNotifications::Default);
WorldEventListener listener3(worldId, Physics::WorldNotifications::Components);
WorldEventListener listener4(worldId, Physics::WorldNotifications::Scripting);
WorldEventListener listener2(worldId, Physics::WorldNotifications::Animation);
AZStd::vector<int> updateEvents;
listener1.m_onPostUpdate = [&](float){
updateEvents.push_back(listener1.m_updateOrder);
};
listener2.m_onPostUpdate = [&](float) {
updateEvents.push_back(listener2.m_updateOrder);
};
listener3.m_onPostUpdate = [&](float) {
updateEvents.push_back(listener3.m_updateOrder);
};
listener4.m_onPostUpdate = [&](float) {
updateEvents.push_back(listener4.m_updateOrder);
};
listener5.m_onPostUpdate = [&](float) {
updateEvents.push_back(listener5.m_updateOrder);
};
// WHEN the world is ticked.
TestUtils::UpdateScene(scene, AzPhysics::SystemConfiguration::DefaultFixedTimestep, 1);
listener1.Cleanup();
listener2.Cleanup();
listener3.Cleanup();
listener4.Cleanup();
listener5.Cleanup();
// THEN all the listeners were updated in the correct order.
EXPECT_EQ(5, updateEvents.size());
EXPECT_TRUE(AZStd::is_sorted(updateEvents.begin(), updateEvents.end()));
if (physicsSystem != nullptr)
{
scene = nullptr;
physicsSystem->RemoveScene(sceneHandle);
sceneHandle = AzPhysics::InvalidSceneHandle;
}
}
}