3b9762142a
Using triangle mesh with a kinematic rigid body is allowed, but the options "Compute COM", "Compute Mass" and "Compute Inertia" are not supported by PhysX and an error in logged that default values for COM, Mass and Inertia will be used. Now this situation is captured and an explanatory warning is used instead. - Improved RigidBody::UpdateMassProperties function to apply the same logic in the treatment of shapes for all three parameters: COM, Mass and Inertia. - Improved UpdateMassProperties function by using references for the override parameters instead of pointers. - Improved function that computes the Center of Mass UpdateCenterOfMass (renamed from UpdateComputedCenterOfMass), to include the same shapes that the compute mass and inertia functions in physx updateMassAndInertia, which is to include all shapes if includeAllShapesInMassCalculation is true, else include only the shapes with eSIMULATION_SHAPE flag. - Removed unused private function RigidBody::ComputeInertia. - Added unit test to check when the warnings are fired correctly when COM, Mass or Inertia are asked to be computed on a rigid body with triangle mesh shapes. - Improved MassComputeFixture tests by not only using Box shape, but also sphere and capture, plus improved the PossibleMassComputeFlags parameters to include all possible variations of the MassComputeFlags flags. Fixes #3322 Fixes #3979 Signed-off-by: moraaar <moraaar@amazon.com>
1257 lines
51 KiB
C++
1257 lines
51 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 <Scene/PhysXScene.h>
|
|
|
|
#include <AzCore/Debug/ProfilerBus.h>
|
|
#include <AzCore/std/containers/variant.h>
|
|
#include <AzCore/std/containers/vector.h>
|
|
#include <AzCore/std/smart_ptr/make_shared.h>
|
|
#include <AzFramework/Physics/Character.h>
|
|
#include <AzFramework/Physics/Collision/CollisionEvents.h>
|
|
#include <AzFramework/Physics/Configuration/RigidBodyConfiguration.h>
|
|
#include <AzFramework/Physics/Configuration/StaticRigidBodyConfiguration.h>
|
|
|
|
#include <Collision.h>
|
|
#include <RigidBody.h>
|
|
#include <RigidBodyStatic.h>
|
|
#include <Shape.h>
|
|
#include <Common/PhysXSceneQueryHelpers.h>
|
|
#include <PhysX/PhysXLocks.h>
|
|
#include <PhysX/Utils.h>
|
|
#include <PhysXCharacters/API/CharacterController.h>
|
|
#include <PhysXCharacters/API/CharacterUtils.h>
|
|
#include <System/PhysXSystem.h>
|
|
#include <PhysX/Joint/Configuration/PhysXJointConfiguration.h>
|
|
#include <PhysX/Debug/PhysXDebugConfiguration.h>
|
|
#include <PhysX/MathConversion.h>
|
|
#include <Joint/PhysXJoint.h>
|
|
|
|
namespace PhysX
|
|
{
|
|
AZ_CLASS_ALLOCATOR_IMPL(PhysXScene, AZ::SystemAllocator, 0);
|
|
|
|
/*static*/ thread_local AZStd::vector<physx::PxRaycastHit> PhysXScene::s_rayCastBuffer;
|
|
/*static*/ thread_local AZStd::vector<physx::PxSweepHit> PhysXScene::s_sweepBuffer;
|
|
/*static*/ thread_local AZStd::vector<physx::PxOverlapHit> PhysXScene::s_overlapBuffer;
|
|
|
|
namespace Internal
|
|
{
|
|
physx::PxScene* CreatePxScene(const AzPhysics::SceneConfiguration& config,
|
|
SceneSimulationFilterCallback* filterCallback,
|
|
SceneSimulationEventCallback* simEventCallback)
|
|
{
|
|
const physx::PxTolerancesScale tolerancesScale = physx::PxTolerancesScale();
|
|
physx::PxSceneDesc sceneDesc(tolerancesScale);
|
|
sceneDesc.gravity = PxMathConvert(config.m_gravity);
|
|
if (config.m_enableCcd)
|
|
{
|
|
sceneDesc.flags |= physx::PxSceneFlag::eENABLE_CCD;
|
|
sceneDesc.filterShader = Collision::DefaultFilterShaderCCD;
|
|
sceneDesc.ccdMaxPasses = config.m_maxCcdPasses;
|
|
if (config.m_enableCcdResweep)
|
|
{
|
|
sceneDesc.flags.clear(physx::PxSceneFlag::eDISABLE_CCD_RESWEEP);
|
|
}
|
|
else
|
|
{
|
|
sceneDesc.flags.set(physx::PxSceneFlag::eDISABLE_CCD_RESWEEP);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sceneDesc.filterShader = Collision::DefaultFilterShader;
|
|
}
|
|
|
|
if (config.m_enableActiveActors)
|
|
{
|
|
sceneDesc.flags |= physx::PxSceneFlag::eENABLE_ACTIVE_ACTORS;
|
|
}
|
|
|
|
if (config.m_enablePcm)
|
|
{
|
|
sceneDesc.flags |= physx::PxSceneFlag::eENABLE_PCM;
|
|
}
|
|
else
|
|
{
|
|
sceneDesc.flags &= ~physx::PxSceneFlag::eENABLE_PCM;
|
|
}
|
|
|
|
if (config.m_kinematicFiltering)
|
|
{
|
|
sceneDesc.kineKineFilteringMode = physx::PxPairFilteringMode::eKEEP;
|
|
}
|
|
|
|
if (config.m_kinematicStaticFiltering)
|
|
{
|
|
sceneDesc.staticKineFilteringMode = physx::PxPairFilteringMode::eKEEP;
|
|
}
|
|
|
|
sceneDesc.bounceThresholdVelocity = config.m_bounceThresholdVelocity;
|
|
|
|
sceneDesc.filterCallback = filterCallback;
|
|
sceneDesc.simulationEventCallback = simEventCallback;
|
|
#ifdef ENABLE_TGS_SOLVER
|
|
// Use Temporal Gauss-Seidel solver by default
|
|
sceneDesc.solverType = physx::PxSolverType::eTGS;
|
|
#endif
|
|
#ifdef PHYSX_ENABLE_MULTI_THREADING
|
|
sceneDesc.flags |= physx::PxSceneFlag::eREQUIRE_RW_LOCK;
|
|
#endif
|
|
|
|
if (auto* physXSystem = GetPhysXSystem())
|
|
{
|
|
sceneDesc.cpuDispatcher = physXSystem->GetPxCpuDispathcher();
|
|
if (physx::PxScene * pxScene = physXSystem->GetPxPhysics()->createScene(sceneDesc))
|
|
{
|
|
if (physx::PxPvdSceneClient* pvdClient = pxScene->getScenePvdClient())
|
|
{
|
|
pvdClient->setScenePvdFlag(physx::PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS, true);
|
|
pvdClient->setScenePvdFlag(physx::PxPvdSceneFlag::eTRANSMIT_CONTACTS, true);
|
|
pvdClient->setScenePvdFlag(physx::PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES, true);
|
|
}
|
|
return pxScene;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool AddShape(AZStd::variant<AzPhysics::RigidBody*, AzPhysics::StaticRigidBody*> simulatedBody, const AzPhysics::ShapeVariantData& shapeData)
|
|
{
|
|
if (auto* shapeColliderPair = AZStd::get_if<AzPhysics::ShapeColliderPair>(&shapeData))
|
|
{
|
|
bool shapeAdded = false;
|
|
auto shapePtr = AZStd::make_shared<Shape>(*(shapeColliderPair->first), *(shapeColliderPair->second));
|
|
AZStd::visit([shapePtr, &shapeAdded](auto&& body)
|
|
{
|
|
if (shapePtr->GetPxShape())
|
|
{
|
|
body->AddShape(shapePtr);
|
|
shapeAdded = true;
|
|
}
|
|
}, simulatedBody);
|
|
return shapeAdded;
|
|
}
|
|
else if (auto* shapeColliderPairList = AZStd::get_if<AZStd::vector<AzPhysics::ShapeColliderPair>>(&shapeData))
|
|
{
|
|
bool shapeAdded = false;
|
|
if (!shapeColliderPairList->empty())
|
|
{
|
|
const auto& shapeColliderConfigs = shapeColliderPairList->front();
|
|
auto shapePtr = AZStd::make_shared<Shape>(*(shapeColliderConfigs.first), *(shapeColliderConfigs.second));
|
|
AZStd::visit([shapePtr, &shapeAdded](auto&& body)
|
|
{
|
|
if (shapePtr->GetPxShape())
|
|
{
|
|
body->AddShape(shapePtr);
|
|
shapeAdded = true;
|
|
}
|
|
}, simulatedBody);
|
|
return shapeAdded;
|
|
}
|
|
}
|
|
else if (auto* shape = AZStd::get_if<AZStd::shared_ptr<Physics::Shape>>(&shapeData))
|
|
{
|
|
AZStd::visit([shape](auto&& body)
|
|
{
|
|
body->AddShape(*shape);
|
|
}, simulatedBody);
|
|
return true;
|
|
}
|
|
else if (auto* shapeList = AZStd::get_if<AZStd::vector<AZStd::shared_ptr<Physics::Shape>>>(&shapeData))
|
|
{
|
|
for (auto shapePtr : *shapeList)
|
|
{
|
|
AZStd::visit([shapePtr](auto&& body)
|
|
{
|
|
body->AddShape(shapePtr);
|
|
}, simulatedBody);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<class SimulatedBodyType, class ConfigurationType>
|
|
AzPhysics::SimulatedBody* CreateSimulatedBody(const ConfigurationType* configuration, AZ::Crc32& crc)
|
|
{
|
|
SimulatedBodyType* newBody = aznew SimulatedBodyType(*configuration);
|
|
if (!AZStd::holds_alternative<AZStd::monostate>(configuration->m_colliderAndShapeData))
|
|
{
|
|
[[maybe_unused]] const bool shapeAdded = AddShape(newBody, configuration->m_colliderAndShapeData);
|
|
AZ_Warning("PhysXScene", shapeAdded, "No Collider or Shape information found when creating Rigid body [%s]", configuration->m_debugName.c_str());
|
|
}
|
|
crc = AZ::Crc32(newBody, sizeof(*newBody));
|
|
return newBody;
|
|
}
|
|
|
|
AzPhysics::SimulatedBody* CreateRigidBody(const AzPhysics::RigidBodyConfiguration* configuration, AZ::Crc32& crc)
|
|
{
|
|
RigidBody* newBody = aznew RigidBody(*configuration);
|
|
if (!AZStd::holds_alternative<AZStd::monostate>(configuration->m_colliderAndShapeData))
|
|
{
|
|
[[maybe_unused]] const bool shapeAdded = AddShape(newBody, configuration->m_colliderAndShapeData);
|
|
AZ_Warning("PhysXScene", shapeAdded, "No Collider or Shape information found when creating Rigid body [%s]", configuration->m_debugName.c_str());
|
|
}
|
|
const AzPhysics::MassComputeFlags& flags = configuration->GetMassComputeFlags();
|
|
newBody->UpdateMassProperties(flags, configuration->m_centerOfMassOffset,
|
|
configuration->m_inertiaTensor, configuration->m_mass);
|
|
|
|
crc = AZ::Crc32(newBody, sizeof(*newBody));
|
|
return newBody;
|
|
}
|
|
|
|
AzPhysics::SimulatedBody* CreateCharacterBody(PhysXScene* scene,
|
|
const Physics::CharacterConfiguration* characterConfig)
|
|
{
|
|
CharacterController* controller = Utils::Characters::CreateCharacterController(scene, *characterConfig);
|
|
if (controller == nullptr)
|
|
{
|
|
AZ_Error("PhysXScene", false, "Failed to create character controller.");
|
|
return nullptr;
|
|
}
|
|
controller->EnablePhysics(*characterConfig);
|
|
controller->SetBasePosition(characterConfig->m_position);
|
|
|
|
for (auto shape : characterConfig->m_colliders)
|
|
{
|
|
controller->AttachShape(shape);
|
|
}
|
|
|
|
return controller;
|
|
}
|
|
|
|
AzPhysics::SimulatedBody* CreateRagdollBody(PhysXScene* scene,
|
|
const Physics::RagdollConfiguration* ragdollConfig)
|
|
{
|
|
return Utils::Characters::CreateRagdoll(const_cast<Physics::RagdollConfiguration&>(*ragdollConfig),
|
|
scene->GetSceneHandle());
|
|
}
|
|
|
|
template<class JointType, class ConfigurationType>
|
|
AzPhysics::Joint* CreateJoint(const ConfigurationType* configuration,
|
|
AzPhysics::SceneHandle sceneHandle,
|
|
AzPhysics::SimulatedBodyHandle parentBodyHandle,
|
|
AzPhysics::SimulatedBodyHandle childBodyHandle,
|
|
AZ::Crc32& crc)
|
|
{
|
|
JointType* newBody = aznew JointType(*configuration, sceneHandle, parentBodyHandle, childBodyHandle);
|
|
crc = AZ::Crc32(newBody, sizeof(*newBody));
|
|
return newBody;
|
|
}
|
|
|
|
//helper to perform a ray cast
|
|
AzPhysics::SceneQueryHits RayCast(const AzPhysics::RayCastRequest* raycastRequest,
|
|
AZStd::vector<physx::PxRaycastHit>& raycastBuffer,
|
|
physx::PxScene* physxScene,
|
|
const physx::PxQueryFilterData queryData,
|
|
const AZ::u64 sceneMaxResults)
|
|
{
|
|
// if this query need to report multiple hits, we need to prepare a buffer to hold up to the max allowed.
|
|
// The filter should also use the eTOUCH flag to find all contacts with the ray.
|
|
// Otherwise the default buffer (1 result) and eBLOCK flag is enough to find the first hit.
|
|
physx::PxRaycastBuffer castResult;
|
|
SceneQueryHelpers::PhysXQueryFilterCallback queryFilterCallback;
|
|
if (raycastRequest->m_reportMultipleHits)
|
|
{
|
|
const AZ::u64 maxSize = AZStd::min(raycastRequest->m_maxResults, sceneMaxResults);
|
|
if (raycastBuffer.size() < maxSize) //todo this needs to be limited by the config setting
|
|
{
|
|
raycastBuffer.resize(maxSize);
|
|
}
|
|
castResult = physx::PxRaycastBuffer(raycastBuffer.begin(), aznumeric_cast<physx::PxU32>(maxSize));
|
|
queryFilterCallback = SceneQueryHelpers::PhysXQueryFilterCallback(
|
|
raycastRequest->m_collisionGroup,
|
|
raycastRequest->m_filterCallback,
|
|
physx::PxQueryHitType::eTOUCH);
|
|
}
|
|
else
|
|
{
|
|
queryFilterCallback = SceneQueryHelpers::PhysXQueryFilterCallback(
|
|
raycastRequest->m_collisionGroup,
|
|
SceneQueryHelpers::GetSceneQueryBlockFilterCallback(raycastRequest->m_filterCallback),
|
|
physx::PxQueryHitType::eBLOCK);
|
|
}
|
|
|
|
const physx::PxVec3 orig = PxMathConvert(raycastRequest->m_start);
|
|
const physx::PxVec3 dir = PxMathConvert(raycastRequest->m_direction.GetNormalized());
|
|
const physx::PxHitFlags hitFlags = SceneQueryHelpers::GetPxHitFlags(raycastRequest->m_hitFlags);
|
|
//Raycast
|
|
bool status = false;
|
|
{
|
|
PHYSX_SCENE_READ_LOCK(physxScene);
|
|
status = physxScene->raycast(orig, dir, raycastRequest->m_distance, castResult, hitFlags, queryData, &queryFilterCallback);
|
|
}
|
|
|
|
AzPhysics::SceneQueryHits hits;
|
|
if (status)
|
|
{
|
|
if (castResult.hasBlock)
|
|
{
|
|
hits.m_hits.emplace_back(SceneQueryHelpers::GetHitFromPxHit(castResult.block));
|
|
}
|
|
|
|
if (raycastRequest->m_reportMultipleHits)
|
|
{
|
|
for (auto i = 0u; i < castResult.getNbTouches(); ++i)
|
|
{
|
|
const auto& pxHit = castResult.getTouch(i);
|
|
hits.m_hits.emplace_back(SceneQueryHelpers::GetHitFromPxHit(pxHit));
|
|
}
|
|
}
|
|
}
|
|
return hits;
|
|
}
|
|
|
|
//helper to preform a shape cast
|
|
AzPhysics::SceneQueryHits ShapeCast(const AzPhysics::ShapeCastRequest* shapecastRequest,
|
|
AZStd::vector<physx::PxSweepHit>& shapecastBuffer,
|
|
physx::PxScene* physxScene,
|
|
const physx::PxQueryFilterData queryData,
|
|
const AZ::u64 sceneMaxResults)
|
|
{
|
|
// if this query need to report multiple hits, we need to prepare a buffer to hold up to the max allowed.
|
|
// The filter should also use the eTOUCH flag to find all contacts with the shape.
|
|
// Otherwise the default buffer (1 result) and eBLOCK flag is enough to find the first hit.
|
|
physx::PxSweepBuffer castResult;
|
|
SceneQueryHelpers::PhysXQueryFilterCallback queryFilterCallback;
|
|
if (shapecastRequest->m_reportMultipleHits)
|
|
{
|
|
const AZ::u64 maxSize = AZStd::min(shapecastRequest->m_maxResults, sceneMaxResults);
|
|
if (shapecastBuffer.size() < maxSize) //todo this needs to be limited by the config setting
|
|
{
|
|
shapecastBuffer.resize(maxSize);
|
|
}
|
|
castResult = physx::PxSweepBuffer(shapecastBuffer.begin(), aznumeric_cast<physx::PxU32>(maxSize));
|
|
queryFilterCallback = SceneQueryHelpers::PhysXQueryFilterCallback(
|
|
shapecastRequest->m_collisionGroup,
|
|
shapecastRequest->m_filterCallback,
|
|
physx::PxQueryHitType::eTOUCH);
|
|
}
|
|
else
|
|
{
|
|
queryFilterCallback = SceneQueryHelpers::PhysXQueryFilterCallback(
|
|
shapecastRequest->m_collisionGroup,
|
|
SceneQueryHelpers::GetSceneQueryBlockFilterCallback(shapecastRequest->m_filterCallback),
|
|
physx::PxQueryHitType::eBLOCK);
|
|
}
|
|
|
|
physx::PxGeometryHolder pxGeometry;
|
|
Utils::CreatePxGeometryFromConfig(*(shapecastRequest->m_shapeConfiguration), pxGeometry);
|
|
|
|
AzPhysics::SceneQueryHits results;
|
|
if (pxGeometry.any().getType() == physx::PxGeometryType::eSPHERE ||
|
|
pxGeometry.any().getType() == physx::PxGeometryType::eBOX ||
|
|
pxGeometry.any().getType() == physx::PxGeometryType::eCAPSULE ||
|
|
pxGeometry.any().getType() == physx::PxGeometryType::eCONVEXMESH)
|
|
{
|
|
const physx::PxTransform pose = PxMathConvert(shapecastRequest->m_start);
|
|
const physx::PxVec3 dir = PxMathConvert(shapecastRequest->m_direction.GetNormalized());
|
|
AZ_Warning("PhysXScene", (static_cast<AZ::u16>(shapecastRequest->m_hitFlags & AzPhysics::SceneQuery::HitFlags::MTD) != 0),
|
|
"Not having MTD set for shape scene queries may result in incorrect reporting of colliders that are in contact or intersect the initial pose of the sweep.");
|
|
const physx::PxHitFlags hitFlags = SceneQueryHelpers::GetPxHitFlags(shapecastRequest->m_hitFlags);
|
|
|
|
bool status = false;
|
|
{
|
|
PHYSX_SCENE_READ_LOCK(*physxScene);
|
|
status = physxScene->sweep(pxGeometry.any(), pose, dir, shapecastRequest->m_distance,
|
|
castResult, hitFlags, queryData, &queryFilterCallback);
|
|
}
|
|
|
|
if (status)
|
|
{
|
|
if (castResult.hasBlock)
|
|
{
|
|
results.m_hits.emplace_back(SceneQueryHelpers::GetHitFromPxHit(castResult.block));
|
|
}
|
|
|
|
if (shapecastRequest->m_reportMultipleHits)
|
|
{
|
|
for (auto i = 0u; i < castResult.getNbTouches(); ++i)
|
|
{
|
|
const auto& pxHit = castResult.getTouch(i);
|
|
results.m_hits.emplace_back(SceneQueryHelpers::GetHitFromPxHit(pxHit));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AZ_Warning("World", false, "Invalid geometry type passed to shape cast. Only sphere, box, capsule or convex mesh is supported");
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
bool OverlapGeneric(physx::PxScene* physxScene, const AzPhysics::OverlapRequest* overlapRequest,
|
|
physx::PxOverlapCallback& overlapCallback, const physx::PxQueryFilterData& filterData)
|
|
{
|
|
// Prepare overlap data
|
|
const physx::PxTransform pose = PxMathConvert(overlapRequest->m_pose);
|
|
physx::PxGeometryHolder pxGeometry;
|
|
Utils::CreatePxGeometryFromConfig(*(overlapRequest->m_shapeConfiguration), pxGeometry);
|
|
|
|
SceneQueryHelpers::PhysXQueryFilterCallback filterCallback(
|
|
overlapRequest->m_collisionGroup,
|
|
SceneQueryHelpers::GetFilterCallbackFromOverlap(overlapRequest->m_filterCallback),
|
|
physx::PxQueryHitType::eTOUCH);
|
|
|
|
bool status = false;
|
|
{
|
|
PHYSX_SCENE_READ_LOCK(*physxScene);
|
|
status = physxScene->overlap(pxGeometry.any(), pose, overlapCallback, filterData, &filterCallback);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
AzPhysics::SceneQueryHits OverlapQuery(const AzPhysics::OverlapRequest* overlapRequest,
|
|
AZStd::vector<physx::PxOverlapHit>& overlapBuffer,
|
|
physx::PxScene* physxScene,
|
|
const physx::PxQueryFilterData queryData,
|
|
const AZ::u64 sceneMaxResults)
|
|
{
|
|
const AZ::u64 maxSize = AZStd::min(overlapRequest->m_maxResults, sceneMaxResults);
|
|
if (overlapBuffer.size() < maxSize)
|
|
{
|
|
overlapBuffer.resize(maxSize);
|
|
}
|
|
|
|
if (overlapRequest->m_unboundedOverlapHitCallback)
|
|
{
|
|
SceneQueryHelpers::UnboundedOverlapCallback callback(overlapRequest->m_unboundedOverlapHitCallback, overlapBuffer);
|
|
const bool status = OverlapGeneric(physxScene, overlapRequest, callback, queryData);
|
|
if (status)
|
|
{
|
|
return callback.m_results;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
physx::PxOverlapBuffer queryHits(overlapBuffer.begin(), aznumeric_cast<physx::PxU32>(maxSize));
|
|
bool status = OverlapGeneric(physxScene, overlapRequest, queryHits, queryData);
|
|
|
|
AzPhysics::SceneQueryHits results;
|
|
if (status)
|
|
{
|
|
// Process results
|
|
AZ::u32 hitNum = queryHits.getNbAnyHits();
|
|
results.m_hits.reserve(hitNum);
|
|
for (AZ::u32 i = 0; i < hitNum; ++i)
|
|
{
|
|
const AzPhysics::SceneQueryHit hit = SceneQueryHelpers::GetHitFromPxOverlapHit(queryHits.getAnyHit(i));
|
|
if (hit.IsValid())
|
|
{
|
|
results.m_hits.emplace_back(hit);
|
|
}
|
|
}
|
|
results.m_hits.shrink_to_fit();
|
|
}
|
|
return results;
|
|
}
|
|
}
|
|
|
|
PhysXScene::PhysXScene(const AzPhysics::SceneConfiguration& config, const AzPhysics::SceneHandle& sceneHandle)
|
|
: Scene(config)
|
|
, m_config(config)
|
|
, m_sceneHandle(sceneHandle)
|
|
, m_physicsSystemConfigChanged([this](const AzPhysics::SystemConfiguration* config)
|
|
{
|
|
m_raycastBufferSize = config->m_raycastBufferSize;
|
|
m_shapecastBufferSize = config->m_shapecastBufferSize;
|
|
m_overlapBufferSize = config->m_overlapBufferSize;
|
|
})
|
|
{
|
|
//setup the scene query buffer sizes
|
|
if (auto* physXSystem = GetPhysXSystem())
|
|
{
|
|
if (const AzPhysics::SystemConfiguration* sysConfig = physXSystem->GetConfiguration())
|
|
{
|
|
m_raycastBufferSize = sysConfig->m_raycastBufferSize;
|
|
m_shapecastBufferSize = sysConfig->m_shapecastBufferSize;
|
|
m_overlapBufferSize = sysConfig->m_overlapBufferSize;
|
|
}
|
|
//register for future changes to the buffer sizes.
|
|
physXSystem->RegisterSystemConfigurationChangedEvent(m_physicsSystemConfigChanged);
|
|
}
|
|
|
|
PhysXScene::s_rayCastBuffer = {};
|
|
PhysXScene::s_sweepBuffer = {};
|
|
PhysXScene::s_overlapBuffer = {};
|
|
|
|
m_pxScene = Internal::CreatePxScene(m_config, &m_collisionFilterCallback, &m_simulationEventCallback);
|
|
AZ_Assert(m_pxScene != nullptr, "PhysX::Scene creation failed.");
|
|
|
|
m_pxScene->userData = this;
|
|
|
|
m_gravity = m_config.m_gravity;
|
|
}
|
|
|
|
PhysXScene::~PhysXScene()
|
|
{
|
|
m_physicsSystemConfigChanged.Disconnect();
|
|
|
|
s_overlapBuffer.swap({});
|
|
s_rayCastBuffer.swap({});
|
|
s_sweepBuffer.swap({});
|
|
|
|
for (auto& simulatedBody : m_simulatedBodies)
|
|
{
|
|
if (simulatedBody.second != nullptr)
|
|
{
|
|
if (simulatedBody.second->m_simulating)
|
|
{
|
|
// Disable simulation on body (not signaling OnSimulationBodySimulationDisabled event)
|
|
DisableSimulationOfBodyInternal(*simulatedBody.second);
|
|
}
|
|
m_simulatedBodyRemovedEvent.Signal(m_sceneHandle, simulatedBody.second->m_bodyHandle);
|
|
delete simulatedBody.second;
|
|
}
|
|
}
|
|
m_simulatedBodies.clear();
|
|
|
|
ClearDeferedDeletions();
|
|
|
|
if (m_controllerManager)
|
|
{
|
|
m_controllerManager->release();
|
|
m_controllerManager = nullptr;
|
|
}
|
|
|
|
if (m_pxScene)
|
|
{
|
|
m_pxScene->release();
|
|
m_pxScene = nullptr;
|
|
}
|
|
}
|
|
|
|
void PhysXScene::StartSimulation(float deltatime)
|
|
{
|
|
AZ_PROFILE_SCOPE(Physics, "PhysXScene::StartSimulation");
|
|
|
|
if (!IsEnabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
{
|
|
AZ_PROFILE_SCOPE(Physics, "OnSceneSimulationStartEvent::Signaled");
|
|
m_sceneSimuationStartEvent.Signal(m_sceneHandle, deltatime);
|
|
}
|
|
|
|
m_currentDeltaTime = deltatime;
|
|
|
|
PHYSX_SCENE_WRITE_LOCK(m_pxScene);
|
|
m_pxScene->simulate(deltatime);
|
|
}
|
|
|
|
void PhysXScene::FinishSimulation()
|
|
{
|
|
AZ_PROFILE_SCOPE(Physics, "PhysXScene::FinishSimulation");
|
|
|
|
if (!IsEnabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
{
|
|
AZ_PROFILE_SCOPE(Physics, "PhysXScene::CheckResults");
|
|
|
|
// Wait for the simulation to complete.
|
|
// In the multithreaded environment we need to make sure we don't lock the scene for write here.
|
|
// This is because contact modification callbacks can be issued from the job threads and cause deadlock
|
|
// due to the callback code locking the scene.
|
|
// https://devtalk.nvidia.com/default/topic/1024408/pxcontactmodifycallback-and-pxscene-locking/
|
|
m_pxScene->checkResults(true);
|
|
}
|
|
|
|
bool activeActorsEnabled = false;
|
|
{
|
|
AZ_PROFILE_SCOPE(Physics, "PhysXScene::FetchResults");
|
|
PHYSX_SCENE_WRITE_LOCK(m_pxScene);
|
|
|
|
activeActorsEnabled = m_pxScene->getFlags() & physx::PxSceneFlag::eENABLE_ACTIVE_ACTORS;
|
|
|
|
// Swap the buffers, invoke callbacks, build the list of active actors.
|
|
m_pxScene->fetchResults(true);
|
|
}
|
|
|
|
if (activeActorsEnabled)
|
|
{
|
|
AZ_PROFILE_SCOPE(Physics, "PhysXScene::ActiveActors");
|
|
|
|
PHYSX_SCENE_READ_LOCK(m_pxScene);
|
|
|
|
physx::PxU32 numActiveActors = 0;
|
|
physx::PxActor** activeActors = m_pxScene->getActiveActors(numActiveActors);
|
|
AzPhysics::SimulatedBodyHandleList activeBodyHandles;
|
|
activeBodyHandles.reserve(numActiveActors);
|
|
for (physx::PxU32 i = 0; i < numActiveActors; ++i)
|
|
{
|
|
if (ActorData* actorData = Utils::GetUserData(activeActors[i]))
|
|
{
|
|
activeBodyHandles.emplace_back(actorData->GetBodyHandle());
|
|
}
|
|
}
|
|
m_sceneActiveSimulatedBodies.Signal(m_sceneHandle, activeBodyHandles);
|
|
}
|
|
|
|
FlushQueuedEvents();
|
|
ClearDeferedDeletions();
|
|
|
|
{
|
|
AZ_PROFILE_SCOPE(Physics, "OnSceneSimulationFinishedEvent::Signaled");
|
|
m_sceneSimuationFinishEvent.Signal(m_sceneHandle, m_currentDeltaTime);
|
|
}
|
|
|
|
UpdateAzProfilerDataPoints();
|
|
}
|
|
|
|
void PhysXScene::FlushQueuedEvents()
|
|
{
|
|
//send queued trigger events
|
|
ProcessTriggerEvents();
|
|
|
|
//send queued collision events
|
|
ProcessCollisionEvents();
|
|
}
|
|
|
|
void PhysXScene::SetEnabled(bool enable)
|
|
{
|
|
m_isEnabled = enable;
|
|
}
|
|
|
|
bool PhysXScene::IsEnabled() const
|
|
{
|
|
return m_isEnabled;
|
|
}
|
|
|
|
const AzPhysics::SceneConfiguration& PhysXScene::GetConfiguration() const
|
|
{
|
|
return m_config;
|
|
}
|
|
|
|
void PhysXScene::UpdateConfiguration(const AzPhysics::SceneConfiguration& config)
|
|
{
|
|
if (m_config != config)
|
|
{
|
|
m_config = config;
|
|
m_configChangeEvent.Signal(m_sceneHandle, m_config);
|
|
|
|
// set gravity verifies this is a new value.
|
|
SetGravity(m_config.m_gravity);
|
|
}
|
|
}
|
|
|
|
AzPhysics::SimulatedBodyHandle PhysXScene::AddSimulatedBody(const AzPhysics::SimulatedBodyConfiguration* simulatedBodyConfig)
|
|
{
|
|
AzPhysics::SimulatedBody* newBody = nullptr;
|
|
AZ::Crc32 newBodyCrc;
|
|
if (azrtti_istypeof<AzPhysics::RigidBodyConfiguration>(simulatedBodyConfig))
|
|
{
|
|
newBody = Internal::CreateRigidBody(
|
|
azdynamic_cast<const AzPhysics::RigidBodyConfiguration*>(simulatedBodyConfig), newBodyCrc);
|
|
}
|
|
else if (azrtti_istypeof<AzPhysics::StaticRigidBodyConfiguration>(simulatedBodyConfig))
|
|
{
|
|
newBody = Internal::CreateSimulatedBody<StaticRigidBody, AzPhysics::StaticRigidBodyConfiguration>(
|
|
azdynamic_cast<const AzPhysics::StaticRigidBodyConfiguration*>(simulatedBodyConfig), newBodyCrc);
|
|
}
|
|
else if (azrtti_istypeof<Physics::CharacterConfiguration>(simulatedBodyConfig))
|
|
{
|
|
newBody = Internal::CreateCharacterBody(this, azdynamic_cast<const Physics::CharacterConfiguration*>(simulatedBodyConfig));
|
|
}
|
|
else if (azrtti_istypeof<Physics::RagdollConfiguration>(simulatedBodyConfig))
|
|
{
|
|
newBody = Internal::CreateRagdollBody(this, azdynamic_cast<const Physics::RagdollConfiguration*>(simulatedBodyConfig));
|
|
}
|
|
else
|
|
{
|
|
AZ_Warning("PhysXScene", false, "Unknown SimulatedBodyConfiguration.");
|
|
return AzPhysics::InvalidSimulatedBodyHandle;
|
|
}
|
|
|
|
if (newBody != nullptr)
|
|
{
|
|
AzPhysics::SimulatedBodyIndex index;
|
|
|
|
if (m_freeSceneSlots.empty())
|
|
{
|
|
m_simulatedBodies.emplace_back(newBodyCrc, newBody);
|
|
index = static_cast<AzPhysics::SimulatedBodyIndex>(m_simulatedBodies.size() - 1);
|
|
}
|
|
else
|
|
{
|
|
//fill any free slots first before increasing the size of the simulatedBodies vector.
|
|
index = m_freeSceneSlots.front();
|
|
m_freeSceneSlots.pop();
|
|
AZ_Assert(index < m_simulatedBodies.size(), "PhysXScene::AddSimulatedBody: Free simulated body index is out of bounds");
|
|
AZ_Assert(m_simulatedBodies[index].second == nullptr, "PhysXScene::AddSimulatedBody: Free simulated body index is not free");
|
|
|
|
m_simulatedBodies[index] = AZStd::make_pair(newBodyCrc, newBody);
|
|
}
|
|
|
|
const AzPhysics::SimulatedBodyHandle newBodyHandle(newBodyCrc, index);
|
|
newBody->m_sceneOwner = m_sceneHandle;
|
|
newBody->m_bodyHandle = newBodyHandle;
|
|
m_simulatedBodyAddedEvent.Signal(m_sceneHandle, newBodyHandle);
|
|
|
|
// Enable simulation by default (not signaling OnSimulationBodySimulationEnabled event)
|
|
if (simulatedBodyConfig->m_startSimulationEnabled)
|
|
{
|
|
EnableSimulationOfBodyInternal(*newBody);
|
|
}
|
|
|
|
return newBodyHandle;
|
|
}
|
|
|
|
return AzPhysics::InvalidSimulatedBodyHandle;
|
|
}
|
|
|
|
AzPhysics::SimulatedBodyHandleList PhysXScene::AddSimulatedBodies(const AzPhysics::SimulatedBodyConfigurationList& simulatedBodyConfigs)
|
|
{
|
|
AzPhysics::SimulatedBodyHandleList newBodyHandles;
|
|
newBodyHandles.reserve(simulatedBodyConfigs.size());
|
|
for (auto* config : simulatedBodyConfigs)
|
|
{
|
|
newBodyHandles.emplace_back(AddSimulatedBody(config));
|
|
}
|
|
return newBodyHandles;
|
|
}
|
|
|
|
AzPhysics::SimulatedBody* PhysXScene::GetSimulatedBodyFromHandle(AzPhysics::SimulatedBodyHandle bodyHandle)
|
|
{
|
|
if (bodyHandle == AzPhysics::InvalidSimulatedBodyHandle)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
AzPhysics::SimulatedBodyIndex index = AZStd::get<AzPhysics::HandleTypeIndex::Index>(bodyHandle);
|
|
if (index < m_simulatedBodies.size()
|
|
&& m_simulatedBodies[index].first == AZStd::get<AzPhysics::HandleTypeIndex::Crc>(bodyHandle))
|
|
{
|
|
return m_simulatedBodies[index].second;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
AzPhysics::SimulatedBodyList PhysXScene::GetSimulatedBodiesFromHandle(const AzPhysics::SimulatedBodyHandleList& bodyHandles)
|
|
{
|
|
AzPhysics::SimulatedBodyList results;
|
|
for (auto& handle : bodyHandles)
|
|
{
|
|
results.emplace_back(GetSimulatedBodyFromHandle(handle));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
void PhysXScene::RemoveSimulatedBody(AzPhysics::SimulatedBodyHandle& bodyHandle)
|
|
{
|
|
if (bodyHandle == AzPhysics::InvalidSimulatedBodyHandle)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AzPhysics::SimulatedBodyIndex index = AZStd::get<AzPhysics::HandleTypeIndex::Index>(bodyHandle);
|
|
if (index < m_simulatedBodies.size()
|
|
&& m_simulatedBodies[index].first == AZStd::get<AzPhysics::HandleTypeIndex::Crc>(bodyHandle))
|
|
{
|
|
if (m_simulatedBodies[index].second->m_simulating)
|
|
{
|
|
// Disable simulation on body (not signaling OnSimulationBodySimulationDisabled event)
|
|
DisableSimulationOfBodyInternal(*m_simulatedBodies[index].second);
|
|
}
|
|
|
|
m_simulatedBodyRemovedEvent.Signal(m_sceneHandle, bodyHandle);
|
|
|
|
m_deferredDeletions.push_back(m_simulatedBodies[index].second);
|
|
m_simulatedBodies[index] = AZStd::make_pair(AZ::Crc32(), nullptr);
|
|
m_freeSceneSlots.push(index);
|
|
|
|
bodyHandle = AzPhysics::InvalidSimulatedBodyHandle;
|
|
}
|
|
}
|
|
|
|
void PhysXScene::RemoveSimulatedBodies(AzPhysics::SimulatedBodyHandleList& bodyHandles)
|
|
{
|
|
for (auto& handle: bodyHandles)
|
|
{
|
|
RemoveSimulatedBody(handle);
|
|
}
|
|
}
|
|
|
|
void PhysXScene::EnableSimulationOfBody(AzPhysics::SimulatedBodyHandle bodyHandle)
|
|
{
|
|
if (bodyHandle == AzPhysics::InvalidSimulatedBodyHandle)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (AzPhysics::SimulatedBody* body = GetSimulatedBodyFromHandle(bodyHandle))
|
|
{
|
|
if (body->m_simulating)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_simulatedBodySimulationEnabledEvent.Signal(m_sceneHandle, bodyHandle);
|
|
|
|
EnableSimulationOfBodyInternal(*body);
|
|
}
|
|
else
|
|
{
|
|
AZ_Warning("PhysXScene", false, "Unable to enable Simulated body, failed to find body.")
|
|
}
|
|
}
|
|
|
|
void PhysXScene::DisableSimulationOfBody(AzPhysics::SimulatedBodyHandle bodyHandle)
|
|
{
|
|
if (bodyHandle == AzPhysics::InvalidSimulatedBodyHandle)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (AzPhysics::SimulatedBody* body = GetSimulatedBodyFromHandle(bodyHandle))
|
|
{
|
|
if (!body->m_simulating)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_simulatedBodySimulationDisabledEvent.Signal(m_sceneHandle, bodyHandle);
|
|
|
|
DisableSimulationOfBodyInternal(*body);
|
|
}
|
|
else
|
|
{
|
|
AZ_Warning("PhysXScene", false, "Unable to disable Simulated body, failed to find body.")
|
|
}
|
|
}
|
|
|
|
AzPhysics::JointHandle PhysXScene::AddJoint(const AzPhysics::JointConfiguration* jointConfig,
|
|
AzPhysics::SimulatedBodyHandle parentBody, AzPhysics::SimulatedBodyHandle childBody)
|
|
{
|
|
AzPhysics::Joint* newJoint = nullptr;
|
|
AZ::Crc32 newJointCrc;
|
|
if (azrtti_istypeof<PhysX::D6JointLimitConfiguration>(jointConfig))
|
|
{
|
|
newJoint = Internal::CreateJoint<PhysXD6Joint, D6JointLimitConfiguration>(
|
|
azdynamic_cast<const D6JointLimitConfiguration*>(jointConfig),
|
|
m_sceneHandle, parentBody, childBody, newJointCrc);
|
|
}
|
|
else if (azrtti_istypeof<PhysX::FixedJointConfiguration*>(jointConfig))
|
|
{
|
|
newJoint = Internal::CreateJoint<PhysXFixedJoint, FixedJointConfiguration>(
|
|
azdynamic_cast<const FixedJointConfiguration*>(jointConfig),
|
|
m_sceneHandle, parentBody, childBody, newJointCrc);
|
|
}
|
|
else if (azrtti_istypeof<PhysX::BallJointConfiguration*>(jointConfig))
|
|
{
|
|
newJoint = Internal::CreateJoint<PhysXBallJoint, BallJointConfiguration>(
|
|
azdynamic_cast<const BallJointConfiguration*>(jointConfig),
|
|
m_sceneHandle, parentBody, childBody, newJointCrc);
|
|
}
|
|
else if (azrtti_istypeof<PhysX::HingeJointConfiguration*>(jointConfig))
|
|
{
|
|
newJoint = Internal::CreateJoint<PhysXHingeJoint, HingeJointConfiguration>(
|
|
azdynamic_cast<const HingeJointConfiguration*>(jointConfig),
|
|
m_sceneHandle, parentBody, childBody, newJointCrc);
|
|
}
|
|
else
|
|
{
|
|
AZ_Warning("PhysXScene", false, "Unknown JointConfiguration.");
|
|
return AzPhysics::InvalidJointHandle;
|
|
}
|
|
|
|
if (newJoint != nullptr)
|
|
{
|
|
AzPhysics::JointIndex index = static_cast<AzPhysics::JointIndex>(m_joints.size());
|
|
m_joints.emplace_back(newJointCrc, newJoint);
|
|
|
|
const AzPhysics::JointHandle newJointHandle(newJointCrc, index);
|
|
newJoint->m_sceneOwner = m_sceneHandle;
|
|
newJoint->m_jointHandle = newJointHandle;
|
|
|
|
return newJointHandle;
|
|
}
|
|
|
|
return AzPhysics::InvalidJointHandle;
|
|
}
|
|
|
|
AzPhysics::Joint* PhysXScene::GetJointFromHandle(AzPhysics::JointHandle jointHandle)
|
|
{
|
|
if (jointHandle == AzPhysics::InvalidJointHandle)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
AzPhysics::JointIndex index = AZStd::get<AzPhysics::HandleTypeIndex::Index>(jointHandle);
|
|
if (index < m_joints.size()
|
|
&& m_joints[index].first == AZStd::get<AzPhysics::HandleTypeIndex::Crc>(jointHandle))
|
|
{
|
|
return m_joints[index].second;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void PhysXScene::RemoveJoint(AzPhysics::JointHandle jointHandle)
|
|
{
|
|
if (jointHandle == AzPhysics::InvalidJointHandle)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AzPhysics::JointIndex index = AZStd::get<AzPhysics::HandleTypeIndex::Index>(jointHandle);
|
|
if (index < m_joints.size()
|
|
&& m_joints[index].first == AZStd::get<AzPhysics::HandleTypeIndex::Crc>(jointHandle))
|
|
{
|
|
m_deferredDeletionsJoints.push_back(m_joints[index].second);
|
|
m_joints[index] = AZStd::make_pair(AZ::Crc32(), nullptr);
|
|
m_freeJointSlots.push(index);
|
|
jointHandle = AzPhysics::InvalidJointHandle;
|
|
}
|
|
}
|
|
|
|
AzPhysics::SceneQueryHits PhysXScene::QueryScene(const AzPhysics::SceneQueryRequest* request)
|
|
{
|
|
if (request == nullptr)
|
|
{
|
|
return {}; //return 0 hits
|
|
}
|
|
|
|
// Query flags.
|
|
const physx::PxQueryFlags queryFlags = SceneQueryHelpers::GetPxQueryFlags(request->m_queryType);
|
|
const physx::PxQueryFilterData queryData(queryFlags);
|
|
|
|
if (azrtti_istypeof<AzPhysics::RayCastRequest>(request))
|
|
{
|
|
return Internal::RayCast(azdynamic_cast<const AzPhysics::RayCastRequest*>(request),
|
|
s_rayCastBuffer, m_pxScene, queryData, m_raycastBufferSize);
|
|
}
|
|
else if (azrtti_istypeof<AzPhysics::ShapeCastRequest>(request))
|
|
{
|
|
return Internal::ShapeCast(azdynamic_cast<const AzPhysics::ShapeCastRequest*>(request),
|
|
s_sweepBuffer, m_pxScene, queryData, m_shapecastBufferSize);
|
|
}
|
|
else if (azrtti_istypeof<AzPhysics::OverlapRequest>(request))
|
|
{
|
|
return Internal::OverlapQuery(azdynamic_cast<const AzPhysics::OverlapRequest*>(request),
|
|
s_overlapBuffer, m_pxScene, queryData, m_overlapBufferSize);
|
|
}
|
|
else
|
|
{
|
|
AZ_Warning("Physx", false, "Unknown Scene Query request type.");
|
|
}
|
|
return AzPhysics::SceneQueryHits();
|
|
}
|
|
|
|
AzPhysics::SceneQueryHitsList PhysXScene::QuerySceneBatch(const AzPhysics::SceneQueryRequests& requests)
|
|
{
|
|
AzPhysics::SceneQueryHitsList results;
|
|
results.reserve(requests.size());
|
|
for (auto& request : requests)
|
|
{
|
|
results.emplace_back(QueryScene(request.get()));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
[[nodiscard]] bool PhysXScene::QuerySceneAsync([[maybe_unused]] AzPhysics::SceneQuery::AsyncRequestId requestId,
|
|
[[maybe_unused]] const AzPhysics::SceneQueryRequest* request, [[maybe_unused]] AzPhysics::SceneQuery::AsyncCallback callback)
|
|
{
|
|
AZ_Warning("Physx", false, "Currently unimplemented."); // LYN-2306
|
|
return false;
|
|
}
|
|
|
|
[[nodiscard]] bool PhysXScene::QuerySceneAsyncBatch([[maybe_unused]] AzPhysics::SceneQuery::AsyncRequestId requestId,
|
|
[[maybe_unused]] const AzPhysics::SceneQueryRequests& requests, [[maybe_unused]] AzPhysics::SceneQuery::AsyncBatchCallback callback)
|
|
{
|
|
AZ_Warning("Physx", false, "Currently unimplemented."); // LYN-2306
|
|
return false;
|
|
}
|
|
|
|
void PhysXScene::SuppressCollisionEvents(
|
|
const AzPhysics::SimulatedBodyHandle& bodyHandleA,
|
|
const AzPhysics::SimulatedBodyHandle& bodyHandleB)
|
|
{
|
|
AzPhysics::SimulatedBody* bodyA = GetSimulatedBodyFromHandle(bodyHandleA);
|
|
AzPhysics::SimulatedBody* bodyB = GetSimulatedBodyFromHandle(bodyHandleB);
|
|
if (bodyA != nullptr && bodyB != nullptr)
|
|
{
|
|
m_collisionFilterCallback.RegisterSuppressedCollision(bodyA, bodyB);
|
|
}
|
|
}
|
|
|
|
void PhysXScene::UnsuppressCollisionEvents(
|
|
const AzPhysics::SimulatedBodyHandle& bodyHandleA,
|
|
const AzPhysics::SimulatedBodyHandle& bodyHandleB)
|
|
{
|
|
AzPhysics::SimulatedBody* bodyA = GetSimulatedBodyFromHandle(bodyHandleA);
|
|
AzPhysics::SimulatedBody* bodyB = GetSimulatedBodyFromHandle(bodyHandleB);
|
|
if (bodyA != nullptr && bodyB != nullptr)
|
|
{
|
|
m_collisionFilterCallback.UnregisterSuppressedCollision(bodyA, bodyB);
|
|
}
|
|
}
|
|
|
|
void PhysXScene::SetGravity(const AZ::Vector3& gravity)
|
|
{
|
|
if (m_pxScene && !m_gravity.IsClose(gravity))
|
|
{
|
|
m_gravity = gravity;
|
|
{
|
|
PHYSX_SCENE_WRITE_LOCK(m_pxScene);
|
|
m_pxScene->setGravity(PxMathConvert(m_gravity));
|
|
}
|
|
m_sceneGravityChangedEvent.Signal(m_sceneHandle, m_gravity);
|
|
}
|
|
}
|
|
|
|
AZ::Vector3 PhysXScene::GetGravity() const
|
|
{
|
|
return m_gravity;
|
|
}
|
|
|
|
void PhysXScene::EnableSimulationOfBodyInternal(AzPhysics::SimulatedBody& body)
|
|
{
|
|
//character controller is a special actor and only needs the m_simulating flag set,
|
|
if (!azrtti_istypeof<PhysX::CharacterController>(body) &&
|
|
!azrtti_istypeof<PhysX::Ragdoll>(body))
|
|
{
|
|
auto pxActor = static_cast<physx::PxActor*>(body.GetNativePointer());
|
|
AZ_Assert(pxActor, "Simulated Body doesn't have a valid physx actor");
|
|
|
|
{
|
|
PHYSX_SCENE_WRITE_LOCK(m_pxScene);
|
|
m_pxScene->addActor(*pxActor);
|
|
}
|
|
|
|
if (azrtti_istypeof<PhysX::RigidBody>(body))
|
|
{
|
|
auto rigidBody = azdynamic_cast<PhysX::RigidBody*>(&body);
|
|
if (rigidBody->ShouldStartAsleep())
|
|
{
|
|
rigidBody->ForceAsleep();
|
|
}
|
|
}
|
|
}
|
|
|
|
body.m_simulating = true;
|
|
}
|
|
|
|
void PhysXScene::DisableSimulationOfBodyInternal(AzPhysics::SimulatedBody& body)
|
|
{
|
|
//character controller is a special actor and only needs the m_simulating flag set,
|
|
if (!azrtti_istypeof<PhysX::CharacterController>(body) &&
|
|
!azrtti_istypeof<PhysX::Ragdoll>(body))
|
|
{
|
|
auto pxActor = static_cast<physx::PxActor*>(body.GetNativePointer());
|
|
AZ_Assert(pxActor, "Simulated Body doesn't have a valid physx actor");
|
|
|
|
{
|
|
PHYSX_SCENE_WRITE_LOCK(m_pxScene);
|
|
m_pxScene->removeActor(*pxActor);
|
|
}
|
|
}
|
|
body.m_simulating = false;
|
|
}
|
|
|
|
physx::PxControllerManager* PhysXScene::GetOrCreateControllerManager()
|
|
{
|
|
if (m_controllerManager)
|
|
{
|
|
return m_controllerManager;
|
|
}
|
|
|
|
if (m_pxScene)
|
|
{
|
|
m_controllerManager = PxCreateControllerManager(*m_pxScene);
|
|
}
|
|
|
|
if (m_controllerManager)
|
|
{
|
|
m_controllerManager->setOverlapRecoveryModule(true);
|
|
}
|
|
else
|
|
{
|
|
AZ_Error("PhysX Character Controller System", false, "Unable to create a Controller Manager.");
|
|
}
|
|
|
|
return m_controllerManager;
|
|
}
|
|
|
|
void* PhysXScene::GetNativePointer() const
|
|
{
|
|
return m_pxScene;
|
|
}
|
|
|
|
void PhysXScene::ClearDeferedDeletions()
|
|
{
|
|
// swap the deletions in case the simulated body
|
|
// manages more bodies and removes them on destruction (ie. Ragdoll).
|
|
AZStd::vector<AzPhysics::SimulatedBody*> deletions;
|
|
deletions.swap(m_deferredDeletions);
|
|
for (auto* simulatedBody : deletions)
|
|
{
|
|
delete simulatedBody;
|
|
}
|
|
|
|
AZStd::vector<AzPhysics::Joint*> jointDeletions;
|
|
jointDeletions.swap(m_deferredDeletionsJoints);
|
|
for (auto* joint : jointDeletions)
|
|
{
|
|
delete joint;
|
|
}
|
|
}
|
|
|
|
void PhysXScene::ProcessTriggerEvents()
|
|
{
|
|
AZ_PROFILE_SCOPE(Physics, "PhysXScene::ProcessTriggerEvents");
|
|
|
|
AzPhysics::TriggerEventList& triggers = m_simulationEventCallback.GetQueuedTriggerEvents();
|
|
if (triggers.empty())
|
|
{
|
|
return; // nothing to signal
|
|
}
|
|
m_sceneTriggerEvent.Signal(m_sceneHandle, triggers);
|
|
|
|
for (auto& triggerEvent : triggers)
|
|
{
|
|
if (triggerEvent.m_triggerBody != nullptr)
|
|
{
|
|
triggerEvent.m_triggerBody->ProcessTriggerEvent(triggerEvent);
|
|
}
|
|
if (triggerEvent.m_otherBody != nullptr)
|
|
{
|
|
triggerEvent.m_otherBody->ProcessTriggerEvent(triggerEvent);
|
|
}
|
|
}
|
|
|
|
//cleanup events for next simulate
|
|
m_simulationEventCallback.FlushQueuedTriggerEvents();
|
|
}
|
|
|
|
void PhysXScene::ProcessCollisionEvents()
|
|
{
|
|
AZ_PROFILE_SCOPE(Physics, "PhysXScene::ProcessCollisionEvents");
|
|
|
|
AzPhysics::CollisionEventList& collisions = m_simulationEventCallback.GetQueuedCollisionEvents();
|
|
if (collisions.empty())
|
|
{
|
|
return; //nothing to signal
|
|
}
|
|
//send all event to any scene listeners
|
|
m_sceneCollisionEvent.Signal(m_sceneHandle, collisions);
|
|
|
|
//send events to each body listener
|
|
for (auto& collision : collisions)
|
|
{
|
|
//trigger on body 1
|
|
if (collision.m_body1 != nullptr)
|
|
{
|
|
collision.m_body1->ProcessCollisionEvent(collision);
|
|
}
|
|
//trigger for body 2
|
|
if (collision.m_body2 != nullptr)
|
|
{
|
|
//swap the data as the event expects the trigger body to be body1.
|
|
//this is ok to do as this event is no longer used after calling TriggerCollisionEvent
|
|
AZStd::swap(collision.m_bodyHandle1, collision.m_bodyHandle2);
|
|
AZStd::swap(collision.m_body1, collision.m_body2);
|
|
AZStd::swap(collision.m_shape1, collision.m_shape2);
|
|
collision.m_body1->ProcessCollisionEvent(collision);
|
|
}
|
|
}
|
|
|
|
//cleanup events for next simulate
|
|
m_simulationEventCallback.FlushQueuedCollisionEvents();
|
|
}
|
|
|
|
void PhysXScene::UpdateAzProfilerDataPoints()
|
|
{
|
|
using physx::PxGeometryType;
|
|
|
|
bool isProfilingActive = false;
|
|
AZ::Debug::ProfilerRequestBus::BroadcastResult(isProfilingActive, &AZ::Debug::ProfilerRequests::IsActive);
|
|
|
|
if (!isProfilingActive)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AZ_PROFILE_SCOPE(Physics, "PhysX::Statistics");
|
|
|
|
physx::PxSimulationStatistics stats;
|
|
|
|
{
|
|
PHYSX_SCENE_READ_LOCK(m_pxScene);
|
|
m_pxScene->getSimulationStatistics(stats);
|
|
}
|
|
|
|
[[maybe_unused]] const char* RootCategory = "PhysX/%s/%s";
|
|
|
|
[[maybe_unused]] const char* ShapesSubCategory = "Shapes";
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbShapes[PxGeometryType::eSPHERE], RootCategory, ShapesSubCategory, "Sphere");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbShapes[PxGeometryType::ePLANE], RootCategory, ShapesSubCategory, "Plane");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbShapes[PxGeometryType::eCAPSULE], RootCategory, ShapesSubCategory, "Capsule");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbShapes[PxGeometryType::eBOX], RootCategory, ShapesSubCategory, "Box");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbShapes[PxGeometryType::eCONVEXMESH], RootCategory, ShapesSubCategory, "ConvexMesh");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbShapes[PxGeometryType::eTRIANGLEMESH], RootCategory, ShapesSubCategory, "TriangleMesh");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbShapes[PxGeometryType::eHEIGHTFIELD], RootCategory, ShapesSubCategory, "Heightfield");
|
|
|
|
[[maybe_unused]] const char* ObjectsSubCategory = "Objects";
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbActiveConstraints, RootCategory, ObjectsSubCategory, "ActiveConstraints");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbActiveDynamicBodies, RootCategory, ObjectsSubCategory, "ActiveDynamicBodies");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbActiveKinematicBodies, RootCategory, ObjectsSubCategory, "ActiveKinematicBodies");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbStaticBodies, RootCategory, ObjectsSubCategory, "StaticBodies");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbDynamicBodies, RootCategory, ObjectsSubCategory, "DynamicBodies");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbKinematicBodies, RootCategory, ObjectsSubCategory, "KinematicBodies");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbAggregates, RootCategory, ObjectsSubCategory, "Aggregates");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbArticulations, RootCategory, ObjectsSubCategory, "Articulations");
|
|
|
|
[[maybe_unused]] const char* SolverSubCategory = "Solver";
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbAxisSolverConstraints, RootCategory, SolverSubCategory, "AxisSolverConstraints");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.compressedContactSize, RootCategory, SolverSubCategory, "CompressedContactSize");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.requiredContactConstraintMemory, RootCategory, SolverSubCategory, "RequiredContactConstraintMemory");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.peakConstraintMemory, RootCategory, SolverSubCategory, "PeakConstraintMemory");
|
|
|
|
[[maybe_unused]] const char* BroadphaseSubCategory = "Broadphase";
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.getNbBroadPhaseAdds(), RootCategory, BroadphaseSubCategory, "BroadPhaseAdds");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.getNbBroadPhaseRemoves(), RootCategory, BroadphaseSubCategory, "BroadPhaseRemoves");
|
|
|
|
// Compute pair stats for all geometry types
|
|
AZ::u32 ccdPairs = 0;
|
|
AZ::u32 modifiedPairs = 0;
|
|
AZ::u32 triggerPairs = 0;
|
|
|
|
for (AZ::u32 i = 0; i < PxGeometryType::eGEOMETRY_COUNT; i++)
|
|
{
|
|
// stat[i][j] = stat[j][i], hence, discarding the symmetric entries
|
|
for (AZ::u32 j = i; j < PxGeometryType::eGEOMETRY_COUNT; j++)
|
|
{
|
|
const PxGeometryType::Enum firstGeom = static_cast<PxGeometryType::Enum>(i);
|
|
const PxGeometryType::Enum secondGeom = static_cast<PxGeometryType::Enum>(j);
|
|
ccdPairs += stats.getRbPairStats(physx::PxSimulationStatistics::eCCD_PAIRS, firstGeom, secondGeom);
|
|
modifiedPairs += stats.getRbPairStats(physx::PxSimulationStatistics::eMODIFIED_CONTACT_PAIRS, firstGeom, secondGeom);
|
|
triggerPairs += stats.getRbPairStats(physx::PxSimulationStatistics::eTRIGGER_PAIRS, firstGeom, secondGeom);
|
|
}
|
|
}
|
|
|
|
[[maybe_unused]] const char* CollisionsSubCategory = "Collisions";
|
|
AZ_PROFILE_DATAPOINT(Physics, ccdPairs, RootCategory, CollisionsSubCategory, "CCDPairs");
|
|
AZ_PROFILE_DATAPOINT(Physics, modifiedPairs, RootCategory, CollisionsSubCategory, "ModifiedPairs");
|
|
AZ_PROFILE_DATAPOINT(Physics, triggerPairs, RootCategory, CollisionsSubCategory, "TriggerPairs");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbDiscreteContactPairsTotal, RootCategory, CollisionsSubCategory, "DiscreteContactPairsTotal");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbDiscreteContactPairsWithCacheHits, RootCategory, CollisionsSubCategory, "DiscreteContactPairsWithCacheHits");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbDiscreteContactPairsWithContacts, RootCategory, CollisionsSubCategory, "DiscreteContactPairsWithContacts");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbNewPairs, RootCategory, CollisionsSubCategory, "NewPairs");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbLostPairs, RootCategory, CollisionsSubCategory, "LostPairs");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbNewTouches, RootCategory, CollisionsSubCategory, "NewTouches");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbLostTouches, RootCategory, CollisionsSubCategory, "LostTouches");
|
|
AZ_PROFILE_DATAPOINT(Physics, stats.nbPartitions, RootCategory, CollisionsSubCategory, "Partitions");
|
|
}
|
|
}
|