SoftBodyMotionProperties.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2023 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <Jolt/Jolt.h>
  5. #include <Jolt/Physics/SoftBody/SoftBodyMotionProperties.h>
  6. #include <Jolt/Physics/SoftBody/SoftBodyCreationSettings.h>
  7. #include <Jolt/Physics/SoftBody/SoftBodyContactListener.h>
  8. #include <Jolt/Physics/SoftBody/SoftBodyManifold.h>
  9. #include <Jolt/Physics/PhysicsSystem.h>
  10. #ifdef JPH_DEBUG_RENDERER
  11. #include <Jolt/Renderer/DebugRenderer.h>
  12. #endif // JPH_DEBUG_RENDERER
  13. JPH_NAMESPACE_BEGIN
  14. void SoftBodyMotionProperties::CalculateMassAndInertia()
  15. {
  16. MassProperties mp;
  17. for (const Vertex &v : mVertices)
  18. if (v.mInvMass > 0.0f)
  19. {
  20. Vec3 pos = v.mPosition;
  21. // Accumulate mass
  22. float mass = 1.0f / v.mInvMass;
  23. mp.mMass += mass;
  24. // Inertia tensor, diagonal
  25. // See equations https://en.wikipedia.org/wiki/Moment_of_inertia section 'Inertia Tensor'
  26. for (int i = 0; i < 3; ++i)
  27. mp.mInertia(i, i) += mass * (Square(pos[(i + 1) % 3]) + Square(pos[(i + 2) % 3]));
  28. // Inertia tensor off diagonal
  29. for (int i = 0; i < 3; ++i)
  30. for (int j = 0; j < 3; ++j)
  31. if (i != j)
  32. mp.mInertia(i, j) -= mass * pos[i] * pos[j];
  33. }
  34. else
  35. {
  36. // If one vertex is kinematic, the entire body will have infinite mass and inertia
  37. SetInverseMass(0.0f);
  38. SetInverseInertia(Vec3::sZero(), Quat::sIdentity());
  39. return;
  40. }
  41. SetMassProperties(EAllowedDOFs::All, mp);
  42. }
  43. void SoftBodyMotionProperties::Initialize(const SoftBodyCreationSettings &inSettings)
  44. {
  45. // Store settings
  46. mSettings = inSettings.mSettings;
  47. mNumIterations = inSettings.mNumIterations;
  48. mPressure = inSettings.mPressure;
  49. mUpdatePosition = inSettings.mUpdatePosition;
  50. // Initialize vertices
  51. mVertices.resize(inSettings.mSettings->mVertices.size());
  52. Mat44 rotation = inSettings.mMakeRotationIdentity? Mat44::sRotation(inSettings.mRotation) : Mat44::sIdentity();
  53. for (Array<Vertex>::size_type v = 0, s = mVertices.size(); v < s; ++v)
  54. {
  55. const SoftBodySharedSettings::Vertex &in_vertex = inSettings.mSettings->mVertices[v];
  56. Vertex &out_vertex = mVertices[v];
  57. out_vertex.mPreviousPosition = out_vertex.mPosition = rotation * Vec3(in_vertex.mPosition);
  58. out_vertex.mVelocity = rotation.Multiply3x3(Vec3(in_vertex.mVelocity));
  59. out_vertex.mCollidingShapeIndex = -1;
  60. out_vertex.mHasContact = false;
  61. out_vertex.mLargestPenetration = -FLT_MAX;
  62. out_vertex.mInvMass = in_vertex.mInvMass;
  63. mLocalBounds.Encapsulate(out_vertex.mPosition);
  64. }
  65. // Allocate space for skinned vertices
  66. if (!inSettings.mSettings->mSkinnedConstraints.empty())
  67. mSkinState.resize(mVertices.size());
  68. // We don't know delta time yet, so we can't predict the bounds and use the local bounds as the predicted bounds
  69. mLocalPredictedBounds = mLocalBounds;
  70. CalculateMassAndInertia();
  71. }
  72. float SoftBodyMotionProperties::GetVolumeTimesSix() const
  73. {
  74. float six_volume = 0.0f;
  75. for (const Face &f : mSettings->mFaces)
  76. {
  77. Vec3 x1 = mVertices[f.mVertex[0]].mPosition;
  78. Vec3 x2 = mVertices[f.mVertex[1]].mPosition;
  79. Vec3 x3 = mVertices[f.mVertex[2]].mPosition;
  80. six_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 accuracy
  81. }
  82. return six_volume;
  83. }
  84. void SoftBodyMotionProperties::DetermineCollidingShapes(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem, const BodyLockInterface &inBodyLockInterface)
  85. {
  86. JPH_PROFILE_FUNCTION();
  87. struct Collector : public CollideShapeBodyCollector
  88. {
  89. Collector(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem, const BodyLockInterface &inBodyLockInterface, Array<CollidingShape> &ioHits) :
  90. mContext(inContext),
  91. mInverseTransform(inContext.mCenterOfMassTransform.InversedRotationTranslation()),
  92. mBodyLockInterface(inBodyLockInterface),
  93. mCombineFriction(inSystem.GetCombineFriction()),
  94. mCombineRestitution(inSystem.GetCombineRestitution()),
  95. mHits(ioHits)
  96. {
  97. }
  98. virtual void AddHit(const BodyID &inResult) override
  99. {
  100. BodyLockRead lock(mBodyLockInterface, inResult);
  101. if (lock.Succeeded())
  102. {
  103. const Body &soft_body = *mContext.mBody;
  104. const Body &body = lock.GetBody();
  105. if (body.IsRigidBody() // TODO: We should support soft body vs soft body
  106. && soft_body.GetCollisionGroup().CanCollide(body.GetCollisionGroup()))
  107. {
  108. // Call the contact listener to see if we should accept this contact
  109. // If there is no contact listener then we can ignore the contact if the other body is a sensor
  110. SoftBodyContactSettings settings;
  111. settings.mIsSensor = body.IsSensor();
  112. if (mContext.mContactListener == nullptr? !settings.mIsSensor : mContext.mContactListener->OnSoftBodyContactValidate(soft_body, body, settings) == SoftBodyValidateResult::AcceptContact)
  113. {
  114. CollidingShape cs;
  115. cs.mCenterOfMassTransform = (mInverseTransform * body.GetCenterOfMassTransform()).ToMat44();
  116. cs.mShape = body.GetShape();
  117. cs.mBodyID = inResult;
  118. cs.mMotionType = body.GetMotionType();
  119. cs.mIsSensor = settings.mIsSensor;
  120. cs.mUpdateVelocities = false;
  121. cs.mFriction = mCombineFriction(soft_body, SubShapeID(), body, SubShapeID());
  122. cs.mRestitution = mCombineRestitution(soft_body, SubShapeID(), body, SubShapeID());
  123. if (cs.mMotionType == EMotionType::Dynamic)
  124. {
  125. const MotionProperties *mp = body.GetMotionProperties();
  126. cs.mInvMass = settings.mInvMassScale2 * mp->GetInverseMass();
  127. cs.mInvInertia = settings.mInvInertiaScale2 * mp->GetInverseInertiaForRotation(cs.mCenterOfMassTransform.GetRotation());
  128. cs.mSoftBodyInvMassScale = settings.mInvMassScale1;
  129. cs.mOriginalLinearVelocity = cs.mLinearVelocity = mInverseTransform.Multiply3x3(mp->GetLinearVelocity());
  130. cs.mOriginalAngularVelocity = cs.mAngularVelocity = mInverseTransform.Multiply3x3(mp->GetAngularVelocity());
  131. }
  132. mHits.push_back(cs);
  133. }
  134. }
  135. }
  136. }
  137. private:
  138. const SoftBodyUpdateContext &mContext;
  139. RMat44 mInverseTransform;
  140. const BodyLockInterface & mBodyLockInterface;
  141. ContactConstraintManager::CombineFunction mCombineFriction;
  142. ContactConstraintManager::CombineFunction mCombineRestitution;
  143. Array<CollidingShape> & mHits;
  144. };
  145. Collector collector(inContext, inSystem, inBodyLockInterface, mCollidingShapes);
  146. AABox bounds = mLocalBounds;
  147. bounds.Encapsulate(mLocalPredictedBounds);
  148. bounds = bounds.Transformed(inContext.mCenterOfMassTransform);
  149. bounds.ExpandBy(Vec3::sReplicate(mSettings->mVertexRadius));
  150. ObjectLayer layer = inContext.mBody->GetObjectLayer();
  151. DefaultBroadPhaseLayerFilter broadphase_layer_filter = inSystem.GetDefaultBroadPhaseLayerFilter(layer);
  152. DefaultObjectLayerFilter object_layer_filter = inSystem.GetDefaultLayerFilter(layer);
  153. inSystem.GetBroadPhaseQuery().CollideAABox(bounds, collector, broadphase_layer_filter, object_layer_filter);
  154. }
  155. void SoftBodyMotionProperties::DetermineCollisionPlanes(const SoftBodyUpdateContext &inContext, uint inVertexStart, uint inNumVertices)
  156. {
  157. JPH_PROFILE_FUNCTION();
  158. // Generate collision planes
  159. for (const CollidingShape &cs : mCollidingShapes)
  160. cs.mShape->CollideSoftBodyVertices(cs.mCenterOfMassTransform, Vec3::sReplicate(1.0f), mVertices.data() + inVertexStart, inNumVertices, inContext.mDeltaTime, inContext.mDisplacementDueToGravity, int(&cs - mCollidingShapes.data()));
  161. }
  162. void SoftBodyMotionProperties::ApplyPressure(const SoftBodyUpdateContext &inContext)
  163. {
  164. JPH_PROFILE_FUNCTION();
  165. float dt = inContext.mSubStepDeltaTime;
  166. float pressure_coefficient = mPressure;
  167. if (pressure_coefficient > 0.0f)
  168. {
  169. // Calculate total volume
  170. float six_volume = GetVolumeTimesSix();
  171. if (six_volume > 0.0f)
  172. {
  173. // Apply pressure
  174. // p = F / A = n R T / V (see https://en.wikipedia.org/wiki/Pressure)
  175. // Our pressure coefficient is n R T so the impulse is:
  176. // P = F dt = pressure_coefficient / V * A * dt
  177. float coefficient = pressure_coefficient * dt / six_volume; // Need to still multiply by 6 for the volume
  178. for (const Face &f : mSettings->mFaces)
  179. {
  180. Vec3 x1 = mVertices[f.mVertex[0]].mPosition;
  181. Vec3 x2 = mVertices[f.mVertex[1]].mPosition;
  182. Vec3 x3 = mVertices[f.mVertex[2]].mPosition;
  183. Vec3 impulse = coefficient * (x2 - x1).Cross(x3 - x1); // Area is half the cross product so need to still divide by 2
  184. for (uint32 i : f.mVertex)
  185. {
  186. Vertex &v = mVertices[i];
  187. v.mVelocity += v.mInvMass * impulse; // Want to divide by 3 because we spread over 3 vertices
  188. }
  189. }
  190. }
  191. }
  192. }
  193. void SoftBodyMotionProperties::IntegratePositions(const SoftBodyUpdateContext &inContext)
  194. {
  195. JPH_PROFILE_FUNCTION();
  196. float dt = inContext.mSubStepDeltaTime;
  197. float linear_damping = max(0.0f, 1.0f - GetLinearDamping() * dt); // See: MotionProperties::ApplyForceTorqueAndDragInternal
  198. // Integrate
  199. Vec3 sub_step_gravity = inContext.mGravity * dt;
  200. for (Vertex &v : mVertices)
  201. if (v.mInvMass > 0.0f)
  202. {
  203. // Gravity
  204. v.mVelocity += sub_step_gravity;
  205. // Damping
  206. v.mVelocity *= linear_damping;
  207. // Integrate
  208. v.mPreviousPosition = v.mPosition;
  209. v.mPosition += v.mVelocity * dt;
  210. }
  211. else
  212. {
  213. // Integrate
  214. v.mPreviousPosition = v.mPosition;
  215. v.mPosition += v.mVelocity * dt;
  216. }
  217. }
  218. void SoftBodyMotionProperties::ApplyVolumeConstraints(const SoftBodyUpdateContext &inContext)
  219. {
  220. JPH_PROFILE_FUNCTION();
  221. float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime);
  222. // Satisfy volume constraints
  223. for (const Volume &v : mSettings->mVolumeConstraints)
  224. {
  225. Vertex &v1 = mVertices[v.mVertex[0]];
  226. Vertex &v2 = mVertices[v.mVertex[1]];
  227. Vertex &v3 = mVertices[v.mVertex[2]];
  228. Vertex &v4 = mVertices[v.mVertex[3]];
  229. Vec3 x1 = v1.mPosition;
  230. Vec3 x2 = v2.mPosition;
  231. Vec3 x3 = v3.mPosition;
  232. Vec3 x4 = v4.mPosition;
  233. // Calculate constraint equation
  234. Vec3 x1x2 = x2 - x1;
  235. Vec3 x1x3 = x3 - x1;
  236. Vec3 x1x4 = x4 - x1;
  237. float c = abs(x1x2.Cross(x1x3).Dot(x1x4)) - v.mSixRestVolume;
  238. // Calculate gradient of constraint equation
  239. Vec3 d1c = (x4 - x2).Cross(x3 - x2);
  240. Vec3 d2c = x1x3.Cross(x1x4);
  241. Vec3 d3c = x1x4.Cross(x1x2);
  242. Vec3 d4c = x1x2.Cross(x1x3);
  243. float w1 = v1.mInvMass;
  244. float w2 = v2.mInvMass;
  245. float w3 = v3.mInvMass;
  246. float w4 = v4.mInvMass;
  247. JPH_ASSERT(w1 > 0.0f || w2 > 0.0f || w3 > 0.0f || w4 > 0.0f);
  248. // Apply correction
  249. float lambda = -c / (w1 * d1c.LengthSq() + w2 * d2c.LengthSq() + w3 * d3c.LengthSq() + w4 * d4c.LengthSq() + v.mCompliance * inv_dt_sq);
  250. v1.mPosition += lambda * w1 * d1c;
  251. v2.mPosition += lambda * w2 * d2c;
  252. v3.mPosition += lambda * w3 * d3c;
  253. v4.mPosition += lambda * w4 * d4c;
  254. }
  255. }
  256. void SoftBodyMotionProperties::ApplySkinConstraints([[maybe_unused]] const SoftBodyUpdateContext &inContext)
  257. {
  258. // Early out if nothing to do
  259. if (mSettings->mSkinnedConstraints.empty())
  260. return;
  261. JPH_ASSERT(mSkinStateTransform == inContext.mCenterOfMassTransform, "Skinning state is stale, artifacts will show!");
  262. // Apply the constraints
  263. Vertex *vertices = mVertices.data();
  264. const SkinState *skin_states = mSkinState.data();
  265. for (const Skinned &s : mSettings->mSkinnedConstraints)
  266. {
  267. Vertex &vertex = vertices[s.mVertex];
  268. const SkinState &skin_state = skin_states[s.mVertex];
  269. if (vertex.mInvMass > 0.0f)
  270. {
  271. // Clamp vertex distance to max distance from skinned position
  272. if (s.mMaxDistance < FLT_MAX)
  273. {
  274. Vec3 delta = vertex.mPosition - skin_state.mPosition;
  275. float delta_len_sq = delta.LengthSq();
  276. float max_distance_sq = Square(s.mMaxDistance);
  277. if (delta_len_sq > max_distance_sq)
  278. vertex.mPosition = skin_state.mPosition + delta * sqrt(max_distance_sq / delta_len_sq);
  279. }
  280. // Move position if it violated the back stop
  281. if (s.mBackStop < s.mMaxDistance)
  282. {
  283. Vec3 delta = vertex.mPosition - skin_state.mPosition;
  284. float violation = -s.mBackStop - skin_state.mNormal.Dot(delta);
  285. if (violation > 0.0f)
  286. vertex.mPosition += violation * skin_state.mNormal;
  287. }
  288. }
  289. else
  290. {
  291. // Kinematic: Just update the vertex position
  292. vertex.mPosition = skin_state.mPosition;
  293. }
  294. }
  295. }
  296. void SoftBodyMotionProperties::ApplyEdgeConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex)
  297. {
  298. JPH_PROFILE_FUNCTION();
  299. float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime);
  300. // Satisfy edge constraints
  301. const Array<Edge> &edge_constraints = mSettings->mEdgeConstraints;
  302. for (uint i = inStartIndex; i < inEndIndex; ++i)
  303. {
  304. const Edge &e = edge_constraints[i];
  305. Vertex &v0 = mVertices[e.mVertex[0]];
  306. Vertex &v1 = mVertices[e.mVertex[1]];
  307. JPH_ASSERT(v0.mInvMass > 0.0f || v1.mInvMass > 0.0f);
  308. // Calculate current length
  309. Vec3 delta = v1.mPosition - v0.mPosition;
  310. float length = delta.Length();
  311. if (length > 0.0f)
  312. {
  313. // Apply correction
  314. Vec3 correction = delta * (length - e.mRestLength) / (length * (v0.mInvMass + v1.mInvMass + e.mCompliance * inv_dt_sq));
  315. v0.mPosition += v0.mInvMass * correction;
  316. v1.mPosition -= v1.mInvMass * correction;
  317. }
  318. }
  319. }
  320. void SoftBodyMotionProperties::ApplyCollisionConstraintsAndUpdateVelocities(const SoftBodyUpdateContext &inContext)
  321. {
  322. JPH_PROFILE_FUNCTION();
  323. float dt = inContext.mSubStepDeltaTime;
  324. float restitution_treshold = -2.0f * inContext.mGravity.Length() * dt;
  325. float vertex_radius = mSettings->mVertexRadius;
  326. for (Vertex &v : mVertices)
  327. if (v.mInvMass > 0.0f)
  328. {
  329. // Remember previous velocity for restitution calculations
  330. Vec3 prev_v = v.mVelocity;
  331. // XPBD velocity update
  332. v.mVelocity = (v.mPosition - v.mPreviousPosition) / dt;
  333. // Satisfy collision constraint
  334. if (v.mCollidingShapeIndex >= 0)
  335. {
  336. // Check if there is a collision
  337. float projected_distance = -v.mCollisionPlane.SignedDistance(v.mPosition) + vertex_radius;
  338. if (projected_distance > 0.0f)
  339. {
  340. // Remember that there was a collision
  341. v.mHasContact = true;
  342. mHasContact = true;
  343. // Sensors should not have a collision response
  344. CollidingShape &cs = mCollidingShapes[v.mCollidingShapeIndex];
  345. if (!cs.mIsSensor)
  346. {
  347. // Note that we already calculated the velocity, so this does not affect the velocity (next iteration starts by setting previous position to current position)
  348. Vec3 contact_normal = v.mCollisionPlane.GetNormal();
  349. v.mPosition += contact_normal * projected_distance;
  350. // Apply friction as described in Detailed Rigid Body Simulation with Extended Position Based Dynamics - Matthias Muller et al.
  351. // See section 3.6:
  352. // Inverse mass: w1 = 1 / m1, w2 = 1 / m2 + (r2 x n)^T I^-1 (r2 x n) = 0 for a static object
  353. // r2 are the contact point relative to the center of mass of body 2
  354. // Lagrange multiplier for contact: lambda = -c / (w1 + w2)
  355. // Where c is the constraint equation (the distance to the plane, negative because penetrating)
  356. // Contact normal force: fn = lambda / dt^2
  357. // Delta velocity due to friction dv = -vt / |vt| * min(dt * friction * fn * (w1 + w2), |vt|) = -vt * min(-friction * c / (|vt| * dt), 1)
  358. // Note that I think there is an error in the paper, I added a mass term, see: https://github.com/matthias-research/pages/issues/29
  359. // Relative velocity: vr = v1 - v2 - omega2 x r2
  360. // Normal velocity: vn = vr . contact_normal
  361. // Tangential velocity: vt = vr - contact_normal * vn
  362. // Impulse: p = dv / (w1 + w2)
  363. // Changes in particle velocities:
  364. // v1 = v1 + p / m1
  365. // v2 = v2 - p / m2 (no change when colliding with a static body)
  366. // w2 = w2 - I^-1 (r2 x p) (no change when colliding with a static body)
  367. if (cs.mMotionType == EMotionType::Dynamic)
  368. {
  369. // Calculate normal and tangential velocity (equation 30)
  370. Vec3 r2 = v.mPosition - cs.mCenterOfMassTransform.GetTranslation();
  371. Vec3 v2 = cs.GetPointVelocity(r2);
  372. Vec3 relative_velocity = v.mVelocity - v2;
  373. Vec3 v_normal = contact_normal * contact_normal.Dot(relative_velocity);
  374. Vec3 v_tangential = relative_velocity - v_normal;
  375. float v_tangential_length = v_tangential.Length();
  376. // Calculate resulting inverse mass of vertex
  377. float vertex_inv_mass = cs.mSoftBodyInvMassScale * v.mInvMass;
  378. // Calculate inverse effective mass
  379. Vec3 r2_cross_n = r2.Cross(contact_normal);
  380. float w2 = cs.mInvMass + r2_cross_n.Dot(cs.mInvInertia * r2_cross_n);
  381. float w1_plus_w2 = vertex_inv_mass + w2;
  382. // Calculate delta relative velocity due to friction (modified equation 31)
  383. Vec3 dv;
  384. if (v_tangential_length > 0.0f)
  385. dv = v_tangential * min(cs.mFriction * projected_distance / (v_tangential_length * dt), 1.0f);
  386. else
  387. dv = Vec3::sZero();
  388. // Calculate delta relative velocity due to restitution (equation 35)
  389. dv += v_normal;
  390. float prev_v_normal = (prev_v - v2).Dot(contact_normal);
  391. if (prev_v_normal < restitution_treshold)
  392. dv += cs.mRestitution * prev_v_normal * contact_normal;
  393. // Calculate impulse
  394. Vec3 p = dv / w1_plus_w2;
  395. // Apply impulse to particle
  396. v.mVelocity -= p * vertex_inv_mass;
  397. // Apply impulse to rigid body
  398. cs.mLinearVelocity += p * cs.mInvMass;
  399. cs.mAngularVelocity += cs.mInvInertia * r2.Cross(p);
  400. // Mark that the velocities of the body we hit need to be updated
  401. cs.mUpdateVelocities = true;
  402. }
  403. else
  404. {
  405. // Body is not movable, equations are simpler
  406. // Calculate normal and tangential velocity (equation 30)
  407. Vec3 v_normal = contact_normal * contact_normal.Dot(v.mVelocity);
  408. Vec3 v_tangential = v.mVelocity - v_normal;
  409. float v_tangential_length = v_tangential.Length();
  410. // Apply friction (modified equation 31)
  411. if (v_tangential_length > 0.0f)
  412. v.mVelocity -= v_tangential * min(cs.mFriction * projected_distance / (v_tangential_length * dt), 1.0f);
  413. // Apply restitution (equation 35)
  414. v.mVelocity -= v_normal;
  415. float prev_v_normal = prev_v.Dot(contact_normal);
  416. if (prev_v_normal < restitution_treshold)
  417. v.mVelocity -= cs.mRestitution * prev_v_normal * contact_normal;
  418. }
  419. }
  420. }
  421. }
  422. }
  423. }
  424. void SoftBodyMotionProperties::UpdateSoftBodyState(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings)
  425. {
  426. JPH_PROFILE_FUNCTION();
  427. // Contact callback
  428. if (mHasContact && ioContext.mContactListener != nullptr)
  429. ioContext.mContactListener->OnSoftBodyContactAdded(*ioContext.mBody, SoftBodyManifold(this));
  430. // Loop through vertices once more to update the global state
  431. float dt = ioContext.mDeltaTime;
  432. float max_linear_velocity_sq = Square(GetMaxLinearVelocity());
  433. float max_v_sq = 0.0f;
  434. Vec3 linear_velocity = Vec3::sZero(), angular_velocity = Vec3::sZero();
  435. mLocalPredictedBounds = mLocalBounds = { };
  436. mHasContact = false;
  437. for (Vertex &v : mVertices)
  438. {
  439. // Calculate max square velocity
  440. float v_sq = v.mVelocity.LengthSq();
  441. max_v_sq = max(max_v_sq, v_sq);
  442. // Clamp if velocity is too high
  443. if (v_sq > max_linear_velocity_sq)
  444. v.mVelocity *= sqrt(max_linear_velocity_sq / v_sq);
  445. // Calculate local linear/angular velocity
  446. linear_velocity += v.mVelocity;
  447. angular_velocity += v.mPosition.Cross(v.mVelocity);
  448. // Update local bounding box
  449. mLocalBounds.Encapsulate(v.mPosition);
  450. // Create predicted position for the next frame in order to detect collisions before they happen
  451. mLocalPredictedBounds.Encapsulate(v.mPosition + v.mVelocity * dt + ioContext.mDisplacementDueToGravity);
  452. // Reset collision data for the next iteration
  453. v.mCollidingShapeIndex = -1;
  454. v.mHasContact = false;
  455. v.mLargestPenetration = -FLT_MAX;
  456. }
  457. // Calculate linear/angular velocity of the body by averaging all vertices and bringing the value to world space
  458. float num_vertices_divider = float(max(int(mVertices.size()), 1));
  459. SetLinearVelocity(ioContext.mCenterOfMassTransform.Multiply3x3(linear_velocity / num_vertices_divider));
  460. SetAngularVelocity(ioContext.mCenterOfMassTransform.Multiply3x3(angular_velocity / num_vertices_divider));
  461. if (mUpdatePosition)
  462. {
  463. // Shift the body so that the position is the center of the local bounds
  464. Vec3 delta = mLocalBounds.GetCenter();
  465. ioContext.mDeltaPosition = ioContext.mCenterOfMassTransform.Multiply3x3(delta);
  466. for (Vertex &v : mVertices)
  467. v.mPosition -= delta;
  468. // Offset bounds to match new position
  469. mLocalBounds.Translate(-delta);
  470. mLocalPredictedBounds.Translate(-delta);
  471. }
  472. else
  473. ioContext.mDeltaPosition = Vec3::sZero();
  474. // Test if we should go to sleep
  475. if (GetAllowSleeping())
  476. {
  477. if (max_v_sq > inPhysicsSettings.mPointVelocitySleepThreshold)
  478. {
  479. ResetSleepTestTimer();
  480. ioContext.mCanSleep = ECanSleep::CannotSleep;
  481. }
  482. else
  483. ioContext.mCanSleep = AccumulateSleepTime(dt, inPhysicsSettings.mTimeBeforeSleep);
  484. }
  485. else
  486. ioContext.mCanSleep = ECanSleep::CannotSleep;
  487. }
  488. void SoftBodyMotionProperties::UpdateRigidBodyVelocities(const SoftBodyUpdateContext &inContext, BodyInterface &inBodyInterface)
  489. {
  490. JPH_PROFILE_FUNCTION();
  491. // Write back velocity deltas
  492. for (const CollidingShape &cs : mCollidingShapes)
  493. if (cs.mUpdateVelocities)
  494. inBodyInterface.AddLinearAndAngularVelocity(cs.mBodyID, inContext.mCenterOfMassTransform.Multiply3x3(cs.mLinearVelocity - cs.mOriginalLinearVelocity), inContext.mCenterOfMassTransform.Multiply3x3(cs.mAngularVelocity - cs.mOriginalAngularVelocity));
  495. // Clear colliding shapes to avoid hanging on to references to shapes
  496. mCollidingShapes.clear();
  497. }
  498. void SoftBodyMotionProperties::InitializeUpdateContext(float inDeltaTime, Body &inSoftBody, const PhysicsSystem &inSystem, SoftBodyUpdateContext &ioContext)
  499. {
  500. JPH_PROFILE_FUNCTION();
  501. // Store body
  502. ioContext.mBody = &inSoftBody;
  503. ioContext.mMotionProperties = this;
  504. ioContext.mContactListener = inSystem.GetSoftBodyContactListener();
  505. // Convert gravity to local space
  506. ioContext.mCenterOfMassTransform = inSoftBody.GetCenterOfMassTransform();
  507. ioContext.mGravity = ioContext.mCenterOfMassTransform.Multiply3x3Transposed(GetGravityFactor() * inSystem.GetGravity());
  508. // Calculate delta time for sub step
  509. ioContext.mDeltaTime = inDeltaTime;
  510. ioContext.mSubStepDeltaTime = inDeltaTime / mNumIterations;
  511. // Calculate total displacement we'll have due to gravity over all sub steps
  512. // The total displacement as produced by our integrator can be written as: Sum(i * g * dt^2, i = 0..mNumIterations).
  513. // This is bigger than 0.5 * g * dt^2 because we first increment the velocity and then update the position
  514. // Using Sum(i, i = 0..n) = n * (n + 1) / 2 we can write this as:
  515. ioContext.mDisplacementDueToGravity = (0.5f * mNumIterations * (mNumIterations + 1) * Square(ioContext.mSubStepDeltaTime)) * ioContext.mGravity;
  516. }
  517. void SoftBodyMotionProperties::StartNextIteration(const SoftBodyUpdateContext &ioContext)
  518. {
  519. ApplyPressure(ioContext);
  520. IntegratePositions(ioContext);
  521. ApplyVolumeConstraints(ioContext);
  522. }
  523. SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelDetermineCollisionPlanes(SoftBodyUpdateContext &ioContext)
  524. {
  525. // 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)
  526. uint num_vertices = (uint)mVertices.size();
  527. if (ioContext.mNextCollisionVertex.load(memory_order_relaxed) < num_vertices)
  528. {
  529. // Fetch next batch of vertices to process
  530. uint next_vertex = ioContext.mNextCollisionVertex.fetch_add(SoftBodyUpdateContext::cVertexCollisionBatch, memory_order_acquire);
  531. if (next_vertex < num_vertices)
  532. {
  533. // Process collision planes
  534. uint num_vertices_to_process = min(SoftBodyUpdateContext::cVertexCollisionBatch, num_vertices - next_vertex);
  535. DetermineCollisionPlanes(ioContext, next_vertex, num_vertices_to_process);
  536. uint vertices_processed = ioContext.mNumCollisionVerticesProcessed.fetch_add(SoftBodyUpdateContext::cVertexCollisionBatch, memory_order_release) + num_vertices_to_process;
  537. if (vertices_processed >= num_vertices)
  538. {
  539. // Start the first iteration
  540. JPH_IF_ENABLE_ASSERTS(uint iteration =) ioContext.mNextIteration.fetch_add(1, memory_order_relaxed);
  541. JPH_ASSERT(iteration == 0);
  542. StartNextIteration(ioContext);
  543. ioContext.mState.store(SoftBodyUpdateContext::EState::ApplyEdgeConstraints, memory_order_release);
  544. }
  545. return EStatus::DidWork;
  546. }
  547. }
  548. return EStatus::NoWork;
  549. }
  550. SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelApplyEdgeConstraints(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings)
  551. {
  552. // 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)
  553. uint num_groups = (uint)mSettings->mEdgeGroupEndIndices.size();
  554. JPH_ASSERT(num_groups > 0, "SoftBodySharedSettings::Optimize should have been called!");
  555. uint32 edge_group, edge_start_idx;
  556. SoftBodyUpdateContext::sGetEdgeGroupAndStartIdx(ioContext.mNextEdgeConstraint.load(memory_order_relaxed), edge_group, edge_start_idx);
  557. if (edge_group < num_groups)
  558. {
  559. uint edge_group_size = mSettings->GetEdgeGroupSize(edge_group);
  560. if (edge_start_idx < edge_group_size || edge_group_size == 0)
  561. {
  562. // Fetch the next batch of edges to process
  563. uint64 next_edge_batch = ioContext.mNextEdgeConstraint.fetch_add(SoftBodyUpdateContext::cEdgeConstraintBatch, memory_order_acquire);
  564. SoftBodyUpdateContext::sGetEdgeGroupAndStartIdx(next_edge_batch, edge_group, edge_start_idx);
  565. if (edge_group < num_groups)
  566. {
  567. bool non_parallel_group = edge_group == num_groups - 1; // Last group is the non-parallel group and goes as a whole
  568. edge_group_size = mSettings->GetEdgeGroupSize(edge_group);
  569. if (non_parallel_group? edge_start_idx == 0 : edge_start_idx < edge_group_size)
  570. {
  571. // Process edges
  572. uint num_edges_to_process = non_parallel_group? edge_group_size : min(SoftBodyUpdateContext::cEdgeConstraintBatch, edge_group_size - edge_start_idx);
  573. if (edge_group > 0)
  574. edge_start_idx += mSettings->mEdgeGroupEndIndices[edge_group - 1];
  575. ApplyEdgeConstraints(ioContext, edge_start_idx, edge_start_idx + num_edges_to_process);
  576. // Test if we're at the end of this group
  577. uint edge_constraints_processed = ioContext.mNumEdgeConstraintsProcessed.fetch_add(num_edges_to_process, memory_order_relaxed) + num_edges_to_process;
  578. if (edge_constraints_processed >= edge_group_size)
  579. {
  580. // Non parallel group is the last group (which is also the only group that can be empty)
  581. if (non_parallel_group || mSettings->GetEdgeGroupSize(edge_group + 1) == 0)
  582. {
  583. // Finish the iteration
  584. ApplyCollisionConstraintsAndUpdateVelocities(ioContext);
  585. ApplySkinConstraints(ioContext);
  586. uint iteration = ioContext.mNextIteration.fetch_add(1, memory_order_relaxed);
  587. if (iteration < mNumIterations)
  588. {
  589. // Start a new iteration
  590. StartNextIteration(ioContext);
  591. // Reset next edge to process
  592. ioContext.mNumEdgeConstraintsProcessed.store(0, memory_order_relaxed);
  593. ioContext.mNextEdgeConstraint.store(0, memory_order_release);
  594. }
  595. else
  596. {
  597. // On final iteration we update the state
  598. UpdateSoftBodyState(ioContext, inPhysicsSettings);
  599. ioContext.mState.store(SoftBodyUpdateContext::EState::Done, memory_order_release);
  600. return EStatus::Done;
  601. }
  602. }
  603. else
  604. {
  605. // Next group
  606. ioContext.mNumEdgeConstraintsProcessed.store(0, memory_order_relaxed);
  607. ioContext.mNextEdgeConstraint.store(SoftBodyUpdateContext::sGetEdgeGroupStart(edge_group + 1), memory_order_release);
  608. }
  609. }
  610. return EStatus::DidWork;
  611. }
  612. }
  613. }
  614. }
  615. return EStatus::NoWork;
  616. }
  617. SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelUpdate(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings)
  618. {
  619. switch (ioContext.mState.load(memory_order_relaxed))
  620. {
  621. case SoftBodyUpdateContext::EState::DetermineCollisionPlanes:
  622. return ParallelDetermineCollisionPlanes(ioContext);
  623. case SoftBodyUpdateContext::EState::ApplyEdgeConstraints:
  624. return ParallelApplyEdgeConstraints(ioContext, inPhysicsSettings);
  625. case SoftBodyUpdateContext::EState::Done:
  626. return EStatus::Done;
  627. default:
  628. JPH_ASSERT(false);
  629. return EStatus::NoWork;
  630. }
  631. }
  632. void SoftBodyMotionProperties::SkinVertices(RMat44Arg inRootTransform, const Mat44 *inJointMatrices, [[maybe_unused]] uint inNumJoints, bool inHardSkinAll, TempAllocator &ioTempAllocator)
  633. {
  634. // Calculate the skin matrices
  635. uint num_skin_matrices = uint(mSettings->mInvBindMatrices.size());
  636. uint skin_matrices_size = num_skin_matrices * sizeof(Mat44);
  637. Mat44 *skin_matrices = (Mat44 *)ioTempAllocator.Allocate(skin_matrices_size);
  638. const Mat44 *skin_matrices_end = skin_matrices + num_skin_matrices;
  639. const InvBind *inv_bind_matrix = mSettings->mInvBindMatrices.data();
  640. for (Mat44 *s = skin_matrices; s < skin_matrices_end; ++s, ++inv_bind_matrix)
  641. *s = inJointMatrices[inv_bind_matrix->mJointIndex] * inv_bind_matrix->mInvBind;
  642. // Skin the vertices
  643. mSkinStateTransform = inRootTransform;
  644. JPH_IF_ENABLE_ASSERTS(uint num_vertices = uint(mSettings->mVertices.size());)
  645. JPH_ASSERT(mSkinState.size() == num_vertices);
  646. const SoftBodySharedSettings::Vertex *in_vertices = mSettings->mVertices.data();
  647. for (const Skinned &s : mSettings->mSkinnedConstraints)
  648. {
  649. // Get bind pose
  650. JPH_ASSERT(s.mVertex < num_vertices);
  651. Vec3 bind_pos = Vec3::sLoadFloat3Unsafe(in_vertices[s.mVertex].mPosition);
  652. // Skin vertex
  653. Vec3 pos = Vec3::sZero();
  654. for (const SkinWeight &w : s.mWeights)
  655. {
  656. JPH_ASSERT(w.mInvBindIndex < num_skin_matrices);
  657. pos += w.mWeight * (skin_matrices[w.mInvBindIndex] * bind_pos);
  658. }
  659. mSkinState[s.mVertex].mPosition = pos;
  660. }
  661. // Calculate the normals
  662. for (const Skinned &s : mSettings->mSkinnedConstraints)
  663. {
  664. Vec3 normal = Vec3::sZero();
  665. uint32 num_faces = s.mNormalInfo >> 24;
  666. if (num_faces > 0)
  667. {
  668. // Calculate normal
  669. const uint32 *f = &mSettings->mSkinnedConstraintNormals[s.mNormalInfo & 0xffffff];
  670. const uint32 *f_end = f + num_faces;
  671. while (f < f_end)
  672. {
  673. const Face &face = mSettings->mFaces[*f];
  674. Vec3 v0 = mSkinState[face.mVertex[0]].mPosition;
  675. Vec3 v1 = mSkinState[face.mVertex[1]].mPosition;
  676. Vec3 v2 = mSkinState[face.mVertex[2]].mPosition;
  677. normal += (v1 - v0).Cross(v2 - v0).NormalizedOr(Vec3::sZero());
  678. ++f;
  679. }
  680. normal = normal.NormalizedOr(Vec3::sZero());
  681. }
  682. mSkinState[s.mVertex].mNormal = normal;
  683. }
  684. ioTempAllocator.Free(skin_matrices, skin_matrices_size);
  685. if (inHardSkinAll)
  686. {
  687. // Hard skin all vertices and reset their velocities
  688. for (const Skinned &s : mSettings->mSkinnedConstraints)
  689. {
  690. Vertex &vertex = mVertices[s.mVertex];
  691. vertex.mPosition = mSkinState[s.mVertex].mPosition;
  692. vertex.mVelocity = Vec3::sZero();
  693. }
  694. }
  695. }
  696. void SoftBodyMotionProperties::CustomUpdate(float inDeltaTime, Body &ioSoftBody, PhysicsSystem &inSystem)
  697. {
  698. JPH_PROFILE_FUNCTION();
  699. // Create update context
  700. SoftBodyUpdateContext context;
  701. InitializeUpdateContext(inDeltaTime, ioSoftBody, inSystem, context);
  702. // Determine bodies we're colliding with
  703. DetermineCollidingShapes(context, inSystem, inSystem.GetBodyLockInterface());
  704. // Call the internal update until it finishes
  705. EStatus status;
  706. const PhysicsSettings &settings = inSystem.GetPhysicsSettings();
  707. while ((status = ParallelUpdate(context, settings)) == EStatus::DidWork)
  708. continue;
  709. JPH_ASSERT(status == EStatus::Done);
  710. // Update the state of the bodies we've collided with
  711. UpdateRigidBodyVelocities(context, inSystem.GetBodyInterface());
  712. // Update position of the soft body
  713. if (mUpdatePosition)
  714. inSystem.GetBodyInterface().SetPosition(ioSoftBody.GetID(), ioSoftBody.GetPosition() + context.mDeltaPosition, EActivation::DontActivate);
  715. }
  716. #ifdef JPH_DEBUG_RENDERER
  717. void SoftBodyMotionProperties::DrawVertices(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const
  718. {
  719. for (const Vertex &v : mVertices)
  720. inRenderer->DrawMarker(inCenterOfMassTransform * v.mPosition, v.mInvMass > 0.0f? Color::sGreen : Color::sRed, 0.05f);
  721. }
  722. void SoftBodyMotionProperties::DrawEdgeConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const
  723. {
  724. for (const Edge &e : mSettings->mEdgeConstraints)
  725. inRenderer->DrawLine(inCenterOfMassTransform * mVertices[e.mVertex[0]].mPosition, inCenterOfMassTransform * mVertices[e.mVertex[1]].mPosition, Color::sWhite);
  726. }
  727. void SoftBodyMotionProperties::DrawVolumeConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const
  728. {
  729. for (const Volume &v : mSettings->mVolumeConstraints)
  730. {
  731. RVec3 x1 = inCenterOfMassTransform * mVertices[v.mVertex[0]].mPosition;
  732. RVec3 x2 = inCenterOfMassTransform * mVertices[v.mVertex[1]].mPosition;
  733. RVec3 x3 = inCenterOfMassTransform * mVertices[v.mVertex[2]].mPosition;
  734. RVec3 x4 = inCenterOfMassTransform * mVertices[v.mVertex[3]].mPosition;
  735. inRenderer->DrawTriangle(x1, x3, x2, Color::sYellow, DebugRenderer::ECastShadow::On);
  736. inRenderer->DrawTriangle(x2, x3, x4, Color::sYellow, DebugRenderer::ECastShadow::On);
  737. inRenderer->DrawTriangle(x1, x4, x3, Color::sYellow, DebugRenderer::ECastShadow::On);
  738. inRenderer->DrawTriangle(x1, x2, x4, Color::sYellow, DebugRenderer::ECastShadow::On);
  739. }
  740. }
  741. void SoftBodyMotionProperties::DrawSkinConstraints(DebugRenderer *inRenderer) const
  742. {
  743. for (const Skinned &s : mSettings->mSkinnedConstraints)
  744. {
  745. const SkinState &skin_state = mSkinState[s.mVertex];
  746. DebugRenderer::sInstance->DrawArrow(mSkinStateTransform * skin_state.mPosition, mSkinStateTransform * (skin_state.mPosition + 0.1f * skin_state.mNormal), Color::sOrange, 0.01f);
  747. }
  748. }
  749. void SoftBodyMotionProperties::DrawPredictedBounds(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const
  750. {
  751. inRenderer->DrawWireBox(inCenterOfMassTransform, mLocalPredictedBounds, Color::sRed);
  752. }
  753. #endif // JPH_DEBUG_RENDERER
  754. void SoftBodyMotionProperties::SaveState(StateRecorder &inStream) const
  755. {
  756. MotionProperties::SaveState(inStream);
  757. for (const Vertex &v : mVertices)
  758. {
  759. inStream.Write(v.mPreviousPosition);
  760. inStream.Write(v.mPosition);
  761. inStream.Write(v.mVelocity);
  762. }
  763. inStream.Write(mLocalBounds.mMin);
  764. inStream.Write(mLocalBounds.mMax);
  765. inStream.Write(mLocalPredictedBounds.mMin);
  766. inStream.Write(mLocalPredictedBounds.mMax);
  767. }
  768. void SoftBodyMotionProperties::RestoreState(StateRecorder &inStream)
  769. {
  770. MotionProperties::RestoreState(inStream);
  771. for (Vertex &v : mVertices)
  772. {
  773. inStream.Read(v.mPreviousPosition);
  774. inStream.Read(v.mPosition);
  775. inStream.Read(v.mVelocity);
  776. }
  777. inStream.Read(mLocalBounds.mMin);
  778. inStream.Read(mLocalBounds.mMax);
  779. inStream.Read(mLocalPredictedBounds.mMin);
  780. inStream.Read(mLocalPredictedBounds.mMax);
  781. }
  782. JPH_NAMESPACE_END