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