Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/SoftBody/SoftBodySharedSettings.h
21884 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2023 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#pragma once56#include <Jolt/Core/Reference.h>7#include <Jolt/Physics/Collision/PhysicsMaterial.h>8#include <Jolt/Core/StreamUtils.h>910JPH_NAMESPACE_BEGIN1112/// This class defines the setup of all particles and their constraints.13/// It is used during the simulation and can be shared between multiple soft bodies.14class JPH_EXPORT SoftBodySharedSettings : public RefTarget<SoftBodySharedSettings>15{16JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SoftBodySharedSettings)1718public:19/// Which type of bend constraint should be created20enum class EBendType21{22None, ///< No bend constraints will be created23Distance, ///< A simple distance constraint24Dihedral, ///< A dihedral bend constraint (most expensive, but also supports triangles that are initially not in the same plane)25};2627/// The type of long range attachment constraint to create28enum class ELRAType29{30None, ///< Don't create a LRA constraint31EuclideanDistance, ///< Create a LRA constraint based on Euclidean distance between the closest kinematic vertex and this vertex32GeodesicDistance, ///< Create a LRA constraint based on the geodesic distance between the closest kinematic vertex and this vertex (follows the edge constraints)33};3435/// Per vertex attributes used during the CreateConstraints function.36/// For an edge or shear constraint, the compliance is averaged between the two attached vertices.37/// For a bend constraint, the compliance is averaged between the two vertices on the shared edge.38struct JPH_EXPORT VertexAttributes39{40/// Constructor41VertexAttributes() = default;42VertexAttributes(float inCompliance, float inShearCompliance, float inBendCompliance, ELRAType inLRAType = ELRAType::None, float inLRAMaxDistanceMultiplier = 1.0f) : mCompliance(inCompliance), mShearCompliance(inShearCompliance), mBendCompliance(inBendCompliance), mLRAType(inLRAType), mLRAMaxDistanceMultiplier(inLRAMaxDistanceMultiplier) { }4344float mCompliance = 0.0f; ///< The compliance of the normal edges. Set to FLT_MAX to disable regular edges for any edge involving this vertex.45float mShearCompliance = 0.0f; ///< The compliance of the shear edges. Set to FLT_MAX to disable shear edges for any edge involving this vertex.46float mBendCompliance = FLT_MAX; ///< The compliance of the bend edges. Set to FLT_MAX to disable bend edges for any bend constraint involving this vertex.47ELRAType mLRAType = ELRAType::None; ///< The type of long range attachment constraint to create.48float mLRAMaxDistanceMultiplier = 1.0f; ///< Multiplier for the max distance of the LRA constraint, e.g. 1.01 means the max distance is 1% longer than the calculated distance in the rest pose.49};5051/// Automatically create constraints based on the faces of the soft body52/// @param inVertexAttributes A list of attributes for each vertex (1-on-1 with mVertices, note that if the list is smaller than mVertices the last element will be repeated). This defines the properties of the constraints that are created.53/// @param inVertexAttributesLength The length of inVertexAttributes54/// @param inBendType The type of bend constraint to create55/// @param inAngleTolerance Shear edges are created when two connected triangles form a quad (are roughly in the same plane and form a square with roughly 90 degree angles). This defines the tolerance (in radians).56void CreateConstraints(const VertexAttributes *inVertexAttributes, uint inVertexAttributesLength, EBendType inBendType = EBendType::Distance, float inAngleTolerance = DegreesToRadians(8.0f));5758/// Calculate the initial lengths of all springs of the edges of this soft body (if you use CreateConstraint, this is already done)59void CalculateEdgeLengths();6061/// Calculate the properties of the rods62/// Note that this can swap mVertex of the RodStretchShear constraints if two rods are connected through a RodBendTwist constraint but point in opposite directions.63void CalculateRodProperties();6465/// Calculate the max lengths for the long range attachment constraints based on Euclidean distance (if you use CreateConstraints, this is already done)66/// @param inMaxDistanceMultiplier Multiplier for the max distance of the LRA constraint, e.g. 1.01 means the max distance is 1% longer than the calculated distance in the rest pose.67void CalculateLRALengths(float inMaxDistanceMultiplier = 1.0f);6869/// Calculate the constants for the bend constraints (if you use CreateConstraints, this is already done)70void CalculateBendConstraintConstants();7172/// Calculates the initial volume of all tetrahedra of this soft body73void CalculateVolumeConstraintVolumes();7475/// Calculate information needed to be able to calculate the skinned constraint normals at run-time76void CalculateSkinnedConstraintNormals();7778/// Information about the optimization of the soft body, the indices of certain elements may have changed.79class OptimizationResults80{81public:82Array<uint> mEdgeRemap; ///< Maps old edge index to new edge index83Array<uint> mLRARemap; ///< Maps old LRA index to new LRA index84Array<uint> mRodStretchShearConstraintRemap; ///< Maps old rod stretch shear constraint index to new stretch shear rod constraint index85Array<uint> mRodBendTwistConstraintRemap; ///< Maps old rod bend twist constraint index to new bend twist rod constraint index86Array<uint> mDihedralBendRemap; ///< Maps old dihedral bend index to new dihedral bend index87Array<uint> mVolumeRemap; ///< Maps old volume constraint index to new volume constraint index88Array<uint> mSkinnedRemap; ///< Maps old skinned constraint index to new skinned constraint index89};9091/// Optimize the soft body settings for simulation. This will reorder constraints so they can be executed in parallel.92void Optimize(OptimizationResults &outResults);9394/// Optimize the soft body settings without results95void Optimize() { OptimizationResults results; Optimize(results); }9697/// Clone this object98Ref<SoftBodySharedSettings> Clone() const;99100/// Saves the state of this object in binary form to inStream. Doesn't store the material list.101void SaveBinaryState(StreamOut &inStream) const;102103/// Restore the state of this object from inStream. Doesn't restore the material list.104void RestoreBinaryState(StreamIn &inStream);105106using SharedSettingsToIDMap = StreamUtils::ObjectToIDMap<SoftBodySharedSettings>;107using IDToSharedSettingsMap = StreamUtils::IDToObjectMap<SoftBodySharedSettings>;108using MaterialToIDMap = StreamUtils::ObjectToIDMap<PhysicsMaterial>;109using IDToMaterialMap = StreamUtils::IDToObjectMap<PhysicsMaterial>;110111/// Save this shared settings and its materials. Pass in an empty map ioSettingsMap / ioMaterialMap or reuse the same map while saving multiple settings objects to the same stream in order to avoid writing duplicates.112void SaveWithMaterials(StreamOut &inStream, SharedSettingsToIDMap &ioSettingsMap, MaterialToIDMap &ioMaterialMap) const;113114using SettingsResult = Result<Ref<SoftBodySharedSettings>>;115116/// Restore a shape and materials. Pass in an empty map in ioSettingsMap / ioMaterialMap or reuse the same map while reading multiple settings objects from the same stream in order to restore duplicates.117static SettingsResult sRestoreWithMaterials(StreamIn &inStream, IDToSharedSettingsMap &ioSettingsMap, IDToMaterialMap &ioMaterialMap);118119/// Create a cube. This can be used to create a simple soft body for testing purposes.120/// It will contain edge constraints, volume constraints and faces.121/// @param inGridSize Number of points along each axis122/// @param inGridSpacing Distance between points123static Ref<SoftBodySharedSettings> sCreateCube(uint inGridSize, float inGridSpacing);124125/// A vertex is a particle, the data in this structure is only used during creation of the soft body and not during simulation126struct JPH_EXPORT Vertex127{128JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Vertex)129130/// Constructor131Vertex() = default;132Vertex(const Float3 &inPosition, const Float3 &inVelocity = Float3(0, 0, 0), float inInvMass = 1.0f) : mPosition(inPosition), mVelocity(inVelocity), mInvMass(inInvMass) { }133134Float3 mPosition { 0, 0, 0 }; ///< Initial position of the vertex135Float3 mVelocity { 0, 0, 0 }; ///< Initial velocity of the vertex136float mInvMass = 1.0f; ///< Initial inverse of the mass of the vertex137};138139/// A face defines the surface of the body140struct JPH_EXPORT Face141{142JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Face)143144/// Constructor145Face() = default;146Face(uint32 inVertex1, uint32 inVertex2, uint32 inVertex3, uint32 inMaterialIndex = 0) : mVertex { inVertex1, inVertex2, inVertex3 }, mMaterialIndex(inMaterialIndex) { }147148/// Check if this is a degenerate face (a face which points to the same vertex twice)149bool IsDegenerate() const { return mVertex[0] == mVertex[1] || mVertex[0] == mVertex[2] || mVertex[1] == mVertex[2]; }150151uint32 mVertex[3]; ///< Indices of the vertices that form the face152uint32 mMaterialIndex = 0; ///< Index of the material of the face in SoftBodySharedSettings::mMaterials153};154155/// An edge keeps two vertices at a constant distance using a spring: |x1 - x2| = rest length156struct JPH_EXPORT Edge157{158JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Edge)159160/// Constructor161Edge() = default;162Edge(uint32 inVertex1, uint32 inVertex2, float inCompliance = 0.0f) : mVertex { inVertex1, inVertex2 }, mCompliance(inCompliance) { }163164/// Return the lowest vertex index of this constraint165uint32 GetMinVertexIndex() const { return min(mVertex[0], mVertex[1]); }166167uint32 mVertex[2]; ///< Indices of the vertices that form the edge168float mRestLength = 1.0f; ///< Rest length of the spring, calculated by CalculateEdgeLengths169float mCompliance = 0.0f; ///< Inverse of the stiffness of the spring170};171172/**173* A dihedral bend constraint keeps the angle between two triangles constant along their shared edge.174*175* x2176* / \177* / t0 \178* x0----x1179* \ t1 /180* \ /181* x3182*183* x0..x3 are the vertices, t0 and t1 are the triangles that share the edge x0..x1184*185* Based on:186* - "Position Based Dynamics" - Matthias Muller et al.187* - "Strain Based Dynamics" - Matthias Muller et al.188* - "Simulation of Clothing with Folds and Wrinkles" - R. Bridson et al.189*/190struct JPH_EXPORT DihedralBend191{192JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, DihedralBend)193194/// Constructor195DihedralBend() = default;196DihedralBend(uint32 inVertex1, uint32 inVertex2, uint32 inVertex3, uint32 inVertex4, float inCompliance = 0.0f) : mVertex { inVertex1, inVertex2, inVertex3, inVertex4 }, mCompliance(inCompliance) { }197198/// Return the lowest vertex index of this constraint199uint32 GetMinVertexIndex() const { return min(min(mVertex[0], mVertex[1]), min(mVertex[2], mVertex[3])); }200201uint32 mVertex[4]; ///< Indices of the vertices of the 2 triangles that share an edge (the first 2 vertices are the shared edge)202float mCompliance = 0.0f; ///< Inverse of the stiffness of the constraint203float mInitialAngle = 0.0f; ///< Initial angle between the normals of the triangles (pi - dihedral angle), calculated by CalculateBendConstraintConstants204};205206/// Volume constraint, keeps the volume of a tetrahedron constant207struct JPH_EXPORT Volume208{209JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Volume)210211/// Constructor212Volume() = default;213Volume(uint32 inVertex1, uint32 inVertex2, uint32 inVertex3, uint32 inVertex4, float inCompliance = 0.0f) : mVertex { inVertex1, inVertex2, inVertex3, inVertex4 }, mCompliance(inCompliance) { }214215/// Return the lowest vertex index of this constraint216uint32 GetMinVertexIndex() const { return min(min(mVertex[0], mVertex[1]), min(mVertex[2], mVertex[3])); }217218uint32 mVertex[4]; ///< Indices of the vertices that form the tetrahedron219float mSixRestVolume = 1.0f; ///< 6 times the rest volume of the tetrahedron, calculated by CalculateVolumeConstraintVolumes220float mCompliance = 0.0f; ///< Inverse of the stiffness of the constraint221};222223/// An inverse bind matrix take a skinned vertex from its bind pose into joint local space224class JPH_EXPORT InvBind225{226JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, InvBind)227228public:229/// Constructor230InvBind() = default;231InvBind(uint32 inJointIndex, Mat44Arg inInvBind) : mJointIndex(inJointIndex), mInvBind(inInvBind) { }232233uint32 mJointIndex = 0; ///< Joint index to which this is attached234Mat44 mInvBind = Mat44::sIdentity(); ///< The inverse bind matrix, this takes a vertex in its bind pose (Vertex::mPosition) to joint local space235};236237/// A joint and its skin weight238class JPH_EXPORT SkinWeight239{240JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SkinWeight)241242public:243/// Constructor244SkinWeight() = default;245SkinWeight(uint32 inInvBindIndex, float inWeight) : mInvBindIndex(inInvBindIndex), mWeight(inWeight) { }246247uint32 mInvBindIndex = 0; ///< Index in mInvBindMatrices248float mWeight = 0.0f; ///< Weight with which it is skinned249};250251/// A constraint that skins a vertex to joints and limits the distance that the simulated vertex can travel from this vertex252class JPH_EXPORT Skinned253{254JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Skinned)255256public:257/// Constructor258Skinned() = default;259Skinned(uint32 inVertex, float inMaxDistance, float inBackStopDistance, float inBackStopRadius) : mVertex(inVertex), mMaxDistance(inMaxDistance), mBackStopDistance(inBackStopDistance), mBackStopRadius(inBackStopRadius) { }260261/// Normalize the weights so that they add up to 1262void NormalizeWeights()263{264// Get the total weight265float total = 0.0f;266for (const SkinWeight &w : mWeights)267total += w.mWeight;268269// Normalize270if (total > 0.0f)271for (SkinWeight &w : mWeights)272w.mWeight /= total;273}274275/// Maximum number of skin weights276static constexpr uint cMaxSkinWeights = 4;277278uint32 mVertex = 0; ///< Index in mVertices which indicates which vertex is being skinned279SkinWeight mWeights[cMaxSkinWeights]; ///< Skin weights, the bind pose of the vertex is assumed to be stored in Vertex::mPosition. The first weight that is zero indicates the end of the list. Weights should add up to 1.280float mMaxDistance = FLT_MAX; ///< Maximum distance that this vertex can reach from the skinned vertex, disabled when FLT_MAX. 0 when you want to hard skin the vertex to the skinned vertex.281float mBackStopDistance = FLT_MAX; ///< Disabled if mBackStopDistance >= mMaxDistance. The faces surrounding mVertex determine an average normal. mBackStopDistance behind the vertex in the opposite direction of this normal, the back stop sphere starts. The simulated vertex will be pushed out of this sphere and it can be used to approximate the volume of the skinned mesh behind the skinned vertex.282float mBackStopRadius = 40.0f; ///< Radius of the backstop sphere. By default this is a fairly large radius so the sphere approximates a plane.283uint32 mNormalInfo = 0; ///< Information needed to calculate the normal of this vertex, lowest 24 bit is start index in mSkinnedConstraintNormals, highest 8 bit is number of faces (generated by CalculateSkinnedConstraintNormals)284};285286/// A long range attachment constraint, this is a constraint that sets a max distance between a kinematic vertex and a dynamic vertex287/// See: "Long Range Attachments - A Method to Simulate Inextensible Clothing in Computer Games", Tae-Yong Kim, Nuttapong Chentanez and Matthias Mueller-Fischer288class JPH_EXPORT LRA289{290JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, LRA)291292public:293/// Constructor294LRA() = default;295LRA(uint32 inVertex1, uint32 inVertex2, float inMaxDistance) : mVertex { inVertex1, inVertex2 }, mMaxDistance(inMaxDistance) { }296297/// Return the lowest vertex index of this constraint298uint32 GetMinVertexIndex() const { return min(mVertex[0], mVertex[1]); }299300uint32 mVertex[2]; ///< The vertices that are connected. The first vertex should be kinematic, the 2nd dynamic.301float mMaxDistance = 0.0f; ///< The maximum distance between the vertices, calculated by CalculateLRALengths302};303304/// A discrete Cosserat rod connects two particles with a rigid rod that has fixed length and inertia.305/// A rod can be used instead of an Edge to constraint two vertices. The orientation of the rod can be306/// used to orient geometry attached to the rod (e.g. a plant leaf). Note that each rod needs to be constrained307/// by at least one RodBendTwist constraint in order to constrain the rotation of the rod. If you don't do308/// this then the orientation is likely to rotate around the rod axis with constant velocity.309/// Based on "Position and Orientation Based Cosserat Rods" - Kugelstadt and Schoemer - SIGGRAPH 2016310/// See: https://www.researchgate.net/publication/325597548_Position_and_Orientation_Based_Cosserat_Rods311struct JPH_EXPORT RodStretchShear312{313JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, RodStretchShear)314315/// Constructor316RodStretchShear() = default;317RodStretchShear(uint32 inVertex1, uint32 inVertex2, float inCompliance = 0.0f) : mVertex { inVertex1, inVertex2 }, mCompliance(inCompliance) { }318319/// Return the lowest vertex index of this constraint320uint32 GetMinVertexIndex() const { return min(mVertex[0], mVertex[1]); }321322uint32 mVertex[2]; ///< Indices of the vertices that form the rod323float mLength = 1.0f; ///< Fixed length of the rod, calculated by CalculateRodProperties324float mInvMass = 1.0f; ///< Inverse of the mass of the rod (0 for static rods), calculated by CalculateRodProperties but can be overridden afterwards325float mCompliance = 0.0f; ///< Inverse of the stiffness of the rod326Quat mBishop = Quat::sZero(); ///< The Bishop frame of the rod (the rotation of the rod in its rest pose so that it has zero twist towards adjacent rods), calculated by CalculateRodProperties327};328329/// A constraint that connects two Cosserat rods and limits bend and twist between the rods.330struct JPH_EXPORT RodBendTwist331{332JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, RodBendTwist)333334/// Constructor335RodBendTwist() = default;336RodBendTwist(uint32 inRod1, uint32 inRod2, float inCompliance = 0.0f) : mRod { inRod1, inRod2 }, mCompliance(inCompliance) { }337338uint32 mRod[2]; ///< Indices of rods that are constrained (index in mRodStretchShearConstraints)339float mCompliance = 0.0f; ///< Inverse of the stiffness of the rod340Quat mOmega0 = Quat::sZero(); ///< The initial rotation between the rods: rod1.mBishop.Conjugated() * rod2.mBishop, calculated by CalculateRodProperties341};342343/// Add a face to this soft body344void AddFace(const Face &inFace) { JPH_ASSERT(!inFace.IsDegenerate()); mFaces.push_back(inFace); }345346Array<Vertex> mVertices; ///< The list of vertices or particles of the body347Array<Face> mFaces; ///< The list of faces of the body348Array<Edge> mEdgeConstraints; ///< The list of edges or springs of the body349Array<DihedralBend> mDihedralBendConstraints; ///< The list of dihedral bend constraints of the body350Array<Volume> mVolumeConstraints; ///< The list of volume constraints of the body that keep the volume of tetrahedra in the soft body constant351Array<Skinned> mSkinnedConstraints; ///< The list of vertices that are constrained to a skinned vertex352Array<InvBind> mInvBindMatrices; ///< The list of inverse bind matrices for skinning vertices353Array<LRA> mLRAConstraints; ///< The list of long range attachment constraints354Array<RodStretchShear> mRodStretchShearConstraints; ///< The list of Cosserat rod constraints that connect two vertices and that limit stretch and shear355Array<RodBendTwist> mRodBendTwistConstraints; ///< The list of Cosserat rod constraints that connect two rods and limit the bend and twist356PhysicsMaterialList mMaterials { PhysicsMaterial::sDefault }; ///< The materials of the faces of the body, referenced by Face::mMaterialIndex357358private:359friend class SoftBodyMotionProperties;360361/// Calculate the closest kinematic vertex array362void CalculateClosestKinematic();363364/// Tracks the closest kinematic vertex365struct ClosestKinematic366{367uint32 mVertex = 0xffffffff; ///< Vertex index of closest kinematic vertex368uint32 mHops = 0xffffffff; ///< Number of hops to the closest kinematic vertex369float mDistance = FLT_MAX; ///< Distance to the closest kinematic vertex370};371372/// Tracks the end indices of the various constraint groups373struct UpdateGroup374{375uint mEdgeEndIndex; ///< The end index of the edge constraints in this group376uint mLRAEndIndex; ///< The end index of the LRA constraints in this group377uint mRodStretchShearEndIndex; ///< The end index of the rod stretch shear constraints in this group378uint mRodBendTwistEndIndex; ///< The end index of the rod bend twist constraints in this group379uint mDihedralBendEndIndex; ///< The end index of the dihedral bend constraints in this group380uint mVolumeEndIndex; ///< The end index of the volume constraints in this group381uint mSkinnedEndIndex; ///< The end index of the skinned constraints in this group382};383384Array<ClosestKinematic> mClosestKinematic; ///< The closest kinematic vertex to each vertex in mVertices385Array<UpdateGroup> mUpdateGroups; ///< The end indices for each group of constraints that can be updated in parallel386Array<uint32> mSkinnedConstraintNormals; ///< A list of indices in the mFaces array used by mSkinnedConstraints, calculated by CalculateSkinnedConstraintNormals387};388389JPH_NAMESPACE_END390391392