Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/SoftBody/SoftBodyMotionProperties.cpp
21520 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#include <Jolt/Jolt.h>
6
7
#include <Jolt/Physics/SoftBody/SoftBodyMotionProperties.h>
8
#include <Jolt/Physics/SoftBody/SoftBodyCreationSettings.h>
9
#include <Jolt/Physics/SoftBody/SoftBodyContactListener.h>
10
#include <Jolt/Physics/SoftBody/SoftBodyManifold.h>
11
#include <Jolt/Physics/Collision/CollideSoftBodyVertexIterator.h>
12
#include <Jolt/Physics/Collision/SimShapeFilterWrapper.h>
13
#include <Jolt/Physics/PhysicsSystem.h>
14
#include <Jolt/Physics/Body/BodyManager.h>
15
#include <Jolt/Core/ScopeExit.h>
16
#ifdef JPH_DEBUG_RENDERER
17
#include <Jolt/Renderer/DebugRenderer.h>
18
#endif // JPH_DEBUG_RENDERER
19
20
JPH_NAMESPACE_BEGIN
21
22
using namespace JPH::literals;
23
24
void SoftBodyMotionProperties::CalculateMassAndInertia()
25
{
26
MassProperties mp;
27
28
for (const Vertex &v : mVertices)
29
if (v.mInvMass > 0.0f)
30
{
31
Vec3 pos = v.mPosition;
32
33
// Accumulate mass
34
float mass = 1.0f / v.mInvMass;
35
mp.mMass += mass;
36
37
// Inertia tensor, diagonal
38
// See equations https://en.wikipedia.org/wiki/Moment_of_inertia section 'Inertia Tensor'
39
for (int i = 0; i < 3; ++i)
40
mp.mInertia(i, i) += mass * (Square(pos[(i + 1) % 3]) + Square(pos[(i + 2) % 3]));
41
42
// Inertia tensor off diagonal
43
for (int i = 0; i < 3; ++i)
44
for (int j = 0; j < 3; ++j)
45
if (i != j)
46
mp.mInertia(i, j) -= mass * pos[i] * pos[j];
47
}
48
else
49
{
50
// If one vertex is kinematic, the entire body will have infinite mass and inertia
51
SetInverseMass(0.0f);
52
SetInverseInertia(Vec3::sZero(), Quat::sIdentity());
53
return;
54
}
55
56
SetMassProperties(EAllowedDOFs::All, mp);
57
}
58
59
void SoftBodyMotionProperties::Initialize(const SoftBodyCreationSettings &inSettings)
60
{
61
// Store settings
62
mSettings = inSettings.mSettings;
63
mNumIterations = inSettings.mNumIterations;
64
mPressure = inSettings.mPressure;
65
mUpdatePosition = inSettings.mUpdatePosition;
66
mFacesDoubleSided = inSettings.mFacesDoubleSided;
67
SetVertexRadius(inSettings.mVertexRadius);
68
69
// Initialize vertices
70
mVertices.resize(inSettings.mSettings->mVertices.size());
71
Mat44 rotation = inSettings.mMakeRotationIdentity? Mat44::sRotation(inSettings.mRotation) : Mat44::sIdentity();
72
for (Array<Vertex>::size_type v = 0, s = mVertices.size(); v < s; ++v)
73
{
74
const SoftBodySharedSettings::Vertex &in_vertex = inSettings.mSettings->mVertices[v];
75
Vertex &out_vertex = mVertices[v];
76
out_vertex.mPreviousPosition = out_vertex.mPosition = rotation * Vec3(in_vertex.mPosition);
77
out_vertex.mVelocity = rotation.Multiply3x3(Vec3(in_vertex.mVelocity));
78
out_vertex.ResetCollision();
79
out_vertex.mInvMass = in_vertex.mInvMass;
80
mLocalBounds.Encapsulate(out_vertex.mPosition);
81
}
82
83
// Initialize rods
84
if (!inSettings.mSettings->mRodStretchShearConstraints.empty())
85
{
86
mRodStates.resize(inSettings.mSettings->mRodStretchShearConstraints.size());
87
Quat rotation_q = rotation.GetQuaternion();
88
for (Array<RodState>::size_type r = 0, s = mRodStates.size(); r < s; ++r)
89
{
90
const SoftBodySharedSettings::RodStretchShear &in_rod = inSettings.mSettings->mRodStretchShearConstraints[r];
91
RodState &out_rod = mRodStates[r];
92
out_rod.mRotation = rotation_q * in_rod.mBishop;
93
out_rod.mAngularVelocity = Vec3::sZero();
94
}
95
}
96
97
// Allocate space for skinned vertices
98
if (!inSettings.mSettings->mSkinnedConstraints.empty())
99
mSkinState.resize(mVertices.size());
100
101
// We don't know delta time yet, so we can't predict the bounds and use the local bounds as the predicted bounds
102
mLocalPredictedBounds = mLocalBounds;
103
104
CalculateMassAndInertia();
105
}
106
107
float SoftBodyMotionProperties::GetVolumeTimesSix() const
108
{
109
float six_volume = 0.0f;
110
for (const Face &f : mSettings->mFaces)
111
{
112
Vec3 x1 = mVertices[f.mVertex[0]].mPosition;
113
Vec3 x2 = mVertices[f.mVertex[1]].mPosition;
114
Vec3 x3 = mVertices[f.mVertex[2]].mPosition;
115
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
116
}
117
return six_volume;
118
}
119
120
void SoftBodyMotionProperties::DetermineCollidingShapes(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem, const BodyLockInterface &inBodyLockInterface)
121
{
122
JPH_PROFILE_FUNCTION();
123
124
// Reset flag prior to collision detection
125
mNeedContactCallback.store(false, memory_order_relaxed);
126
127
struct Collector : public CollideShapeBodyCollector
128
{
129
Collector(const SoftBodyUpdateContext &inContext, const PhysicsSystem &inSystem, const BodyLockInterface &inBodyLockInterface, const AABox &inLocalBounds, SimShapeFilterWrapper &inShapeFilter, Array<CollidingShape> &ioHits, Array<CollidingSensor> &ioSensors) :
130
mContext(inContext),
131
mInverseTransform(inContext.mCenterOfMassTransform.InversedRotationTranslation()),
132
mLocalBounds(inLocalBounds),
133
mBodyLockInterface(inBodyLockInterface),
134
mCombineFriction(inSystem.GetCombineFriction()),
135
mCombineRestitution(inSystem.GetCombineRestitution()),
136
mShapeFilter(inShapeFilter),
137
mHits(ioHits),
138
mSensors(ioSensors)
139
{
140
}
141
142
virtual void AddHit(const BodyID &inResult) override
143
{
144
BodyLockRead lock(mBodyLockInterface, inResult);
145
if (lock.Succeeded())
146
{
147
const Body &soft_body = *mContext.mBody;
148
const Body &body = lock.GetBody();
149
if (body.IsRigidBody() // TODO: We should support soft body vs soft body
150
&& soft_body.GetCollisionGroup().CanCollide(body.GetCollisionGroup()))
151
{
152
SoftBodyContactSettings settings;
153
settings.mIsSensor = body.IsSensor();
154
155
if (mContext.mContactListener == nullptr)
156
{
157
// If we have no contact listener, we can ignore sensors
158
if (settings.mIsSensor)
159
return;
160
}
161
else
162
{
163
// Call the contact listener to see if we should accept this contact
164
if (mContext.mContactListener->OnSoftBodyContactValidate(soft_body, body, settings) != SoftBodyValidateResult::AcceptContact)
165
return;
166
167
// Check if there will be any interaction
168
if (!settings.mIsSensor
169
&& settings.mInvMassScale1 == 0.0f
170
&& (body.GetMotionType() != EMotionType::Dynamic || settings.mInvMassScale2 == 0.0f))
171
return;
172
}
173
174
// Calculate transform of this body relative to the soft body
175
Mat44 com = (mInverseTransform * body.GetCenterOfMassTransform()).ToMat44();
176
177
// Collect leaf shapes
178
mShapeFilter.SetBody2(&body);
179
struct LeafShapeCollector : public TransformedShapeCollector
180
{
181
virtual void AddHit(const TransformedShape &inResult) override
182
{
183
mHits.emplace_back(Mat44::sRotationTranslation(inResult.mShapeRotation, Vec3(inResult.mShapePositionCOM)), inResult.GetShapeScale(), inResult.mShape);
184
}
185
186
Array<LeafShape> mHits;
187
};
188
LeafShapeCollector collector;
189
body.GetShape()->CollectTransformedShapes(mLocalBounds, com.GetTranslation(), com.GetQuaternion(), Vec3::sOne(), SubShapeIDCreator(), collector, mShapeFilter.GetFilter());
190
if (collector.mHits.empty())
191
return;
192
193
if (settings.mIsSensor)
194
{
195
CollidingSensor cs;
196
cs.mCenterOfMassTransform = com;
197
cs.mShapes = std::move(collector.mHits);
198
cs.mBodyID = inResult;
199
mSensors.push_back(cs);
200
}
201
else
202
{
203
CollidingShape cs;
204
cs.mCenterOfMassTransform = com;
205
cs.mShapes = std::move(collector.mHits);
206
cs.mBodyID = inResult;
207
cs.mMotionType = body.GetMotionType();
208
cs.mUpdateVelocities = false;
209
cs.mFriction = mCombineFriction(soft_body, SubShapeID(), body, SubShapeID());
210
cs.mRestitution = mCombineRestitution(soft_body, SubShapeID(), body, SubShapeID());
211
cs.mSoftBodyInvMassScale = settings.mInvMassScale1;
212
if (cs.mMotionType == EMotionType::Dynamic)
213
{
214
const MotionProperties *mp = body.GetMotionProperties();
215
cs.mInvMass = settings.mInvMassScale2 * mp->GetInverseMass();
216
cs.mInvInertia = settings.mInvInertiaScale2 * mp->GetInverseInertiaForRotation(cs.mCenterOfMassTransform.GetRotation());
217
cs.mOriginalLinearVelocity = cs.mLinearVelocity = mInverseTransform.Multiply3x3(mp->GetLinearVelocity());
218
cs.mOriginalAngularVelocity = cs.mAngularVelocity = mInverseTransform.Multiply3x3(mp->GetAngularVelocity());
219
}
220
mHits.push_back(cs);
221
}
222
}
223
}
224
}
225
226
private:
227
const SoftBodyUpdateContext &mContext;
228
RMat44 mInverseTransform;
229
AABox mLocalBounds;
230
const BodyLockInterface & mBodyLockInterface;
231
ContactConstraintManager::CombineFunction mCombineFriction;
232
ContactConstraintManager::CombineFunction mCombineRestitution;
233
SimShapeFilterWrapper & mShapeFilter;
234
Array<CollidingShape> & mHits;
235
Array<CollidingSensor> & mSensors;
236
};
237
238
// Calculate local bounding box
239
AABox local_bounds = mLocalBounds;
240
local_bounds.Encapsulate(mLocalPredictedBounds);
241
local_bounds.ExpandBy(Vec3::sReplicate(mVertexRadius));
242
243
// Calculate world space bounding box
244
AABox world_bounds = local_bounds.Transformed(inContext.mCenterOfMassTransform);
245
246
// Create shape filter
247
SimShapeFilterWrapper shape_filter(inContext.mSimShapeFilter, inContext.mBody);
248
249
Collector collector(inContext, inSystem, inBodyLockInterface, local_bounds, shape_filter, mCollidingShapes, mCollidingSensors);
250
ObjectLayer layer = inContext.mBody->GetObjectLayer();
251
DefaultBroadPhaseLayerFilter broadphase_layer_filter = inSystem.GetDefaultBroadPhaseLayerFilter(layer);
252
DefaultObjectLayerFilter object_layer_filter = inSystem.GetDefaultLayerFilter(layer);
253
inSystem.GetBroadPhaseQuery().CollideAABox(world_bounds, collector, broadphase_layer_filter, object_layer_filter);
254
mNumSensors = uint(mCollidingSensors.size()); // Workaround for TSAN false positive: store mCollidingSensors.size() in a separate variable.
255
}
256
257
void SoftBodyMotionProperties::DetermineCollisionPlanes(uint inVertexStart, uint inNumVertices)
258
{
259
JPH_PROFILE_FUNCTION();
260
261
// Generate collision planes
262
for (const CollidingShape &cs : mCollidingShapes)
263
for (const LeafShape &shape : cs.mShapes)
264
shape.mShape->CollideSoftBodyVertices(shape.mTransform, shape.mScale, CollideSoftBodyVertexIterator(mVertices.data() + inVertexStart), inNumVertices, int(&cs - mCollidingShapes.data()));
265
}
266
267
void SoftBodyMotionProperties::DetermineSensorCollisions(CollidingSensor &ioSensor)
268
{
269
JPH_PROFILE_FUNCTION();
270
271
Plane collision_plane;
272
float largest_penetration = -FLT_MAX;
273
int colliding_shape_idx = -1;
274
275
// Collide sensor against all vertices
276
CollideSoftBodyVertexIterator vertex_iterator(
277
StridedPtr<const Vec3>(&mVertices[0].mPosition, sizeof(SoftBodyVertex)), // The position and mass come from the soft body vertex
278
StridedPtr<const float>(&mVertices[0].mInvMass, sizeof(SoftBodyVertex)),
279
StridedPtr<Plane>(&collision_plane, 0), // We want all vertices to result in a single collision so we pass stride 0
280
StridedPtr<float>(&largest_penetration, 0),
281
StridedPtr<int>(&colliding_shape_idx, 0));
282
for (const LeafShape &shape : ioSensor.mShapes)
283
shape.mShape->CollideSoftBodyVertices(shape.mTransform, shape.mScale, vertex_iterator, uint(mVertices.size()), 0);
284
ioSensor.mHasContact = largest_penetration > 0.0f;
285
286
// We need a contact callback if one of the sensors collided
287
if (ioSensor.mHasContact)
288
mNeedContactCallback.store(true, memory_order_relaxed);
289
}
290
291
void SoftBodyMotionProperties::ApplyPressure(const SoftBodyUpdateContext &inContext)
292
{
293
JPH_PROFILE_FUNCTION();
294
295
float dt = inContext.mSubStepDeltaTime;
296
float pressure_coefficient = mPressure;
297
if (pressure_coefficient > 0.0f)
298
{
299
// Calculate total volume
300
float six_volume = GetVolumeTimesSix();
301
if (six_volume > 0.0f)
302
{
303
// Apply pressure
304
// p = F / A = n R T / V (see https://en.wikipedia.org/wiki/Pressure)
305
// Our pressure coefficient is n R T so the impulse is:
306
// P = F dt = pressure_coefficient / V * A * dt
307
float coefficient = pressure_coefficient * dt / six_volume; // Need to still multiply by 6 for the volume
308
for (const Face &f : mSettings->mFaces)
309
{
310
Vec3 x1 = mVertices[f.mVertex[0]].mPosition;
311
Vec3 x2 = mVertices[f.mVertex[1]].mPosition;
312
Vec3 x3 = mVertices[f.mVertex[2]].mPosition;
313
314
Vec3 impulse = coefficient * (x2 - x1).Cross(x3 - x1); // Area is half the cross product so need to still divide by 2
315
for (uint32 i : f.mVertex)
316
{
317
Vertex &v = mVertices[i];
318
v.mVelocity += v.mInvMass * impulse; // Want to divide by 3 because we spread over 3 vertices
319
}
320
}
321
}
322
}
323
}
324
325
void SoftBodyMotionProperties::IntegratePositions(const SoftBodyUpdateContext &inContext)
326
{
327
JPH_PROFILE_FUNCTION();
328
329
float dt = inContext.mSubStepDeltaTime;
330
float linear_damping = max(0.0f, 1.0f - GetLinearDamping() * dt); // See: MotionProperties::ApplyForceTorqueAndDragInternal
331
332
// Integrate
333
Vec3 sub_step_gravity = inContext.mGravity * dt;
334
Vec3 sub_step_impulse = GetAccumulatedForce() * dt / max(float(mVertices.size()), 1.0f);
335
for (Vertex &v : mVertices)
336
{
337
if (v.mInvMass > 0.0f)
338
{
339
// Gravity
340
v.mVelocity += sub_step_gravity + sub_step_impulse * v.mInvMass;
341
342
// Damping
343
v.mVelocity *= linear_damping;
344
}
345
346
// Integrate
347
Vec3 position = v.mPosition;
348
v.mPreviousPosition = position;
349
v.mPosition = position + v.mVelocity * dt;
350
}
351
352
// Integrate rod orientations
353
float half_dt = 0.5f * dt;
354
for (RodState &r : mRodStates)
355
{
356
// Damping
357
r.mAngularVelocity *= linear_damping;
358
359
// Integrate
360
Quat rotation = r.mRotation;
361
Quat delta_rotation = half_dt * Quat::sMultiplyImaginary(r.mAngularVelocity, rotation);
362
r.mPreviousRotationInternal = rotation; // Overwrites mAngularVelocity
363
r.mRotation = (rotation + delta_rotation).Normalized();
364
}
365
}
366
367
void SoftBodyMotionProperties::ApplyDihedralBendConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex)
368
{
369
JPH_PROFILE_FUNCTION();
370
371
float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime);
372
373
for (const DihedralBend *b = mSettings->mDihedralBendConstraints.data() + inStartIndex, *b_end = mSettings->mDihedralBendConstraints.data() + inEndIndex; b < b_end; ++b)
374
{
375
Vertex &v0 = mVertices[b->mVertex[0]];
376
Vertex &v1 = mVertices[b->mVertex[1]];
377
Vertex &v2 = mVertices[b->mVertex[2]];
378
Vertex &v3 = mVertices[b->mVertex[3]];
379
380
// Get positions
381
Vec3 x0 = v0.mPosition;
382
Vec3 x1 = v1.mPosition;
383
Vec3 x2 = v2.mPosition;
384
Vec3 x3 = v3.mPosition;
385
386
/*
387
x2
388
e1/ \e3
389
/ \
390
x0----x1
391
\ e0 /
392
e2\ /e4
393
x3
394
*/
395
396
// Calculate the shared edge of the triangles
397
Vec3 e = x1 - x0;
398
float e_len = e.Length();
399
if (e_len < 1.0e-6f)
400
continue;
401
402
// Calculate the normals of the triangles
403
Vec3 x1x2 = x2 - x1;
404
Vec3 x1x3 = x3 - x1;
405
Vec3 n1 = (x2 - x0).Cross(x1x2);
406
Vec3 n2 = x1x3.Cross(x3 - x0);
407
float n1_len_sq = n1.LengthSq();
408
float n2_len_sq = n2.LengthSq();
409
float n1_len_sq_n2_len_sq = n1_len_sq * n2_len_sq;
410
if (n1_len_sq_n2_len_sq < 1.0e-24f)
411
continue;
412
413
// Calculate constraint equation
414
// 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 correct
415
float sign = Sign(n2.Cross(n1).Dot(e));
416
float d = n1.Dot(n2) / sqrt(n1_len_sq_n2_len_sq);
417
float c = sign * ACosApproximate(d) - b->mInitialAngle;
418
419
// Ensure the range is -PI to PI
420
if (c > JPH_PI)
421
c -= 2.0f * JPH_PI;
422
else if (c < -JPH_PI)
423
c += 2.0f * JPH_PI;
424
425
// Calculate gradient of constraint equation
426
// Taken from "Strain Based Dynamics" - Matthias Muller et al. (Appendix A)
427
// with p1 = x2, p2 = x3, p3 = x0 and p4 = x1
428
// which in turn is based on "Simulation of Clothing with Folds and Wrinkles" - R. Bridson et al. (Section 4)
429
n1 /= n1_len_sq;
430
n2 /= n2_len_sq;
431
Vec3 d0c = (x1x2.Dot(e) * n1 + x1x3.Dot(e) * n2) / e_len;
432
Vec3 d2c = e_len * n1;
433
Vec3 d3c = e_len * n2;
434
435
// The sum of the gradients must be zero (see "Strain Based Dynamics" section 4)
436
Vec3 d1c = -d0c - d2c - d3c;
437
438
// Get masses
439
float w0 = v0.mInvMass;
440
float w1 = v1.mInvMass;
441
float w2 = v2.mInvMass;
442
float w3 = v3.mInvMass;
443
444
// Calculate -lambda
445
float denom = w0 * d0c.LengthSq() + w1 * d1c.LengthSq() + w2 * d2c.LengthSq() + w3 * d3c.LengthSq() + b->mCompliance * inv_dt_sq;
446
if (denom < 1.0e-12f)
447
continue;
448
float minus_lambda = c / denom;
449
450
// Apply correction
451
v0.mPosition = x0 - minus_lambda * w0 * d0c;
452
v1.mPosition = x1 - minus_lambda * w1 * d1c;
453
v2.mPosition = x2 - minus_lambda * w2 * d2c;
454
v3.mPosition = x3 - minus_lambda * w3 * d3c;
455
}
456
}
457
458
void SoftBodyMotionProperties::ApplyVolumeConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex)
459
{
460
JPH_PROFILE_FUNCTION();
461
462
float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime);
463
464
// Satisfy volume constraints
465
for (const Volume *v = mSettings->mVolumeConstraints.data() + inStartIndex, *v_end = mSettings->mVolumeConstraints.data() + inEndIndex; v < v_end; ++v)
466
{
467
Vertex &v1 = mVertices[v->mVertex[0]];
468
Vertex &v2 = mVertices[v->mVertex[1]];
469
Vertex &v3 = mVertices[v->mVertex[2]];
470
Vertex &v4 = mVertices[v->mVertex[3]];
471
472
Vec3 x1 = v1.mPosition;
473
Vec3 x2 = v2.mPosition;
474
Vec3 x3 = v3.mPosition;
475
Vec3 x4 = v4.mPosition;
476
477
// Calculate constraint equation
478
Vec3 x1x2 = x2 - x1;
479
Vec3 x1x3 = x3 - x1;
480
Vec3 x1x4 = x4 - x1;
481
float c = abs(x1x2.Cross(x1x3).Dot(x1x4)) - v->mSixRestVolume;
482
483
// Calculate gradient of constraint equation
484
Vec3 d1c = (x4 - x2).Cross(x3 - x2);
485
Vec3 d2c = x1x3.Cross(x1x4);
486
Vec3 d3c = x1x4.Cross(x1x2);
487
Vec3 d4c = x1x2.Cross(x1x3);
488
489
// Get masses
490
float w1 = v1.mInvMass;
491
float w2 = v2.mInvMass;
492
float w3 = v3.mInvMass;
493
float w4 = v4.mInvMass;
494
495
// Calculate -lambda
496
float denom = w1 * d1c.LengthSq() + w2 * d2c.LengthSq() + w3 * d3c.LengthSq() + w4 * d4c.LengthSq() + v->mCompliance * inv_dt_sq;
497
if (denom < 1.0e-12f)
498
continue;
499
float minus_lambda = c / denom;
500
501
// Apply correction
502
v1.mPosition = x1 - minus_lambda * w1 * d1c;
503
v2.mPosition = x2 - minus_lambda * w2 * d2c;
504
v3.mPosition = x3 - minus_lambda * w3 * d3c;
505
v4.mPosition = x4 - minus_lambda * w4 * d4c;
506
}
507
}
508
509
void SoftBodyMotionProperties::ApplySkinConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex)
510
{
511
// Early out if nothing to do
512
if (mSettings->mSkinnedConstraints.empty() || !mEnableSkinConstraints)
513
return;
514
515
JPH_PROFILE_FUNCTION();
516
517
// We're going to iterate multiple times over the skin constraints, update the skinned position accordingly.
518
// 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.
519
float factor = mSkinStatePreviousPositionValid? inContext.mNextIteration.load(std::memory_order_relaxed) / float(mNumIterations) : 1.0f;
520
float prev_factor = 1.0f - factor;
521
522
// Apply the constraints
523
Vertex *vertices = mVertices.data();
524
const SkinState *skin_states = mSkinState.data();
525
for (const Skinned *s = mSettings->mSkinnedConstraints.data() + inStartIndex, *s_end = mSettings->mSkinnedConstraints.data() + inEndIndex; s < s_end; ++s)
526
{
527
Vertex &vertex = vertices[s->mVertex];
528
const SkinState &skin_state = skin_states[s->mVertex];
529
float max_distance = s->mMaxDistance * mSkinnedMaxDistanceMultiplier;
530
531
// Calculate the skinned position by interpolating from previous to current position
532
Vec3 skin_pos = prev_factor * skin_state.mPreviousPosition + factor * skin_state.mPosition;
533
534
if (max_distance > 0.0f)
535
{
536
// Move vertex if it violated the back stop
537
if (s->mBackStopDistance < max_distance)
538
{
539
// Center of the back stop sphere
540
Vec3 center = skin_pos - skin_state.mNormal * (s->mBackStopDistance + s->mBackStopRadius);
541
542
// Check if we're inside the back stop sphere
543
Vec3 delta = vertex.mPosition - center;
544
float delta_len_sq = delta.LengthSq();
545
if (delta_len_sq < Square(s->mBackStopRadius))
546
{
547
// Push the vertex to the surface of the back stop sphere
548
float delta_len = sqrt(delta_len_sq);
549
vertex.mPosition = delta_len > 0.0f?
550
center + delta * (s->mBackStopRadius / delta_len)
551
: center + skin_state.mNormal * s->mBackStopRadius;
552
}
553
}
554
555
// Clamp vertex distance to max distance from skinned position
556
if (max_distance < FLT_MAX)
557
{
558
Vec3 delta = vertex.mPosition - skin_pos;
559
float delta_len_sq = delta.LengthSq();
560
float max_distance_sq = Square(max_distance);
561
if (delta_len_sq > max_distance_sq)
562
vertex.mPosition = skin_pos + delta * sqrt(max_distance_sq / delta_len_sq);
563
}
564
}
565
else
566
{
567
// Kinematic: Just update the vertex position
568
vertex.mPosition = skin_pos;
569
}
570
}
571
}
572
573
void SoftBodyMotionProperties::ApplyEdgeConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex)
574
{
575
JPH_PROFILE_FUNCTION();
576
577
float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime);
578
579
// Satisfy edge constraints
580
for (const Edge *e = mSettings->mEdgeConstraints.data() + inStartIndex, *e_end = mSettings->mEdgeConstraints.data() + inEndIndex; e < e_end; ++e)
581
{
582
Vertex &v0 = mVertices[e->mVertex[0]];
583
Vertex &v1 = mVertices[e->mVertex[1]];
584
585
// Get positions
586
Vec3 x0 = v0.mPosition;
587
Vec3 x1 = v1.mPosition;
588
589
// Calculate current length
590
Vec3 delta = x1 - x0;
591
float length = delta.Length();
592
593
// Apply correction
594
float denom = length * (v0.mInvMass + v1.mInvMass + e->mCompliance * inv_dt_sq);
595
if (denom < 1.0e-12f)
596
continue;
597
Vec3 correction = delta * (length - e->mRestLength) / denom;
598
v0.mPosition = x0 + v0.mInvMass * correction;
599
v1.mPosition = x1 - v1.mInvMass * correction;
600
}
601
}
602
603
void SoftBodyMotionProperties::ApplyRodStretchShearConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex)
604
{
605
JPH_PROFILE_FUNCTION();
606
607
float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime);
608
609
RodState *rod_state = mRodStates.data() + inStartIndex;
610
for (const RodStretchShear *r = mSettings->mRodStretchShearConstraints.data() + inStartIndex, *r_end = mSettings->mRodStretchShearConstraints.data() + inEndIndex; r < r_end; ++r, ++rod_state)
611
{
612
// Get positions
613
Vertex &v0 = mVertices[r->mVertex[0]];
614
Vertex &v1 = mVertices[r->mVertex[1]];
615
616
// Apply stretch and shear constraint
617
// Equation 37 from "Position and Orientation Based Cosserat Rods" - Kugelstadt and Schoemer - SIGGRAPH 2016
618
float denom = v0.mInvMass + v1.mInvMass + 4.0f * r->mInvMass * Square(r->mLength) + r->mCompliance * inv_dt_sq;
619
if (denom < 1.0e-12f)
620
continue;
621
Vec3 x0 = v0.mPosition;
622
Vec3 x1 = v1.mPosition;
623
Quat rotation = rod_state->mRotation;
624
Vec3 d3 = rotation.RotateAxisZ();
625
Vec3 delta = (x1 - x0 - d3 * r->mLength) / denom;
626
v0.mPosition = x0 + v0.mInvMass * delta;
627
v1.mPosition = x1 - v1.mInvMass * delta;
628
// q * e3_bar = q * (0, 0, -1, 0) = [-qy, qx, -qw, qz]
629
Quat q_e3_bar(rotation.GetXYZW().Swizzle<SWIZZLE_Y, SWIZZLE_X, SWIZZLE_W, SWIZZLE_Z>().FlipSign<-1, 1, -1, 1>());
630
rotation += (2.0f * r->mInvMass * r->mLength) * Quat::sMultiplyImaginary(delta, q_e3_bar);
631
632
// Renormalize
633
rod_state->mRotation = rotation.Normalized();
634
}
635
}
636
637
void SoftBodyMotionProperties::ApplyRodBendTwistConstraints(const SoftBodyUpdateContext &inContext, uint inStartIndex, uint inEndIndex)
638
{
639
JPH_PROFILE_FUNCTION();
640
641
float inv_dt_sq = 1.0f / Square(inContext.mSubStepDeltaTime);
642
643
const Array<RodStretchShear> &rods = mSettings->mRodStretchShearConstraints;
644
645
for (const RodBendTwist *r = mSettings->mRodBendTwistConstraints.data() + inStartIndex, *r_end = mSettings->mRodBendTwistConstraints.data() + inEndIndex; r < r_end; ++r)
646
{
647
uint32 rod1_index = r->mRod[0];
648
uint32 rod2_index = r->mRod[1];
649
const RodStretchShear &rod1 = rods[rod1_index];
650
const RodStretchShear &rod2 = rods[rod2_index];
651
RodState &rod1_state = mRodStates[rod1_index];
652
RodState &rod2_state = mRodStates[rod2_index];
653
654
// Apply bend and twist constraint
655
// Equation 40 from "Position and Orientation Based Cosserat Rods" - Kugelstadt and Schoemer - SIGGRAPH 2016
656
float denom = rod1.mInvMass + rod2.mInvMass + r->mCompliance * inv_dt_sq;
657
if (denom < 1.0e-12f)
658
continue;
659
Quat rotation1 = rod1_state.mRotation;
660
Quat rotation2 = rod2_state.mRotation;
661
Quat omega = rotation1.Conjugated() * rotation2;
662
Quat omega0 = r->mOmega0;
663
Vec4 omega_min_omega0 = (omega - omega0).GetXYZW();
664
Vec4 omega_plus_omega0 = (omega + omega0).GetXYZW();
665
// Take the shortest of the two rotations
666
Quat 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))));
667
delta_omega /= denom;
668
delta_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.
669
Quat delta_rod2 = rod2.mInvMass * rotation1 * delta_omega;
670
rotation1 += rod1.mInvMass * rotation2 * delta_omega;
671
rotation2 -= delta_rod2;
672
673
// Renormalize
674
rod1_state.mRotation = rotation1.Normalized();
675
rod2_state.mRotation = rotation2.Normalized();
676
}
677
}
678
679
void SoftBodyMotionProperties::ApplyLRAConstraints(uint inStartIndex, uint inEndIndex)
680
{
681
JPH_PROFILE_FUNCTION();
682
683
// Satisfy LRA constraints
684
Vertex *vertices = mVertices.data();
685
for (const LRA *lra = mSettings->mLRAConstraints.data() + inStartIndex, *lra_end = mSettings->mLRAConstraints.data() + inEndIndex; lra < lra_end; ++lra)
686
{
687
JPH_ASSERT(lra->mVertex[0] < mVertices.size());
688
JPH_ASSERT(lra->mVertex[1] < mVertices.size());
689
const Vertex &vertex0 = vertices[lra->mVertex[0]];
690
Vertex &vertex1 = vertices[lra->mVertex[1]];
691
692
Vec3 x0 = vertex0.mPosition;
693
Vec3 delta = vertex1.mPosition - x0;
694
float delta_len_sq = delta.LengthSq();
695
if (delta_len_sq > Square(lra->mMaxDistance))
696
vertex1.mPosition = x0 + delta * lra->mMaxDistance / sqrt(delta_len_sq);
697
}
698
}
699
700
void SoftBodyMotionProperties::ApplyCollisionConstraintsAndUpdateVelocities(const SoftBodyUpdateContext &inContext)
701
{
702
JPH_PROFILE_FUNCTION();
703
704
float dt = inContext.mSubStepDeltaTime;
705
float restitution_threshold = -2.0f * inContext.mGravity.Length() * dt;
706
float vertex_radius = mVertexRadius;
707
for (Vertex &v : mVertices)
708
if (v.mInvMass > 0.0f)
709
{
710
// Remember previous velocity for restitution calculations
711
Vec3 prev_v = v.mVelocity;
712
713
// XPBD velocity update
714
v.mVelocity = (v.mPosition - v.mPreviousPosition) / dt;
715
716
// Satisfy collision constraint
717
if (v.mCollidingShapeIndex >= 0)
718
{
719
// Check if there is a collision
720
float projected_distance = -v.mCollisionPlane.SignedDistance(v.mPosition) + vertex_radius;
721
if (projected_distance > 0.0f)
722
{
723
// Remember that there was a collision
724
v.mHasContact = true;
725
726
// We need a contact callback if one of the vertices collided
727
mNeedContactCallback.store(true, memory_order_relaxed);
728
729
// Note that we already calculated the velocity, so this does not affect the velocity (next iteration starts by setting previous position to current position)
730
CollidingShape &cs = mCollidingShapes[v.mCollidingShapeIndex];
731
Vec3 contact_normal = v.mCollisionPlane.GetNormal();
732
v.mPosition += contact_normal * projected_distance;
733
734
// Apply friction as described in Detailed Rigid Body Simulation with Extended Position Based Dynamics - Matthias Muller et al.
735
// See section 3.6:
736
// Inverse mass: w1 = 1 / m1, w2 = 1 / m2 + (r2 x n)^T I^-1 (r2 x n) = 0 for a static object
737
// r2 are the contact point relative to the center of mass of body 2
738
// Lagrange multiplier for contact: lambda = -c / (w1 + w2)
739
// Where c is the constraint equation (the distance to the plane, negative because penetrating)
740
// Contact normal force: fn = lambda / dt^2
741
// Delta velocity due to friction dv = -vt / |vt| * min(dt * friction * fn * (w1 + w2), |vt|) = -vt * min(-friction * c / (|vt| * dt), 1)
742
// 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
743
// Relative velocity: vr = v1 - v2 - omega2 x r2
744
// Normal velocity: vn = vr . contact_normal
745
// Tangential velocity: vt = vr - contact_normal * vn
746
// Impulse: p = dv / (w1 + w2)
747
// Changes in particle velocities:
748
// v1 = v1 + p / m1
749
// v2 = v2 - p / m2 (no change when colliding with a static body)
750
// w2 = w2 - I^-1 (r2 x p) (no change when colliding with a static body)
751
if (cs.mMotionType == EMotionType::Dynamic)
752
{
753
// Calculate normal and tangential velocity (equation 30)
754
Vec3 r2 = v.mPosition - cs.mCenterOfMassTransform.GetTranslation();
755
Vec3 v2 = cs.GetPointVelocity(r2);
756
Vec3 relative_velocity = v.mVelocity - v2;
757
Vec3 v_normal = contact_normal * contact_normal.Dot(relative_velocity);
758
Vec3 v_tangential = relative_velocity - v_normal;
759
float v_tangential_length = v_tangential.Length();
760
761
// Calculate resulting inverse mass of vertex
762
float vertex_inv_mass = cs.mSoftBodyInvMassScale * v.mInvMass;
763
764
// Calculate inverse effective mass
765
Vec3 r2_cross_n = r2.Cross(contact_normal);
766
float w2 = cs.mInvMass + r2_cross_n.Dot(cs.mInvInertia * r2_cross_n);
767
float w1_plus_w2 = vertex_inv_mass + w2;
768
if (w1_plus_w2 > 0.0f)
769
{
770
// Calculate delta relative velocity due to friction (modified equation 31)
771
Vec3 dv;
772
if (v_tangential_length > 0.0f)
773
dv = v_tangential * min(cs.mFriction * projected_distance / (v_tangential_length * dt), 1.0f);
774
else
775
dv = Vec3::sZero();
776
777
// Calculate delta relative velocity due to restitution (equation 35)
778
dv += v_normal;
779
float prev_v_normal = (prev_v - v2).Dot(contact_normal);
780
if (prev_v_normal < restitution_threshold)
781
dv += cs.mRestitution * prev_v_normal * contact_normal;
782
783
// Calculate impulse
784
Vec3 p = dv / w1_plus_w2;
785
786
// Apply impulse to particle
787
v.mVelocity -= p * vertex_inv_mass;
788
789
// Apply impulse to rigid body
790
cs.mLinearVelocity += p * cs.mInvMass;
791
cs.mAngularVelocity += cs.mInvInertia * r2.Cross(p);
792
793
// Mark that the velocities of the body we hit need to be updated
794
cs.mUpdateVelocities = true;
795
}
796
}
797
else if (cs.mSoftBodyInvMassScale > 0.0f)
798
{
799
// Body is not movable, equations are simpler
800
801
// Calculate normal and tangential velocity (equation 30)
802
Vec3 v_normal = contact_normal * contact_normal.Dot(v.mVelocity);
803
Vec3 v_tangential = v.mVelocity - v_normal;
804
float v_tangential_length = v_tangential.Length();
805
806
// Apply friction (modified equation 31)
807
if (v_tangential_length > 0.0f)
808
v.mVelocity -= v_tangential * min(cs.mFriction * projected_distance / (v_tangential_length * dt), 1.0f);
809
810
// Apply restitution (equation 35)
811
v.mVelocity -= v_normal;
812
float prev_v_normal = prev_v.Dot(contact_normal);
813
if (prev_v_normal < restitution_threshold)
814
v.mVelocity -= cs.mRestitution * prev_v_normal * contact_normal;
815
}
816
}
817
}
818
}
819
820
// Calculate the new angular velocity for all rods
821
float two_div_dt = 2.0f / dt;
822
for (RodState &r : mRodStates)
823
r.mAngularVelocity = two_div_dt * (r.mRotation * r.mPreviousRotationInternal.Conjugated()).GetXYZ(); // Overwrites mPreviousRotationInternal
824
}
825
826
void SoftBodyMotionProperties::UpdateSoftBodyState(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings)
827
{
828
JPH_PROFILE_FUNCTION();
829
830
// Contact callback
831
if (mNeedContactCallback.load(memory_order_relaxed) && ioContext.mContactListener != nullptr)
832
{
833
// Remove non-colliding sensors from the list
834
for (int i = int(mCollidingSensors.size()) - 1; i >= 0; --i)
835
if (!mCollidingSensors[i].mHasContact)
836
{
837
mCollidingSensors[i] = std::move(mCollidingSensors.back());
838
mCollidingSensors.pop_back();
839
}
840
841
ioContext.mContactListener->OnSoftBodyContactAdded(*ioContext.mBody, SoftBodyManifold(this));
842
}
843
844
// Loop through vertices once more to update the global state
845
float dt = ioContext.mDeltaTime;
846
float max_linear_velocity_sq = Square(GetMaxLinearVelocity());
847
float max_v_sq = 0.0f;
848
Vec3 linear_velocity = Vec3::sZero(), angular_velocity = Vec3::sZero();
849
mLocalPredictedBounds = mLocalBounds = { };
850
for (Vertex &v : mVertices)
851
{
852
// Calculate max square velocity
853
float v_sq = v.mVelocity.LengthSq();
854
max_v_sq = max(max_v_sq, v_sq);
855
856
// Clamp if velocity is too high
857
if (v_sq > max_linear_velocity_sq)
858
v.mVelocity *= sqrt(max_linear_velocity_sq / v_sq);
859
860
// Calculate local linear/angular velocity
861
linear_velocity += v.mVelocity;
862
angular_velocity += v.mPosition.Cross(v.mVelocity);
863
864
// Update local bounding box
865
mLocalBounds.Encapsulate(v.mPosition);
866
867
// Create predicted position for the next frame in order to detect collisions before they happen
868
mLocalPredictedBounds.Encapsulate(v.mPosition + v.mVelocity * dt + ioContext.mDisplacementDueToGravity);
869
870
// Reset collision data for the next iteration
871
v.ResetCollision();
872
}
873
874
// Calculate linear/angular velocity of the body by averaging all vertices and bringing the value to world space
875
float num_vertices_divider = float(max(int(mVertices.size()), 1));
876
SetLinearVelocityClamped(ioContext.mCenterOfMassTransform.Multiply3x3(linear_velocity / num_vertices_divider));
877
SetAngularVelocity(ioContext.mCenterOfMassTransform.Multiply3x3(angular_velocity / num_vertices_divider));
878
879
if (mUpdatePosition)
880
{
881
// Shift the body so that the position is the center of the local bounds
882
Vec3 delta = mLocalBounds.GetCenter();
883
ioContext.mDeltaPosition = ioContext.mCenterOfMassTransform.Multiply3x3(delta);
884
for (Vertex &v : mVertices)
885
v.mPosition -= delta;
886
887
// Update the skin state too since we will use this position as the previous position in the next update
888
for (SkinState &s : mSkinState)
889
s.mPosition -= delta;
890
JPH_IF_DEBUG_RENDERER(mSkinStateTransform.SetTranslation(mSkinStateTransform.GetTranslation() + ioContext.mDeltaPosition);)
891
892
// Offset bounds to match new position
893
mLocalBounds.Translate(-delta);
894
mLocalPredictedBounds.Translate(-delta);
895
}
896
else
897
ioContext.mDeltaPosition = Vec3::sZero();
898
899
// Test if we should go to sleep
900
if (GetAllowSleeping())
901
{
902
if (max_v_sq > inPhysicsSettings.mPointVelocitySleepThreshold)
903
{
904
ResetSleepTestTimer();
905
ioContext.mCanSleep = ECanSleep::CannotSleep;
906
}
907
else
908
ioContext.mCanSleep = AccumulateSleepTime(dt, inPhysicsSettings.mTimeBeforeSleep);
909
}
910
else
911
ioContext.mCanSleep = ECanSleep::CannotSleep;
912
913
// If SkinVertices is not called after this then don't use the previous position as the skin is static
914
mSkinStatePreviousPositionValid = false;
915
916
// Reset force accumulator
917
ResetForce();
918
}
919
920
void SoftBodyMotionProperties::UpdateRigidBodyVelocities(const SoftBodyUpdateContext &inContext, BodyInterface &inBodyInterface)
921
{
922
JPH_PROFILE_FUNCTION();
923
924
// Write back velocity deltas
925
for (const CollidingShape &cs : mCollidingShapes)
926
if (cs.mUpdateVelocities)
927
inBodyInterface.AddLinearAndAngularVelocity(cs.mBodyID, inContext.mCenterOfMassTransform.Multiply3x3(cs.mLinearVelocity - cs.mOriginalLinearVelocity), inContext.mCenterOfMassTransform.Multiply3x3(cs.mAngularVelocity - cs.mOriginalAngularVelocity));
928
929
// Clear colliding shapes/sensors to avoid hanging on to references to shapes
930
mCollidingShapes.clear();
931
mCollidingSensors.clear();
932
}
933
934
void SoftBodyMotionProperties::InitializeUpdateContext(float inDeltaTime, Body &inSoftBody, const PhysicsSystem &inSystem, SoftBodyUpdateContext &ioContext)
935
{
936
JPH_PROFILE_FUNCTION();
937
938
// Store body
939
ioContext.mBody = &inSoftBody;
940
ioContext.mMotionProperties = this;
941
ioContext.mContactListener = inSystem.GetSoftBodyContactListener();
942
ioContext.mSimShapeFilter = inSystem.GetSimShapeFilter();
943
944
// Convert gravity to local space
945
ioContext.mCenterOfMassTransform = inSoftBody.GetCenterOfMassTransform();
946
ioContext.mGravity = ioContext.mCenterOfMassTransform.Multiply3x3Transposed(GetGravityFactor() * inSystem.GetGravity());
947
948
// Calculate delta time for sub step
949
ioContext.mDeltaTime = inDeltaTime;
950
ioContext.mSubStepDeltaTime = inDeltaTime / mNumIterations;
951
952
// Calculate total displacement we'll have due to gravity over all sub steps
953
// The total displacement as produced by our integrator can be written as: Sum(i * g * dt^2, i = 0..mNumIterations).
954
// This is bigger than 0.5 * g * dt^2 because we first increment the velocity and then update the position
955
// Using Sum(i, i = 0..n) = n * (n + 1) / 2 we can write this as:
956
ioContext.mDisplacementDueToGravity = (0.5f * mNumIterations * (mNumIterations + 1) * Square(ioContext.mSubStepDeltaTime)) * ioContext.mGravity;
957
}
958
959
void SoftBodyMotionProperties::StartNextIteration(const SoftBodyUpdateContext &ioContext)
960
{
961
ApplyPressure(ioContext);
962
963
IntegratePositions(ioContext);
964
}
965
966
void SoftBodyMotionProperties::StartFirstIteration(SoftBodyUpdateContext &ioContext)
967
{
968
// Start the first iteration
969
JPH_IF_ENABLE_ASSERTS(uint iteration =) ioContext.mNextIteration.fetch_add(1, memory_order_relaxed);
970
JPH_ASSERT(iteration == 0);
971
StartNextIteration(ioContext);
972
ioContext.mState.store(SoftBodyUpdateContext::EState::ApplyConstraints, memory_order_release);
973
}
974
975
SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelDetermineCollisionPlanes(SoftBodyUpdateContext &ioContext)
976
{
977
// 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)
978
uint num_vertices = (uint)mVertices.size();
979
if (ioContext.mNextCollisionVertex.load(memory_order_relaxed) < num_vertices)
980
{
981
// Fetch next batch of vertices to process
982
uint next_vertex = ioContext.mNextCollisionVertex.fetch_add(SoftBodyUpdateContext::cVertexCollisionBatch, memory_order_acquire);
983
if (next_vertex < num_vertices)
984
{
985
// Process collision planes
986
uint num_vertices_to_process = min(SoftBodyUpdateContext::cVertexCollisionBatch, num_vertices - next_vertex);
987
DetermineCollisionPlanes(next_vertex, num_vertices_to_process);
988
uint vertices_processed = ioContext.mNumCollisionVerticesProcessed.fetch_add(SoftBodyUpdateContext::cVertexCollisionBatch, memory_order_acq_rel) + num_vertices_to_process;
989
if (vertices_processed >= num_vertices)
990
{
991
// Determine next state
992
if (mCollidingSensors.empty())
993
StartFirstIteration(ioContext);
994
else
995
ioContext.mState.store(SoftBodyUpdateContext::EState::DetermineSensorCollisions, memory_order_release);
996
}
997
return EStatus::DidWork;
998
}
999
}
1000
1001
return EStatus::NoWork;
1002
}
1003
1004
SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelDetermineSensorCollisions(SoftBodyUpdateContext &ioContext)
1005
{
1006
// Do a relaxed read to see if there are more sensors to process
1007
if (ioContext.mNextSensorIndex.load(memory_order_relaxed) < mNumSensors)
1008
{
1009
// Fetch next sensor to process
1010
uint sensor_index = ioContext.mNextSensorIndex.fetch_add(1, memory_order_acquire);
1011
if (sensor_index < mNumSensors)
1012
{
1013
// Process this sensor
1014
DetermineSensorCollisions(mCollidingSensors[sensor_index]);
1015
1016
// Determine next state
1017
uint sensors_processed = ioContext.mNumSensorsProcessed.fetch_add(1, memory_order_acq_rel) + 1;
1018
if (sensors_processed >= mNumSensors)
1019
StartFirstIteration(ioContext);
1020
return EStatus::DidWork;
1021
}
1022
}
1023
1024
return EStatus::NoWork;
1025
}
1026
1027
void SoftBodyMotionProperties::ProcessGroup(const SoftBodyUpdateContext &ioContext, uint inGroupIndex)
1028
{
1029
// Determine start and end
1030
SoftBodySharedSettings::UpdateGroup start { 0, 0, 0, 0, 0, 0, 0 };
1031
const SoftBodySharedSettings::UpdateGroup &prev = inGroupIndex > 0? mSettings->mUpdateGroups[inGroupIndex - 1] : start;
1032
const SoftBodySharedSettings::UpdateGroup &current = mSettings->mUpdateGroups[inGroupIndex];
1033
1034
// Process volume constraints
1035
ApplyVolumeConstraints(ioContext, prev.mVolumeEndIndex, current.mVolumeEndIndex);
1036
1037
// Process bend constraints
1038
ApplyDihedralBendConstraints(ioContext, prev.mDihedralBendEndIndex, current.mDihedralBendEndIndex);
1039
1040
// Process skinned constraints
1041
ApplySkinConstraints(ioContext, prev.mSkinnedEndIndex, current.mSkinnedEndIndex);
1042
1043
// Process edges
1044
ApplyEdgeConstraints(ioContext, prev.mEdgeEndIndex, current.mEdgeEndIndex);
1045
1046
// Process rods
1047
ApplyRodStretchShearConstraints(ioContext, prev.mRodStretchShearEndIndex, current.mRodStretchShearEndIndex);
1048
ApplyRodBendTwistConstraints(ioContext, prev.mRodBendTwistEndIndex, current.mRodBendTwistEndIndex);
1049
1050
// Process LRA constraints
1051
ApplyLRAConstraints(prev.mLRAEndIndex, current.mLRAEndIndex);
1052
}
1053
1054
SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelApplyConstraints(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings)
1055
{
1056
uint num_groups = (uint)mSettings->mUpdateGroups.size();
1057
JPH_ASSERT(num_groups > 0, "SoftBodySharedSettings::Optimize should have been called!");
1058
--num_groups; // Last group is the non-parallel group, we don't want to execute it in parallel
1059
1060
// 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)
1061
uint next_group = ioContext.mNextConstraintGroup.load(memory_order_relaxed);
1062
if (next_group < num_groups || (num_groups == 0 && next_group == 0))
1063
{
1064
// Fetch the next group process
1065
next_group = ioContext.mNextConstraintGroup.fetch_add(1, memory_order_acquire);
1066
if (next_group < num_groups || (num_groups == 0 && next_group == 0))
1067
{
1068
uint num_groups_processed = 0;
1069
if (num_groups > 0)
1070
{
1071
// Process this group
1072
ProcessGroup(ioContext, next_group);
1073
1074
// Increment total number of groups processed
1075
num_groups_processed = ioContext.mNumConstraintGroupsProcessed.fetch_add(1, memory_order_acq_rel) + 1;
1076
}
1077
1078
if (num_groups_processed >= num_groups)
1079
{
1080
// Finish the iteration
1081
JPH_PROFILE("FinishIteration");
1082
1083
// Process non-parallel group
1084
ProcessGroup(ioContext, num_groups);
1085
1086
ApplyCollisionConstraintsAndUpdateVelocities(ioContext);
1087
1088
uint iteration = ioContext.mNextIteration.fetch_add(1, memory_order_relaxed);
1089
if (iteration < mNumIterations)
1090
{
1091
// Start a new iteration
1092
StartNextIteration(ioContext);
1093
1094
// Reset group logic
1095
ioContext.mNumConstraintGroupsProcessed.store(0, memory_order_release);
1096
ioContext.mNextConstraintGroup.store(0, memory_order_release);
1097
}
1098
else
1099
{
1100
// On final iteration we update the state
1101
UpdateSoftBodyState(ioContext, inPhysicsSettings);
1102
1103
ioContext.mState.store(SoftBodyUpdateContext::EState::Done, memory_order_release);
1104
return EStatus::Done;
1105
}
1106
}
1107
1108
return EStatus::DidWork;
1109
}
1110
}
1111
return EStatus::NoWork;
1112
}
1113
1114
SoftBodyMotionProperties::EStatus SoftBodyMotionProperties::ParallelUpdate(SoftBodyUpdateContext &ioContext, const PhysicsSettings &inPhysicsSettings)
1115
{
1116
switch (ioContext.mState.load(memory_order_acquire))
1117
{
1118
case SoftBodyUpdateContext::EState::DetermineCollisionPlanes:
1119
return ParallelDetermineCollisionPlanes(ioContext);
1120
1121
case SoftBodyUpdateContext::EState::DetermineSensorCollisions:
1122
return ParallelDetermineSensorCollisions(ioContext);
1123
1124
case SoftBodyUpdateContext::EState::ApplyConstraints:
1125
return ParallelApplyConstraints(ioContext, inPhysicsSettings);
1126
1127
case SoftBodyUpdateContext::EState::Done:
1128
return EStatus::Done;
1129
1130
default:
1131
JPH_ASSERT(false);
1132
return EStatus::NoWork;
1133
}
1134
}
1135
1136
void SoftBodyMotionProperties::SkinVertices([[maybe_unused]] RMat44Arg inCenterOfMassTransform, const Mat44 *inJointMatrices, [[maybe_unused]] uint inNumJoints, bool inHardSkinAll, TempAllocator &ioTempAllocator)
1137
{
1138
// Calculate the skin matrices
1139
uint num_skin_matrices = uint(mSettings->mInvBindMatrices.size());
1140
uint skin_matrices_size = num_skin_matrices * sizeof(Mat44);
1141
Mat44 *skin_matrices = (Mat44 *)ioTempAllocator.Allocate(skin_matrices_size);
1142
JPH_SCOPE_EXIT([&ioTempAllocator, skin_matrices, skin_matrices_size]{ ioTempAllocator.Free(skin_matrices, skin_matrices_size); });
1143
const Mat44 *skin_matrices_end = skin_matrices + num_skin_matrices;
1144
const InvBind *inv_bind_matrix = mSettings->mInvBindMatrices.data();
1145
for (Mat44 *s = skin_matrices; s < skin_matrices_end; ++s, ++inv_bind_matrix)
1146
{
1147
JPH_ASSERT(inv_bind_matrix->mJointIndex < inNumJoints);
1148
*s = inJointMatrices[inv_bind_matrix->mJointIndex] * inv_bind_matrix->mInvBind;
1149
}
1150
1151
// Skin the vertices
1152
JPH_IF_DEBUG_RENDERER(mSkinStateTransform = inCenterOfMassTransform;)
1153
JPH_IF_ENABLE_ASSERTS(uint num_vertices = uint(mSettings->mVertices.size());)
1154
JPH_ASSERT(mSkinState.size() == num_vertices);
1155
const SoftBodySharedSettings::Vertex *in_vertices = mSettings->mVertices.data();
1156
for (const Skinned &s : mSettings->mSkinnedConstraints)
1157
{
1158
// Get bind pose
1159
JPH_ASSERT(s.mVertex < num_vertices);
1160
Vec3 bind_pos = Vec3::sLoadFloat3Unsafe(in_vertices[s.mVertex].mPosition);
1161
1162
// Skin vertex
1163
Vec3 pos = Vec3::sZero();
1164
for (const SkinWeight &w : s.mWeights)
1165
{
1166
// We assume that the first zero weight is the end of the list
1167
if (w.mWeight == 0.0f)
1168
break;
1169
1170
JPH_ASSERT(w.mInvBindIndex < num_skin_matrices);
1171
pos += w.mWeight * (skin_matrices[w.mInvBindIndex] * bind_pos);
1172
}
1173
SkinState &skin_state = mSkinState[s.mVertex];
1174
skin_state.mPreviousPosition = skin_state.mPosition;
1175
skin_state.mPosition = pos;
1176
}
1177
1178
// Calculate the normals
1179
for (const Skinned &s : mSettings->mSkinnedConstraints)
1180
{
1181
Vec3 normal = Vec3::sZero();
1182
uint32 num_faces = s.mNormalInfo >> 24;
1183
if (num_faces > 0)
1184
{
1185
// Calculate normal
1186
const uint32 *f = &mSettings->mSkinnedConstraintNormals[s.mNormalInfo & 0xffffff];
1187
const uint32 *f_end = f + num_faces;
1188
while (f < f_end)
1189
{
1190
const Face &face = mSettings->mFaces[*f];
1191
Vec3 v0 = mSkinState[face.mVertex[0]].mPosition;
1192
Vec3 v1 = mSkinState[face.mVertex[1]].mPosition;
1193
Vec3 v2 = mSkinState[face.mVertex[2]].mPosition;
1194
normal += (v1 - v0).Cross(v2 - v0).NormalizedOr(Vec3::sZero());
1195
++f;
1196
}
1197
normal = normal.NormalizedOr(Vec3::sZero());
1198
}
1199
mSkinState[s.mVertex].mNormal = normal;
1200
}
1201
1202
if (inHardSkinAll)
1203
{
1204
// Hard skin all vertices and reset their velocities
1205
for (const Skinned &s : mSettings->mSkinnedConstraints)
1206
{
1207
Vertex &vertex = mVertices[s.mVertex];
1208
SkinState &skin_state = mSkinState[s.mVertex];
1209
skin_state.mPreviousPosition = skin_state.mPosition;
1210
vertex.mPosition = skin_state.mPosition;
1211
vertex.mVelocity = Vec3::sZero();
1212
}
1213
}
1214
else if (!mEnableSkinConstraints)
1215
{
1216
// Hard skin only the kinematic vertices as we will not solve the skin constraints later
1217
for (const Skinned &s : mSettings->mSkinnedConstraints)
1218
if (s.mMaxDistance == 0.0f)
1219
{
1220
Vertex &vertex = mVertices[s.mVertex];
1221
vertex.mPosition = mSkinState[s.mVertex].mPosition;
1222
}
1223
}
1224
1225
// Indicate that the previous positions are valid for the coming update
1226
mSkinStatePreviousPositionValid = true;
1227
}
1228
1229
void SoftBodyMotionProperties::CustomUpdate(float inDeltaTime, Body &ioSoftBody, PhysicsSystem &inSystem)
1230
{
1231
JPH_PROFILE_FUNCTION();
1232
1233
// Create update context
1234
SoftBodyUpdateContext context;
1235
InitializeUpdateContext(inDeltaTime, ioSoftBody, inSystem, context);
1236
1237
// Determine bodies we're colliding with
1238
DetermineCollidingShapes(context, inSystem, inSystem.GetBodyLockInterface());
1239
1240
// Call the internal update until it finishes
1241
EStatus status;
1242
const PhysicsSettings &settings = inSystem.GetPhysicsSettings();
1243
while ((status = ParallelUpdate(context, settings)) == EStatus::DidWork)
1244
continue;
1245
JPH_ASSERT(status == EStatus::Done);
1246
1247
// Update the state of the bodies we've collided with
1248
UpdateRigidBodyVelocities(context, inSystem.GetBodyInterface());
1249
1250
// Update position of the soft body
1251
if (mUpdatePosition)
1252
inSystem.GetBodyInterface().SetPosition(ioSoftBody.GetID(), ioSoftBody.GetPosition() + context.mDeltaPosition, EActivation::DontActivate);
1253
}
1254
1255
#ifdef JPH_DEBUG_RENDERER
1256
1257
void SoftBodyMotionProperties::DrawVertices(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const
1258
{
1259
for (const Vertex &v : mVertices)
1260
inRenderer->DrawMarker(inCenterOfMassTransform * v.mPosition, v.mInvMass > 0.0f? Color::sGreen : Color::sRed, 0.05f);
1261
}
1262
1263
void SoftBodyMotionProperties::DrawVertexVelocities(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const
1264
{
1265
for (const Vertex &v : mVertices)
1266
inRenderer->DrawArrow(inCenterOfMassTransform * v.mPosition, inCenterOfMassTransform * (v.mPosition + v.mVelocity), Color::sYellow, 0.01f);
1267
}
1268
1269
template <typename GetEndIndex, typename DrawConstraint>
1270
inline void SoftBodyMotionProperties::DrawConstraints(ESoftBodyConstraintColor inConstraintColor, const GetEndIndex &inGetEndIndex, const DrawConstraint &inDrawConstraint, ColorArg inBaseColor) const
1271
{
1272
uint start = 0;
1273
for (uint i = 0; i < (uint)mSettings->mUpdateGroups.size(); ++i)
1274
{
1275
uint end = inGetEndIndex(mSettings->mUpdateGroups[i]);
1276
1277
Color base_color;
1278
if (inConstraintColor != ESoftBodyConstraintColor::ConstraintType)
1279
base_color = Color::sGetDistinctColor((uint)mSettings->mUpdateGroups.size() - i - 1); // Ensure that color 0 is always the last group
1280
else
1281
base_color = inBaseColor;
1282
1283
for (uint idx = start; idx < end; ++idx)
1284
{
1285
Color color = inConstraintColor == ESoftBodyConstraintColor::ConstraintOrder? base_color * (float(idx - start) / (end - start)) : base_color;
1286
inDrawConstraint(idx, color);
1287
}
1288
1289
start = end;
1290
}
1291
}
1292
1293
void SoftBodyMotionProperties::DrawEdgeConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const
1294
{
1295
DrawConstraints(inConstraintColor,
1296
[](const SoftBodySharedSettings::UpdateGroup &inGroup) {
1297
return inGroup.mEdgeEndIndex;
1298
},
1299
[this, inRenderer, &inCenterOfMassTransform](uint inIndex, ColorArg inColor) {
1300
const Edge &e = mSettings->mEdgeConstraints[inIndex];
1301
inRenderer->DrawLine(inCenterOfMassTransform * mVertices[e.mVertex[0]].mPosition, inCenterOfMassTransform * mVertices[e.mVertex[1]].mPosition, inColor);
1302
},
1303
Color::sWhite);
1304
}
1305
1306
void SoftBodyMotionProperties::DrawRods(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const
1307
{
1308
DrawConstraints(inConstraintColor,
1309
[](const SoftBodySharedSettings::UpdateGroup &inGroup) {
1310
return inGroup.mRodStretchShearEndIndex;
1311
},
1312
[this, inRenderer, &inCenterOfMassTransform](uint inIndex, ColorArg inColor) {
1313
const RodStretchShear &r = mSettings->mRodStretchShearConstraints[inIndex];
1314
inRenderer->DrawLine(inCenterOfMassTransform * mVertices[r.mVertex[0]].mPosition, inCenterOfMassTransform * mVertices[r.mVertex[1]].mPosition, inColor);
1315
},
1316
Color::sWhite);
1317
}
1318
1319
void SoftBodyMotionProperties::DrawRodStates(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const
1320
{
1321
DrawConstraints(inConstraintColor,
1322
[](const SoftBodySharedSettings::UpdateGroup &inGroup) {
1323
return inGroup.mRodStretchShearEndIndex;
1324
},
1325
[this, inRenderer, &inCenterOfMassTransform](uint inIndex, ColorArg inColor) {
1326
const RodState &state = mRodStates[inIndex];
1327
const RodStretchShear &rod = mSettings->mRodStretchShearConstraints[inIndex];
1328
1329
RVec3 x0 = inCenterOfMassTransform * mVertices[rod.mVertex[0]].mPosition;
1330
RVec3 x1 = inCenterOfMassTransform * mVertices[rod.mVertex[1]].mPosition;
1331
1332
RMat44 rod_center = inCenterOfMassTransform;
1333
rod_center.SetTranslation(0.5_r * (x0 + x1));
1334
inRenderer->DrawArrow(rod_center.GetTranslation(), rod_center.GetTranslation() + state.mAngularVelocity, inColor, 0.01f * rod.mLength);
1335
1336
RMat44 rod_frame = rod_center * RMat44::sRotation(state.mRotation);
1337
inRenderer->DrawCoordinateSystem(rod_frame, 0.3f * rod.mLength);
1338
},
1339
Color::sOrange);
1340
}
1341
1342
void SoftBodyMotionProperties::DrawRodBendTwistConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const
1343
{
1344
DrawConstraints(inConstraintColor,
1345
[](const SoftBodySharedSettings::UpdateGroup &inGroup) {
1346
return inGroup.mRodBendTwistEndIndex;
1347
},
1348
[this, inRenderer, &inCenterOfMassTransform](uint inIndex, ColorArg inColor) {
1349
uint r1 = mSettings->mRodBendTwistConstraints[inIndex].mRod[0];
1350
uint r2 = mSettings->mRodBendTwistConstraints[inIndex].mRod[1];
1351
const RodStretchShear &rod1 = mSettings->mRodStretchShearConstraints[r1];
1352
const RodStretchShear &rod2 = mSettings->mRodStretchShearConstraints[r2];
1353
1354
RVec3 x0 = inCenterOfMassTransform * (0.4f * mVertices[rod1.mVertex[0]].mPosition + 0.6f * mVertices[rod1.mVertex[1]].mPosition);
1355
RVec3 x1 = inCenterOfMassTransform * (0.6f * mVertices[rod2.mVertex[0]].mPosition + 0.4f * mVertices[rod2.mVertex[1]].mPosition);
1356
1357
inRenderer->DrawLine(x0, x1, inColor);
1358
},
1359
Color::sGreen);
1360
}
1361
1362
void SoftBodyMotionProperties::DrawBendConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const
1363
{
1364
DrawConstraints(inConstraintColor,
1365
[](const SoftBodySharedSettings::UpdateGroup &inGroup) {
1366
return inGroup.mDihedralBendEndIndex;
1367
},
1368
[this, inRenderer, &inCenterOfMassTransform](uint inIndex, ColorArg inColor) {
1369
const DihedralBend &b = mSettings->mDihedralBendConstraints[inIndex];
1370
1371
RVec3 x0 = inCenterOfMassTransform * mVertices[b.mVertex[0]].mPosition;
1372
RVec3 x1 = inCenterOfMassTransform * mVertices[b.mVertex[1]].mPosition;
1373
RVec3 x2 = inCenterOfMassTransform * mVertices[b.mVertex[2]].mPosition;
1374
RVec3 x3 = inCenterOfMassTransform * mVertices[b.mVertex[3]].mPosition;
1375
RVec3 c_edge = 0.5_r * (x0 + x1);
1376
RVec3 c0 = (x0 + x1 + x2) / 3.0_r;
1377
RVec3 c1 = (x0 + x1 + x3) / 3.0_r;
1378
1379
inRenderer->DrawArrow(0.9_r * x0 + 0.1_r * x1, 0.1_r * x0 + 0.9_r * x1, inColor, 0.01f);
1380
inRenderer->DrawLine(c_edge, 0.1_r * c_edge + 0.9_r * c0, inColor);
1381
inRenderer->DrawLine(c_edge, 0.1_r * c_edge + 0.9_r * c1, inColor);
1382
},
1383
Color::sGreen);
1384
}
1385
1386
void SoftBodyMotionProperties::DrawVolumeConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const
1387
{
1388
DrawConstraints(inConstraintColor,
1389
[](const SoftBodySharedSettings::UpdateGroup &inGroup) {
1390
return inGroup.mVolumeEndIndex;
1391
},
1392
[this, inRenderer, &inCenterOfMassTransform](uint inIndex, ColorArg inColor) {
1393
const Volume &v = mSettings->mVolumeConstraints[inIndex];
1394
1395
RVec3 x1 = inCenterOfMassTransform * mVertices[v.mVertex[0]].mPosition;
1396
RVec3 x2 = inCenterOfMassTransform * mVertices[v.mVertex[1]].mPosition;
1397
RVec3 x3 = inCenterOfMassTransform * mVertices[v.mVertex[2]].mPosition;
1398
RVec3 x4 = inCenterOfMassTransform * mVertices[v.mVertex[3]].mPosition;
1399
1400
inRenderer->DrawTriangle(x1, x3, x2, inColor, DebugRenderer::ECastShadow::On);
1401
inRenderer->DrawTriangle(x2, x3, x4, inColor, DebugRenderer::ECastShadow::On);
1402
inRenderer->DrawTriangle(x1, x4, x3, inColor, DebugRenderer::ECastShadow::On);
1403
inRenderer->DrawTriangle(x1, x2, x4, inColor, DebugRenderer::ECastShadow::On);
1404
},
1405
Color::sYellow);
1406
}
1407
1408
void SoftBodyMotionProperties::DrawSkinConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const
1409
{
1410
DrawConstraints(inConstraintColor,
1411
[](const SoftBodySharedSettings::UpdateGroup &inGroup) {
1412
return inGroup.mSkinnedEndIndex;
1413
},
1414
[this, inRenderer, &inCenterOfMassTransform](uint inIndex, ColorArg inColor) {
1415
const Skinned &s = mSettings->mSkinnedConstraints[inIndex];
1416
const SkinState &skin_state = mSkinState[s.mVertex];
1417
inRenderer->DrawArrow(mSkinStateTransform * skin_state.mPosition, mSkinStateTransform * (skin_state.mPosition + 0.1f * skin_state.mNormal), inColor, 0.01f);
1418
inRenderer->DrawLine(mSkinStateTransform * skin_state.mPosition, inCenterOfMassTransform * mVertices[s.mVertex].mPosition, Color::sBlue);
1419
},
1420
Color::sOrange);
1421
}
1422
1423
void SoftBodyMotionProperties::DrawLRAConstraints(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, ESoftBodyConstraintColor inConstraintColor) const
1424
{
1425
DrawConstraints(inConstraintColor,
1426
[](const SoftBodySharedSettings::UpdateGroup &inGroup) {
1427
return inGroup.mLRAEndIndex;
1428
},
1429
[this, inRenderer, &inCenterOfMassTransform](uint inIndex, ColorArg inColor) {
1430
const LRA &l = mSettings->mLRAConstraints[inIndex];
1431
inRenderer->DrawLine(inCenterOfMassTransform * mVertices[l.mVertex[0]].mPosition, inCenterOfMassTransform * mVertices[l.mVertex[1]].mPosition, inColor);
1432
},
1433
Color::sGrey);
1434
}
1435
1436
void SoftBodyMotionProperties::DrawPredictedBounds(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform) const
1437
{
1438
inRenderer->DrawWireBox(inCenterOfMassTransform, mLocalPredictedBounds, Color::sRed);
1439
}
1440
1441
#endif // JPH_DEBUG_RENDERER
1442
1443
void SoftBodyMotionProperties::SaveState(StateRecorder &inStream) const
1444
{
1445
MotionProperties::SaveState(inStream);
1446
1447
for (const Vertex &v : mVertices)
1448
{
1449
inStream.Write(v.mPosition);
1450
inStream.Write(v.mVelocity);
1451
}
1452
1453
for (const RodState &r : mRodStates)
1454
{
1455
inStream.Write(r.mRotation);
1456
inStream.Write(r.mAngularVelocity);
1457
}
1458
1459
for (const SkinState &s : mSkinState)
1460
{
1461
inStream.Write(s.mPreviousPosition);
1462
inStream.Write(s.mPosition);
1463
inStream.Write(s.mNormal);
1464
}
1465
1466
inStream.Write(mLocalBounds.mMin);
1467
inStream.Write(mLocalBounds.mMax);
1468
inStream.Write(mLocalPredictedBounds.mMin);
1469
inStream.Write(mLocalPredictedBounds.mMax);
1470
}
1471
1472
void SoftBodyMotionProperties::RestoreState(StateRecorder &inStream)
1473
{
1474
MotionProperties::RestoreState(inStream);
1475
1476
for (Vertex &v : mVertices)
1477
{
1478
inStream.Read(v.mPosition);
1479
inStream.Read(v.mVelocity);
1480
}
1481
1482
for (RodState &r : mRodStates)
1483
{
1484
inStream.Read(r.mRotation);
1485
inStream.Read(r.mAngularVelocity);
1486
}
1487
1488
for (SkinState &s : mSkinState)
1489
{
1490
inStream.Read(s.mPreviousPosition);
1491
inStream.Read(s.mPosition);
1492
inStream.Read(s.mNormal);
1493
}
1494
1495
inStream.Read(mLocalBounds.mMin);
1496
inStream.Read(mLocalBounds.mMax);
1497
inStream.Read(mLocalPredictedBounds.mMin);
1498
inStream.Read(mLocalPredictedBounds.mMax);
1499
}
1500
1501
JPH_NAMESPACE_END
1502
1503