godot-module-template/engine/thirdparty/jolt_physics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.h

298 lines
16 KiB
C++

// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Jolt/Geometry/AABox.h>
#include <Jolt/Physics/Body/BodyID.h>
#include <Jolt/Physics/Body/MotionProperties.h>
#include <Jolt/Physics/Collision/TransformedShape.h>
#include <Jolt/Physics/SoftBody/SoftBodySharedSettings.h>
#include <Jolt/Physics/SoftBody/SoftBodyVertex.h>
#include <Jolt/Physics/SoftBody/SoftBodyUpdateContext.h>
JPH_NAMESPACE_BEGIN
class PhysicsSystem;
class BodyInterface;
class BodyLockInterface;
struct PhysicsSettings;
class Body;
class Shape;
class SoftBodyCreationSettings;
class TempAllocator;
#ifdef JPH_DEBUG_RENDERER
class DebugRenderer;
enum class ESoftBodyConstraintColor;
#endif // JPH_DEBUG_RENDERER
/// This class contains the runtime information of a soft body.
//
// Based on: XPBD, Extended Position Based Dynamics, Matthias Muller, Ten Minute Physics
// See: https://matthias-research.github.io/pages/tenMinutePhysics/09-xpbd.pdf
class JPH_EXPORT SoftBodyMotionProperties : public MotionProperties
{
public:
using Vertex = SoftBodyVertex;
using Edge = SoftBodySharedSettings::Edge;
using Face = SoftBodySharedSettings::Face;
using DihedralBend = SoftBodySharedSettings::DihedralBend;
using Volume = SoftBodySharedSettings::Volume;
using InvBind = SoftBodySharedSettings::InvBind;
using SkinWeight = SoftBodySharedSettings::SkinWeight;
using Skinned = SoftBodySharedSettings::Skinned;
using LRA = SoftBodySharedSettings::LRA;
/// Initialize the soft body motion properties
void Initialize(const SoftBodyCreationSettings &inSettings);
/// Get the shared settings of the soft body
const SoftBodySharedSettings * GetSettings() const { return mSettings; }
/// Get the vertices of the soft body
const Array<Vertex> & GetVertices() const { return mVertices; }
Array<Vertex> & GetVertices() { return mVertices; }
/// Access an individual vertex
const Vertex & GetVertex(uint inIndex) const { return mVertices[inIndex]; }
Vertex & GetVertex(uint inIndex) { return mVertices[inIndex]; }
/// Get the materials of the soft body
const PhysicsMaterialList & GetMaterials() const { return mSettings->mMaterials; }
/// Get the faces of the soft body
const Array<Face> & GetFaces() const { return mSettings->mFaces; }
/// Access to an individual face
const Face & GetFace(uint inIndex) const { return mSettings->mFaces[inIndex]; }
/// Get the number of solver iterations
uint32 GetNumIterations() const { return mNumIterations; }
void SetNumIterations(uint32 inNumIterations) { mNumIterations = inNumIterations; }
/// Get the pressure of the soft body
float GetPressure() const { return mPressure; }
void SetPressure(float inPressure) { mPressure = inPressure; }
/// Update the position of the body while simulating (set to false for something that is attached to the static world)
bool GetUpdatePosition() const { return mUpdatePosition; }
void SetUpdatePosition(bool inUpdatePosition) { mUpdatePosition = inUpdatePosition; }
/// Global setting to turn on/off skin constraints
bool GetEnableSkinConstraints() const { return mEnableSkinConstraints; }
void SetEnableSkinConstraints(bool inEnableSkinConstraints) { mEnableSkinConstraints = inEnableSkinConstraints; }
/// Multiplier applied to Skinned::mMaxDistance to allow tightening or loosening of the skin constraints. 0 to hard skin all vertices.
float GetSkinnedMaxDistanceMultiplier() const { return mSkinnedMaxDistanceMultiplier; }
void SetSkinnedMaxDistanceMultiplier(float inSkinnedMaxDistanceMultiplier) { mSkinnedMaxDistanceMultiplier = inSkinnedMaxDistanceMultiplier; }
/// Get local bounding box
const AABox & GetLocalBounds() const { return mLocalBounds; }
/// Get the volume of the soft body. Note can become negative if the shape is inside out!
float GetVolume() const { return GetVolumeTimesSix() / 6.0f; }
/// Calculate the total mass and inertia of this body based on the current state of the vertices
void CalculateMassAndInertia();
#ifdef JPH_DEBUG_RENDERER
/// Draw the state of a soft body
void DrawVertices(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const;
void DrawVertexVelocities(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const;
void DrawEdgeConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const;
void DrawBendConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const;
void DrawVolumeConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const;
void DrawSkinConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const;
void DrawLRAConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const;
void DrawPredictedBounds(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const;
#endif // JPH_DEBUG_RENDERER
/// Saving state for replay
void SaveState(StateRecorder &inStream) const;
/// Restoring state for replay
void RestoreState(StateRecorder &inStream);
/// Skin vertices to supplied joints, information is used by the skinned constraints.
/// @param inCenterOfMassTransform Value of Body::GetCenterOfMassTransform().
/// @param inJointMatrices The joint matrices must be expressed relative to inCenterOfMassTransform.
/// @param inNumJoints Indicates how large the inJointMatrices array is (used only for validating out of bounds).
/// @param inHardSkinAll Can be used to position all vertices on the skinned vertices and can be used to hard reset the soft body.
/// @param ioTempAllocator Allocator.
void SkinVertices(RMat44Arg inCenterOfMassTransform, const Mat44 *inJointMatrices, uint inNumJoints, bool inHardSkinAll, TempAllocator &ioTempAllocator);
/// This function allows you to update the soft body immediately without going through the PhysicsSystem.
/// This is useful if the soft body is teleported and needs to 'settle' or it can be used if a the soft body
/// is not added to the PhysicsSystem and needs to be updated manually. One reason for not adding it to the
/// PhysicsSystem is that you might want to update a soft body immediately after updating an animated object
/// that has the soft body attached to it. If the soft body is added to the PhysicsSystem it will be updated
/// by it, so calling this function will effectively update it twice. Note that when you use this function,
/// only the current thread will be used, whereas if you update through the PhysicsSystem, multiple threads may
/// be used.
/// Note that this will bypass any sleep checks. Since the dynamic objects that the soft body touches
/// will not move during this call, there can be simulation artifacts if you call this function multiple times
/// without running the physics simulation step.
void CustomUpdate(float inDeltaTime, Body &ioSoftBody, PhysicsSystem &inSystem);
////////////////////////////////////////////////////////////
// FUNCTIONS BELOW THIS LINE ARE FOR INTERNAL USE ONLY
////////////////////////////////////////////////////////////
/// Initialize the update context. Not part of the public API.
void InitializeUpdateContext(float inDeltaTime, Body &inSoftBody, const PhysicsSystem &inSystem, SoftBodyUpdateContext &ioContext);
/// Do a broad phase check and collect all bodies that can possibly collide with this soft body. Not part of the public API.
void DetermineCollidingShapes(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem, const BodyLockInterface &inBodyLockInterface);
/// Return code for ParallelUpdate
enum class EStatus
{
NoWork = 1 << 0, ///< No work was done because other threads were still working on a batch that cannot run concurrently
DidWork = 1 << 1, ///< Work was done to progress the update
Done = 1 << 2, ///< All work is done
};
/// Update the soft body, will process a batch of work. Not part of the public API.
EStatus ParallelUpdate(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings);
/// Update the velocities of all rigid bodies that we collided with. Not part of the public API.
void UpdateRigidBodyVelocities(const SoftBodyUpdateContext &inContext, BodyInterface &inBodyInterface);
private:
// SoftBodyManifold needs to have access to CollidingShape
friend class SoftBodyManifold;
// Information about a leaf shape that we're colliding with
struct LeafShape
{
LeafShape() = default;
LeafShape(Mat44Arg inTransform, Vec3Arg inScale, const Shape *inShape) : mTransform(inTransform), mScale(inScale), mShape(inShape) { }
Mat44 mTransform; ///< Transform of the shape relative to the soft body
Vec3 mScale; ///< Scale of the shape
RefConst<Shape> mShape; ///< Shape
};
// Collect information about the colliding bodies
struct CollidingShape
{
/// Get the velocity of a point on this body
Vec3 GetPointVelocity(Vec3Arg inPointRelativeToCOM) const
{
return mLinearVelocity + mAngularVelocity.Cross(inPointRelativeToCOM);
}
Mat44 mCenterOfMassTransform; ///< Transform of the body relative to the soft body
Array<LeafShape> mShapes; ///< Leaf shapes of the body we hit
BodyID mBodyID; ///< Body ID of the body we hit
EMotionType mMotionType; ///< Motion type of the body we hit
float mInvMass; ///< Inverse mass of the body we hit
float mFriction; ///< Combined friction of the two bodies
float mRestitution; ///< Combined restitution of the two bodies
float mSoftBodyInvMassScale; ///< Scale factor for the inverse mass of the soft body vertices
bool mUpdateVelocities; ///< If the linear/angular velocity changed and the body needs to be updated
Mat44 mInvInertia; ///< Inverse inertia in local space to the soft body
Vec3 mLinearVelocity; ///< Linear velocity of the body in local space to the soft body
Vec3 mAngularVelocity; ///< Angular velocity of the body in local space to the soft body
Vec3 mOriginalLinearVelocity; ///< Linear velocity of the body in local space to the soft body at start
Vec3 mOriginalAngularVelocity; ///< Angular velocity of the body in local space to the soft body at start
};
// Collect information about the colliding sensors
struct CollidingSensor
{
Mat44 mCenterOfMassTransform; ///< Transform of the body relative to the soft body
Array<LeafShape> mShapes; ///< Leaf shapes of the body we hit
BodyID mBodyID; ///< Body ID of the body we hit
bool mHasContact; ///< If the sensor collided with the soft body
};
// Information about the state of all skinned vertices
struct SkinState
{
Vec3 mPreviousPosition = Vec3::sZero(); ///< Previous position of the skinned vertex, used to interpolate between the previous and current position
Vec3 mPosition = Vec3::sNaN(); ///< Current position of the skinned vertex
Vec3 mNormal = Vec3::sNaN(); ///< Normal of the skinned vertex
};
/// Do a narrow phase check and determine the closest feature that we can collide with
void DetermineCollisionPlanes(uint inVertexStart, uint inNumVertices);
/// Do a narrow phase check between a single sensor and the soft body
void DetermineSensorCollisions(CollidingSensor &ioSensor);
/// Apply pressure force and update the vertex velocities
void ApplyPressure(const SoftBodyUpdateContext &inContext);
/// Integrate the positions of all vertices by 1 sub step
void IntegratePositions(const SoftBodyUpdateContext &inContext);
/// Enforce all bend constraints
void ApplyDihedralBendConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex);
/// Enforce all volume constraints
void ApplyVolumeConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex);
/// Enforce all skin constraints
void ApplySkinConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex);
/// Enforce all edge constraints
void ApplyEdgeConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex);
/// Enforce all LRA constraints
void ApplyLRAConstraints(uint inStartIndex, uint inEndIndex);
/// Enforce all collision constraints & update all velocities according the XPBD algorithm
void ApplyCollisionConstraintsAndUpdateVelocities(const SoftBodyUpdateContext &inContext);
/// Update the state of the soft body (position, velocity, bounds)
void UpdateSoftBodyState(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings);
/// Start the first solver iteration
void StartFirstIteration(SoftBodyUpdateContext &ioContext);
/// Executes tasks that need to run on the start of an iteration (i.e. the stuff that can't run in parallel)
void StartNextIteration(const SoftBodyUpdateContext &ioContext);
/// Helper function for ParallelUpdate that works on batches of collision planes
EStatus ParallelDetermineCollisionPlanes(SoftBodyUpdateContext &ioContext);
/// Helper function for ParallelUpdate that works on sensor collisions
EStatus ParallelDetermineSensorCollisions(SoftBodyUpdateContext &ioContext);
/// Helper function for ParallelUpdate that works on batches of constraints
EStatus ParallelApplyConstraints(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings);
/// Helper function to update a single group of constraints
void ProcessGroup(const SoftBodyUpdateContext &ioContext, uint inGroupIndex);
/// Returns 6 times the volume of the soft body
float GetVolumeTimesSix() const;
#ifdef JPH_DEBUG_RENDERER
/// Helper function to draw constraints
template <typename GetEndIndex, typename DrawConstraint>
inline void DrawConstraints(ESoftBodyConstraintColor inConstraintColor, const GetEndIndex &inGetEndIndex, const DrawConstraint &inDrawConstraint, ColorArg inBaseColor) const;
RMat44 mSkinStateTransform = RMat44::sIdentity(); ///< The matrix that transforms mSkinState to world space
#endif // JPH_DEBUG_RENDERER
RefConst<SoftBodySharedSettings> mSettings; ///< Configuration of the particles and constraints
Array<Vertex> mVertices; ///< Current state of all vertices in the simulation
Array<CollidingShape> mCollidingShapes; ///< List of colliding shapes retrieved during the last update
Array<CollidingSensor> mCollidingSensors; ///< List of colliding sensors retrieved during the last update
Array<SkinState> mSkinState; ///< List of skinned positions (1-on-1 with mVertices but only those that are used by the skinning constraints are filled in)
AABox mLocalBounds; ///< Bounding box of all vertices
AABox mLocalPredictedBounds; ///< Predicted bounding box for all vertices using extrapolation of velocity by last step delta time
uint32 mNumIterations; ///< Number of solver iterations
float mPressure; ///< n * R * T, amount of substance * ideal gas constant * absolute temperature, see https://en.wikipedia.org/wiki/Pressure
float mSkinnedMaxDistanceMultiplier = 1.0f; ///< Multiplier applied to Skinned::mMaxDistance to allow tightening or loosening of the skin constraints
bool mUpdatePosition; ///< Update the position of the body while simulating (set to false for something that is attached to the static world)
bool mNeedContactCallback = false; ///< True if the soft body has collided with anything in the last update
bool mEnableSkinConstraints = true; ///< If skin constraints are enabled
bool mSkinStatePreviousPositionValid = false; ///< True if the skinning was updated in the last update so that the previous position of the skin state is valid
};
JPH_NAMESPACE_END