Files
o3de/Gems/PhysX/Code/Source/System/PhysXSystem.cpp
T
2021-03-08 14:30:57 -08:00

433 lines
15 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates, or
* a third party where indicated.
*
* 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 <AzCore/Math/MathUtils.h>
#include <Scene/PhysXScene.h>
#include <System/PhysXSystem.h>
#include <System/PhysXAllocator.h>
#include <System/PhysXCpuDispatcher.h>
#include <PxPhysicsAPI.h>
namespace PhysX
{
PhysXSystem::MaterialLibraryAssetHelper::MaterialLibraryAssetHelper(PhysXSystem* physXSystem)
: m_physXSystem(physXSystem)
{
}
void PhysXSystem::MaterialLibraryAssetHelper::Connect(const AZ::Data::AssetId& materialLibraryId)
{
if (!AZ::Data::AssetBus::Handler::BusIsConnectedId(materialLibraryId))
{
AZ::Data::AssetBus::Handler::BusDisconnect();
AZ::Data::AssetBus::Handler::BusConnect(materialLibraryId);
}
}
void PhysXSystem::MaterialLibraryAssetHelper::Disconnect()
{
AZ::Data::AssetBus::Handler::BusDisconnect();
}
void PhysXSystem::MaterialLibraryAssetHelper::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
if (m_physXSystem == nullptr || m_physXSystem->GetDefaultMaterialLibrary() != asset)
{
return;
}
m_physXSystem->UpdateDefaultMaterialLibrary(asset);
}
PhysXSystem::PhysXSystem(PhysXSettingsRegistryManager* registryManager, const physx::PxCookingParams& cookingParams)
: m_registryManager(*registryManager)
, m_materialLibraryAssetHelper(this)
{
// Start PhysX allocator
PhysXAllocator::Descriptor allocatorDescriptor;
allocatorDescriptor.m_custom = &AZ::AllocatorInstance<AZ::SystemAllocator>::Get();
AZ::AllocatorInstance<PhysXAllocator>::Create();
InitializePhysXSdk(cookingParams);
}
PhysXSystem::~PhysXSystem()
{
Shutdown();
ShutdownPhysXSdk();
AZ::AllocatorInstance<PhysXAllocator>::Destroy();
}
void PhysXSystem::Initialize(const AzPhysics::SystemConfiguration* config)
{
if (m_state == State::Initialized)
{
AZ_Warning("PhysXSystem", false, "PhysX system already initialized, Shutdown must be called first OR call Reinitialize or UpdateConfiguration(forceReinit=true) to reboot");
return;
}
if (const auto* physXConfig = azdynamic_cast<const PhysXSystemConfiguration*>(config))
{
m_systemConfig = *physXConfig;
}
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
m_state = State::Initialized;
m_initializeEvent.Signal(&m_systemConfig);
}
void PhysXSystem::Reinitialize()
{
//To be implemented with LYN-1146
AZ_Warning("PhysXSystem", false, "PhysX Reinitialize currently not supported.");
}
void PhysXSystem::Shutdown()
{
if (m_state != State::Initialized)
{
return;
}
RemoveAllScenes();
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
m_materialLibraryAssetHelper.Disconnect();
// Clear the asset reference in deactivate. The asset system is shut down before destructors are called
// for system components, causing any hanging asset references to become crashes on shutdown in release builds.
m_systemConfig.m_defaultMaterialLibrary.Reset();
m_accumulatedTime = 0.0f;
m_state = State::Shutdown;
}
void PhysXSystem::Simulate(float deltaTime)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Physics);
if (m_state != State::Initialized)
{
AZ_Warning("PhysXSystem", false, "Call Simulate when PhysX system is not initialized");
return;
}
auto simulateScenes = [this](float timeStep)
{
for (auto* scene : m_sceneList)
{
if (scene != nullptr && scene->IsEnabled())
{
scene->StartSimulation(timeStep);
scene->FinishSimulation();
}
}
};
deltaTime = AZ::GetClamp(deltaTime, 0.0f, m_systemConfig.m_maxTimestep);
AZ_Assert(m_systemConfig.m_fixedTimestep >= 0.0f, "PhysXSystem - fixed timestep is negitive.");
if (m_systemConfig.m_fixedTimestep > 0.0f) //use the fixed timestep
{
m_accumulatedTime += deltaTime;
//divide accumulated time by the fixed step and floor it to get the number of steps that would occur. Then multiply by fixedTimeStep to get the total executed time.
const float tickTime = AZStd::floorf(m_accumulatedTime / m_systemConfig.m_fixedTimestep) * m_systemConfig.m_fixedTimestep;
m_preSimulateEvent.Signal(tickTime);
while (m_accumulatedTime >= m_systemConfig.m_fixedTimestep)
{
simulateScenes(m_systemConfig.m_fixedTimestep);
m_accumulatedTime -= m_systemConfig.m_fixedTimestep;
}
}
else
{
m_preSimulateEvent.Signal(deltaTime);
simulateScenes(deltaTime);
}
m_postSimulateEvent.Signal();
}
AzPhysics::SceneHandle PhysXSystem::AddScene(const AzPhysics::SceneConfiguration& config)
{
if (!m_freeSceneSlots.empty()) //fill any free slots first before increasing the size of the scene list vector.
{
AZ::u64 freeIndex = m_freeSceneSlots.front();
m_freeSceneSlots.pop();
AZ_Assert(freeIndex < m_sceneList.size(), "PhysXSystem::AddScene: Free scene index is out of bounds!");
AZ_Assert(m_sceneList[freeIndex] == nullptr, "PhysXSystem::AddScene: Free scene index is not free");
m_sceneList[freeIndex] = new PhysXScene(config);
return AzPhysics::SceneHandle(config.m_legacyId, freeIndex);
}
if (m_sceneList.size() < std::numeric_limits<AzPhysics::SceneIndex>::max()) //add a new scene if it is under the limit
{
m_sceneList.emplace_back(new PhysXScene(config));
return AzPhysics::SceneHandle(config.m_legacyId, (m_sceneList.size() - 1));
}
AZ_Warning("Physx", false, "Scene Limit reached[%d], unable to add new scene [%s]",
std::numeric_limits<AzPhysics::SceneIndex>::max(),
config.m_sceneName.c_str());
return AzPhysics::InvalidSceneHandle;
}
AzPhysics::SceneHandleList PhysXSystem::AddScenes(const AzPhysics::SceneConfigurationList& configs)
{
AzPhysics::SceneHandleList sceneHandles;
sceneHandles.reserve(configs.size());
for (const auto& config : configs)
{
AzPhysics::SceneHandle sceneHandle = AddScene(config);
sceneHandles.emplace_back(sceneHandle);
}
return sceneHandles;
}
AzPhysics::Scene* PhysXSystem::GetScene(AzPhysics::SceneHandle handle)
{
if (handle == AzPhysics::InvalidSceneHandle)
{
return nullptr;
}
AzPhysics::SceneIndex index = AZStd::get<AzPhysics::SceneHandleValues::Index>(handle);
if (index < m_sceneList.size())
{
if (AzPhysics::Scene* scene = m_sceneList[index])
{
if (scene->GetConfiguration().m_legacyId == AZStd::get<AzPhysics::SceneHandleValues::Crc>(handle))
{
return scene;
}
}
}
return nullptr;
}
AzPhysics::SceneList PhysXSystem::GetScenes(const AzPhysics::SceneHandleList& handles)
{
AzPhysics::SceneList requestedSceneList;
requestedSceneList.reserve(handles.size());
for (const auto& handle : handles)
{
AzPhysics::Scene* scene = GetScene(handle);
requestedSceneList.emplace_back(scene);
}
return requestedSceneList;
}
AzPhysics::SceneList& PhysXSystem::GetAllScenes()
{
return m_sceneList;
}
void PhysXSystem::RemoveScene(AzPhysics::SceneHandle handle)
{
if (handle == AzPhysics::InvalidSceneHandle)
{
return;
}
AZ::u64 index = AZStd::get<AzPhysics::SceneHandleValues::Index>(handle);
if (index < m_sceneList.size())
{
delete m_sceneList[index];
m_sceneList[index] = nullptr;
m_freeSceneSlots.push(index);
}
}
void PhysXSystem::RemoveScenes(const AzPhysics::SceneHandleList& handles)
{
for (const auto& handle : handles)
{
RemoveScene(handle);
}
}
void PhysXSystem::RemoveAllScenes()
{
for (auto* scene : m_sceneList)
{
delete scene;
scene = nullptr;
}
m_sceneList.clear();
//clear the free slots queue
AZStd::queue<AzPhysics::SceneIndex> empty;
m_freeSceneSlots.swap(empty);
}
const AzPhysics::SystemConfiguration* PhysXSystem::GetConfiguration() const
{
return &m_systemConfig;
}
void PhysXSystem::OnCatalogLoaded([[maybe_unused]]const char* catalogFile)
{
//now that assets can be resolved, lets load the default material library.
LoadDefaultMaterialLibrary();
}
void PhysXSystem::UpdateConfiguration(const AzPhysics::SystemConfiguration* newConfig, [[maybe_unused]] bool forceReinitialization /*= false*/)
{
if (const auto* physXConfig = azdynamic_cast<const PhysXSystemConfiguration*>(newConfig);
m_systemConfig != (*physXConfig))
{
const bool newMaterialLibrary = m_systemConfig.m_defaultMaterialLibrary != physXConfig->m_defaultMaterialLibrary;
m_systemConfig = (*physXConfig);
m_configChangeEvent.Signal(physXConfig);
//LYN-1146 -- Restarting the simulation if required
if (newMaterialLibrary)
{
LoadDefaultMaterialLibrary();
m_onDefaultMaterialLibraryChangedEvent.Signal(m_systemConfig.m_defaultMaterialLibrary.GetId());
}
}
}
void PhysXSystem::InitializePhysXSdk(const physx::PxCookingParams& cookingParams)
{
m_physXSdk.m_foundation = PxCreateFoundation(PX_PHYSICS_VERSION, m_physXAllocatorCallback, m_physXErrorCallback);
physx::PxPvd* pvd = m_physXDebug.InitializePhysXPvd(m_physXSdk.m_foundation);
// create PhysX basis
bool physXTrackOutstandingAllocations = false;
#ifdef AZ_PHYSICS_DEBUG_ENABLED
physXTrackOutstandingAllocations = true;
#endif
m_physXSdk.m_physics = PxCreatePhysics(PX_PHYSICS_VERSION, *m_physXSdk.m_foundation, physx::PxTolerancesScale(), physXTrackOutstandingAllocations, pvd);
PxInitExtensions(*m_physXSdk.m_physics, pvd);
// set up cooking for height fields, meshes etc.
m_physXSdk.m_cooking = PxCreateCooking(PX_PHYSICS_VERSION, *m_physXSdk.m_foundation, cookingParams);
// Set up CPU dispatcher
#if defined(AZ_PLATFORM_LINUX)
// Temporary workaround for linux. At the moment using AzPhysXCpuDispatcher results in an assert at
// PhysX mutex indicating it must be unlocked only by the thread that has already acquired lock.
m_cpuDispatcher = physx::PxDefaultCpuDispatcherCreate(0);
#else
m_cpuDispatcher = PhysXCpuDispatcherCreate();
#endif
PxSetProfilerCallback(&m_pxAzProfilerCallback);
}
void PhysXSystem::ShutdownPhysXSdk()
{
delete m_cpuDispatcher;
m_cpuDispatcher = nullptr;
m_physXSdk.m_cooking->release();
m_physXSdk.m_cooking = nullptr;
PxCloseExtensions();
m_physXSdk.m_physics->release();
m_physXSdk.m_physics = nullptr;
m_physXDebug.ShutdownPhysXPvd();
m_physXSdk.m_foundation->release();
m_physXSdk.m_foundation = nullptr;
}
const PhysXSystemConfiguration& PhysXSystem::GetPhysXConfiguration() const
{
return m_systemConfig;
}
void PhysXSystem::UpdateDefaultMaterialLibrary(const AZ::Data::Asset<Physics::MaterialLibraryAsset>& materialLibrary)
{
if (m_systemConfig.m_defaultMaterialLibrary == materialLibrary)
{
return;
}
m_systemConfig.m_defaultMaterialLibrary = materialLibrary;
LoadDefaultMaterialLibrary();
m_onDefaultMaterialLibraryChangedEvent.Signal(materialLibrary.GetId());
}
const AZ::Data::Asset<Physics::MaterialLibraryAsset>& PhysXSystem::GetDefaultMaterialLibrary() const
{
return m_systemConfig.m_defaultMaterialLibrary;
}
void PhysXSystem::UpdateDefaultSceneConfiguration(const AzPhysics::SceneConfiguration& sceneConfiguration)
{
if (m_defaultSceneConfiguration != sceneConfiguration)
{
m_defaultSceneConfiguration = sceneConfiguration;
m_onDefaultSceneConfigurationChangedEvent.Signal(&m_defaultSceneConfiguration);
}
}
const AzPhysics::SceneConfiguration& PhysXSystem::GetDefaultSceneConfiguration() const
{
return m_defaultSceneConfiguration;
}
const PhysXSettingsRegistryManager& PhysXSystem::GetSettingsRegistryManager() const
{
return m_registryManager;
}
bool PhysXSystem::LoadDefaultMaterialLibrary()
{
AZ::Data::Asset<Physics::MaterialLibraryAsset>& materialLibrary = m_systemConfig.m_defaultMaterialLibrary;
const AZ::Data::AssetId& materialLibraryId = materialLibrary.GetId();
if (!materialLibraryId.IsValid())
{
AZ_Warning("PhysX", false,
"LoadDefaultMaterialLibrary: Default Material Library asset ID is invalid.");
return false;
}
// Listen for material library asset modification events
m_materialLibraryAssetHelper.Connect(materialLibraryId);
const AZ::Data::AssetFilterCB assetLoadFilterCB = nullptr;
materialLibrary = AZ::Data::AssetManager::Instance().GetAsset<Physics::MaterialLibraryAsset>(materialLibrary.GetId(), materialLibrary.GetAutoLoadBehavior(), AZ::Data::AssetLoadParameters{ assetLoadFilterCB });
materialLibrary.BlockUntilLoadComplete();
AZ_Warning("PhysX", (materialLibrary.GetData() != nullptr),
"LoadDefaultMaterialLibrary: Default Material Library asset data is invalid.");
return materialLibrary.GetData() != nullptr;
}
//TEMP -- until these are fully moved over here
void PhysXSystem::SetCollisionLayerName(int index, const AZStd::string& layerName)
{
m_systemConfig.m_collisionConfig.m_collisionLayers.SetName(aznumeric_cast<AZ::u64>(index), layerName);
}
void PhysXSystem::CreateCollisionGroup(const AZStd::string& groupName, const AzPhysics::CollisionGroup& group)
{
m_systemConfig.m_collisionConfig.m_collisionGroups.CreateGroup(groupName, group);
}
PhysXSystem* GetPhysXSystem()
{
return azdynamic_cast<PhysXSystem*>(AZ::Interface<AzPhysics::SystemInterface>::Get());
}
} //namespace PhysX