/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace PhysX { bool SystemComponent::VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) { using GlobalCollisionDebugState = Debug::DebugDisplayData::GlobalCollisionDebugState; if (classElement.GetVersion() <= 1) { const int pvdTransportTypeElemIndex = classElement.FindElement(AZ_CRC("PvdTransportType", 0x91e0b21e)); if (pvdTransportTypeElemIndex >= 0) { Debug::PvdTransportType pvdTransportTypeValue; AZ::SerializeContext::DataElementNode& pvdTransportElement = classElement.GetSubElement(pvdTransportTypeElemIndex); pvdTransportElement.GetData(pvdTransportTypeValue); if (pvdTransportTypeValue == static_cast(2)) { // version 2 removes Disabled (2) value default to Network instead. const bool success = pvdTransportElement.SetData(context, Debug::PvdTransportType::Network); if (!success) { return false; } } } } if (classElement.GetVersion() <= 2) { const int globalColliderDebugDrawElemIndex = classElement.FindElement(AZ_CRC("GlobalColliderDebugDraw", 0xca73ed43)); if (globalColliderDebugDrawElemIndex >= 0) { bool oldGlobalColliderDebugDrawElemDebug = false; AZ::SerializeContext::DataElementNode& globalColliderDebugDrawElem = classElement.GetSubElement(globalColliderDebugDrawElemIndex); // Previously globalColliderDebugDraw was a bool indicating whether to always draw debug or to manually set on the element if (!globalColliderDebugDrawElem.GetData(oldGlobalColliderDebugDrawElemDebug)) { return false; } classElement.RemoveElement(globalColliderDebugDrawElemIndex); const GlobalCollisionDebugState newValue = oldGlobalColliderDebugDrawElemDebug ? GlobalCollisionDebugState::AlwaysOn : GlobalCollisionDebugState::Manual; classElement.AddElementWithData(context, "GlobalColliderDebugDraw", newValue); } } if (classElement.GetVersion() <= 3) { classElement.AddElementWithData(context, "ShowJointHierarchy", true); classElement.AddElementWithData(context, "JointHierarchyLeadColor", Debug::DebugDisplayData::JointLeadColor::Aquamarine); classElement.AddElementWithData(context, "JointHierarchyFollowerColor", Debug::DebugDisplayData::JointFollowerColor::Magenta); classElement.AddElementWithData(context, "jointHierarchyDistanceThreshold", 1.0f); } return true; } void SystemComponent::Reflect(AZ::ReflectContext* context) { Pipeline::MeshAsset::Reflect(context); PhysX::ReflectionUtils::ReflectPhysXOnlyApi(context); if (auto* serialize = azrtti_cast(context)) { serialize->Class() ->Version(1) ->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector({ AZ_CRC_CE("AssetBuilder") })) ->Field("Enabled", &SystemComponent::m_enabled) ; if (AZ::EditContext* editContext = serialize->GetEditContext()) { editContext->Class("PhysX", "Global PhysX physics configuration.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "PhysX") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b)) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &SystemComponent::m_enabled, "Enabled", "Enables the PhysX system component.") ; } } } void SystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { provided.push_back(AZ_CRC("PhysXService", 0x75beae2d)); } void SystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { incompatible.push_back(AZ_CRC("PhysXService", 0x75beae2d)); } void SystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required) { } void SystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) { dependent.push_back(AZ_CRC_CE("AssetDatabaseService")); dependent.push_back(AZ_CRC_CE("AssetCatalogService")); } SystemComponent::SystemComponent() : m_enabled(true) , m_onSystemInitializedHandler( [this](const AzPhysics::SystemConfiguration* config) { EnableAutoManagedPhysicsTick(config->m_autoManageSimulationUpdate); }) , m_onSystemConfigChangedHandler( [this](const AzPhysics::SystemConfiguration* config) { EnableAutoManagedPhysicsTick(config->m_autoManageSimulationUpdate); }) { } SystemComponent::~SystemComponent() { if (m_collisionRequests.Get() == this) { m_collisionRequests.Unregister(this); } if (m_physicsSystem.Get() == this) { m_physicsSystem.Unregister(this); } } // AZ::Component interface implementation void SystemComponent::Init() { //Old API interfaces if (m_collisionRequests.Get() == nullptr) { m_collisionRequests.Register(this); } if (m_physicsSystem.Get() == nullptr) { m_physicsSystem.Register(this); } } template void RegisterAsset(AZStd::vector>& assetHandlers) { AssetHandlerT* handler = aznew AssetHandlerT(); AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::EnableCatalogForAsset, AZ::AzTypeInfo::Uuid()); AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::AddExtension, AssetHandlerT::s_assetFileExtension); assetHandlers.emplace_back(handler); } void SystemComponent::Activate() { if (!m_enabled) { return; } m_materialManager.Connect(); m_defaultWorldComponent.Activate(); // Assets related work auto* materialAsset = aznew AzFramework::GenericAssetHandler("Physics Material", "Physics", "physmaterial"); materialAsset->Register(); m_assetHandlers.emplace_back(materialAsset); // Add asset types and extensions to AssetCatalog. Uses "AssetCatalogService". RegisterAsset(m_assetHandlers); RegisterAsset(m_assetHandlers); // Connect to relevant buses Physics::SystemRequestBus::Handler::BusConnect(); PhysX::SystemRequestsBus::Handler::BusConnect(); Physics::CollisionRequestBus::Handler::BusConnect(); ActivatePhysXSystem(); } void SystemComponent::Deactivate() { AZ::TickBus::Handler::BusDisconnect(); Physics::CollisionRequestBus::Handler::BusDisconnect(); PhysX::SystemRequestsBus::Handler::BusDisconnect(); Physics::SystemRequestBus::Handler::BusDisconnect(); // Reset material manager m_materialManager.ReleaseAllMaterials(); m_defaultWorldComponent.Deactivate(); m_materialManager.Disconnect(); m_windProvider.reset(); m_onSystemInitializedHandler.Disconnect(); m_onSystemConfigChangedHandler.Disconnect(); if (m_physXSystem != nullptr) { m_physXSystem->Shutdown(); m_physXSystem = nullptr; } m_assetHandlers.clear(); //this need to be after m_physXSystem->Shutdown(); For it will drop the default material library reference. } physx::PxConvexMesh* SystemComponent::CreateConvexMesh(const void* vertices, AZ::u32 vertexNum, AZ::u32 vertexStride) { physx::PxConvexMeshDesc desc; desc.points.data = vertices; desc.points.count = vertexNum; desc.points.stride = vertexStride; // we provide points only, therefore the PxConvexFlag::eCOMPUTE_CONVEX flag must be specified desc.flags = physx::PxConvexFlag::eCOMPUTE_CONVEX; //TEMP until this in moved over physx::PxConvexMesh* convex = m_physXSystem->GetPxCooking()->createConvexMesh(desc, m_physXSystem->GetPxPhysics()->getPhysicsInsertionCallback()); AZ_Error("PhysX", convex, "Error. Unable to create convex mesh"); return convex; } physx::PxHeightField* SystemComponent::CreateHeightField(const physx::PxHeightFieldSample* samples, AZ::u32 numRows, AZ::u32 numColumns) { physx::PxHeightFieldDesc desc; desc.format = physx::PxHeightFieldFormat::eS16_TM; desc.nbColumns = numColumns; desc.nbRows = numRows; desc.samples.data = samples; desc.samples.stride = sizeof(physx::PxHeightFieldSample); physx::PxHeightField* heightfield = m_physXSystem->GetPxCooking()->createHeightField(desc, m_physXSystem->GetPxPhysics()->getPhysicsInsertionCallback()); AZ_Error("PhysX", heightfield, "Error. Unable to create heightfield"); return heightfield; } bool SystemComponent::CookConvexMeshToFile(const AZStd::string& filePath, const AZ::Vector3* vertices, AZ::u32 vertexCount) { AZStd::vector physxData; if (CookConvexMeshToMemory(vertices, vertexCount, physxData)) { return Utils::WriteCookedMeshToFile(filePath, physxData, Physics::CookedMeshShapeConfiguration::MeshType::Convex); } AZ_Error("PhysX", false, "CookConvexMeshToFile. Convex cooking failed for %s.", filePath.c_str()); return false; } bool SystemComponent::CookTriangleMeshToFile(const AZStd::string& filePath, const AZ::Vector3* vertices, AZ::u32 vertexCount, const AZ::u32* indices, AZ::u32 indexCount) { AZStd::vector physxData; if (CookTriangleMeshToMemory(vertices, vertexCount, indices, indexCount, physxData)) { return Utils::WriteCookedMeshToFile(filePath, physxData, Physics::CookedMeshShapeConfiguration::MeshType::TriangleMesh); } AZ_Error("PhysX", false, "CookTriangleMeshToFile. Mesh cooking failed for %s.", filePath.c_str()); return false; } bool SystemComponent::CookConvexMeshToMemory(const AZ::Vector3* vertices, AZ::u32 vertexCount, AZStd::vector& result) { physx::PxDefaultMemoryOutputStream memoryStream; bool cookingResult = Utils::CookConvexToPxOutputStream(vertices, vertexCount, memoryStream); if(cookingResult) { result.insert(result.end(), memoryStream.getData(), memoryStream.getData() + memoryStream.getSize()); } return cookingResult; } bool SystemComponent::CookTriangleMeshToMemory(const AZ::Vector3* vertices, AZ::u32 vertexCount, const AZ::u32* indices, AZ::u32 indexCount, AZStd::vector& result) { physx::PxDefaultMemoryOutputStream memoryStream; bool cookingResult = Utils::CookTriangleMeshToToPxOutputStream(vertices, vertexCount, indices, indexCount, memoryStream); if (cookingResult) { result.insert(result.end(), memoryStream.getData(), memoryStream.getData() + memoryStream.getSize()); } return cookingResult; } physx::PxConvexMesh* SystemComponent::CreateConvexMeshFromCooked(const void* cookedMeshData, AZ::u32 bufferSize) { physx::PxDefaultMemoryInputData inpStream(reinterpret_cast(const_cast(cookedMeshData)), bufferSize); //TEMP until this in moved over return m_physXSystem->GetPxPhysics()->createConvexMesh(inpStream); } physx::PxTriangleMesh* SystemComponent::CreateTriangleMeshFromCooked(const void* cookedMeshData, AZ::u32 bufferSize) { physx::PxDefaultMemoryInputData inpStream(reinterpret_cast(const_cast(cookedMeshData)), bufferSize); //TEMP until this in moved over return m_physXSystem->GetPxPhysics()->createTriangleMesh(inpStream); } AZStd::shared_ptr SystemComponent::CreateShape(const Physics::ColliderConfiguration& colliderConfiguration, const Physics::ShapeConfiguration& configuration) { auto shapePtr = AZStd::make_shared(colliderConfiguration, configuration); if (shapePtr->GetPxShape()) { return shapePtr; } AZ_Error("PhysX", false, "SystemComponent::CreateShape error. Unable to create a shape from configuration."); return nullptr; } AZStd::shared_ptr SystemComponent::CreateMaterial(const Physics::MaterialConfiguration& materialConfiguration) { return AZStd::make_shared(materialConfiguration); } void SystemComponent::ReleaseNativeHeightfieldObject(void* nativeHeightfieldObject) { if (nativeHeightfieldObject) { static_cast(nativeHeightfieldObject)->release(); } } void SystemComponent::ReleaseNativeMeshObject(void* nativeMeshObject) { if (nativeMeshObject) { static_cast(nativeMeshObject)->release(); } } AzPhysics::CollisionLayer SystemComponent::GetCollisionLayerByName(const AZStd::string& layerName) { return m_physXSystem->GetPhysXConfiguration().m_collisionConfig.m_collisionLayers.GetLayer(layerName); } AZStd::string SystemComponent::GetCollisionLayerName(const AzPhysics::CollisionLayer& layer) { return m_physXSystem->GetPhysXConfiguration().m_collisionConfig.m_collisionLayers.GetName(layer); } bool SystemComponent::TryGetCollisionLayerByName(const AZStd::string& layerName, AzPhysics::CollisionLayer& layer) { return m_physXSystem->GetPhysXConfiguration().m_collisionConfig.m_collisionLayers.TryGetLayer(layerName, layer); } AzPhysics::CollisionGroup SystemComponent::GetCollisionGroupByName(const AZStd::string& groupName) { return m_physXSystem->GetPhysXConfiguration().m_collisionConfig.m_collisionGroups.FindGroupByName(groupName); } bool SystemComponent::TryGetCollisionGroupByName(const AZStd::string& groupName, AzPhysics::CollisionGroup& group) { return m_physXSystem->GetPhysXConfiguration().m_collisionConfig.m_collisionGroups.TryFindGroupByName(groupName, group); } AZStd::string SystemComponent::GetCollisionGroupName(const AzPhysics::CollisionGroup& collisionGroup) { AZStd::string groupName; for (const auto& group : m_physXSystem->GetPhysXConfiguration().m_collisionConfig.m_collisionGroups.GetPresets()) { if (group.m_group.GetMask() == collisionGroup.GetMask()) { groupName = group.m_name; break; } } return groupName; } AzPhysics::CollisionGroup SystemComponent::GetCollisionGroupById(const AzPhysics::CollisionGroups::Id& groupId) { return m_physXSystem->GetPhysXConfiguration().m_collisionConfig.m_collisionGroups.FindGroupById(groupId); } void SystemComponent::SetCollisionLayerName(int index, const AZStd::string& layerName) { m_physXSystem->SetCollisionLayerName(aznumeric_cast(index), layerName); } void SystemComponent::CreateCollisionGroup(const AZStd::string& groupName, const AzPhysics::CollisionGroup& group) { m_physXSystem->CreateCollisionGroup(groupName, group); } physx::PxFilterData SystemComponent::CreateFilterData(const AzPhysics::CollisionLayer& layer, const AzPhysics::CollisionGroup& group) { return PhysX::Collision::CreateFilterData(layer, group); } physx::PxCooking* SystemComponent::GetCooking() { //TEMP until this in moved over return m_physXSystem->GetPxCooking(); } void SystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) { if (m_physXSystem) { m_physXSystem->Simulate(deltaTime); } } int SystemComponent::GetTickOrder() { return AZ::ComponentTickBus::TICK_PHYSICS_SYSTEM; } void SystemComponent::EnableAutoManagedPhysicsTick(bool shouldTick) { if (shouldTick && !m_isTickingPhysics) { AZ::TickBus::Handler::BusConnect(); } else if (!shouldTick && m_isTickingPhysics) { AZ::TickBus::Handler::BusDisconnect(); } m_isTickingPhysics = shouldTick; } void SystemComponent::ActivatePhysXSystem() { m_physXSystem = GetPhysXSystem(); if (m_physXSystem) { m_physXSystem->RegisterSystemInitializedEvent(m_onSystemInitializedHandler); m_physXSystem->RegisterSystemConfigurationChangedEvent(m_onSystemConfigChangedHandler); const PhysXSettingsRegistryManager& registryManager = m_physXSystem->GetSettingsRegistryManager(); if (AZStd::optional config = registryManager.LoadSystemConfiguration(); config.has_value()) { m_physXSystem->Initialize(&(*config)); } else //load defaults if there is no config { const PhysXSystemConfiguration defaultConfig = PhysXSystemConfiguration::CreateDefault(); m_physXSystem->Initialize(&defaultConfig); auto saveCallback = []([[maybe_unused]] const PhysXSystemConfiguration& config, [[maybe_unused]] PhysXSettingsRegistryManager::Result result) { AZ_Warning("PhysX", result == PhysXSettingsRegistryManager::Result::Success, "Unable to save the default PhysX configuration."); }; registryManager.SaveSystemConfiguration(defaultConfig, saveCallback); } //Load the DefaultSceneConfig if (AZStd::optional config = registryManager.LoadDefaultSceneConfiguration(); config.has_value()) { m_physXSystem->UpdateDefaultSceneConfiguration((*config)); } else //load defaults if there is no config { const AzPhysics::SceneConfiguration defaultConfig = AzPhysics::SceneConfiguration::CreateDefault(); m_physXSystem->UpdateDefaultSceneConfiguration(defaultConfig); auto saveCallback = []([[maybe_unused]] const AzPhysics::SceneConfiguration& config, [[maybe_unused]] PhysXSettingsRegistryManager::Result result) { AZ_Warning("PhysX", result == PhysXSettingsRegistryManager::Result::Success, "Unable to save the default Scene configuration."); }; registryManager.SaveDefaultSceneConfiguration(defaultConfig, saveCallback); } //load the debug configuration and initialize the PhysX debug interface if (auto* debug = AZ::Interface::Get()) { if (AZStd::optional config = registryManager.LoadDebugConfiguration(); config.has_value()) { debug->Initialize(*config); } else //load defaults if there is no config { const Debug::DebugConfiguration defaultConfig = Debug::DebugConfiguration::CreateDefault(); debug->Initialize(defaultConfig); auto saveCallback = []([[maybe_unused]] const Debug::DebugConfiguration& config, [[maybe_unused]] PhysXSettingsRegistryManager::Result result) { AZ_Warning("PhysX", result == PhysXSettingsRegistryManager::Result::Success, "Unable to save the default PhysX Debug configuration."); }; registryManager.SaveDebugConfiguration(defaultConfig, saveCallback); } } } m_windProvider = AZStd::make_unique(); } } // namespace PhysX