Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.cpp
21520 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2023 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#include <Jolt/Jolt.h>56#include <Jolt/Physics/SoftBody/SoftBodyMotionProperties.h>7#include <Jolt/Physics/SoftBody/SoftBodyCreationSettings.h>8#include <Jolt/Physics/SoftBody/SoftBodyContactListener.h>9#include <Jolt/Physics/SoftBody/SoftBodyManifold.h>10#include <Jolt/Physics/Collision/CollideSoftBodyVertexIterator.h>11#include <Jolt/Physics/Collision/SimShapeFilterWrapper.h>12#include <Jolt/Physics/PhysicsSystem.h>13#include <Jolt/Physics/Body/BodyManager.h>14#include <Jolt/Core/ScopeExit.h>15#ifdef JPH_DEBUG_RENDERER16#include <Jolt/Renderer/DebugRenderer.h>17#endif // JPH_DEBUG_RENDERER1819JPH_NAMESPACE_BEGIN2021using namespace JPH::literals;2223void SoftBodyMotionProperties::CalculateMassAndInertia()24{25MassProperties mp;2627for (const Vertex &v : mVertices)28if (v.mInvMass > 0.0f)29{30Vec3 pos = v.mPosition;3132// Accumulate mass33float mass = 1.0f / v.mInvMass;34mp.mMass += mass;3536// Inertia tensor, diagonal37// See equations https://en.wikipedia.org/wiki/Moment_of_inertia section 'Inertia Tensor'38for (int i = 0; i < 3; ++i)39mp.mInertia(i, i) += mass * (Square(pos[(i + 1) % 3]) + Square(pos[(i + 2) % 3]));4041// Inertia tensor off diagonal42for (int i = 0; i < 3; ++i)43for (int j = 0; j < 3; ++j)44if (i != j)45mp.mInertia(i, j) -= mass * pos[i] * pos[j];46}47else48{49// If one vertex is kinematic, the entire body will have infinite mass and inertia50SetInverseMass(0.0f);51SetInverseInertia(Vec3::sZero(), Quat::sIdentity());52return;53}5455SetMassProperties(EAllowedDOFs::All, mp);56}5758void SoftBodyMotionProperties::Initialize(const SoftBodyCreationSettings &inSettings)59{60// Store settings61mSettings = inSettings.mSettings;62mNumIterations = inSettings.mNumIterations;63mPressure = inSettings.mPressure;64mUpdatePosition = inSettings.mUpdatePosition;65mFacesDoubleSided = inSettings.mFacesDoubleSided;66SetVertexRadius(inSettings.mVertexRadius);6768// Initialize vertices69mVertices.resize(inSettings.mSettings->mVertices.size());70Mat44 rotation = inSettings.mMakeRotationIdentity? Mat44::sRotation(inSettings.mRotation) : Mat44::sIdentity();71for (Array<Vertex>::size_type v = 0, s = mVertices.size(); v < s; ++v)72{73const SoftBodySharedSettings::Vertex &in_vertex = inSettings.mSettings->mVertices[v];74Vertex &out_vertex = mVertices[v];75out_vertex.mPreviousPosition = out_vertex.mPosition = rotation * Vec3(in_vertex.mPosition);76out_vertex.mVelocity = rotation.Multiply3x3(Vec3(in_vertex.mVelocity));77out_vertex.ResetCollision();78out_vertex.mInvMass = in_vertex.mInvMass;79mLocalBounds.Encapsulate(out_vertex.mPosition);80}8182// Initialize rods83if (!inSettings.mSettings->mRodStretchShearConstraints.empty())84{85mRodStates.resize(inSettings.mSettings->mRodStretchShearConstraints.size());86Quat rotation_q = rotation.GetQuaternion();87for (Array<RodState>::size_type r = 0, s = mRodStates.size(); r < s; ++r)88{89const SoftBodySharedSettings::RodStretchShear &in_rod = inSettings.mSettings->mRodStretchShearConstraints[r];90RodState &out_rod = mRodStates[r];91out_rod.mRotation = rotation_q * in_rod.mBishop;92out_rod.mAngularVelocity = Vec3::sZero();93}94}9596// Allocate space for skinned vertices97if (!inSettings.mSettings->mSkinnedConstraints.empty())98mSkinState.resize(mVertices.size());99100// We don't know delta time yet, so we can't predict the bounds and use the local bounds as the predicted bounds101mLocalPredictedBounds = mLocalBounds;102103CalculateMassAndInertia();104}105106float SoftBodyMotionProperties::GetVolumeTimesSix() const107{108float six_volume = 0.0f;109for (const Face &f : mSettings->mFaces)110{111Vec3 x1 = mVertices[f.mVertex[0]].mPosition;112Vec3 x2 = mVertices[f.mVertex[1]].mPosition;113Vec3 x3 = mVertices[f.mVertex[2]].mPosition;114six_volume += x1.Cross(x2).Dot(x3); // We pick zero as the origin as this is the center of the bounding box so should give good accuracy115}116return six_volume;117}118119void SoftBodyMotionProperties::DetermineCollidingShapes(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem, const BodyLockInterface &inBodyLockInterface)120{121JPH_PROFILE_FUNCTION();122123// Reset flag prior to collision detection124mNeedContactCallback.store(false, memory_order_relaxed);125126struct Collector : public CollideShapeBodyCollector127{128Collector(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem, const BodyLockInterface &inBodyLockInterface, const AABox &inLocalBounds, SimShapeFilterWrapper &inShapeFilter, Array<CollidingShape> &ioHits, Array<CollidingSensor> &ioSensors) :129mContext(inContext),130mInverseTransform(inContext.mCenterOfMassTransform.InversedRotationTranslation()),131mLocalBounds(inLocalBounds),132mBodyLockInterface(inBodyLockInterface),133mCombineFriction(inSystem.GetCombineFriction()),134mCombineRestitution(inSystem.GetCombineRestitution()),135mShapeFilter(inShapeFilter),136mHits(ioHits),137mSensors(ioSensors)138{139}140141virtual void AddHit(const BodyID &inResult) override142{143BodyLockRead lock(mBodyLockInterface, inResult);144if (lock.Succeeded())145{146const Body &soft_body = *mContext.mBody;147const Body &body = lock.GetBody();148if (body.IsRigidBody() // TODO: We should support soft body vs soft body149&& soft_body.GetCollisionGroup().CanCollide(body.GetCollisionGroup()))150{151SoftBodyContactSettings settings;152settings.mIsSensor = body.IsSensor();153154if (mContext.mContactListener == nullptr)155{156// If we have no contact listener, we can ignore sensors157if (settings.mIsSensor)158return;159}160else161{162// Call the contact listener to see if we should accept this contact163if (mContext.mContactListener->OnSoftBodyContactValidate(soft_body, body, settings) != SoftBodyValidateResult::AcceptContact)164return;165166// Check if there will be any interaction167if (!settings.mIsSensor168&& settings.mInvMassScale1 == 0.0f169&& (body.GetMotionType() != EMotionType::Dynamic || settings.mInvMassScale2 == 0.0f))170return;171}172173// Calculate transform of this body relative to the soft body174Mat44 com = (mInverseTransform * body.GetCenterOfMassTransform()).ToMat44();175176// Collect leaf shapes177mShapeFilter.SetBody2(&body);178struct LeafShapeCollector : public TransformedShapeCollector179{180virtual void AddHit(const TransformedShape &inResult) override181{182mHits.emplace_back(Mat44::sRotationTranslation(inResult.mShapeRotation, Vec3(inResult.mShapePositionCOM)), inResult.GetShapeScale(), inResult.mShape);183}184185Array<LeafShape> mHits;186};187LeafShapeCollector collector;188body.GetShape()->CollectTransformedShapes(mLocalBounds, com.GetTranslation(), com.GetQuaternion(), Vec3::sOne(), SubShapeIDCreator(), collector, mShapeFilter.GetFilter());189if (collector.mHits.empty())190return;191192if (settings.mIsSensor)193{194CollidingSensor cs;195cs.mCenterOfMassTransform = com;196cs.mShapes = std::move(collector.mHits);197cs.mBodyID = inResult;198mSensors.push_back(cs);199}200else201{202CollidingShape cs;203cs.mCenterOfMassTransform = com;204cs.mShapes = std::move(collector.mHits);205cs.mBodyID = inResult;206cs.mMotionType = body.GetMotionType();207cs.mUpdateVelocities = false;208cs.mFriction = mCombineFriction(soft_body, SubShapeID(), body, SubShapeID());209cs.mRestitution = mCombineRestitution(soft_body, SubShapeID(), body, SubShapeID());210cs.mSoftBodyInvMassScale = settings.mInvMassScale1;211if (cs.mMotionType == EMotionType::Dynamic)212{213const MotionProperties *mp = body.GetMotionProperties();214cs.mInvMass = settings.mInvMassScale2 * mp->GetInverseMass();215cs.mInvInertia = settings.mInvInertiaScale2 * mp->GetInverseInertiaForRotation(cs.mCenterOfMassTransform.GetRotation());216cs.mOriginalLinearVelocity = cs.mLinearVelocity = mInverseTransform.Multiply3x3(mp->GetLinearVelocity());217cs.mOriginalAngularVelocity = cs.mAngularVelocity = mInverseTransform.Multiply3x3(mp->GetAngularVelocity());218}219mHits.push_back(cs);220}221}222}223}224225private:226const SoftBodyUpdateContext &mContext;227RMat44 mInverseTransform;228AABox mLocalBounds;229const BodyLockInterface & mBodyLockInterface;230ContactConstraintManager::CombineFunction mCombineFriction;231ContactConstraintManager::CombineFunction mCombineRestitution;232SimShapeFilterWrapper & mShapeFilter;233Array<CollidingShape> & mHits;234Array<CollidingSensor> & mSensors;235};236237// Calculate local bounding box238AABox local_bounds = mLocalBounds;239local_bounds.Encapsulate(mLocalPredictedBounds);240local_bounds.ExpandBy(Vec3::sReplicate(mVertexRadius));241242// Calculate world space bounding box243AABox world_bounds = local_bounds.Transformed(inContext.mCenterOfMassTransform);244245// Create shape filter246SimShapeFilterWrapper shape_filter(inContext.mSimShapeFilter, inContext.mBody);247248Collector collector(inContext, inSystem, inBodyLockInterface, local_bounds, shape_filter, mCollidingShapes, mCollidingSensors);249ObjectLayer layer = inContext.mBody->GetObjectLayer();250DefaultBroadPhaseLayerFilter broadphase_layer_filter = inSystem.GetDefaultBroadPhaseLayerFilter(layer);251DefaultObjectLayerFilter object_layer_filter = inSystem.GetDefaultLayerFilter(layer);252inSystem.GetBroadPhaseQuery().CollideAABox(world_bounds, collector, broadphase_layer_filter, object_layer_filter);253mNumSensors = uint(mCollidingSensors.size()); // Workaround for TSAN false positive: store mCollidingSensors.size() in a separate variable.254}255256void SoftBodyMotionProperties::DetermineCollisionPlanes(uint inVertexStart, uint inNumVertices)257{258JPH_PROFILE_FUNCTION();259260// Generate collision planes261for (const CollidingShape &cs : mCollidingShapes)262for (const LeafShape &shape : cs.mShapes)263shape.mShape->CollideSoftBodyVertices(shape.mTransform, shape.mScale, CollideSoftBodyVertexIterator(mVertices.data() + inVertexStart), inNumVertices, int(&cs - mCollidingShapes.data()));264}265266void SoftBodyMotionProperties::DetermineSensorCollisions(CollidingSensor &ioSensor)267{268JPH_PROFILE_FUNCTION();269270Plane collision_plane;271float largest_penetration = -FLT_MAX;272int colliding_shape_idx = -1;273274// Collide sensor against all vertices275CollideSoftBodyVertexIterator vertex_iterator(276StridedPtr<const Vec3>(&mVertices[0].mPosition, sizeof(SoftBodyVertex)), // The position and mass come from the soft body vertex277StridedPtr<const float>(&mVertices[0].mInvMass, sizeof(SoftBodyVertex)),278StridedPtr<Plane>(&collision_plane, 0), // We want all vertices to result in a single collision so we pass stride 0279StridedPtr<float>(&largest_penetration, 0),280StridedPtr<int>(&colliding_shape_idx, 0));281for (const LeafShape &shape : ioSensor.mShapes)282shape.mShape->CollideSoftBodyVertices(shape.mTransform, shape.mScale, vertex_iterator, uint(mVertices.size()), 0);283ioSensor.mHasContact = largest_penetration > 0.0f;284285// We need a contact callback if one of the sensors collided286if (ioSensor.mHasContact)287mNeedContactCallback.store(true, memory_order_relaxed);288}289290void SoftBodyMotionProperties::ApplyPressure(const SoftBodyUpdateContext &inContext)291{292JPH_PROFILE_FUNCTION();293294float dt = inContext.mSubStepDeltaTime;295float pressure_coefficient = mPressure;296if (pressure_coefficient > 0.0f)297{298// Calculate total volume299float six_volume = GetVolumeTimesSix();300if (six_volume > 0.0f)301{302// Apply pressure303// p = F / A = n R T / V (see https://en.wikipedia.org/wiki/Pressure)304// Our pressure coefficient is n R T so the impulse is:305// P = F dt = pressure_coefficient / V * A * dt306float coefficient = pressure_coefficient * dt / six_volume; // Need to still multiply by 6 for the volume307for (const Face &f : mSettings->mFaces)308{309Vec3 x1 = mVertices[f.mVertex[0]].mPosition;310Vec3 x2 = mVertices[f.mVertex[1]].mPosition;311Vec3 x3 = mVertices[f.mVertex[2]].mPosition;312313Vec3 impulse = coefficient * (x2 - x1).Cross(x3 - x1); // Area is half the cross product so need to still divide by 2314for (uint32 i : f.mVertex)315{316Vertex &v = mVertices[i];317v.mVelocity += v.mInvMass * impulse; // Want to divide by 3 because we spread over 3 vertices318}319}320}321}322}323324void SoftBodyMotionProperties::IntegratePositions(const SoftBodyUpdateContext &inContext)325{326JPH_PROFILE_FUNCTION();327328float dt = inContext.mSubStepDeltaTime;329float linear_damping = max(0.0f, 1.0f - GetLinearDamping() * dt); // See: MotionProperties::ApplyForceTorqueAndDragInternal330331// Integrate332Vec3 sub_step_gravity = inContext.mGravity * dt;333Vec3 sub_step_impulse = GetAccumulatedForce() * dt / max(float(mVertices.size()), 1.0f);334for (Vertex &v : mVertices)335{336if (v.mInvMass > 0.0f)337{338// Gravity339v.mVelocity += sub_step_gravity + sub_step_impulse * v.mInvMass;340341// Damping342v.mVelocity *= linear_damping;343}344345// Integrate346Vec3 position = v.mPosition;347v.mPreviousPosition = position;348v.mPosition = position + v.mVelocity * dt;349}350351// Integrate rod orientations352float half_dt = 0.5f * dt;353for (RodState &r : mRodStates)354{355// Damping356r.mAngularVelocity *= linear_damping;357358// Integrate359Quat rotation = r.mRotation;360Quat delta_rotation = half_dt * Quat::sMultiplyImaginary(r.mAngularVelocity, rotation);361r.mPreviousRotationInternal = rotation; // Overwrites mAngularVelocity362r.mRotation = (rotation + delta_rotation).Normalized();363}364}365366void SoftBodyMotionProperties::ApplyDihedralBendConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex)367{368JPH_PROFILE_FUNCTION();369370float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime);371372for (const DihedralBend *b = mSettings->mDihedralBendConstraints.data() + inStartIndex, *b_end = mSettings->mDihedralBendConstraints.data() + inEndIndex; b < b_end; ++b)373{374Vertex &v0 = mVertices[b->mVertex[0]];375Vertex &v1 = mVertices[b->mVertex[1]];376Vertex &v2 = mVertices[b->mVertex[2]];377Vertex &v3 = mVertices[b->mVertex[3]];378379// Get positions380Vec3 x0 = v0.mPosition;381Vec3 x1 = v1.mPosition;382Vec3 x2 = v2.mPosition;383Vec3 x3 = v3.mPosition;384385/*386x2387e1/ \e3388/ \389x0----x1390\ e0 /391e2\ /e4392x3393*/394395// Calculate the shared edge of the triangles396Vec3 e = x1 - x0;397float e_len = e.Length();398if (e_len < 1.0e-6f)399continue;400401// Calculate the normals of the triangles402Vec3 x1x2 = x2 - x1;403Vec3 x1x3 = x3 - x1;404Vec3 n1 = (x2 - x0).Cross(x1x2);405Vec3 n2 = x1x3.Cross(x3 - x0);406float n1_len_sq = n1.LengthSq();407float n2_len_sq = n2.LengthSq();408float n1_len_sq_n2_len_sq = n1_len_sq * n2_len_sq;409if (n1_len_sq_n2_len_sq < 1.0e-24f)410continue;411412// Calculate constraint equation413// As per "Strain Based Dynamics" Appendix A we need to negate the gradients when (n1 x n2) . e > 0, instead we make sure that the sign of the constraint equation is correct414float sign = Sign(n2.Cross(n1).Dot(e));415float d = n1.Dot(n2) / sqrt(n1_len_sq_n2_len_sq);416float c = sign * ACosApproximate(d) - b->mInitialAngle;417418// Ensure the range is -PI to PI419if (c > JPH_PI)420c -= 2.0f * JPH_PI;421else if (c < -JPH_PI)422c += 2.0f * JPH_PI;423424// Calculate gradient of constraint equation425// Taken from "Strain Based Dynamics" - Matthias Muller et al. (Appendix A)426// with p1 = x2, p2 = x3, p3 = x0 and p4 = x1427// which in turn is based on "Simulation of Clothing with Folds and Wrinkles" - R. Bridson et al. (Section 4)428n1 /= n1_len_sq;429n2 /= n2_len_sq;430Vec3 d0c = (x1x2.Dot(e) * n1 + x1x3.Dot(e) * n2) / e_len;431Vec3 d2c = e_len * n1;432Vec3 d3c = e_len * n2;433434// The sum of the gradients must be zero (see "Strain Based Dynamics" section 4)435Vec3 d1c = -d0c - d2c - d3c;436437// Get masses438float w0 = v0.mInvMass;439float w1 = v1.mInvMass;440float w2 = v2.mInvMass;441float w3 = v3.mInvMass;442443// Calculate -lambda444float denom = w0 * d0c.LengthSq() + w1 * d1c.LengthSq() + w2 * d2c.LengthSq() + w3 * d3c.LengthSq() + b->mCompliance * inv_dt_sq;445if (denom < 1.0e-12f)446continue;447float minus_lambda = c / denom;448449// Apply correction450v0.mPosition = x0 - minus_lambda * w0 * d0c;451v1.mPosition = x1 - minus_lambda * w1 * d1c;452v2.mPosition = x2 - minus_lambda * w2 * d2c;453v3.mPosition = x3 - minus_lambda * w3 * d3c;454}455}456457void SoftBodyMotionProperties::ApplyVolumeConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex)458{459JPH_PROFILE_FUNCTION();460461float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime);462463// Satisfy volume constraints464for (const Volume *v = mSettings->mVolumeConstraints.data() + inStartIndex, *v_end = mSettings->mVolumeConstraints.data() + inEndIndex; v < v_end; ++v)465{466Vertex &v1 = mVertices[v->mVertex[0]];467Vertex &v2 = mVertices[v->mVertex[1]];468Vertex &v3 = mVertices[v->mVertex[2]];469Vertex &v4 = mVertices[v->mVertex[3]];470471Vec3 x1 = v1.mPosition;472Vec3 x2 = v2.mPosition;473Vec3 x3 = v3.mPosition;474Vec3 x4 = v4.mPosition;475476// Calculate constraint equation477Vec3 x1x2 = x2 - x1;478Vec3 x1x3 = x3 - x1;479Vec3 x1x4 = x4 - x1;480float c = abs(x1x2.Cross(x1x3).Dot(x1x4)) - v->mSixRestVolume;481482// Calculate gradient of constraint equation483Vec3 d1c = (x4 - x2).Cross(x3 - x2);484Vec3 d2c = x1x3.Cross(x1x4);485Vec3 d3c = x1x4.Cross(x1x2);486Vec3 d4c = x1x2.Cross(x1x3);487488// Get masses489float w1 = v1.mInvMass;490float w2 = v2.mInvMass;491float w3 = v3.mInvMass;492float w4 = v4.mInvMass;493494// Calculate -lambda495float denom = w1 * d1c.LengthSq() + w2 * d2c.LengthSq() + w3 * d3c.LengthSq() + w4 * d4c.LengthSq() + v->mCompliance * inv_dt_sq;496if (denom < 1.0e-12f)497continue;498float minus_lambda = c / denom;499500// Apply correction501v1.mPosition = x1 - minus_lambda * w1 * d1c;502v2.mPosition = x2 - minus_lambda * w2 * d2c;503v3.mPosition = x3 - minus_lambda * w3 * d3c;504v4.mPosition = x4 - minus_lambda * w4 * d4c;505}506}507508void SoftBodyMotionProperties::ApplySkinConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex)509{510// Early out if nothing to do511if (mSettings->mSkinnedConstraints.empty() || !mEnableSkinConstraints)512return;513514JPH_PROFILE_FUNCTION();515516// We're going to iterate multiple times over the skin constraints, update the skinned position accordingly.517// If we don't do this, the simulation will see a big jump and the first iteration will cause a big velocity change in the system.518float factor = mSkinStatePreviousPositionValid? inContext.mNextIteration.load(std::memory_order_relaxed) / float(mNumIterations) : 1.0f;519float prev_factor = 1.0f - factor;520521// Apply the constraints522Vertex *vertices = mVertices.data();523const SkinState *skin_states = mSkinState.data();524for (const Skinned *s = mSettings->mSkinnedConstraints.data() + inStartIndex, *s_end = mSettings->mSkinnedConstraints.data() + inEndIndex; s < s_end; ++s)525{526Vertex &vertex = vertices[s->mVertex];527const SkinState &skin_state = skin_states[s->mVertex];528float max_distance = s->mMaxDistance * mSkinnedMaxDistanceMultiplier;529530// Calculate the skinned position by interpolating from previous to current position531Vec3 skin_pos = prev_factor * skin_state.mPreviousPosition + factor * skin_state.mPosition;532533if (max_distance > 0.0f)534{535// Move vertex if it violated the back stop536if (s->mBackStopDistance < max_distance)537{538// Center of the back stop sphere539Vec3 center = skin_pos - skin_state.mNormal * (s->mBackStopDistance + s->mBackStopRadius);540541// Check if we're inside the back stop sphere542Vec3 delta = vertex.mPosition - center;543float delta_len_sq = delta.LengthSq();544if (delta_len_sq < Square(s->mBackStopRadius))545{546// Push the vertex to the surface of the back stop sphere547float delta_len = sqrt(delta_len_sq);548vertex.mPosition = delta_len > 0.0f?549center + delta * (s->mBackStopRadius / delta_len)550: center + skin_state.mNormal * s->mBackStopRadius;551}552}553554// Clamp vertex distance to max distance from skinned position555if (max_distance < FLT_MAX)556{557Vec3 delta = vertex.mPosition - skin_pos;558float delta_len_sq = delta.LengthSq();559float max_distance_sq = Square(max_distance);560if (delta_len_sq > max_distance_sq)561vertex.mPosition = skin_pos + delta * sqrt(max_distance_sq / delta_len_sq);562}563}564else565{566// Kinematic: Just update the vertex position567vertex.mPosition = skin_pos;568}569}570}571572void SoftBodyMotionProperties::ApplyEdgeConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex)573{574JPH_PROFILE_FUNCTION();575576float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime);577578// Satisfy edge constraints579for (const Edge *e = mSettings->mEdgeConstraints.data() + inStartIndex, *e_end = mSettings->mEdgeConstraints.data() + inEndIndex; e < e_end; ++e)580{581Vertex &v0 = mVertices[e->mVertex[0]];582Vertex &v1 = mVertices[e->mVertex[1]];583584// Get positions585Vec3 x0 = v0.mPosition;586Vec3 x1 = v1.mPosition;587588// Calculate current length589Vec3 delta = x1 - x0;590float length = delta.Length();591592// Apply correction593float denom = length * (v0.mInvMass + v1.mInvMass + e->mCompliance * inv_dt_sq);594if (denom < 1.0e-12f)595continue;596Vec3 correction = delta * (length - e->mRestLength) / denom;597v0.mPosition = x0 + v0.mInvMass * correction;598v1.mPosition = x1 - v1.mInvMass * correction;599}600}601602void SoftBodyMotionProperties::ApplyRodStretchShearConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex)603{604JPH_PROFILE_FUNCTION();605606float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime);607608RodState *rod_state = mRodStates.data() + inStartIndex;609for (const RodStretchShear *r = mSettings->mRodStretchShearConstraints.data() + inStartIndex, *r_end = mSettings->mRodStretchShearConstraints.data() + inEndIndex; r < r_end; ++r, ++rod_state)610{611// Get positions612Vertex &v0 = mVertices[r->mVertex[0]];613Vertex &v1 = mVertices[r->mVertex[1]];614615// Apply stretch and shear constraint616// Equation 37 from "Position and Orientation Based Cosserat Rods" - Kugelstadt and Schoemer - SIGGRAPH 2016617float denom = v0.mInvMass + v1.mInvMass + 4.0f * r->mInvMass * Square(r->mLength) + r->mCompliance * inv_dt_sq;618if (denom < 1.0e-12f)619continue;620Vec3 x0 = v0.mPosition;621Vec3 x1 = v1.mPosition;622Quat rotation = rod_state->mRotation;623Vec3 d3 = rotation.RotateAxisZ();624Vec3 delta = (x1 - x0 - d3 * r->mLength) / denom;625v0.mPosition = x0 + v0.mInvMass * delta;626v1.mPosition = x1 - v1.mInvMass * delta;627// q * e3_bar = q * (0, 0, -1, 0) = [-qy, qx, -qw, qz]628Quat q_e3_bar(rotation.GetXYZW().Swizzle<SWIZZLE_Y, SWIZZLE_X, SWIZZLE_W, SWIZZLE_Z>().FlipSign<-1, 1, -1, 1>());629rotation += (2.0f * r->mInvMass * r->mLength) * Quat::sMultiplyImaginary(delta, q_e3_bar);630631// Renormalize632rod_state->mRotation = rotation.Normalized();633}634}635636void SoftBodyMotionProperties::ApplyRodBendTwistConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex)637{638JPH_PROFILE_FUNCTION();639640float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime);641642const Array<RodStretchShear> &rods = mSettings->mRodStretchShearConstraints;643644for (const RodBendTwist *r = mSettings->mRodBendTwistConstraints.data() + inStartIndex, *r_end = mSettings->mRodBendTwistConstraints.data() + inEndIndex; r < r_end; ++r)645{646uint32 rod1_index = r->mRod[0];647uint32 rod2_index = r->mRod[1];648const RodStretchShear &rod1 = rods[rod1_index];649const RodStretchShear &rod2 = rods[rod2_index];650RodState &rod1_state = mRodStates[rod1_index];651RodState &rod2_state = mRodStates[rod2_index];652653// Apply bend and twist constraint654// Equation 40 from "Position and Orientation Based Cosserat Rods" - Kugelstadt and Schoemer - SIGGRAPH 2016655float denom = rod1.mInvMass + rod2.mInvMass + r->mCompliance * inv_dt_sq;656if (denom < 1.0e-12f)657continue;658Quat rotation1 = rod1_state.mRotation;659Quat rotation2 = rod2_state.mRotation;660Quat omega = rotation1.Conjugated() * rotation2;661Quat omega0 = r->mOmega0;662Vec4 omega_min_omega0 = (omega - omega0).GetXYZW();663Vec4 omega_plus_omega0 = (omega + omega0).GetXYZW();664// Take the shortest of the two rotations665Quat delta_omega(Vec4::sSelect(omega_min_omega0, omega_plus_omega0, Vec4::sLess(omega_plus_omega0.DotV(omega_plus_omega0), omega_min_omega0.DotV(omega_min_omega0))));666delta_omega /= denom;667delta_omega.SetW(0.0f); // Scalar part needs to be zero because the real part of the Darboux vector doesn't vanish, see text between eq. 39 and 40.668Quat delta_rod2 = rod2.mInvMass * rotation1 * delta_omega;669rotation1 += rod1.mInvMass * rotation2 * delta_omega;670rotation2 -= delta_rod2;671672// Renormalize673rod1_state.mRotation = rotation1.Normalized();674rod2_state.mRotation = rotation2.Normalized();675}676}677678void SoftBodyMotionProperties::ApplyLRAConstraints(uint inStartIndex, uint inEndIndex)679{680JPH_PROFILE_FUNCTION();681682// Satisfy LRA constraints683Vertex *vertices = mVertices.data();684for (const LRA *lra = mSettings->mLRAConstraints.data() + inStartIndex, *lra_end = mSettings->mLRAConstraints.data() + inEndIndex; lra < lra_end; ++lra)685{686JPH_ASSERT(lra->mVertex[0] < mVertices.size());687JPH_ASSERT(lra->mVertex[1] < mVertices.size());688const Vertex &vertex0 = vertices[lra->mVertex[0]];689Vertex &vertex1 = vertices[lra->mVertex[1]];690691Vec3 x0 = vertex0.mPosition;692Vec3 delta = vertex1.mPosition - x0;693float delta_len_sq = delta.LengthSq();694if (delta_len_sq > Square(lra->mMaxDistance))695vertex1.mPosition = x0 + delta * lra->mMaxDistance / sqrt(delta_len_sq);696}697}698699void SoftBodyMotionProperties::ApplyCollisionConstraintsAndUpdateVelocities(const SoftBodyUpdateContext &inContext)700{701JPH_PROFILE_FUNCTION();702703float dt = inContext.mSubStepDeltaTime;704float restitution_threshold = -2.0f * inContext.mGravity.Length() * dt;705float vertex_radius = mVertexRadius;706for (Vertex &v : mVertices)707if (v.mInvMass > 0.0f)708{709// Remember previous velocity for restitution calculations710Vec3 prev_v = v.mVelocity;711712// XPBD velocity update713v.mVelocity = (v.mPosition - v.mPreviousPosition) / dt;714715// Satisfy collision constraint716if (v.mCollidingShapeIndex >= 0)717{718// Check if there is a collision719float projected_distance = -v.mCollisionPlane.SignedDistance(v.mPosition) + vertex_radius;720if (projected_distance > 0.0f)721{722// Remember that there was a collision723v.mHasContact = true;724725// We need a contact callback if one of the vertices collided726mNeedContactCallback.store(true, memory_order_relaxed);727728// Note that we already calculated the velocity, so this does not affect the velocity (next iteration starts by setting previous position to current position)729CollidingShape &cs = mCollidingShapes[v.mCollidingShapeIndex];730Vec3 contact_normal = v.mCollisionPlane.GetNormal();731v.mPosition += contact_normal * projected_distance;732733// Apply friction as described in Detailed Rigid Body Simulation with Extended Position Based Dynamics - Matthias Muller et al.734// See section 3.6:735// Inverse mass: w1 = 1 / m1, w2 = 1 / m2 + (r2 x n)^T I^-1 (r2 x n) = 0 for a static object736// r2 are the contact point relative to the center of mass of body 2737// Lagrange multiplier for contact: lambda = -c / (w1 + w2)738// Where c is the constraint equation (the distance to the plane, negative because penetrating)739// Contact normal force: fn = lambda / dt^2740// Delta velocity due to friction dv = -vt / |vt| * min(dt * friction * fn * (w1 + w2), |vt|) = -vt * min(-friction * c / (|vt| * dt), 1)741// Note that I think there is an error in the paper, I added a mass term, see: https://github.com/matthias-research/pages/issues/29742// Relative velocity: vr = v1 - v2 - omega2 x r2743// Normal velocity: vn = vr . contact_normal744// Tangential velocity: vt = vr - contact_normal * vn745// Impulse: p = dv / (w1 + w2)746// Changes in particle velocities:747// v1 = v1 + p / m1748// v2 = v2 - p / m2 (no change when colliding with a static body)749// w2 = w2 - I^-1 (r2 x p) (no change when colliding with a static body)750if (cs.mMotionType == EMotionType::Dynamic)751{752// Calculate normal and tangential velocity (equation 30)753Vec3 r2 = v.mPosition - cs.mCenterOfMassTransform.GetTranslation();754Vec3 v2 = cs.GetPointVelocity(r2);755Vec3 relative_velocity = v.mVelocity - v2;756Vec3 v_normal = contact_normal * contact_normal.Dot(relative_velocity);757Vec3 v_tangential = relative_velocity - v_normal;758float v_tangential_length = v_tangential.Length();759760// Calculate resulting inverse mass of vertex761float vertex_inv_mass = cs.mSoftBodyInvMassScale * v.mInvMass;762763// Calculate inverse effective mass764Vec3 r2_cross_n = r2.Cross(contact_normal);765float w2 = cs.mInvMass + r2_cross_n.Dot(cs.mInvInertia * r2_cross_n);766float w1_plus_w2 = vertex_inv_mass + w2;767if (w1_plus_w2 > 0.0f)768{769// Calculate delta relative velocity due to friction (modified equation 31)770Vec3 dv;771if (v_tangential_length > 0.0f)772dv = v_tangential * min(cs.mFriction * projected_distance / (v_tangential_length * dt), 1.0f);773else774dv = Vec3::sZero();775776// Calculate delta relative velocity due to restitution (equation 35)777dv += v_normal;778float prev_v_normal = (prev_v - v2).Dot(contact_normal);779if (prev_v_normal < restitution_threshold)780dv += cs.mRestitution * prev_v_normal * contact_normal;781782// Calculate impulse783Vec3 p = dv / w1_plus_w2;784785// Apply impulse to particle786v.mVelocity -= p * vertex_inv_mass;787788// Apply impulse to rigid body789cs.mLinearVelocity += p * cs.mInvMass;790cs.mAngularVelocity += cs.mInvInertia * r2.Cross(p);791792// Mark that the velocities of the body we hit need to be updated793cs.mUpdateVelocities = true;794}795}796else if (cs.mSoftBodyInvMassScale > 0.0f)797{798// Body is not movable, equations are simpler799800// Calculate normal and tangential velocity (equation 30)801Vec3 v_normal = contact_normal * contact_normal.Dot(v.mVelocity);802Vec3 v_tangential = v.mVelocity - v_normal;803float v_tangential_length = v_tangential.Length();804805// Apply friction (modified equation 31)806if (v_tangential_length > 0.0f)807v.mVelocity -= v_tangential * min(cs.mFriction * projected_distance / (v_tangential_length * dt), 1.0f);808809// Apply restitution (equation 35)810v.mVelocity -= v_normal;811float prev_v_normal = prev_v.Dot(contact_normal);812if (prev_v_normal < restitution_threshold)813v.mVelocity -= cs.mRestitution * prev_v_normal * contact_normal;814}815}816}817}818819// Calculate the new angular velocity for all rods820float two_div_dt = 2.0f / dt;821for (RodState &r : mRodStates)822r.mAngularVelocity = two_div_dt * (r.mRotation * r.mPreviousRotationInternal.Conjugated()).GetXYZ(); // Overwrites mPreviousRotationInternal823}824825void SoftBodyMotionProperties::UpdateSoftBodyState(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings)826{827JPH_PROFILE_FUNCTION();828829// Contact callback830if (mNeedContactCallback.load(memory_order_relaxed) && ioContext.mContactListener != nullptr)831{832// Remove non-colliding sensors from the list833for (int i = int(mCollidingSensors.size()) - 1; i >= 0; --i)834if (!mCollidingSensors[i].mHasContact)835{836mCollidingSensors[i] = std::move(mCollidingSensors.back());837mCollidingSensors.pop_back();838}839840ioContext.mContactListener->OnSoftBodyContactAdded(*ioContext.mBody, SoftBodyManifold(this));841}842843// Loop through vertices once more to update the global state844float dt = ioContext.mDeltaTime;845float max_linear_velocity_sq = Square(GetMaxLinearVelocity());846float max_v_sq = 0.0f;847Vec3 linear_velocity = Vec3::sZero(), angular_velocity = Vec3::sZero();848mLocalPredictedBounds = mLocalBounds = { };849for (Vertex &v : mVertices)850{851// Calculate max square velocity852float v_sq = v.mVelocity.LengthSq();853max_v_sq = max(max_v_sq, v_sq);854855// Clamp if velocity is too high856if (v_sq > max_linear_velocity_sq)857v.mVelocity *= sqrt(max_linear_velocity_sq / v_sq);858859// Calculate local linear/angular velocity860linear_velocity += v.mVelocity;861angular_velocity += v.mPosition.Cross(v.mVelocity);862863// Update local bounding box864mLocalBounds.Encapsulate(v.mPosition);865866// Create predicted position for the next frame in order to detect collisions before they happen867mLocalPredictedBounds.Encapsulate(v.mPosition + v.mVelocity * dt + ioContext.mDisplacementDueToGravity);868869// Reset collision data for the next iteration870v.ResetCollision();871}872873// Calculate linear/angular velocity of the body by averaging all vertices and bringing the value to world space874float num_vertices_divider = float(max(int(mVertices.size()), 1));875SetLinearVelocityClamped(ioContext.mCenterOfMassTransform.Multiply3x3(linear_velocity / num_vertices_divider));876SetAngularVelocity(ioContext.mCenterOfMassTransform.Multiply3x3(angular_velocity / num_vertices_divider));877878if (mUpdatePosition)879{880// Shift the body so that the position is the center of the local bounds881Vec3 delta = mLocalBounds.GetCenter();882ioContext.mDeltaPosition = ioContext.mCenterOfMassTransform.Multiply3x3(delta);883for (Vertex &v : mVertices)884v.mPosition -= delta;885886// Update the skin state too since we will use this position as the previous position in the next update887for (SkinState &s : mSkinState)888s.mPosition -= delta;889JPH_IF_DEBUG_RENDERER(mSkinStateTransform.SetTranslation(mSkinStateTransform.GetTranslation() + ioContext.mDeltaPosition);)890891// Offset bounds to match new position892mLocalBounds.Translate(-delta);893mLocalPredictedBounds.Translate(-delta);894}895else896ioContext.mDeltaPosition = Vec3::sZero();897898// Test if we should go to sleep899if (GetAllowSleeping())900{901if (max_v_sq > inPhysicsSettings.mPointVelocitySleepThreshold)902{903ResetSleepTestTimer();904ioContext.mCanSleep = ECanSleep::CannotSleep;905}906else907ioContext.mCanSleep = AccumulateSleepTime(dt, inPhysicsSettings.mTimeBeforeSleep);908}909else910ioContext.mCanSleep = ECanSleep::CannotSleep;911912// If SkinVertices is not called after this then don't use the previous position as the skin is static913mSkinStatePreviousPositionValid = false;914915// Reset force accumulator916ResetForce();917}918919void SoftBodyMotionProperties::UpdateRigidBodyVelocities(const SoftBodyUpdateContext &inContext, BodyInterface &inBodyInterface)920{921JPH_PROFILE_FUNCTION();922923// Write back velocity deltas924for (const CollidingShape &cs : mCollidingShapes)925if (cs.mUpdateVelocities)926inBodyInterface.AddLinearAndAngularVelocity(cs.mBodyID, inContext.mCenterOfMassTransform.Multiply3x3(cs.mLinearVelocity - cs.mOriginalLinearVelocity), inContext.mCenterOfMassTransform.Multiply3x3(cs.mAngularVelocity - cs.mOriginalAngularVelocity));927928// Clear colliding shapes/sensors to avoid hanging on to references to shapes929mCollidingShapes.clear();930mCollidingSensors.clear();931}932933void SoftBodyMotionProperties::InitializeUpdateContext(float inDeltaTime, Body &inSoftBody, const PhysicsSystem &inSystem, SoftBodyUpdateContext &ioContext)934{935JPH_PROFILE_FUNCTION();936937// Store body938ioContext.mBody = &inSoftBody;939ioContext.mMotionProperties = this;940ioContext.mContactListener = inSystem.GetSoftBodyContactListener();941ioContext.mSimShapeFilter = inSystem.GetSimShapeFilter();942943// Convert gravity to local space944ioContext.mCenterOfMassTransform = inSoftBody.GetCenterOfMassTransform();945ioContext.mGravity = ioContext.mCenterOfMassTransform.Multiply3x3Transposed(GetGravityFactor() * inSystem.GetGravity());946947// Calculate delta time for sub step948ioContext.mDeltaTime = inDeltaTime;949ioContext.mSubStepDeltaTime = inDeltaTime / mNumIterations;950951// Calculate total displacement we'll have due to gravity over all sub steps952// The total displacement as produced by our integrator can be written as: Sum(i * g * dt^2, i = 0..mNumIterations).953// This is bigger than 0.5 * g * dt^2 because we first increment the velocity and then update the position954// Using Sum(i, i = 0..n) = n * (n + 1) / 2 we can write this as:955ioContext.mDisplacementDueToGravity = (0.5f * mNumIterations * (mNumIterations + 1) * Square(ioContext.mSubStepDeltaTime)) * ioContext.mGravity;956}957958void SoftBodyMotionProperties::StartNextIteration(const SoftBodyUpdateContext &ioContext)959{960ApplyPressure(ioContext);961962IntegratePositions(ioContext);963}964965void SoftBodyMotionProperties::StartFirstIteration(SoftBodyUpdateContext &ioContext)966{967// Start the first iteration968JPH_IF_ENABLE_ASSERTS(uint iteration =) ioContext.mNextIteration.fetch_add(1, memory_order_relaxed);969JPH_ASSERT(iteration == 0);970StartNextIteration(ioContext);971ioContext.mState.store(SoftBodyUpdateContext::EState::ApplyConstraints, memory_order_release);972}973974SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelDetermineCollisionPlanes(SoftBodyUpdateContext &ioContext)975{976// Do a relaxed read first to see if there is any work to do (this prevents us from doing expensive atomic operations and also prevents us from continuously incrementing the counter and overflowing it)977uint num_vertices = (uint)mVertices.size();978if (ioContext.mNextCollisionVertex.load(memory_order_relaxed) < num_vertices)979{980// Fetch next batch of vertices to process981uint next_vertex = ioContext.mNextCollisionVertex.fetch_add(SoftBodyUpdateContext::cVertexCollisionBatch, memory_order_acquire);982if (next_vertex < num_vertices)983{984// Process collision planes985uint num_vertices_to_process = min(SoftBodyUpdateContext::cVertexCollisionBatch, num_vertices - next_vertex);986DetermineCollisionPlanes(next_vertex, num_vertices_to_process);987uint vertices_processed = ioContext.mNumCollisionVerticesProcessed.fetch_add(SoftBodyUpdateContext::cVertexCollisionBatch, memory_order_acq_rel) + num_vertices_to_process;988if (vertices_processed >= num_vertices)989{990// Determine next state991if (mCollidingSensors.empty())992StartFirstIteration(ioContext);993else994ioContext.mState.store(SoftBodyUpdateContext::EState::DetermineSensorCollisions, memory_order_release);995}996return EStatus::DidWork;997}998}9991000return EStatus::NoWork;1001}10021003SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelDetermineSensorCollisions(SoftBodyUpdateContext &ioContext)1004{1005// Do a relaxed read to see if there are more sensors to process1006if (ioContext.mNextSensorIndex.load(memory_order_relaxed) < mNumSensors)1007{1008// Fetch next sensor to process1009uint sensor_index = ioContext.mNextSensorIndex.fetch_add(1, memory_order_acquire);1010if (sensor_index < mNumSensors)1011{1012// Process this sensor1013DetermineSensorCollisions(mCollidingSensors[sensor_index]);10141015// Determine next state1016uint sensors_processed = ioContext.mNumSensorsProcessed.fetch_add(1, memory_order_acq_rel) + 1;1017if (sensors_processed >= mNumSensors)1018StartFirstIteration(ioContext);1019return EStatus::DidWork;1020}1021}10221023return EStatus::NoWork;1024}10251026void SoftBodyMotionProperties::ProcessGroup(const SoftBodyUpdateContext &ioContext, uint inGroupIndex)1027{1028// Determine start and end1029SoftBodySharedSettings::UpdateGroup start { 0, 0, 0, 0, 0, 0, 0 };1030const SoftBodySharedSettings::UpdateGroup &prev = inGroupIndex > 0? mSettings->mUpdateGroups[inGroupIndex - 1] : start;1031const SoftBodySharedSettings::UpdateGroup ¤t = mSettings->mUpdateGroups[inGroupIndex];10321033// Process volume constraints1034ApplyVolumeConstraints(ioContext, prev.mVolumeEndIndex, current.mVolumeEndIndex);10351036// Process bend constraints1037ApplyDihedralBendConstraints(ioContext, prev.mDihedralBendEndIndex, current.mDihedralBendEndIndex);10381039// Process skinned constraints1040ApplySkinConstraints(ioContext, prev.mSkinnedEndIndex, current.mSkinnedEndIndex);10411042// Process edges1043ApplyEdgeConstraints(ioContext, prev.mEdgeEndIndex, current.mEdgeEndIndex);10441045// Process rods1046ApplyRodStretchShearConstraints(ioContext, prev.mRodStretchShearEndIndex, current.mRodStretchShearEndIndex);1047ApplyRodBendTwistConstraints(ioContext, prev.mRodBendTwistEndIndex, current.mRodBendTwistEndIndex);10481049// Process LRA constraints1050ApplyLRAConstraints(prev.mLRAEndIndex, current.mLRAEndIndex);1051}10521053SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelApplyConstraints(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings)1054{1055uint num_groups = (uint)mSettings->mUpdateGroups.size();1056JPH_ASSERT(num_groups > 0, "SoftBodySharedSettings::Optimize should have been called!");1057--num_groups; // Last group is the non-parallel group, we don't want to execute it in parallel10581059// Do a relaxed read first to see if there is any work to do (this prevents us from doing expensive atomic operations and also prevents us from continuously incrementing the counter and overflowing it)1060uint next_group = ioContext.mNextConstraintGroup.load(memory_order_relaxed);1061if (next_group < num_groups || (num_groups == 0 && next_group == 0))1062{1063// Fetch the next group process1064next_group = ioContext.mNextConstraintGroup.fetch_add(1, memory_order_acquire);1065if (next_group < num_groups || (num_groups == 0 && next_group == 0))1066{1067uint num_groups_processed = 0;1068if (num_groups > 0)1069{1070// Process this group1071ProcessGroup(ioContext, next_group);10721073// Increment total number of groups processed1074num_groups_processed = ioContext.mNumConstraintGroupsProcessed.fetch_add(1, memory_order_acq_rel) + 1;1075}10761077if (num_groups_processed >= num_groups)1078{1079// Finish the iteration1080JPH_PROFILE("FinishIteration");10811082// Process non-parallel group1083ProcessGroup(ioContext, num_groups);10841085ApplyCollisionConstraintsAndUpdateVelocities(ioContext);10861087uint iteration = ioContext.mNextIteration.fetch_add(1, memory_order_relaxed);1088if (iteration < mNumIterations)1089{1090// Start a new iteration1091StartNextIteration(ioContext);10921093// Reset group logic1094ioContext.mNumConstraintGroupsProcessed.store(0, memory_order_release);1095ioContext.mNextConstraintGroup.store(0, memory_order_release);1096}1097else1098{1099// On final iteration we update the state1100UpdateSoftBodyState(ioContext, inPhysicsSettings);11011102ioContext.mState.store(SoftBodyUpdateContext::EState::Done, memory_order_release);1103return EStatus::Done;1104}1105}11061107return EStatus::DidWork;1108}1109}1110return EStatus::NoWork;1111}11121113SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelUpdate(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings)1114{1115switch (ioContext.mState.load(memory_order_acquire))1116{1117case SoftBodyUpdateContext::EState::DetermineCollisionPlanes:1118return ParallelDetermineCollisionPlanes(ioContext);11191120case SoftBodyUpdateContext::EState::DetermineSensorCollisions:1121return ParallelDetermineSensorCollisions(ioContext);11221123case SoftBodyUpdateContext::EState::ApplyConstraints:1124return ParallelApplyConstraints(ioContext, inPhysicsSettings);11251126case SoftBodyUpdateContext::EState::Done:1127return EStatus::Done;11281129default:1130JPH_ASSERT(false);1131return EStatus::NoWork;1132}1133}11341135void SoftBodyMotionProperties::SkinVertices([[maybe_unused]] RMat44Arg inCenterOfMassTransform, const Mat44 *inJointMatrices, [[maybe_unused]] uint inNumJoints, bool inHardSkinAll, TempAllocator &ioTempAllocator)1136{1137// Calculate the skin matrices1138uint num_skin_matrices = uint(mSettings->mInvBindMatrices.size());1139uint skin_matrices_size = num_skin_matrices * sizeof(Mat44);1140Mat44 *skin_matrices = (Mat44 *)ioTempAllocator.Allocate(skin_matrices_size);1141JPH_SCOPE_EXIT([&ioTempAllocator, skin_matrices, skin_matrices_size]{ ioTempAllocator.Free(skin_matrices, skin_matrices_size); });1142const Mat44 *skin_matrices_end = skin_matrices + num_skin_matrices;1143const InvBind *inv_bind_matrix = mSettings->mInvBindMatrices.data();1144for (Mat44 *s = skin_matrices; s < skin_matrices_end; ++s, ++inv_bind_matrix)1145{1146JPH_ASSERT(inv_bind_matrix->mJointIndex < inNumJoints);1147*s = inJointMatrices[inv_bind_matrix->mJointIndex] * inv_bind_matrix->mInvBind;1148}11491150// Skin the vertices1151JPH_IF_DEBUG_RENDERER(mSkinStateTransform = inCenterOfMassTransform;)1152JPH_IF_ENABLE_ASSERTS(uint num_vertices = uint(mSettings->mVertices.size());)1153JPH_ASSERT(mSkinState.size() == num_vertices);1154const SoftBodySharedSettings::Vertex *in_vertices = mSettings->mVertices.data();1155for (const Skinned &s : mSettings->mSkinnedConstraints)1156{1157// Get bind pose1158JPH_ASSERT(s.mVertex < num_vertices);1159Vec3 bind_pos = Vec3::sLoadFloat3Unsafe(in_vertices[s.mVertex].mPosition);11601161// Skin vertex1162Vec3 pos = Vec3::sZero();1163for (const SkinWeight &w : s.mWeights)1164{1165// We assume that the first zero weight is the end of the list1166if (w.mWeight == 0.0f)1167break;11681169JPH_ASSERT(w.mInvBindIndex < num_skin_matrices);1170pos += w.mWeight * (skin_matrices[w.mInvBindIndex] * bind_pos);1171}1172SkinState &skin_state = mSkinState[s.mVertex];1173skin_state.mPreviousPosition = skin_state.mPosition;1174skin_state.mPosition = pos;1175}11761177// Calculate the normals1178for (const Skinned &s : mSettings->mSkinnedConstraints)1179{1180Vec3 normal = Vec3::sZero();1181uint32 num_faces = s.mNormalInfo >> 24;1182if (num_faces > 0)1183{1184// Calculate normal1185const uint32 *f = &mSettings->mSkinnedConstraintNormals[s.mNormalInfo & 0xffffff];1186const uint32 *f_end = f + num_faces;1187while (f < f_end)1188{1189const Face &face = mSettings->mFaces[*f];1190Vec3 v0 = mSkinState[face.mVertex[0]].mPosition;1191Vec3 v1 = mSkinState[face.mVertex[1]].mPosition;1192Vec3 v2 = mSkinState[face.mVertex[2]].mPosition;1193normal += (v1 - v0).Cross(v2 - v0).NormalizedOr(Vec3::sZero());1194++f;1195}1196normal = normal.NormalizedOr(Vec3::sZero());1197}1198mSkinState[s.mVertex].mNormal = normal;1199}12001201if (inHardSkinAll)1202{1203// Hard skin all vertices and reset their velocities1204for (const Skinned &s : mSettings->mSkinnedConstraints)1205{1206Vertex &vertex = mVertices[s.mVertex];1207SkinState &skin_state = mSkinState[s.mVertex];1208skin_state.mPreviousPosition = skin_state.mPosition;1209vertex.mPosition = skin_state.mPosition;1210vertex.mVelocity = Vec3::sZero();1211}1212}1213else if (!mEnableSkinConstraints)1214{1215// Hard skin only the kinematic vertices as we will not solve the skin constraints later1216for (const Skinned &s : mSettings->mSkinnedConstraints)1217if (s.mMaxDistance == 0.0f)1218{1219Vertex &vertex = mVertices[s.mVertex];1220vertex.mPosition = mSkinState[s.mVertex].mPosition;1221}1222}12231224// Indicate that the previous positions are valid for the coming update1225mSkinStatePreviousPositionValid = true;1226}12271228void SoftBodyMotionProperties::CustomUpdate(float inDeltaTime, Body &ioSoftBody, PhysicsSystem &inSystem)1229{1230JPH_PROFILE_FUNCTION();12311232// Create update context1233SoftBodyUpdateContext context;1234InitializeUpdateContext(inDeltaTime, ioSoftBody, inSystem, context);12351236// Determine bodies we're colliding with1237DetermineCollidingShapes(context, inSystem, inSystem.GetBodyLockInterface());12381239// Call the internal update until it finishes1240EStatus status;1241const PhysicsSettings &settings = inSystem.GetPhysicsSettings();1242while ((status = ParallelUpdate(context, settings)) == EStatus::DidWork)1243continue;1244JPH_ASSERT(status == EStatus::Done);12451246// Update the state of the bodies we've collided with1247UpdateRigidBodyVelocities(context, inSystem.GetBodyInterface());12481249// Update position of the soft body1250if (mUpdatePosition)1251inSystem.GetBodyInterface().SetPosition(ioSoftBody.GetID(), ioSoftBody.GetPosition() + context.mDeltaPosition, EActivation::DontActivate);1252}12531254#ifdef JPH_DEBUG_RENDERER12551256void SoftBodyMotionProperties::DrawVertices(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const1257{1258for (const Vertex &v : mVertices)1259inRenderer->DrawMarker(inCenterOfMassTransform * v.mPosition, v.mInvMass > 0.0f? Color::sGreen : Color::sRed, 0.05f);1260}12611262void SoftBodyMotionProperties::DrawVertexVelocities(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const1263{1264for (const Vertex &v : mVertices)1265inRenderer->DrawArrow(inCenterOfMassTransform * v.mPosition, inCenterOfMassTransform * (v.mPosition + v.mVelocity), Color::sYellow, 0.01f);1266}12671268template <typename GetEndIndex, typename DrawConstraint>1269inline void SoftBodyMotionProperties::DrawConstraints(ESoftBodyConstraintColor inConstraintColor, const GetEndIndex &inGetEndIndex, const DrawConstraint &inDrawConstraint, ColorArg inBaseColor) const1270{1271uint start = 0;1272for (uint i = 0; i < (uint)mSettings->mUpdateGroups.size(); ++i)1273{1274uint end = inGetEndIndex(mSettings->mUpdateGroups[i]);12751276Color base_color;1277if (inConstraintColor != ESoftBodyConstraintColor::ConstraintType)1278base_color = Color::sGetDistinctColor((uint)mSettings->mUpdateGroups.size() - i - 1); // Ensure that color 0 is always the last group1279else1280base_color = inBaseColor;12811282for (uint idx = start; idx < end; ++idx)1283{1284Color color = inConstraintColor == ESoftBodyConstraintColor::ConstraintOrder? base_color * (float(idx - start) / (end - start)) : base_color;1285inDrawConstraint(idx, color);1286}12871288start = end;1289}1290}12911292void SoftBodyMotionProperties::DrawEdgeConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const1293{1294DrawConstraints(inConstraintColor,1295[](const SoftBodySharedSettings::UpdateGroup &inGroup) {1296return inGroup.mEdgeEndIndex;1297},1298[this, inRenderer, &inCenterOfMassTransform](uint inIndex, ColorArg inColor) {1299const Edge &e = mSettings->mEdgeConstraints[inIndex];1300inRenderer->DrawLine(inCenterOfMassTransform * mVertices[e.mVertex[0]].mPosition, inCenterOfMassTransform * mVertices[e.mVertex[1]].mPosition, inColor);1301},1302Color::sWhite);1303}13041305void SoftBodyMotionProperties::DrawRods(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const1306{1307DrawConstraints(inConstraintColor,1308[](const SoftBodySharedSettings::UpdateGroup &inGroup) {1309return inGroup.mRodStretchShearEndIndex;1310},1311[this, inRenderer, &inCenterOfMassTransform](uint inIndex, ColorArg inColor) {1312const RodStretchShear &r = mSettings->mRodStretchShearConstraints[inIndex];1313inRenderer->DrawLine(inCenterOfMassTransform * mVertices[r.mVertex[0]].mPosition, inCenterOfMassTransform * mVertices[r.mVertex[1]].mPosition, inColor);1314},1315Color::sWhite);1316}13171318void SoftBodyMotionProperties::DrawRodStates(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const1319{1320DrawConstraints(inConstraintColor,1321[](const SoftBodySharedSettings::UpdateGroup &inGroup) {1322return inGroup.mRodStretchShearEndIndex;1323},1324[this, inRenderer, &inCenterOfMassTransform](uint inIndex, ColorArg inColor) {1325const RodState &state = mRodStates[inIndex];1326const RodStretchShear &rod = mSettings->mRodStretchShearConstraints[inIndex];13271328RVec3 x0 = inCenterOfMassTransform * mVertices[rod.mVertex[0]].mPosition;1329RVec3 x1 = inCenterOfMassTransform * mVertices[rod.mVertex[1]].mPosition;13301331RMat44 rod_center = inCenterOfMassTransform;1332rod_center.SetTranslation(0.5_r * (x0 + x1));1333inRenderer->DrawArrow(rod_center.GetTranslation(), rod_center.GetTranslation() + state.mAngularVelocity, inColor, 0.01f * rod.mLength);13341335RMat44 rod_frame = rod_center * RMat44::sRotation(state.mRotation);1336inRenderer->DrawCoordinateSystem(rod_frame, 0.3f * rod.mLength);1337},1338Color::sOrange);1339}13401341void SoftBodyMotionProperties::DrawRodBendTwistConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const1342{1343DrawConstraints(inConstraintColor,1344[](const SoftBodySharedSettings::UpdateGroup &inGroup) {1345return inGroup.mRodBendTwistEndIndex;1346},1347[this, inRenderer, &inCenterOfMassTransform](uint inIndex, ColorArg inColor) {1348uint r1 = mSettings->mRodBendTwistConstraints[inIndex].mRod[0];1349uint r2 = mSettings->mRodBendTwistConstraints[inIndex].mRod[1];1350const RodStretchShear &rod1 = mSettings->mRodStretchShearConstraints[r1];1351const RodStretchShear &rod2 = mSettings->mRodStretchShearConstraints[r2];13521353RVec3 x0 = inCenterOfMassTransform * (0.4f * mVertices[rod1.mVertex[0]].mPosition + 0.6f * mVertices[rod1.mVertex[1]].mPosition);1354RVec3 x1 = inCenterOfMassTransform * (0.6f * mVertices[rod2.mVertex[0]].mPosition + 0.4f * mVertices[rod2.mVertex[1]].mPosition);13551356inRenderer->DrawLine(x0, x1, inColor);1357},1358Color::sGreen);1359}13601361void SoftBodyMotionProperties::DrawBendConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const1362{1363DrawConstraints(inConstraintColor,1364[](const SoftBodySharedSettings::UpdateGroup &inGroup) {1365return inGroup.mDihedralBendEndIndex;1366},1367[this, inRenderer, &inCenterOfMassTransform](uint inIndex, ColorArg inColor) {1368const DihedralBend &b = mSettings->mDihedralBendConstraints[inIndex];13691370RVec3 x0 = inCenterOfMassTransform * mVertices[b.mVertex[0]].mPosition;1371RVec3 x1 = inCenterOfMassTransform * mVertices[b.mVertex[1]].mPosition;1372RVec3 x2 = inCenterOfMassTransform * mVertices[b.mVertex[2]].mPosition;1373RVec3 x3 = inCenterOfMassTransform * mVertices[b.mVertex[3]].mPosition;1374RVec3 c_edge = 0.5_r * (x0 + x1);1375RVec3 c0 = (x0 + x1 + x2) / 3.0_r;1376RVec3 c1 = (x0 + x1 + x3) / 3.0_r;13771378inRenderer->DrawArrow(0.9_r * x0 + 0.1_r * x1, 0.1_r * x0 + 0.9_r * x1, inColor, 0.01f);1379inRenderer->DrawLine(c_edge, 0.1_r * c_edge + 0.9_r * c0, inColor);1380inRenderer->DrawLine(c_edge, 0.1_r * c_edge + 0.9_r * c1, inColor);1381},1382Color::sGreen);1383}13841385void SoftBodyMotionProperties::DrawVolumeConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const1386{1387DrawConstraints(inConstraintColor,1388[](const SoftBodySharedSettings::UpdateGroup &inGroup) {1389return inGroup.mVolumeEndIndex;1390},1391[this, inRenderer, &inCenterOfMassTransform](uint inIndex, ColorArg inColor) {1392const Volume &v = mSettings->mVolumeConstraints[inIndex];13931394RVec3 x1 = inCenterOfMassTransform * mVertices[v.mVertex[0]].mPosition;1395RVec3 x2 = inCenterOfMassTransform * mVertices[v.mVertex[1]].mPosition;1396RVec3 x3 = inCenterOfMassTransform * mVertices[v.mVertex[2]].mPosition;1397RVec3 x4 = inCenterOfMassTransform * mVertices[v.mVertex[3]].mPosition;13981399inRenderer->DrawTriangle(x1, x3, x2, inColor, DebugRenderer::ECastShadow::On);1400inRenderer->DrawTriangle(x2, x3, x4, inColor, DebugRenderer::ECastShadow::On);1401inRenderer->DrawTriangle(x1, x4, x3, inColor, DebugRenderer::ECastShadow::On);1402inRenderer->DrawTriangle(x1, x2, x4, inColor, DebugRenderer::ECastShadow::On);1403},1404Color::sYellow);1405}14061407void SoftBodyMotionProperties::DrawSkinConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const1408{1409DrawConstraints(inConstraintColor,1410[](const SoftBodySharedSettings::UpdateGroup &inGroup) {1411return inGroup.mSkinnedEndIndex;1412},1413[this, inRenderer, &inCenterOfMassTransform](uint inIndex, ColorArg inColor) {1414const Skinned &s = mSettings->mSkinnedConstraints[inIndex];1415const SkinState &skin_state = mSkinState[s.mVertex];1416inRenderer->DrawArrow(mSkinStateTransform * skin_state.mPosition, mSkinStateTransform * (skin_state.mPosition + 0.1f * skin_state.mNormal), inColor, 0.01f);1417inRenderer->DrawLine(mSkinStateTransform * skin_state.mPosition, inCenterOfMassTransform * mVertices[s.mVertex].mPosition, Color::sBlue);1418},1419Color::sOrange);1420}14211422void SoftBodyMotionProperties::DrawLRAConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const1423{1424DrawConstraints(inConstraintColor,1425[](const SoftBodySharedSettings::UpdateGroup &inGroup) {1426return inGroup.mLRAEndIndex;1427},1428[this, inRenderer, &inCenterOfMassTransform](uint inIndex, ColorArg inColor) {1429const LRA &l = mSettings->mLRAConstraints[inIndex];1430inRenderer->DrawLine(inCenterOfMassTransform * mVertices[l.mVertex[0]].mPosition, inCenterOfMassTransform * mVertices[l.mVertex[1]].mPosition, inColor);1431},1432Color::sGrey);1433}14341435void SoftBodyMotionProperties::DrawPredictedBounds(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const1436{1437inRenderer->DrawWireBox(inCenterOfMassTransform, mLocalPredictedBounds, Color::sRed);1438}14391440#endif // JPH_DEBUG_RENDERER14411442void SoftBodyMotionProperties::SaveState(StateRecorder &inStream) const1443{1444MotionProperties::SaveState(inStream);14451446for (const Vertex &v : mVertices)1447{1448inStream.Write(v.mPosition);1449inStream.Write(v.mVelocity);1450}14511452for (const RodState &r : mRodStates)1453{1454inStream.Write(r.mRotation);1455inStream.Write(r.mAngularVelocity);1456}14571458for (const SkinState &s : mSkinState)1459{1460inStream.Write(s.mPreviousPosition);1461inStream.Write(s.mPosition);1462inStream.Write(s.mNormal);1463}14641465inStream.Write(mLocalBounds.mMin);1466inStream.Write(mLocalBounds.mMax);1467inStream.Write(mLocalPredictedBounds.mMin);1468inStream.Write(mLocalPredictedBounds.mMax);1469}14701471void SoftBodyMotionProperties::RestoreState(StateRecorder &inStream)1472{1473MotionProperties::RestoreState(inStream);14741475for (Vertex &v : mVertices)1476{1477inStream.Read(v.mPosition);1478inStream.Read(v.mVelocity);1479}14801481for (RodState &r : mRodStates)1482{1483inStream.Read(r.mRotation);1484inStream.Read(r.mAngularVelocity);1485}14861487for (SkinState &s : mSkinState)1488{1489inStream.Read(s.mPreviousPosition);1490inStream.Read(s.mPosition);1491inStream.Read(s.mNormal);1492}14931494inStream.Read(mLocalBounds.mMin);1495inStream.Read(mLocalBounds.mMax);1496inStream.Read(mLocalPredictedBounds.mMin);1497inStream.Read(mLocalPredictedBounds.mMax);1498}14991500JPH_NAMESPACE_END150115021503