Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Ragdoll/Ragdoll.cpp
9912 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#include <Jolt/Jolt.h>
6
7
#include <Jolt/Physics/Ragdoll/Ragdoll.h>
8
#include <Jolt/Physics/Constraints/SwingTwistConstraint.h>
9
#include <Jolt/Physics/PhysicsSystem.h>
10
#include <Jolt/Physics/Body/BodyLockMulti.h>
11
#include <Jolt/Physics/Collision/GroupFilterTable.h>
12
#include <Jolt/Physics/Collision/CollisionCollectorImpl.h>
13
#include <Jolt/Physics/Collision/CollideShape.h>
14
#include <Jolt/Physics/Collision/CollisionDispatch.h>
15
#include <Jolt/ObjectStream/TypeDeclarations.h>
16
#include <Jolt/Core/StreamIn.h>
17
#include <Jolt/Core/StreamOut.h>
18
19
JPH_NAMESPACE_BEGIN
20
21
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(RagdollSettings::Part)
22
{
23
JPH_ADD_BASE_CLASS(RagdollSettings::Part, BodyCreationSettings)
24
25
JPH_ADD_ATTRIBUTE(RagdollSettings::Part, mToParent)
26
}
27
28
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(RagdollSettings::AdditionalConstraint)
29
{
30
JPH_ADD_ATTRIBUTE(RagdollSettings::AdditionalConstraint, mBodyIdx)
31
JPH_ADD_ATTRIBUTE(RagdollSettings::AdditionalConstraint, mConstraint)
32
}
33
34
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(RagdollSettings)
35
{
36
JPH_ADD_ATTRIBUTE(RagdollSettings, mSkeleton)
37
JPH_ADD_ATTRIBUTE(RagdollSettings, mParts)
38
JPH_ADD_ATTRIBUTE(RagdollSettings, mAdditionalConstraints)
39
}
40
41
static inline BodyInterface &sRagdollGetBodyInterface(PhysicsSystem *inSystem, bool inLockBodies)
42
{
43
return inLockBodies? inSystem->GetBodyInterface() : inSystem->GetBodyInterfaceNoLock();
44
}
45
46
static inline const BodyLockInterface &sRagdollGetBodyLockInterface(const PhysicsSystem *inSystem, bool inLockBodies)
47
{
48
return inLockBodies? static_cast<const BodyLockInterface &>(inSystem->GetBodyLockInterface()) : static_cast<const BodyLockInterface &>(inSystem->GetBodyLockInterfaceNoLock());
49
}
50
51
bool RagdollSettings::Stabilize()
52
{
53
// Based on: Stop my Constraints from Blowing Up! - Oliver Strunk (Havok)
54
// Do 2 things:
55
// 1. Limit the mass ratios between parents and children (slide 16)
56
// 2. Increase the inertia of parents so that they're bigger or equal to the sum of their children (slide 34)
57
58
// If we don't have any joints there's nothing to stabilize
59
if (mSkeleton->GetJointCount() == 0)
60
return true;
61
62
// The skeleton can contain one or more static bodies. We can't modify the mass for those so we start a new stabilization chain for each joint under a static body until we reach the next static body.
63
// This array keeps track of which joints have been processed.
64
Array<bool> visited;
65
visited.resize(mSkeleton->GetJointCount());
66
for (size_t v = 0; v < visited.size(); ++v)
67
{
68
// Mark static bodies as visited so we won't process these
69
Part &p = mParts[v];
70
bool has_mass_properties = p.HasMassProperties();
71
visited[v] = !has_mass_properties;
72
73
if (has_mass_properties && p.mOverrideMassProperties != EOverrideMassProperties::MassAndInertiaProvided)
74
{
75
// Mass properties not yet calculated, do it now
76
p.mMassPropertiesOverride = p.GetMassProperties();
77
p.mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided;
78
}
79
}
80
81
// Find first unvisited part that either has no parent or that has a parent that was visited
82
for (int first_idx = 0; first_idx < mSkeleton->GetJointCount(); ++first_idx)
83
{
84
int first_idx_parent = mSkeleton->GetJoint(first_idx).mParentJointIndex;
85
if (!visited[first_idx] && (first_idx_parent == -1 || visited[first_idx_parent]))
86
{
87
// Find all children of first_idx and their children up to the next static part
88
int next_to_process = 0;
89
Array<int> indices;
90
indices.reserve(mSkeleton->GetJointCount());
91
visited[first_idx] = true;
92
indices.push_back(first_idx);
93
do
94
{
95
int parent_idx = indices[next_to_process++];
96
for (int child_idx = 0; child_idx < mSkeleton->GetJointCount(); ++child_idx)
97
if (!visited[child_idx] && mSkeleton->GetJoint(child_idx).mParentJointIndex == parent_idx)
98
{
99
visited[child_idx] = true;
100
indices.push_back(child_idx);
101
}
102
} while (next_to_process < (int)indices.size());
103
104
// If there's only 1 body, we can't redistribute mass
105
if (indices.size() == 1)
106
continue;
107
108
const float cMinMassRatio = 0.8f;
109
const float cMaxMassRatio = 1.2f;
110
111
// Ensure that the mass ratio from parent to child is within a range
112
float total_mass_ratio = 1.0f;
113
Array<float> mass_ratios;
114
mass_ratios.resize(mSkeleton->GetJointCount());
115
mass_ratios[indices[0]] = 1.0f;
116
for (int i = 1; i < (int)indices.size(); ++i)
117
{
118
int child_idx = indices[i];
119
int parent_idx = mSkeleton->GetJoint(child_idx).mParentJointIndex;
120
float ratio = mParts[child_idx].mMassPropertiesOverride.mMass / mParts[parent_idx].mMassPropertiesOverride.mMass;
121
mass_ratios[child_idx] = mass_ratios[parent_idx] * Clamp(ratio, cMinMassRatio, cMaxMassRatio);
122
total_mass_ratio += mass_ratios[child_idx];
123
}
124
125
// Calculate total mass of this chain
126
float total_mass = 0.0f;
127
for (int idx : indices)
128
total_mass += mParts[idx].mMassPropertiesOverride.mMass;
129
130
// Calculate how much mass belongs to a ratio of 1
131
float ratio_to_mass = total_mass / total_mass_ratio;
132
133
// Adjust all masses and inertia tensors for the new mass
134
for (int i : indices)
135
{
136
Part &p = mParts[i];
137
float old_mass = p.mMassPropertiesOverride.mMass;
138
float new_mass = mass_ratios[i] * ratio_to_mass;
139
p.mMassPropertiesOverride.mMass = new_mass;
140
p.mMassPropertiesOverride.mInertia *= new_mass / old_mass;
141
p.mMassPropertiesOverride.mInertia.SetColumn4(3, Vec4(0, 0, 0, 1));
142
}
143
144
const float cMaxInertiaIncrease = 2.0f;
145
146
// Get the principal moments of inertia for all parts
147
struct Principal
148
{
149
Mat44 mRotation;
150
Vec3 mDiagonal;
151
float mChildSum = 0.0f;
152
};
153
Array<Principal> principals;
154
principals.resize(mParts.size());
155
for (int i : indices)
156
if (!mParts[i].mMassPropertiesOverride.DecomposePrincipalMomentsOfInertia(principals[i].mRotation, principals[i].mDiagonal))
157
{
158
JPH_ASSERT(false, "Failed to decompose the inertia tensor!");
159
return false;
160
}
161
162
// Calculate sum of child inertias
163
// Walk backwards so we sum the leaves first
164
for (int i = (int)indices.size() - 1; i > 0; --i)
165
{
166
int child_idx = indices[i];
167
int parent_idx = mSkeleton->GetJoint(child_idx).mParentJointIndex;
168
principals[parent_idx].mChildSum += principals[child_idx].mDiagonal[0] + principals[child_idx].mChildSum;
169
}
170
171
// Adjust inertia tensors for all parts
172
for (int i : indices)
173
{
174
Part &p = mParts[i];
175
Principal &principal = principals[i];
176
if (principal.mChildSum != 0.0f)
177
{
178
// Calculate minimum inertia this object should have based on it children
179
float minimum = min(cMaxInertiaIncrease * principal.mDiagonal[0], principal.mChildSum);
180
principal.mDiagonal = Vec3::sMax(principal.mDiagonal, Vec3::sReplicate(minimum));
181
182
// Recalculate moment of inertia in body space
183
p.mMassPropertiesOverride.mInertia = principal.mRotation * Mat44::sScale(principal.mDiagonal) * principal.mRotation.Inversed3x3();
184
}
185
}
186
}
187
}
188
189
return true;
190
}
191
192
void RagdollSettings::DisableParentChildCollisions(const Mat44 *inJointMatrices, float inMinSeparationDistance)
193
{
194
int joint_count = mSkeleton->GetJointCount();
195
JPH_ASSERT(joint_count == (int)mParts.size());
196
197
// Create a group filter table that disables collisions between parent and child
198
Ref<GroupFilterTable> group_filter = new GroupFilterTable(joint_count);
199
for (int joint_idx = 0; joint_idx < joint_count; ++joint_idx)
200
{
201
int parent_joint = mSkeleton->GetJoint(joint_idx).mParentJointIndex;
202
if (parent_joint >= 0)
203
group_filter->DisableCollision(joint_idx, parent_joint);
204
}
205
206
// If joint matrices are provided
207
if (inJointMatrices != nullptr)
208
{
209
// Loop over all joints
210
for (int j1 = 0; j1 < joint_count; ++j1)
211
{
212
// Shape and transform for joint 1
213
const Part &part1 = mParts[j1];
214
const Shape *shape1 = part1.GetShape();
215
Vec3 scale1;
216
Mat44 com1 = (inJointMatrices[j1].PreTranslated(shape1->GetCenterOfMass())).Decompose(scale1);
217
218
// Loop over all other joints
219
for (int j2 = j1 + 1; j2 < joint_count; ++j2)
220
if (group_filter->IsCollisionEnabled(j1, j2)) // Only if collision is still enabled we need to test
221
{
222
// Shape and transform for joint 2
223
const Part &part2 = mParts[j2];
224
const Shape *shape2 = part2.GetShape();
225
Vec3 scale2;
226
Mat44 com2 = (inJointMatrices[j2].PreTranslated(shape2->GetCenterOfMass())).Decompose(scale2);
227
228
// Collision settings
229
CollideShapeSettings settings;
230
settings.mActiveEdgeMode = EActiveEdgeMode::CollideWithAll;
231
settings.mBackFaceMode = EBackFaceMode::CollideWithBackFaces;
232
settings.mMaxSeparationDistance = inMinSeparationDistance;
233
234
// Only check if one of the two bodies can become dynamic
235
if (part1.HasMassProperties() || part2.HasMassProperties())
236
{
237
// If there is a collision, disable the collision between the joints
238
AnyHitCollisionCollector<CollideShapeCollector> collector;
239
if (part1.HasMassProperties()) // Ensure that the first shape is always a dynamic one (we can't check mesh vs convex but we can check convex vs mesh)
240
CollisionDispatch::sCollideShapeVsShape(shape1, shape2, scale1, scale2, com1, com2, SubShapeIDCreator(), SubShapeIDCreator(), settings, collector);
241
else
242
CollisionDispatch::sCollideShapeVsShape(shape2, shape1, scale2, scale1, com2, com1, SubShapeIDCreator(), SubShapeIDCreator(), settings, collector);
243
if (collector.HadHit())
244
group_filter->DisableCollision(j1, j2);
245
}
246
}
247
}
248
}
249
250
// Loop over the body parts and assign them a sub group ID and the group filter
251
for (int joint_idx = 0; joint_idx < joint_count; ++joint_idx)
252
{
253
Part &part = mParts[joint_idx];
254
part.mCollisionGroup.SetSubGroupID(joint_idx);
255
part.mCollisionGroup.SetGroupFilter(group_filter);
256
}
257
}
258
259
void RagdollSettings::SaveBinaryState(StreamOut &inStream, bool inSaveShapes, bool inSaveGroupFilter) const
260
{
261
BodyCreationSettings::ShapeToIDMap shape_to_id;
262
BodyCreationSettings::MaterialToIDMap material_to_id;
263
BodyCreationSettings::GroupFilterToIDMap group_filter_to_id;
264
265
// Save skeleton
266
mSkeleton->SaveBinaryState(inStream);
267
268
// Save parts
269
inStream.Write((uint32)mParts.size());
270
for (const Part &p : mParts)
271
{
272
// Write body creation settings
273
p.SaveWithChildren(inStream, inSaveShapes? &shape_to_id : nullptr, inSaveShapes? &material_to_id : nullptr, inSaveGroupFilter? &group_filter_to_id : nullptr);
274
275
// Save constraint
276
inStream.Write(p.mToParent != nullptr);
277
if (p.mToParent != nullptr)
278
p.mToParent->SaveBinaryState(inStream);
279
}
280
281
// Save additional constraints
282
inStream.Write((uint32)mAdditionalConstraints.size());
283
for (const AdditionalConstraint &c : mAdditionalConstraints)
284
{
285
// Save bodies indices
286
inStream.Write(c.mBodyIdx);
287
288
// Save constraint
289
c.mConstraint->SaveBinaryState(inStream);
290
}
291
}
292
293
RagdollSettings::RagdollResult RagdollSettings::sRestoreFromBinaryState(StreamIn &inStream)
294
{
295
RagdollResult result;
296
297
// Restore skeleton
298
Skeleton::SkeletonResult skeleton_result = Skeleton::sRestoreFromBinaryState(inStream);
299
if (skeleton_result.HasError())
300
{
301
result.SetError(skeleton_result.GetError());
302
return result;
303
}
304
305
// Create ragdoll
306
Ref<RagdollSettings> ragdoll = new RagdollSettings();
307
ragdoll->mSkeleton = skeleton_result.Get();
308
309
BodyCreationSettings::IDToShapeMap id_to_shape;
310
BodyCreationSettings::IDToMaterialMap id_to_material;
311
BodyCreationSettings::IDToGroupFilterMap id_to_group_filter;
312
313
// Reserve some memory to avoid frequent reallocations
314
id_to_shape.reserve(1024);
315
id_to_material.reserve(128);
316
id_to_group_filter.reserve(128);
317
318
// Read parts
319
uint32 len = 0;
320
inStream.Read(len);
321
ragdoll->mParts.resize(len);
322
for (Part &p : ragdoll->mParts)
323
{
324
// Read creation settings
325
BodyCreationSettings::BCSResult bcs_result = BodyCreationSettings::sRestoreWithChildren(inStream, id_to_shape, id_to_material, id_to_group_filter);
326
if (bcs_result.HasError())
327
{
328
result.SetError(bcs_result.GetError());
329
return result;
330
}
331
static_cast<BodyCreationSettings &>(p) = bcs_result.Get();
332
333
// Read constraint
334
bool has_constraint = false;
335
inStream.Read(has_constraint);
336
if (has_constraint)
337
{
338
ConstraintSettings::ConstraintResult constraint_result = ConstraintSettings::sRestoreFromBinaryState(inStream);
339
if (constraint_result.HasError())
340
{
341
result.SetError(constraint_result.GetError());
342
return result;
343
}
344
p.mToParent = DynamicCast<TwoBodyConstraintSettings>(constraint_result.Get());
345
}
346
}
347
348
// Read additional constraints
349
len = 0;
350
inStream.Read(len);
351
ragdoll->mAdditionalConstraints.resize(len);
352
for (AdditionalConstraint &c : ragdoll->mAdditionalConstraints)
353
{
354
// Read body indices
355
inStream.Read(c.mBodyIdx);
356
357
// Read constraint
358
ConstraintSettings::ConstraintResult constraint_result = ConstraintSettings::sRestoreFromBinaryState(inStream);
359
if (constraint_result.HasError())
360
{
361
result.SetError(constraint_result.GetError());
362
return result;
363
}
364
c.mConstraint = DynamicCast<TwoBodyConstraintSettings>(constraint_result.Get());
365
}
366
367
// Create mapping tables
368
ragdoll->CalculateBodyIndexToConstraintIndex();
369
ragdoll->CalculateConstraintIndexToBodyIdxPair();
370
371
result.Set(ragdoll);
372
return result;
373
}
374
375
Ragdoll *RagdollSettings::CreateRagdoll(CollisionGroup::GroupID inCollisionGroup, uint64 inUserData, PhysicsSystem *inSystem) const
376
{
377
Ragdoll *r = new Ragdoll(inSystem);
378
r->mRagdollSettings = this;
379
r->mBodyIDs.reserve(mParts.size());
380
r->mConstraints.reserve(mParts.size() + mAdditionalConstraints.size());
381
382
// Create bodies and constraints
383
BodyInterface &bi = inSystem->GetBodyInterface();
384
Body **bodies = (Body **)JPH_STACK_ALLOC(mParts.size() * sizeof(Body *));
385
int joint_idx = 0;
386
for (const Part &p : mParts)
387
{
388
Body *body2 = bi.CreateBody(p);
389
if (body2 == nullptr)
390
{
391
// Out of bodies, failed to create ragdoll
392
delete r;
393
return nullptr;
394
}
395
body2->GetCollisionGroup().SetGroupID(inCollisionGroup);
396
body2->SetUserData(inUserData);
397
398
// Temporarily store body pointer for hooking up constraints
399
bodies[joint_idx] = body2;
400
401
// Create constraint
402
if (p.mToParent != nullptr)
403
{
404
Body *body1 = bodies[mSkeleton->GetJoint(joint_idx).mParentJointIndex];
405
r->mConstraints.push_back(p.mToParent->Create(*body1, *body2));
406
}
407
408
// Store body ID and constraint in parallel arrays
409
r->mBodyIDs.push_back(body2->GetID());
410
411
++joint_idx;
412
}
413
414
// Add additional constraints
415
for (const AdditionalConstraint &c : mAdditionalConstraints)
416
{
417
Body *body1 = bodies[c.mBodyIdx[0]];
418
Body *body2 = bodies[c.mBodyIdx[1]];
419
r->mConstraints.push_back(c.mConstraint->Create(*body1, *body2));
420
}
421
422
return r;
423
}
424
425
void RagdollSettings::CalculateBodyIndexToConstraintIndex()
426
{
427
mBodyIndexToConstraintIndex.clear();
428
mBodyIndexToConstraintIndex.reserve(mParts.size());
429
430
int constraint_index = 0;
431
for (const Part &p : mParts)
432
{
433
if (p.mToParent != nullptr)
434
mBodyIndexToConstraintIndex.push_back(constraint_index++);
435
else
436
mBodyIndexToConstraintIndex.push_back(-1);
437
}
438
}
439
440
void RagdollSettings::CalculateConstraintIndexToBodyIdxPair()
441
{
442
mConstraintIndexToBodyIdxPair.clear();
443
mConstraintIndexToBodyIdxPair.reserve(mParts.size() + mAdditionalConstraints.size());
444
445
// Add constraints between parts
446
int joint_idx = 0;
447
for (const Part &p : mParts)
448
{
449
if (p.mToParent != nullptr)
450
{
451
int parent_joint_idx = mSkeleton->GetJoint(joint_idx).mParentJointIndex;
452
mConstraintIndexToBodyIdxPair.emplace_back(parent_joint_idx, joint_idx);
453
}
454
455
++joint_idx;
456
}
457
458
// Add additional constraints
459
for (const AdditionalConstraint &c : mAdditionalConstraints)
460
mConstraintIndexToBodyIdxPair.emplace_back(c.mBodyIdx[0], c.mBodyIdx[1]);
461
}
462
463
Ragdoll::~Ragdoll()
464
{
465
// Destroy all bodies
466
mSystem->GetBodyInterface().DestroyBodies(mBodyIDs.data(), (int)mBodyIDs.size());
467
}
468
469
void Ragdoll::AddToPhysicsSystem(EActivation inActivationMode, bool inLockBodies)
470
{
471
// Scope for JPH_STACK_ALLOC
472
{
473
// Create copy of body ids since they will be shuffled
474
int num_bodies = (int)mBodyIDs.size();
475
BodyID *bodies = (BodyID *)JPH_STACK_ALLOC(num_bodies * sizeof(BodyID));
476
memcpy(bodies, mBodyIDs.data(), num_bodies * sizeof(BodyID));
477
478
// Insert bodies as a batch
479
BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);
480
BodyInterface::AddState add_state = bi.AddBodiesPrepare(bodies, num_bodies);
481
bi.AddBodiesFinalize(bodies, num_bodies, add_state, inActivationMode);
482
}
483
484
// Add all constraints
485
mSystem->AddConstraints((Constraint **)mConstraints.data(), (int)mConstraints.size());
486
}
487
488
void Ragdoll::RemoveFromPhysicsSystem(bool inLockBodies)
489
{
490
// Remove all constraints before removing the bodies
491
mSystem->RemoveConstraints((Constraint **)mConstraints.data(), (int)mConstraints.size());
492
493
// Scope for JPH_STACK_ALLOC
494
{
495
// Create copy of body ids since they will be shuffled
496
int num_bodies = (int)mBodyIDs.size();
497
BodyID *bodies = (BodyID *)JPH_STACK_ALLOC(num_bodies * sizeof(BodyID));
498
memcpy(bodies, mBodyIDs.data(), num_bodies * sizeof(BodyID));
499
500
// Remove all bodies as a batch
501
sRagdollGetBodyInterface(mSystem, inLockBodies).RemoveBodies(bodies, num_bodies);
502
}
503
}
504
505
void Ragdoll::Activate(bool inLockBodies)
506
{
507
sRagdollGetBodyInterface(mSystem, inLockBodies).ActivateBodies(mBodyIDs.data(), (int)mBodyIDs.size());
508
}
509
510
bool Ragdoll::IsActive(bool inLockBodies) const
511
{
512
// Lock the bodies
513
int body_count = (int)mBodyIDs.size();
514
BodyLockMultiRead lock(sRagdollGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count);
515
516
// Test if any body is active
517
for (int b = 0; b < body_count; ++b)
518
{
519
const Body *body = lock.GetBody(b);
520
if (body->IsActive())
521
return true;
522
}
523
524
return false;
525
}
526
527
void Ragdoll::SetGroupID(CollisionGroup::GroupID inGroupID, bool inLockBodies)
528
{
529
// Lock the bodies
530
int body_count = (int)mBodyIDs.size();
531
BodyLockMultiWrite lock(sRagdollGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count);
532
533
// Update group ID
534
for (int b = 0; b < body_count; ++b)
535
{
536
Body *body = lock.GetBody(b);
537
body->GetCollisionGroup().SetGroupID(inGroupID);
538
}
539
}
540
541
void Ragdoll::SetPose(const SkeletonPose &inPose, bool inLockBodies)
542
{
543
JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton);
544
545
SetPose(inPose.GetRootOffset(), inPose.GetJointMatrices().data(), inLockBodies);
546
}
547
548
void Ragdoll::SetPose(RVec3Arg inRootOffset, const Mat44 *inJointMatrices, bool inLockBodies)
549
{
550
// Move bodies instantly into the correct position
551
BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);
552
for (int i = 0; i < (int)mBodyIDs.size(); ++i)
553
{
554
const Mat44 &joint = inJointMatrices[i];
555
bi.SetPositionAndRotation(mBodyIDs[i], inRootOffset + joint.GetTranslation(), joint.GetQuaternion(), EActivation::DontActivate);
556
}
557
}
558
559
void Ragdoll::GetPose(SkeletonPose &outPose, bool inLockBodies)
560
{
561
JPH_ASSERT(outPose.GetSkeleton() == mRagdollSettings->mSkeleton);
562
563
RVec3 root_offset;
564
GetPose(root_offset, outPose.GetJointMatrices().data(), inLockBodies);
565
outPose.SetRootOffset(root_offset);
566
}
567
568
void Ragdoll::GetPose(RVec3 &outRootOffset, Mat44 *outJointMatrices, bool inLockBodies)
569
{
570
// Lock the bodies
571
int body_count = (int)mBodyIDs.size();
572
if (body_count == 0)
573
return;
574
BodyLockMultiRead lock(sRagdollGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count);
575
576
// Get root matrix
577
const Body *root = lock.GetBody(0);
578
RMat44 root_transform = root->GetWorldTransform();
579
outRootOffset = root_transform.GetTranslation();
580
outJointMatrices[0] = Mat44(root_transform.GetColumn4(0), root_transform.GetColumn4(1), root_transform.GetColumn4(2), Vec4(0, 0, 0, 1));
581
582
// Get other matrices
583
for (int b = 1; b < body_count; ++b)
584
{
585
const Body *body = lock.GetBody(b);
586
RMat44 transform = body->GetWorldTransform();
587
outJointMatrices[b] = Mat44(transform.GetColumn4(0), transform.GetColumn4(1), transform.GetColumn4(2), Vec4(Vec3(transform.GetTranslation() - outRootOffset), 1));
588
}
589
}
590
591
void Ragdoll::ResetWarmStart()
592
{
593
for (TwoBodyConstraint *c : mConstraints)
594
c->ResetWarmStart();
595
}
596
597
void Ragdoll::DriveToPoseUsingKinematics(const SkeletonPose &inPose, float inDeltaTime, bool inLockBodies)
598
{
599
JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton);
600
601
DriveToPoseUsingKinematics(inPose.GetRootOffset(), inPose.GetJointMatrices().data(), inDeltaTime, inLockBodies);
602
}
603
604
void Ragdoll::DriveToPoseUsingKinematics(RVec3Arg inRootOffset, const Mat44 *inJointMatrices, float inDeltaTime, bool inLockBodies)
605
{
606
// Move bodies into the correct position using kinematics
607
BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);
608
for (int i = 0; i < (int)mBodyIDs.size(); ++i)
609
{
610
const Mat44 &joint = inJointMatrices[i];
611
bi.MoveKinematic(mBodyIDs[i], inRootOffset + joint.GetTranslation(), joint.GetQuaternion(), inDeltaTime);
612
}
613
}
614
615
void Ragdoll::DriveToPoseUsingMotors(const SkeletonPose &inPose)
616
{
617
JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton);
618
619
// Move bodies into the correct position using constraints
620
for (int i = 0; i < (int)inPose.GetJointMatrices().size(); ++i)
621
{
622
int constraint_idx = mRagdollSettings->GetConstraintIndexForBodyIndex(i);
623
if (constraint_idx >= 0)
624
{
625
// Get desired rotation of this body relative to its parent
626
const SkeletalAnimation::JointState &joint_state = inPose.GetJoint(i);
627
628
// Drive constraint to target
629
TwoBodyConstraint *constraint = mConstraints[constraint_idx];
630
EConstraintSubType sub_type = constraint->GetSubType();
631
if (sub_type == EConstraintSubType::SwingTwist)
632
{
633
SwingTwistConstraint *st_constraint = static_cast<SwingTwistConstraint *>(constraint);
634
st_constraint->SetSwingMotorState(EMotorState::Position);
635
st_constraint->SetTwistMotorState(EMotorState::Position);
636
st_constraint->SetTargetOrientationBS(joint_state.mRotation);
637
}
638
else
639
JPH_ASSERT(false, "Constraint type not implemented!");
640
}
641
}
642
}
643
644
void Ragdoll::SetLinearAndAngularVelocity(Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, bool inLockBodies)
645
{
646
BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);
647
for (BodyID body_id : mBodyIDs)
648
bi.SetLinearAndAngularVelocity(body_id, inLinearVelocity, inAngularVelocity);
649
}
650
651
void Ragdoll::SetLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies)
652
{
653
BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);
654
for (BodyID body_id : mBodyIDs)
655
bi.SetLinearVelocity(body_id, inLinearVelocity);
656
}
657
658
void Ragdoll::AddLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies)
659
{
660
BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);
661
for (BodyID body_id : mBodyIDs)
662
bi.AddLinearVelocity(body_id, inLinearVelocity);
663
}
664
665
void Ragdoll::AddImpulse(Vec3Arg inImpulse, bool inLockBodies)
666
{
667
BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);
668
for (BodyID body_id : mBodyIDs)
669
bi.AddImpulse(body_id, inImpulse);
670
}
671
672
void Ragdoll::GetRootTransform(RVec3 &outPosition, Quat &outRotation, bool inLockBodies) const
673
{
674
BodyLockRead lock(sRagdollGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs[0]);
675
if (lock.Succeeded())
676
{
677
const Body &body = lock.GetBody();
678
outPosition = body.GetPosition();
679
outRotation = body.GetRotation();
680
}
681
else
682
{
683
outPosition = RVec3::sZero();
684
outRotation = Quat::sIdentity();
685
}
686
}
687
688
AABox Ragdoll::GetWorldSpaceBounds(bool inLockBodies) const
689
{
690
// Lock the bodies
691
int body_count = (int)mBodyIDs.size();
692
BodyLockMultiRead lock(sRagdollGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count);
693
694
// Encapsulate all bodies
695
AABox bounds;
696
for (int b = 0; b < body_count; ++b)
697
{
698
const Body *body = lock.GetBody(b);
699
if (body != nullptr)
700
bounds.Encapsulate(body->GetWorldSpaceBounds());
701
}
702
return bounds;
703
}
704
705
JPH_NAMESPACE_END
706
707