Refactor Rigidbody automatic mass calculation to include physical material Density

This commit is contained in:
Wojtek Figat
2021-11-01 21:44:09 +01:00
parent e90f06748c
commit 62b0d19866
6 changed files with 77 additions and 73 deletions

View File

@@ -1,9 +1,11 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "RigidBody.h"
#include "PxMaterial.h"
#include "Engine/Physics/Utilities.h"
#include "Engine/Physics/Colliders/Collider.h"
#include "Engine/Physics/Physics.h"
#include "Engine/Physics/PhysicalMaterial.h"
#include "Engine/Serialization/Serialization.h"
#include <ThirdParty/PhysX/extensions/PxRigidBodyExt.h>
#include <ThirdParty/PhysX/PxRigidActor.h>
@@ -133,6 +135,11 @@ void RigidBody::SetMaxAngularVelocity(float value)
_actor->setMaxAngularVelocity(value);
}
bool RigidBody::GetOverrideMass() const
{
return _overrideMass != 0;
}
void RigidBody::SetOverrideMass(bool value)
{
if (value == GetOverrideMass())
@@ -143,6 +150,11 @@ void RigidBody::SetOverrideMass(bool value)
UpdateMass();
}
float RigidBody::GetMass() const
{
return _mass;
}
void RigidBody::SetMass(float value)
{
if (Math::NearEqual(value, _mass))
@@ -156,15 +168,18 @@ void RigidBody::SetMass(float value)
UpdateMass();
}
float RigidBody::GetMassScale() const
{
return _massScale;
}
void RigidBody::SetMassScale(float value)
{
if (Math::NearEqual(value, _massScale))
return;
_massScale = value;
if (!_overrideMass)
UpdateMass();
UpdateMass();
}
void RigidBody::SetCenterOfMassOffset(const Vector3& value)
@@ -267,44 +282,51 @@ void RigidBody::WakeUp() const
void RigidBody::UpdateMass()
{
// Skip if no actor created
if (_actor == nullptr)
return;
// Physical material
float densityKGPerCubicUU = 1.0f;
float raiseMassToPower = 0.75f;
// TODO: link physical material or expose density parameter
PxRigidBodyExt::updateMassAndInertia(*_actor, densityKGPerCubicUU);
// Grab old mass so we can apply new mass while maintaining inertia tensor
const float oldMass = _actor->getMass();
float newMass;
if (_overrideMass == false)
if (_overrideMass)
{
const float usePow = Math::Clamp<float>(raiseMassToPower, ZeroTolerance, 1.0f);
newMass = Math::Pow(oldMass, usePow);
// Apply user-defined mass scaling
newMass *= Math::Clamp<float>(_massScale, 0.01f, 100.0f);
_mass = newMass;
// Use fixed mass
PxRigidBodyExt::setMassAndUpdateInertia(*_actor, Math::Max(_mass * _massScale, 0.001f));
}
else
{
// Min weight of 1g
newMass = Math::Max(_mass, 0.001f);
// Calculate per-shape densities (convert kg/m^3 into engine units)
const float minDensity = 0.08375f; // Hydrogen density
const float defaultDensity = 1000.0f; // Water density
Array<float, InlinedAllocation<32>> densities;
for (uint32 i = 0; i < _actor->getNbShapes(); i++)
{
PxShape* shape;
_actor->getShapes(&shape, 1, i);
if (shape->getFlags() & PxShapeFlag::eSIMULATION_SHAPE)
{
float density = defaultDensity;
PxMaterial* material;
if (shape->getMaterials(&material, 1, 0) == 1)
{
if (const auto mat = (PhysicalMaterial*)material->userData)
{
density = Math::Max(mat->Density, minDensity);
}
}
densities.Add(KgPerM3ToKgPerCm3(density));
}
}
if (densities.IsEmpty())
densities.Add(KgPerM3ToKgPerCm3(defaultDensity));
// Auto calculated mass
PxRigidBodyExt::updateMassAndInertia(*_actor, densities.Get(), densities.Count());
_mass = _actor->getMass();
const float massScale = Math::Max(_massScale, 0.001f);
if (!Math::IsOne(massScale))
{
_mass *= massScale;
_actor->setMass(_mass);
}
}
ASSERT(newMass > 0.0f);
const float massRatio = newMass / oldMass;
const PxVec3 inertiaTensor = _actor->getMassSpaceInertiaTensor();
_actor->setMassSpaceInertiaTensor(inertiaTensor * massRatio);
_actor->setMass(newMass);
}
void RigidBody::AddForce(const Vector3& force, ForceMode mode) const

View File

@@ -51,7 +51,6 @@ public:
/// Kinematic rigidbodies are incompatible with CCD.
/// </para>
/// </remarks>
/// <returns>The value.</returns>
API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(false), EditorDisplay(\"Rigid Body\")")
FORCE_INLINE bool GetIsKinematic() const
{
@@ -81,7 +80,6 @@ public:
/// <remarks>
/// Linear damping can be used to slow down an object. The higher the drag the more the object slows down.
/// </remarks>
/// <returns>The value.</returns>
API_PROPERTY(Attributes="EditorOrder(60), DefaultValue(0.01f), Limit(0), EditorDisplay(\"Rigid Body\")")
FORCE_INLINE float GetLinearDamping() const
{
@@ -103,7 +101,6 @@ public:
/// <remarks>
/// Angular damping can be used to slow down the rotation of an object. The higher the drag the more the rotation slows down.
/// </remarks>
/// <returns>The value.</returns>
API_PROPERTY(Attributes="EditorOrder(70), DefaultValue(0.05f), Limit(0), EditorDisplay(\"Rigid Body\")")
FORCE_INLINE float GetAngularDamping() const
{
@@ -122,7 +119,6 @@ public:
/// <summary>
/// If true simulation and collisions detection will be enabled for the rigidbody.
/// </summary>
/// <returns>The value.</returns>
API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(true), EditorDisplay(\"Rigid Body\")")
FORCE_INLINE bool GetEnableSimulation() const
{
@@ -138,7 +134,6 @@ public:
/// <summary>
/// If true Continuous Collision Detection (CCD) will be used for this component.
/// </summary>
/// <returns>The value.</returns>
API_PROPERTY(Attributes="EditorOrder(30), DefaultValue(false), EditorDisplay(\"Rigid Body\", \"Use CCD\")")
FORCE_INLINE bool GetUseCCD() const
{
@@ -154,7 +149,6 @@ public:
/// <summary>
/// If object should have the force of gravity applied.
/// </summary>
/// <returns>The value.</returns>
API_PROPERTY(Attributes="EditorOrder(40), DefaultValue(true), EditorDisplay(\"Rigid Body\")")
FORCE_INLINE bool GetEnableGravity() const
{
@@ -170,7 +164,6 @@ public:
/// <summary>
/// If object should start awake, or if it should initially be sleeping.
/// </summary>
/// <returns>The value.</returns>
API_PROPERTY(Attributes="EditorOrder(50), DefaultValue(true), EditorDisplay(\"Rigid Body\")")
FORCE_INLINE bool GetStartAwake() const
{
@@ -186,7 +179,6 @@ public:
/// <summary>
/// If true, it will update mass when actor scale changes.
/// </summary>
/// <returns>The value.</returns>
API_PROPERTY(Attributes="EditorOrder(130), DefaultValue(false), EditorDisplay(\"Rigid Body\")")
FORCE_INLINE bool GetUpdateMassWhenScaleChanges() const
{
@@ -196,7 +188,7 @@ public:
/// <summary>
/// If true, it will update mass when actor scale changes.
/// </summary>
/// <param name="value">The value.</
/// <param name="value">The value.</param>
API_PROPERTY() void SetUpdateMassWhenScaleChanges(bool value);
/// <summary>
@@ -206,7 +198,6 @@ public:
/// The angular velocity of rigidbodies is clamped to MaxAngularVelocity to avoid numerical instability with fast rotating bodies.
/// Because this may prevent intentional fast rotations on objects such as wheels, you can override this value per rigidbody.
/// </remarks>
/// <returns>The value.</returns>
API_PROPERTY(Attributes="EditorOrder(90), DefaultValue(7.0f), Limit(0), EditorDisplay(\"Rigid Body\")")
FORCE_INLINE float GetMaxAngularVelocity() const
{
@@ -226,12 +217,8 @@ public:
/// <summary>
/// Override the auto computed mass.
/// </summary>
/// <returns>The value.</returns>
API_PROPERTY(Attributes="HideInEditor")
FORCE_INLINE bool GetOverrideMass() const
{
return _overrideMass != 0;
}
bool GetOverrideMass() const;
/// <summary>
/// Override the auto computed mass.
@@ -240,33 +227,23 @@ public:
API_PROPERTY() void SetOverrideMass(bool value);
/// <summary>
/// Gets the mass value measured in kilograms (use override value only if EnableOverrideMass is enabled).
/// Gets the mass value measured in kilograms (use override value only if OverrideMass is checked).
/// </summary>
/// <returns>The value.</returns>
API_PROPERTY(Attributes="EditorOrder(110), Limit(0), EditorDisplay(\"Rigid Body\")")
FORCE_INLINE float GetMass() const
{
return _mass;
}
float GetMass() const;
/// <summary>
/// Sets the mass value measured in kilograms (use override value only if EnableOverrideMass is enabled).
/// Sets the mass value measured in kilograms (use override value only if OverrideMass is checked).
/// </summary>
/// <remarks>
/// If set auto enables mass override.
/// </remarks>
/// <remarks>If set auto enables mass override.</remarks>
/// <param name="value">The value.</param>
API_PROPERTY() void SetMass(float value);
/// <summary>
/// Gets the per-instance scaling of the mass.
/// </summary>
/// <returns>The value.</returns>
API_PROPERTY(Attributes="EditorOrder(120), DefaultValue(1.0f), Limit(0.001f, 100.0f), EditorDisplay(\"Rigid Body\")")
FORCE_INLINE float GetMassScale() const
{
return _massScale;
}
float GetMassScale() const;
/// <summary>
/// Sets the per-instance scaling of the mass.
@@ -277,7 +254,6 @@ public:
/// <summary>
/// Gets the user specified offset for the center of mass of this object, from the calculated location.
/// </summary>
/// <returns>The value.</returns>
API_PROPERTY(Attributes="EditorOrder(140), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorDisplay(\"Rigid Body\", \"Center Of Mass Offset\")")
FORCE_INLINE Vector3 GetCenterOfMassOffset() const
{
@@ -293,7 +269,6 @@ public:
/// <summary>
/// Gets the object movement constraint flags that define degrees of freedom are allowed for the simulation of object.
/// </summary>
/// <returns>The value.</returns>
API_PROPERTY(Attributes="EditorOrder(150), DefaultValue(RigidbodyConstraints.None), EditorDisplay(\"Rigid Body\")")
FORCE_INLINE RigidbodyConstraints GetConstraints() const
{
@@ -314,7 +289,6 @@ public:
/// <remarks>
/// It's used mostly to get the current velocity. Manual modifications may result in unrealistic behaviour.
/// </remarks>
/// <returns>The value.</returns>
API_PROPERTY(Attributes="HideInEditor")
Vector3 GetLinearVelocity() const;
@@ -333,7 +307,6 @@ public:
/// <remarks>
/// It's used mostly to get the current angular velocity. Manual modifications may result in unrealistic behaviour.
/// </remarks>
/// <returns>The value.</returns>
API_PROPERTY(Attributes="HideInEditor")
Vector3 GetAngularVelocity() const;
@@ -389,13 +362,11 @@ public:
/// <summary>
/// Gets the center of the mass in the local space.
/// </summary>
/// <returns>The value.</returns>
API_PROPERTY() Vector3 GetCenterOfMass() const;
/// <summary>
/// Determines whether this rigidbody is sleeping.
/// </summary>
/// <returns><c>true</c> if this rigidbody is sleeping; otherwise, <c>false</c>.</returns>
API_PROPERTY() bool IsSleeping() const;
public: