merge from development

Signed-off-by: greerdv <greerdv@amazon.com>
This commit is contained in:
greerdv
2021-10-15 09:58:36 +01:00
122 changed files with 3114 additions and 982 deletions
+152 -135
View File
@@ -8,6 +8,7 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/Math/ToString.h>
#include <AzFramework/Physics/Utils.h>
#include <AzFramework/Physics/Configuration/RigidBodyConfiguration.h>
#include <PhysX/NativeTypeIdentifiers.h>
@@ -23,6 +24,28 @@
namespace PhysX
{
namespace
{
const AZ::Vector3 DefaultCenterOfMass = AZ::Vector3::CreateZero();
const float DefaultMass = 1.0f;
const AZ::Matrix3x3 DefaultInertiaTensor = AZ::Matrix3x3::CreateIdentity();
bool IsSimulationShape(const physx::PxShape& pxShape)
{
return (pxShape.getFlags() & physx::PxShapeFlag::eSIMULATION_SHAPE);
}
bool CanShapeComputeMassProperties(const physx::PxShape& pxShape)
{
// Note: List based on computeMassAndInertia function in ExtRigidBodyExt.cpp file in PhysX.
const physx::PxGeometryType::Enum geometryType = pxShape.getGeometryType();
return geometryType == physx::PxGeometryType::eSPHERE
|| geometryType == physx::PxGeometryType::eBOX
|| geometryType == physx::PxGeometryType::eCAPSULE
|| geometryType == physx::PxGeometryType::eCONVEXMESH;
}
}
void RigidBody::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
@@ -152,104 +175,120 @@ namespace PhysX
m_shapes.erase(found);
}
void RigidBody::UpdateMassProperties(AzPhysics::MassComputeFlags flags, const AZ::Vector3* centerOfMassOffsetOverride, const AZ::Matrix3x3* inertiaTensorOverride, const float* massOverride)
void RigidBody::UpdateMassProperties(AzPhysics::MassComputeFlags flags, const AZ::Vector3& centerOfMassOffsetOverride, const AZ::Matrix3x3& inertiaTensorOverride, const float massOverride)
{
// Input validation
bool computeCenterOfMass = AzPhysics::MassComputeFlags::COMPUTE_COM == (flags & AzPhysics::MassComputeFlags::COMPUTE_COM);
AZ_Assert(computeCenterOfMass || centerOfMassOffsetOverride,
"UpdateMassProperties: MassComputeFlags::COMPUTE_COM is not set but COM offset is not specified");
computeCenterOfMass = computeCenterOfMass || !centerOfMassOffsetOverride;
const bool computeCenterOfMass = AzPhysics::MassComputeFlags::COMPUTE_COM == (flags & AzPhysics::MassComputeFlags::COMPUTE_COM);
const bool computeInertiaTensor = AzPhysics::MassComputeFlags::COMPUTE_INERTIA == (flags & AzPhysics::MassComputeFlags::COMPUTE_INERTIA);
const bool computeMass = AzPhysics::MassComputeFlags::COMPUTE_MASS == (flags & AzPhysics::MassComputeFlags::COMPUTE_MASS);
const bool needsCompute = computeCenterOfMass || computeInertiaTensor || computeMass;
const bool includeAllShapesInMassCalculation = AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES == (flags & AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES);
bool computeInertiaTensor = AzPhysics::MassComputeFlags::COMPUTE_INERTIA == (flags & AzPhysics::MassComputeFlags::COMPUTE_INERTIA);
AZ_Assert(computeInertiaTensor || inertiaTensorOverride,
"UpdateMassProperties: MassComputeFlags::COMPUTE_INERTIA is not set but inertia tensor is not specified");
computeInertiaTensor = computeInertiaTensor || !inertiaTensorOverride;
bool computeMass = AzPhysics::MassComputeFlags::COMPUTE_MASS == (flags & AzPhysics::MassComputeFlags::COMPUTE_MASS);
AZ_Assert(computeMass || massOverride,
"UpdateMassProperties: MassComputeFlags::COMPUTE_MASS is not set but mass is not specified");
computeMass = computeMass || !massOverride;
AZ::u32 shapesCount = GetShapeCount();
// Basic cases when we don't need to compute anything
if (shapesCount == 0 || flags == AzPhysics::MassComputeFlags::NONE)
// Basic case where all properties are set directly.
if (!needsCompute)
{
if (massOverride)
{
SetMass(*massOverride);
}
if (inertiaTensorOverride)
{
SetInertia(*inertiaTensorOverride);
}
if (centerOfMassOffsetOverride)
{
SetCenterOfMassOffset(*centerOfMassOffsetOverride);
}
SetCenterOfMassOffset(centerOfMassOffsetOverride);
SetMass(massOverride);
SetInertia(inertiaTensorOverride);
return;
}
// Setup center of mass offset pointer for PxRigidBodyExt::updateMassAndInertia function
AZStd::optional<physx::PxVec3> optionalComOverride;
if (!computeCenterOfMass && centerOfMassOffsetOverride)
// If there are no shapes then set the properties directly without computing anything.
if (m_shapes.empty())
{
optionalComOverride = PxMathConvert(*centerOfMassOffsetOverride);
}
const physx::PxVec3* massLocalPose = optionalComOverride.has_value() ? &optionalComOverride.value() : nullptr;
bool includeAllShapesInMassCalculation =
AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES == (flags & AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES);
// Handle the case when we don't compute mass
if (!computeMass)
{
{
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
physx::PxRigidBodyExt::setMassAndUpdateInertia(*m_pxRigidActor, *massOverride, massLocalPose,
includeAllShapesInMassCalculation);
}
if (!computeInertiaTensor)
{
SetInertia(*inertiaTensorOverride);
}
SetCenterOfMassOffset(computeCenterOfMass ? DefaultCenterOfMass : centerOfMassOffsetOverride);
SetMass(computeMass ? DefaultMass : massOverride);
SetInertia(computeInertiaTensor ? DefaultInertiaTensor : inertiaTensorOverride);
return;
}
// Handle the cases when mass should be computed from density
if (shapesCount == 1)
auto cannotComputeMassProperties = [this, includeAllShapesInMassCalculation]
{
AZStd::shared_ptr<Physics::Shape> shape = GetShape(0);
float density = shape->GetMaterial()->GetDensity();
PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene());
return AZStd::any_of(m_shapes.cbegin(), m_shapes.cend(),
[includeAllShapesInMassCalculation](const AZStd::shared_ptr<PhysX::Shape>& shape)
{
const physx::PxShape& pxShape = *shape->GetPxShape();
const bool includeShape = includeAllShapesInMassCalculation || IsSimulationShape(pxShape);
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor, density, massLocalPose,
includeAllShapesInMassCalculation);
return includeShape && !CanShapeComputeMassProperties(pxShape);
});
};
// If contains shapes that cannot compute mass properties (triangle mesh,
// plane or heightfield) then default values will be used.
if (cannotComputeMassProperties())
{
AZ_Warning("RigidBody", !computeCenterOfMass,
"Rigid body '%s' cannot compute COM because it contains triangle mesh, plane or heightfield shapes, it will default to %s.",
GetName().c_str(), AZ::ToString(DefaultCenterOfMass).c_str());
AZ_Warning("RigidBody", !computeMass,
"Rigid body '%s' cannot compute Mass because it contains triangle mesh, plane or heightfield shapes, it will default to %0.1f.",
GetName().c_str(), DefaultMass);
AZ_Warning("RigidBody", !computeInertiaTensor,
"Rigid body '%s' cannot compute Inertia because it contains triangle mesh, plane or heightfield shapes, it will default to %s.",
GetName().c_str(), AZ::ToString(DefaultInertiaTensor.RetrieveScale()).c_str());
SetCenterOfMassOffset(computeCenterOfMass ? DefaultCenterOfMass : centerOfMassOffsetOverride);
SetMass(computeMass ? DefaultMass : massOverride);
SetInertia(computeInertiaTensor ? DefaultInertiaTensor : inertiaTensorOverride);
return;
}
// Center of mass needs to be considered first since
// it's needed when computing mass and inertia.
if (computeCenterOfMass)
{
// Compute Center of Mass
UpdateCenterOfMass(includeAllShapesInMassCalculation);
}
else
{
AZStd::vector<float> densities(shapesCount);
for (AZ::u32 i = 0; i < shapesCount; ++i)
SetCenterOfMassOffset(centerOfMassOffsetOverride);
}
const physx::PxVec3 pxCenterOfMass = PxMathConvert(GetCenterOfMassLocal());
if (computeMass)
{
// Gather material densities from all shapes,
// mass computation is based on them.
AZStd::vector<float> densities;
densities.reserve(m_shapes.size());
for (const auto& shape : m_shapes)
{
densities[i] = GetShape(i)->GetMaterial()->GetDensity();
densities.emplace_back(shape->GetMaterial()->GetDensity());
}
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor, densities.data(),
shapesCount, massLocalPose, includeAllShapesInMassCalculation);
}
// Compute Mass + Inertia
{
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
physx::PxRigidBodyExt::updateMassAndInertia(*m_pxRigidActor,
densities.data(), static_cast<AZ::u32>(densities.size()),
&pxCenterOfMass, includeAllShapesInMassCalculation);
}
// Set the overrides if provided.
// Note: We don't set the center of mass here because it was already provided
// to PxRigidBodyExt::updateMassAndInertia above
if (!computeInertiaTensor)
// There is no physx function to only compute the mass without
// computing the inertia. So now that both have been computed
// we can override the inertia if it's suppose to use a
// specific value set by the user.
if (!computeInertiaTensor)
{
SetInertia(inertiaTensorOverride);
}
}
else
{
SetInertia(*inertiaTensorOverride);
if (computeInertiaTensor)
{
// Set Mass + Compute Inertia
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
physx::PxRigidBodyExt::setMassAndUpdateInertia(*m_pxRigidActor, massOverride,
&pxCenterOfMass, includeAllShapesInMassCalculation);
}
else
{
SetMass(massOverride);
SetInertia(inertiaTensorOverride);
}
}
}
@@ -344,52 +383,49 @@ namespace PhysX
}
}
void RigidBody::UpdateComputedCenterOfMass()
void RigidBody::UpdateCenterOfMass(bool includeAllShapesInMassCalculation)
{
if (m_pxRigidActor)
if (m_shapes.empty())
{
physx::PxU32 shapeCount = 0;
SetCenterOfMassOffset(DefaultCenterOfMass);
return;
}
AZStd::vector<const physx::PxShape*> pxShapes;
pxShapes.reserve(m_shapes.size());
{
// Filter shapes in the same way that updateMassAndInertia function does.
PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene());
for (const auto& shape : m_shapes)
{
PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene());
shapeCount = m_pxRigidActor->getNbShapes();
}
if (shapeCount > 0)
{
AZStd::vector<physx::PxShape*> shapes;
shapes.resize(shapeCount);
const physx::PxShape& pxShape = *shape->GetPxShape();
const bool includeShape = includeAllShapesInMassCalculation || IsSimulationShape(pxShape);
if (includeShape && CanShapeComputeMassProperties(pxShape))
{
PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene());
m_pxRigidActor->getShapes(&shapes[0], shapeCount);
pxShapes.emplace_back(&pxShape);
}
shapes.erase(AZStd::remove_if(shapes.begin()
, shapes.end()
, [](const physx::PxShape* shape)
{
return shape->getFlags() & physx::PxShapeFlag::eTRIGGER_SHAPE;
})
, shapes.end());
shapeCount = static_cast<physx::PxU32>(shapes.size());
if (shapeCount == 0)
{
SetZeroCenterOfMass();
return;
}
const auto properties = physx::PxRigidBodyExt::computeMassPropertiesFromShapes(&shapes[0], shapeCount);
const physx::PxTransform computedCenterOfMass(properties.centerOfMass);
{
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
m_pxRigidActor->setCMassLocalPose(computedCenterOfMass);
}
}
else
{
SetZeroCenterOfMass();
}
}
if (pxShapes.empty())
{
SetCenterOfMassOffset(DefaultCenterOfMass);
return;
}
const physx::PxMassProperties pxMassProperties = [this, &pxShapes]
{
// Note: PhysX computeMassPropertiesFromShapes function does not use densities
// to compute the shape's masses, which are needed to calculate the center of mass.
// This differs from updateMassAndInertia function, which uses material density values.
// So the masses used during center of mass calculation do not match the masses
// used during mass/inertia calculation. This is an inconsistency in PhysX.
PHYSX_SCENE_READ_LOCK(m_pxRigidActor->getScene());
return physx::PxRigidBodyExt::computeMassPropertiesFromShapes(pxShapes.data(), static_cast<physx::PxU32>(pxShapes.size()));
}();
SetCenterOfMassOffset(PxMathConvert(pxMassProperties.centerOfMass));
}
void RigidBody::SetInertia(const AZ::Matrix3x3& inertia)
@@ -401,16 +437,6 @@ namespace PhysX
}
}
void RigidBody::ComputeInertia()
{
if (m_pxRigidActor)
{
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
auto localPose = m_pxRigidActor->getCMassLocalPose().p;
physx::PxRigidBodyExt::setMassAndUpdateInertia(*m_pxRigidActor, m_pxRigidActor->getMass(), &localPose);
}
}
AZ::Vector3 RigidBody::GetLinearVelocity() const
{
if (m_pxRigidActor)
@@ -783,13 +809,4 @@ namespace PhysX
{
return m_name;
}
void RigidBody::SetZeroCenterOfMass()
{
if (m_pxRigidActor)
{
PHYSX_SCENE_WRITE_LOCK(m_pxRigidActor->getScene());
m_pxRigidActor->setCMassLocalPose(physx::PxTransform(PxMathConvert(AZ::Vector3::CreateZero())));
}
}
}
+4 -6
View File
@@ -109,17 +109,15 @@ namespace PhysX
void RemoveShape(AZStd::shared_ptr<Physics::Shape> shape) override;
void UpdateMassProperties(AzPhysics::MassComputeFlags flags = AzPhysics::MassComputeFlags::DEFAULT,
const AZ::Vector3* centerOfMassOffsetOverride = nullptr,
const AZ::Matrix3x3* inertiaTensorOverride = nullptr,
const float* massOverride = nullptr) override;
const AZ::Vector3& centerOfMassOffsetOverride = AZ::Vector3::CreateZero(),
const AZ::Matrix3x3& inertiaTensorOverride = AZ::Matrix3x3::CreateIdentity(),
const float massOverride = 1.0f) override;
private:
void CreatePhysXActor(const AzPhysics::RigidBodyConfiguration& configuration);
void UpdateComputedCenterOfMass();
void ComputeInertia();
void UpdateCenterOfMass(bool includeAllShapesInMassCalculation);
void SetInertia(const AZ::Matrix3x3& inertia);
void SetZeroCenterOfMass();
AZStd::shared_ptr<physx::PxRigidDynamic> m_pxRigidActor;
AZStd::vector<AZStd::shared_ptr<PhysX::Shape>> m_shapes;
+2 -2
View File
@@ -198,8 +198,8 @@ namespace PhysX
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);
newBody->UpdateMassProperties(flags, configuration->m_centerOfMassOffset,
configuration->m_inertiaTensor, configuration->m_mass);
crc = AZ::Crc32(newBody, sizeof(*newBody));
return newBody;
+172 -51
View File
@@ -12,6 +12,8 @@
#include <AzTest/AzTest.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/UnitTest/UnitTest.h>
#include <AZTestShared/Math/MathTestHelpers.h>
#include <AZTestShared/Utils/Utils.h>
#include <AzFramework/Physics/SystemBus.h>
#include <AzFramework/Physics/Collision/CollisionGroups.h>
@@ -1283,11 +1285,13 @@ namespace PhysX
EXPECT_TRUE(AZ::IsClose(expectedMass, mass, 0.001f));
}
// Valid material density values: [0.01f, 1e5f]
INSTANTIATE_TEST_CASE_P(PhysX, MultiShapesDensityTestFixture,
::testing::Values(
AZStd::make_pair(std::numeric_limits<float>::min(), std::numeric_limits<float>::max()),
AZStd::make_pair(-std::numeric_limits<float>::max(), 0.0f),
AZStd::make_pair(1.0f, 1e9f)
AZStd::make_pair(0.01f, 0.01f),
AZStd::make_pair(1e5f, 1e5f),
AZStd::make_pair(0.01f, 1e5f),
AZStd::make_pair(2364.0f, 10.0f)
));
// Fixture for testing extreme density values
@@ -1311,6 +1315,7 @@ namespace PhysX
&& resultingDensity <= Physics::MaterialConfiguration::MaxDensityLimit);
}
// Valid material density values: [0.01f, 1e5f]
INSTANTIATE_TEST_CASE_P(PhysX, DensityBoundariesTestFixture,
::testing::Values(
std::numeric_limits<float>::min(),
@@ -1318,7 +1323,9 @@ namespace PhysX
-std::numeric_limits<float>::max(),
0.0f,
1.0f,
1e9f
1e9f,
0.01f,
1e5f
));
enum class SimulatedShapesMode
@@ -1329,7 +1336,7 @@ namespace PhysX
};
class MassComputeFixture
: public ::testing::TestWithParam<::testing::tuple<SimulatedShapesMode, AzPhysics::MassComputeFlags, bool>>
: public ::testing::TestWithParam<::testing::tuple<Physics::ShapeType, SimulatedShapesMode, AzPhysics::MassComputeFlags, bool, bool>>
{
public:
void SetUp() override final
@@ -1349,6 +1356,8 @@ namespace PhysX
AzPhysics::SimulatedBodyHandle simBodyHandle = sceneInterface->AddSimulatedBody(m_testSceneHandle, &m_rigidBodyConfig);
m_rigidBody = azdynamic_cast<AzPhysics::RigidBody*>(sceneInterface->GetSimulatedBodyFromHandle(m_testSceneHandle, simBodyHandle));
}
ASSERT_TRUE(m_rigidBody != nullptr);
}
void TearDown() override final
@@ -1363,130 +1372,242 @@ namespace PhysX
m_rigidBody = nullptr;
}
SimulatedShapesMode GetShapesMode() const
Physics::ShapeType GetShapeType() const
{
return ::testing::get<0>(GetParam());
}
AzPhysics::MassComputeFlags GetMassComputeFlags() const
SimulatedShapesMode GetShapesMode() const
{
return ::testing::get<1>(GetParam());
}
AzPhysics::MassComputeFlags GetMassComputeFlags() const
{
const AzPhysics::MassComputeFlags massComputeFlags = ::testing::get<2>(GetParam());
if (IncludeAllShapes())
{
return massComputeFlags | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES;
}
else
{
return massComputeFlags;
}
}
bool IncludeAllShapes() const
{
return ::testing::get<3>(GetParam());
}
bool IsMultiShapeTest() const
{
return ::testing::get<2>(GetParam());
return ::testing::get<4>(GetParam());
}
bool IsMassExpectedToChange() const
{
return m_rigidBodyConfig.m_computeMass &&
(!(GetShapesMode() == SimulatedShapesMode::NONE) || m_rigidBodyConfig.m_includeAllShapesInMassCalculation);
(GetShapesMode() != SimulatedShapesMode::NONE || m_rigidBodyConfig.m_includeAllShapesInMassCalculation);
}
bool IsComExpectedToChange() const
{
return m_rigidBodyConfig.m_computeCenterOfMass &&
(!(GetShapesMode() == SimulatedShapesMode::NONE) || m_rigidBodyConfig.m_includeAllShapesInMassCalculation);
(GetShapesMode() != SimulatedShapesMode::NONE || m_rigidBodyConfig.m_includeAllShapesInMassCalculation);
}
bool IsInertiaExpectedToChange() const
{
return m_rigidBodyConfig.m_computeInertiaTensor &&
(!(GetShapesMode() == SimulatedShapesMode::NONE) || m_rigidBodyConfig.m_includeAllShapesInMassCalculation);
(GetShapesMode() != SimulatedShapesMode::NONE || m_rigidBodyConfig.m_includeAllShapesInMassCalculation);
}
AZStd::shared_ptr<Physics::Shape> CreateShape(const Physics::ColliderConfiguration& colliderConfiguration, Physics::ShapeType shapeType)
{
AZStd::shared_ptr<Physics::Shape> shape;
Physics::System* physics = AZ::Interface<Physics::System>::Get();
switch (shapeType)
{
case Physics::ShapeType::Sphere:
shape = physics->CreateShape(colliderConfiguration, Physics::SphereShapeConfiguration());
break;
case Physics::ShapeType::Box:
shape = physics->CreateShape(colliderConfiguration, Physics::BoxShapeConfiguration());
break;
case Physics::ShapeType::Capsule:
shape = physics->CreateShape(colliderConfiguration, Physics::CapsuleShapeConfiguration());
break;
}
return shape;
};
AzPhysics::RigidBodyConfiguration m_rigidBodyConfig;
AzPhysics::RigidBody* m_rigidBody;
AzPhysics::RigidBody* m_rigidBody = nullptr;
AzPhysics::SceneHandle m_testSceneHandle = AzPhysics::InvalidSceneHandle;
};
TEST_P(MassComputeFixture, RigidBody_ComputeMassFlagsCombinationsTwoShapes_MassPropertiesCalculatedAccordingly)
{
SimulatedShapesMode shapeMode = GetShapesMode();
AzPhysics::MassComputeFlags massComputeFlags = GetMassComputeFlags();
bool multiShapeTest = IsMultiShapeTest();
Physics::System* physics = AZ::Interface<Physics::System>::Get();
const Physics::ShapeType shapeType = GetShapeType();
const SimulatedShapesMode shapeMode = GetShapesMode();
const AzPhysics::MassComputeFlags massComputeFlags = GetMassComputeFlags();
const bool multiShapeTest = IsMultiShapeTest();
// Save initial values
AZ::Vector3 comBefore = m_rigidBody->GetCenterOfMassWorld();
AZ::Matrix3x3 inertiaBefore = m_rigidBody->GetInverseInertiaWorld();
float massBefore = m_rigidBody->GetMass();
const AZ::Vector3 comBefore = m_rigidBody->GetCenterOfMassWorld();
const AZ::Matrix3x3 inertiaBefore = m_rigidBody->GetInverseInertiaWorld();
const float massBefore = m_rigidBody->GetMass();
// Box shape will be simulated for ALL and MIXED shape modes
Physics::ColliderConfiguration boxColliderConfig;
boxColliderConfig.m_isSimulated =
// Shape will be simulated for ALL and MIXED shape modes
Physics::ColliderConfiguration colliderConfig;
colliderConfig.m_isSimulated =
(shapeMode == SimulatedShapesMode::ALL || shapeMode == SimulatedShapesMode::MIXED);
boxColliderConfig.m_position = AZ::Vector3(1.0f, 0.0f, 0.0f);
colliderConfig.m_position = AZ::Vector3(1.0f, 0.0f, 0.0f);
AZStd::shared_ptr<Physics::Shape> boxShape =
physics->CreateShape(boxColliderConfig, Physics::BoxShapeConfiguration());
m_rigidBody->AddShape(boxShape);
AZStd::shared_ptr<Physics::Shape> shape = CreateShape(colliderConfig, shapeType);
m_rigidBody->AddShape(shape);
if (multiShapeTest)
{
// Sphere shape will be simulated only for the ALL shape mode
Physics::ColliderConfiguration sphereColliderConfig;
sphereColliderConfig.m_isSimulated = (shapeMode == SimulatedShapesMode::ALL);
sphereColliderConfig.m_position = AZ::Vector3(-1.0f, 0.0f, 0.0f);
AZStd::shared_ptr<Physics::Shape> sphereShape =
physics->CreateShape(sphereColliderConfig, Physics::SphereShapeConfiguration());
sphereColliderConfig.m_position = AZ::Vector3(-2.0f, 0.0f, 0.0f);
AZStd::shared_ptr<Physics::Shape> sphereShape = CreateShape(sphereColliderConfig, Physics::ShapeType::Sphere);
m_rigidBody->AddShape(sphereShape);
}
// Verify swapping materials results in changes in the mass.
m_rigidBody->UpdateMassProperties(massComputeFlags, &m_rigidBodyConfig.m_centerOfMassOffset,
&m_rigidBodyConfig.m_inertiaTensor, &m_rigidBodyConfig.m_mass);
m_rigidBody->UpdateMassProperties(massComputeFlags, m_rigidBodyConfig.m_centerOfMassOffset,
m_rigidBodyConfig.m_inertiaTensor, m_rigidBodyConfig.m_mass);
float massAfter = m_rigidBody->GetMass();
AZ::Vector3 comAfter = m_rigidBody->GetCenterOfMassWorld();
AZ::Matrix3x3 inertiaAfter = m_rigidBody->GetInverseInertiaWorld();
const float massAfter = m_rigidBody->GetMass();
const AZ::Vector3 comAfter = m_rigidBody->GetCenterOfMassWorld();
const AZ::Matrix3x3 inertiaAfter = m_rigidBody->GetInverseInertiaWorld();
using ::testing::Not;
using ::testing::FloatNear;
using ::UnitTest::IsClose;
if (IsMassExpectedToChange())
{
EXPECT_FALSE(AZ::IsClose(massBefore, massAfter, FLT_EPSILON));
EXPECT_THAT(massBefore, Not(FloatNear(massAfter, FLT_EPSILON)));
}
else
{
EXPECT_TRUE(AZ::IsClose(massBefore, massAfter, FLT_EPSILON));
EXPECT_THAT(massBefore, FloatNear(massAfter, FLT_EPSILON));
}
if (IsComExpectedToChange())
{
EXPECT_FALSE(comBefore.IsClose(comAfter));
EXPECT_THAT(comBefore, Not(IsClose(comAfter)));
}
else
{
EXPECT_TRUE(comBefore.IsClose(comAfter));
EXPECT_THAT(comBefore, IsClose(comAfter));
}
if (IsInertiaExpectedToChange())
{
EXPECT_FALSE(inertiaBefore.IsClose(inertiaAfter));
EXPECT_THAT(inertiaBefore, Not(IsClose(inertiaAfter)));
}
else
{
EXPECT_TRUE(inertiaBefore.IsClose(inertiaAfter));
EXPECT_THAT(inertiaBefore, IsClose(inertiaAfter));
}
}
AzPhysics::MassComputeFlags possibleMassComputeFlags[] = {
AzPhysics::MassComputeFlags::NONE, AzPhysics::MassComputeFlags::DEFAULT, AzPhysics::MassComputeFlags::COMPUTE_MASS,
AzPhysics::MassComputeFlags::COMPUTE_COM, AzPhysics::MassComputeFlags::COMPUTE_INERTIA,
AzPhysics::MassComputeFlags::DEFAULT | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES,
AzPhysics::MassComputeFlags::COMPUTE_COM, AzPhysics::MassComputeFlags::COMPUTE_INERTIA, AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES,
static const AzPhysics::MassComputeFlags PossibleMassComputeFlags[] =
{
// No compute
AzPhysics::MassComputeFlags::NONE,
// Compute Mass only
AzPhysics::MassComputeFlags::COMPUTE_MASS,
// Compute Inertia only
AzPhysics::MassComputeFlags::COMPUTE_INERTIA,
// Compute COM only
AzPhysics::MassComputeFlags::COMPUTE_COM,
// Compute combinations of 2
AzPhysics::MassComputeFlags::COMPUTE_MASS | AzPhysics::MassComputeFlags::COMPUTE_COM,
AzPhysics::MassComputeFlags::COMPUTE_MASS | AzPhysics::MassComputeFlags::COMPUTE_COM | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES,
AzPhysics::MassComputeFlags::COMPUTE_MASS | AzPhysics::MassComputeFlags::COMPUTE_INERTIA,
AzPhysics::MassComputeFlags::COMPUTE_MASS | AzPhysics::MassComputeFlags::COMPUTE_INERTIA | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES,
AzPhysics::MassComputeFlags::COMPUTE_COM | AzPhysics::MassComputeFlags::COMPUTE_INERTIA,
AzPhysics::MassComputeFlags::COMPUTE_COM | AzPhysics::MassComputeFlags::COMPUTE_INERTIA | AzPhysics::MassComputeFlags::INCLUDE_ALL_SHAPES
// Compute all
AzPhysics::MassComputeFlags::DEFAULT, // COMPUTE_COM | COMPUTE_INERTIA | COMPUTE_MASS
};
INSTANTIATE_TEST_CASE_P(PhysX, MassComputeFixture, ::testing::Combine(
::testing::ValuesIn({ SimulatedShapesMode::NONE, SimulatedShapesMode::MIXED, SimulatedShapesMode::ALL }),
::testing::ValuesIn(possibleMassComputeFlags),
::testing::Bool()));
::testing::ValuesIn({ Physics::ShapeType::Sphere, Physics::ShapeType::Box, Physics::ShapeType::Capsule }), // Values for GetShapeType()
::testing::ValuesIn({ SimulatedShapesMode::NONE, SimulatedShapesMode::MIXED, SimulatedShapesMode::ALL }), // Values for GetShapesMode()
::testing::ValuesIn(PossibleMassComputeFlags), // Values for GetMassComputeFlags()
::testing::Bool(), // Values for IncludeAllShapes()
::testing::Bool())); // Values for IsMultiShapeTest()
class MassPropertiesWithTriangleMesh
: public ::testing::TestWithParam<AzPhysics::MassComputeFlags>
{
public:
void SetUp() override
{
if (auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get())
{
AzPhysics::SceneConfiguration sceneConfiguration = physicsSystem->GetDefaultSceneConfiguration();
sceneConfiguration.m_sceneName = AzPhysics::DefaultPhysicsSceneName;
m_testSceneHandle = physicsSystem->AddScene(sceneConfiguration);
}
}
void TearDown() override
{
// Clean up the Test scene
if (auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get())
{
physicsSystem->RemoveScene(m_testSceneHandle);
}
m_testSceneHandle = AzPhysics::InvalidSceneHandle;
}
AzPhysics::MassComputeFlags GetMassComputeFlags() const
{
return GetParam();
}
AzPhysics::SceneHandle m_testSceneHandle = AzPhysics::InvalidSceneHandle;
};
TEST_P(MassPropertiesWithTriangleMesh, KinematicRigidBody_ComputeMassProperties_TriggersWarnings)
{
const AzPhysics::MassComputeFlags flags = GetMassComputeFlags();
const bool doesComputeCenterOfMass = AzPhysics::MassComputeFlags::COMPUTE_COM == (flags & AzPhysics::MassComputeFlags::COMPUTE_COM);
const bool doesComputeMass = AzPhysics::MassComputeFlags::COMPUTE_MASS == (flags & AzPhysics::MassComputeFlags::COMPUTE_MASS);
const bool doesComputeInertia = AzPhysics::MassComputeFlags::COMPUTE_INERTIA == (flags & AzPhysics::MassComputeFlags::COMPUTE_INERTIA);
UnitTest::ErrorHandler computeCenterOfMassWarningHandler(
"cannot compute COM");
UnitTest::ErrorHandler computeMassWarningHandler(
"cannot compute Mass");
UnitTest::ErrorHandler computeIneriaWarningHandler(
"cannot compute Inertia");
AzPhysics::SimulatedBodyHandle rigidBodyhandle = TestUtils::AddKinematicTriangleMeshCubeToScene(m_testSceneHandle, 3.0f, flags);
EXPECT_TRUE(rigidBodyhandle != AzPhysics::InvalidSimulatedBodyHandle);
EXPECT_EQ(computeCenterOfMassWarningHandler.GetExpectedWarningCount(), doesComputeCenterOfMass ? 1 : 0);
EXPECT_EQ(computeMassWarningHandler.GetExpectedWarningCount(), doesComputeMass ? 1 : 0);
EXPECT_EQ(computeIneriaWarningHandler.GetExpectedWarningCount(), doesComputeInertia ? 1 : 0);
if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
{
sceneInterface->RemoveSimulatedBody(m_testSceneHandle, rigidBodyhandle);
}
}
INSTANTIATE_TEST_CASE_P(PhysX, MassPropertiesWithTriangleMesh,
::testing::ValuesIn(PossibleMassComputeFlags)); // Values for GetMassComputeFlags()
} // namespace PhysX
+30
View File
@@ -253,6 +253,36 @@ namespace PhysX
return AzPhysics::InvalidSimulatedBodyHandle;
}
AzPhysics::SimulatedBodyHandle AddKinematicTriangleMeshCubeToScene(AzPhysics::SceneHandle scene, float halfExtent, AzPhysics::MassComputeFlags massComputeFlags)
{
// Generate input data
VertexIndexData cubeMeshData = GenerateCubeMeshData(halfExtent);
AZStd::vector<AZ::u8> cookedData;
bool cookingResult = false;
Physics::SystemRequestBus::BroadcastResult(cookingResult, &Physics::SystemRequests::CookTriangleMeshToMemory,
cubeMeshData.first.data(), static_cast<AZ::u32>(cubeMeshData.first.size()),
cubeMeshData.second.data(), static_cast<AZ::u32>(cubeMeshData.second.size()),
cookedData);
AZ_Assert(cookingResult, "Failed to cook the cube mesh.");
// Setup shape & collider configurations
auto shapeConfig = AZStd::make_shared<Physics::CookedMeshShapeConfiguration>();
shapeConfig->SetCookedMeshData(cookedData.data(), cookedData.size(),
Physics::CookedMeshShapeConfiguration::MeshType::TriangleMesh);
AzPhysics::RigidBodyConfiguration rigidBodyConfiguration;
rigidBodyConfiguration.m_kinematic = true;
rigidBodyConfiguration.SetMassComputeFlags(massComputeFlags);
rigidBodyConfiguration.m_colliderAndShapeData = AzPhysics::ShapeColliderPair(
AZStd::make_shared<Physics::ColliderConfiguration>(), shapeConfig);
if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
{
return sceneInterface->AddSimulatedBody(scene, &rigidBodyConfiguration);
}
return AzPhysics::InvalidSimulatedBodyHandle;
}
void SetCollisionLayer(EntityPtr& entity, const AZStd::string& layerName, const AZStd::string& colliderTag)
{
Physics::CollisionFilteringRequestBus::Event(entity->GetId(), &Physics::CollisionFilteringRequests::SetCollisionLayer, layerName, AZ::Crc32(colliderTag.c_str()));
+1
View File
@@ -89,6 +89,7 @@ namespace PhysX
const AzPhysics::CollisionLayer& layer = AzPhysics::CollisionLayer::Default);
AzPhysics::SimulatedBodyHandle AddStaticTriangleMeshCubeToScene(AzPhysics::SceneHandle scene, float halfExtent);
AzPhysics::SimulatedBodyHandle AddKinematicTriangleMeshCubeToScene(AzPhysics::SceneHandle scene, float halfExtent, AzPhysics::MassComputeFlags massComputeFlags);
// Collision Filtering
void SetCollisionLayer(EntityPtr& entity, const AZStd::string& layerName, const AZStd::string& colliderTag = "");