Ragdoll.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #include <Jolt/Jolt.h>
  4. #include <Jolt/Physics/Constraints/SwingTwistConstraint.h>
  5. #include <Jolt/Physics/Ragdoll/Ragdoll.h>
  6. #include <Jolt/Physics/PhysicsSystem.h>
  7. #include <Jolt/Physics/Body/BodyLockMulti.h>
  8. #include <Jolt/Physics/Collision/GroupFilterTable.h>
  9. #include <Jolt/Physics/Collision/CollisionCollectorImpl.h>
  10. #include <Jolt/Physics/Collision/CollideShape.h>
  11. #include <Jolt/Physics/Collision/CollisionDispatch.h>
  12. #include <Jolt/ObjectStream/TypeDeclarations.h>
  13. #include <Jolt/Core/StreamIn.h>
  14. #include <Jolt/Core/StreamOut.h>
  15. JPH_NAMESPACE_BEGIN
  16. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(RagdollSettings::Part)
  17. {
  18. JPH_ADD_BASE_CLASS(RagdollSettings::Part, BodyCreationSettings)
  19. JPH_ADD_ATTRIBUTE(RagdollSettings::Part, mToParent)
  20. }
  21. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(RagdollSettings)
  22. {
  23. JPH_ADD_ATTRIBUTE(RagdollSettings, mSkeleton)
  24. JPH_ADD_ATTRIBUTE(RagdollSettings, mParts)
  25. }
  26. static inline BodyInterface &sGetBodyInterface(PhysicsSystem *inSystem, bool inLockBodies)
  27. {
  28. return inLockBodies? inSystem->GetBodyInterface() : inSystem->GetBodyInterfaceNoLock();
  29. }
  30. static inline const BodyLockInterface &sGetBodyLockInterface(const PhysicsSystem *inSystem, bool inLockBodies)
  31. {
  32. return inLockBodies? static_cast<const BodyLockInterface &>(inSystem->GetBodyLockInterface()) : static_cast<const BodyLockInterface &>(inSystem->GetBodyLockInterfaceNoLock());
  33. }
  34. bool RagdollSettings::Stabilize()
  35. {
  36. // Based on: Stop my Constraints from Blowing Up! - Oliver Strunk (Havok)
  37. // Do 2 things:
  38. // 1. Limit the mass ratios between parents and children (slide 16)
  39. // 2. Increase the inertia of parents so that they're bigger or equal to the sum of their children (slide 34)
  40. // If we don't have any joints there's nothing to stabilize
  41. if (mSkeleton->GetJointCount() == 0)
  42. return true;
  43. // 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.
  44. // This array keeps track of which joints have been processed.
  45. vector<bool> visited;
  46. visited.resize(mSkeleton->GetJointCount());
  47. for (size_t v = 0; v < visited.size(); ++v)
  48. {
  49. // Mark static bodies as visited so we won't process these
  50. Part &p = mParts[v];
  51. bool has_mass_properties = p.HasMassProperties();
  52. visited[v] = !has_mass_properties;
  53. if (has_mass_properties && p.mOverrideMassProperties != EOverrideMassProperties::MassAndInertiaProvided)
  54. {
  55. // Mass properties not yet calculated, do it now
  56. p.mMassPropertiesOverride = p.GetMassProperties();
  57. p.mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided;
  58. }
  59. }
  60. // Find first unvisited part that either has no parent or that has a parent that was visited
  61. for (int first_idx = 0; first_idx < mSkeleton->GetJointCount(); ++first_idx)
  62. {
  63. int first_idx_parent = mSkeleton->GetJoint(first_idx).mParentJointIndex;
  64. if (!visited[first_idx] && (first_idx_parent == -1 || visited[first_idx_parent]))
  65. {
  66. // Find all children of first_idx and their children up to the next static part
  67. int next_to_process = 0;
  68. vector<int> indices;
  69. indices.reserve(mSkeleton->GetJointCount());
  70. visited[first_idx] = true;
  71. indices.push_back(first_idx);
  72. do
  73. {
  74. int parent_idx = indices[next_to_process++];
  75. for (int child_idx = 0; child_idx < mSkeleton->GetJointCount(); ++child_idx)
  76. if (!visited[child_idx] && mSkeleton->GetJoint(child_idx).mParentJointIndex == parent_idx)
  77. {
  78. visited[child_idx] = true;
  79. indices.push_back(child_idx);
  80. }
  81. } while (next_to_process < (int)indices.size());
  82. // If there's only 1 body, we can't redistribute mass
  83. if (indices.size() == 1)
  84. continue;
  85. const float cMinMassRatio = 0.8f;
  86. const float cMaxMassRatio = 1.2f;
  87. // Ensure that the mass ratio from parent to child is within a range
  88. float total_mass_ratio = 1.0f;
  89. vector<float> mass_ratios;
  90. mass_ratios.resize(mSkeleton->GetJointCount());
  91. mass_ratios[indices[0]] = 1.0f;
  92. for (int i = 1; i < (int)indices.size(); ++i)
  93. {
  94. int child_idx = indices[i];
  95. int parent_idx = mSkeleton->GetJoint(child_idx).mParentJointIndex;
  96. float ratio = mParts[child_idx].mMassPropertiesOverride.mMass / mParts[parent_idx].mMassPropertiesOverride.mMass;
  97. mass_ratios[child_idx] = mass_ratios[parent_idx] * Clamp(ratio, cMinMassRatio, cMaxMassRatio);
  98. total_mass_ratio += mass_ratios[child_idx];
  99. }
  100. // Calculate total mass of this chain
  101. float total_mass = 0.0f;
  102. for (int idx : indices)
  103. total_mass += mParts[idx].mMassPropertiesOverride.mMass;
  104. // Calculate how much mass belongs to a ratio of 1
  105. float ratio_to_mass = total_mass / total_mass_ratio;
  106. // Adjust all masses and inertia tensors for the new mass
  107. for (int i : indices)
  108. {
  109. Part &p = mParts[i];
  110. float old_mass = p.mMassPropertiesOverride.mMass;
  111. float new_mass = mass_ratios[i] * ratio_to_mass;
  112. p.mMassPropertiesOverride.mMass = new_mass;
  113. p.mMassPropertiesOverride.mInertia *= new_mass / old_mass;
  114. p.mMassPropertiesOverride.mInertia.SetColumn4(3, Vec4(0, 0, 0, 1));
  115. }
  116. const float cMaxInertiaIncrease = 2.0f;
  117. // Get the principal moments of inertia for all parts
  118. struct Principal
  119. {
  120. Mat44 mRotation;
  121. Vec3 mDiagonal;
  122. float mChildSum = 0.0f;
  123. };
  124. vector<Principal> principals;
  125. principals.resize(mParts.size());
  126. for (int i : indices)
  127. if (!mParts[i].mMassPropertiesOverride.DecomposePrincipalMomentsOfInertia(principals[i].mRotation, principals[i].mDiagonal))
  128. {
  129. JPH_ASSERT(false, "Failed to decompose the inertia tensor!");
  130. return false;
  131. }
  132. // Calculate sum of child inertias
  133. // Walk backwards so we sum the leaves first
  134. for (int i = (int)indices.size() - 1; i > 0; --i)
  135. {
  136. int child_idx = indices[i];
  137. int parent_idx = mSkeleton->GetJoint(child_idx).mParentJointIndex;
  138. principals[parent_idx].mChildSum += principals[child_idx].mDiagonal[0] + principals[child_idx].mChildSum;
  139. }
  140. // Adjust inertia tensors for all parts
  141. for (int i : indices)
  142. {
  143. Part &p = mParts[i];
  144. Principal &principal = principals[i];
  145. if (principal.mChildSum != 0.0f)
  146. {
  147. // Calculate minimum inertia this object should have based on it children
  148. float minimum = min(cMaxInertiaIncrease * principal.mDiagonal[0], principal.mChildSum);
  149. principal.mDiagonal = Vec3::sMax(principal.mDiagonal, Vec3::sReplicate(minimum));
  150. // Recalculate moment of inertia in body space
  151. p.mMassPropertiesOverride.mInertia = principal.mRotation * Mat44::sScale(principal.mDiagonal) * principal.mRotation.Inversed3x3();
  152. }
  153. }
  154. }
  155. }
  156. return true;
  157. }
  158. void RagdollSettings::DisableParentChildCollisions(const Mat44 *inJointMatrices, float inMinSeparationDistance)
  159. {
  160. int joint_count = mSkeleton->GetJointCount();
  161. JPH_ASSERT(joint_count == (int)mParts.size());
  162. // Create a group filter table that disables collisions between parent and child
  163. Ref<GroupFilterTable> group_filter = new GroupFilterTable(joint_count);
  164. for (int joint_idx = 0; joint_idx < joint_count; ++joint_idx)
  165. {
  166. int parent_joint = mSkeleton->GetJoint(joint_idx).mParentJointIndex;
  167. if (parent_joint >= 0)
  168. group_filter->DisableCollision(joint_idx, parent_joint);
  169. }
  170. // If joint matrices are provided
  171. if (inJointMatrices != nullptr)
  172. {
  173. // Loop over all joints
  174. for (int j1 = 0; j1 < joint_count; ++j1)
  175. {
  176. // Shape and transform for joint 1
  177. const Part &part1 = mParts[j1];
  178. const Shape *shape1 = part1.GetShape();
  179. Vec3 scale1;
  180. Mat44 com1 = (inJointMatrices[j1].PreTranslated(shape1->GetCenterOfMass())).Decompose(scale1);
  181. // Loop over all other joints
  182. for (int j2 = j1 + 1; j2 < joint_count; ++j2)
  183. if (group_filter->IsCollisionEnabled(j1, j2)) // Only if collision is still enabled we need to test
  184. {
  185. // Shape and transform for joint 2
  186. const Part &part2 = mParts[j2];
  187. const Shape *shape2 = part2.GetShape();
  188. Vec3 scale2;
  189. Mat44 com2 = (inJointMatrices[j2].PreTranslated(shape2->GetCenterOfMass())).Decompose(scale2);
  190. // Collision settings
  191. CollideShapeSettings settings;
  192. settings.mActiveEdgeMode = EActiveEdgeMode::CollideWithAll;
  193. settings.mBackFaceMode = EBackFaceMode::CollideWithBackFaces;
  194. settings.mMaxSeparationDistance = inMinSeparationDistance;
  195. // Only check if one of the two bodies can become dynamic
  196. if (part1.HasMassProperties() || part2.HasMassProperties())
  197. {
  198. // If there is a collision, disable the collision between the joints
  199. AnyHitCollisionCollector<CollideShapeCollector> collector;
  200. 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)
  201. CollisionDispatch::sCollideShapeVsShape(shape1, shape2, scale1, scale2, com1, com2, SubShapeIDCreator(), SubShapeIDCreator(), settings, collector);
  202. else
  203. CollisionDispatch::sCollideShapeVsShape(shape2, shape1, scale2, scale1, com2, com1, SubShapeIDCreator(), SubShapeIDCreator(), settings, collector);
  204. if (collector.HadHit())
  205. group_filter->DisableCollision(j1, j2);
  206. }
  207. }
  208. }
  209. }
  210. // Loop over the body parts and assign them a sub group ID and the group filter
  211. for (int joint_idx = 0; joint_idx < joint_count; ++joint_idx)
  212. {
  213. Part &part = mParts[joint_idx];
  214. part.mCollisionGroup.SetSubGroupID(joint_idx);
  215. part.mCollisionGroup.SetGroupFilter(group_filter);
  216. }
  217. }
  218. void RagdollSettings::SaveBinaryState(StreamOut &inStream, bool inSaveShapes, bool inSaveGroupFilter) const
  219. {
  220. BodyCreationSettings::ShapeToIDMap shape_to_id;
  221. BodyCreationSettings::MaterialToIDMap material_to_id;
  222. BodyCreationSettings::GroupFilterToIDMap group_filter_to_id;
  223. // Save skeleton
  224. mSkeleton->SaveBinaryState(inStream);
  225. // Save parts
  226. inStream.Write((uint32)mParts.size());
  227. for (const Part &p : mParts)
  228. {
  229. // Write body creation settings
  230. p.SaveWithChildren(inStream, inSaveShapes? &shape_to_id : nullptr, inSaveShapes? &material_to_id : nullptr, inSaveGroupFilter? &group_filter_to_id : nullptr);
  231. // Save constraint
  232. inStream.Write(p.mToParent != nullptr);
  233. if (p.mToParent != nullptr)
  234. p.mToParent->SaveBinaryState(inStream);
  235. }
  236. }
  237. RagdollSettings::RagdollResult RagdollSettings::sRestoreFromBinaryState(StreamIn &inStream)
  238. {
  239. RagdollResult result;
  240. // Restore skeleton
  241. Skeleton::SkeletonResult skeleton_result = Skeleton::sRestoreFromBinaryState(inStream);
  242. if (skeleton_result.HasError())
  243. {
  244. result.SetError(skeleton_result.GetError());
  245. return result;
  246. }
  247. // Create ragdoll
  248. Ref<RagdollSettings> ragdoll = new RagdollSettings();
  249. ragdoll->mSkeleton = skeleton_result.Get();
  250. BodyCreationSettings::IDToShapeMap id_to_shape;
  251. BodyCreationSettings::IDToMaterialMap id_to_material;
  252. BodyCreationSettings::IDToGroupFilterMap id_to_group_filter;
  253. // Reserve some memory to avoid frequent reallocations
  254. id_to_shape.reserve(1024);
  255. id_to_material.reserve(128);
  256. id_to_group_filter.reserve(128);
  257. // Read parts
  258. uint32 len = 0;
  259. inStream.Read(len);
  260. ragdoll->mParts.resize(len);
  261. for (Part &p : ragdoll->mParts)
  262. {
  263. // Read creation settings
  264. BodyCreationSettings::BCSResult bcs_result = BodyCreationSettings::sRestoreWithChildren(inStream, id_to_shape, id_to_material, id_to_group_filter);
  265. if (bcs_result.HasError())
  266. {
  267. result.SetError(bcs_result.GetError());
  268. return result;
  269. }
  270. static_cast<BodyCreationSettings &>(p) = bcs_result.Get();
  271. // Read constraint
  272. bool has_constraint = false;
  273. inStream.Read(has_constraint);
  274. if (has_constraint)
  275. {
  276. ConstraintSettings::ConstraintResult constraint_result = ConstraintSettings::sRestoreFromBinaryState(inStream);
  277. if (constraint_result.HasError())
  278. {
  279. result.SetError(constraint_result.GetError());
  280. return result;
  281. }
  282. p.mToParent = DynamicCast<TwoBodyConstraintSettings>(constraint_result.Get().GetPtr());
  283. }
  284. }
  285. // Create mapping tables
  286. ragdoll->CalculateBodyIndexToConstraintIndex();
  287. ragdoll->CalculateConstraintIndexToBodyIdxPair();
  288. result.Set(ragdoll);
  289. return result;
  290. }
  291. Ragdoll *RagdollSettings::CreateRagdoll(CollisionGroup::GroupID inCollisionGroup, uint64 inUserData, PhysicsSystem *inSystem) const
  292. {
  293. Ragdoll *r = new Ragdoll(inSystem);
  294. r->mRagdollSettings = this;
  295. r->mBodyIDs.reserve(mParts.size());
  296. r->mConstraints.reserve(mParts.size());
  297. BodyInterface &bi = inSystem->GetBodyInterface();
  298. Body **bodies = (Body **)JPH_STACK_ALLOC(mParts.size() * sizeof(Body *));
  299. int joint_idx = 0;
  300. for (const Part &p : mParts)
  301. {
  302. Body *body2 = bi.CreateBody(p);
  303. if (body2 == nullptr)
  304. {
  305. // Out of bodies, failed to create ragdoll
  306. delete r;
  307. return nullptr;
  308. }
  309. body2->GetCollisionGroup().SetGroupID(inCollisionGroup);
  310. body2->SetUserData(inUserData);
  311. #ifdef _DEBUG
  312. body2->SetDebugName(mSkeleton->GetJoint(joint_idx).mName);
  313. #endif
  314. // Temporarily store body pointer for hooking up constraints
  315. bodies[joint_idx] = body2;
  316. // Create constraint
  317. if (p.mToParent != nullptr)
  318. {
  319. Body *body1 = bodies[mSkeleton->GetJoint(joint_idx).mParentJointIndex];
  320. r->mConstraints.push_back(p.mToParent->Create(*body1, *body2));
  321. }
  322. // Store body ID and constraint in parallel arrays
  323. r->mBodyIDs.push_back(body2->GetID());
  324. ++joint_idx;
  325. }
  326. return r;
  327. }
  328. void RagdollSettings::CalculateBodyIndexToConstraintIndex()
  329. {
  330. mBodyIndexToConstraintIndex.clear();
  331. mBodyIndexToConstraintIndex.reserve(mParts.size());
  332. int constraint_index = 0;
  333. for (const Part &p : mParts)
  334. {
  335. if (p.mToParent != nullptr)
  336. mBodyIndexToConstraintIndex.push_back(constraint_index++);
  337. else
  338. mBodyIndexToConstraintIndex.push_back(-1);
  339. }
  340. }
  341. void RagdollSettings::CalculateConstraintIndexToBodyIdxPair()
  342. {
  343. mConstraintIndexToBodyIdxPair.clear();
  344. int joint_idx = 0;
  345. for (const Part &p : mParts)
  346. {
  347. if (p.mToParent != nullptr)
  348. {
  349. int parent_joint_idx = mSkeleton->GetJoint(joint_idx).mParentJointIndex;
  350. mConstraintIndexToBodyIdxPair.push_back(BodyIdxPair(parent_joint_idx, joint_idx));
  351. }
  352. ++joint_idx;
  353. }
  354. }
  355. Ragdoll::~Ragdoll()
  356. {
  357. // Destroy all bodies
  358. mSystem->GetBodyInterface().DestroyBodies(mBodyIDs.data(), (int)mBodyIDs.size());
  359. }
  360. void Ragdoll::AddToPhysicsSystem(EActivation inActivationMode, bool inLockBodies)
  361. {
  362. // Scope for JPH_STACK_ALLOC
  363. {
  364. // Create copy of body ids since they will be shuffled
  365. int num_bodies = (int)mBodyIDs.size();
  366. BodyID *bodies = (BodyID *)JPH_STACK_ALLOC(num_bodies * sizeof(BodyID));
  367. memcpy(bodies, mBodyIDs.data(), num_bodies * sizeof(BodyID));
  368. // Insert bodies as a batch
  369. BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies);
  370. BodyInterface::AddState add_state = bi.AddBodiesPrepare(bodies, num_bodies);
  371. bi.AddBodiesFinalize(bodies, num_bodies, add_state, inActivationMode);
  372. }
  373. // Add all constraints
  374. mSystem->AddConstraints((Constraint **)mConstraints.data(), (int)mConstraints.size());
  375. }
  376. void Ragdoll::RemoveFromPhysicsSystem(bool inLockBodies)
  377. {
  378. // Remove all constraints before removing the bodies
  379. mSystem->RemoveConstraints((Constraint **)mConstraints.data(), (int)mConstraints.size());
  380. // Scope for JPH_STACK_ALLOC
  381. {
  382. // Create copy of body ids since they will be shuffled
  383. int num_bodies = (int)mBodyIDs.size();
  384. BodyID *bodies = (BodyID *)JPH_STACK_ALLOC(num_bodies * sizeof(BodyID));
  385. memcpy(bodies, mBodyIDs.data(), num_bodies * sizeof(BodyID));
  386. // Remove all bodies as a batch
  387. sGetBodyInterface(mSystem, inLockBodies).RemoveBodies(bodies, num_bodies);
  388. }
  389. }
  390. void Ragdoll::Activate(bool inLockBodies)
  391. {
  392. sGetBodyInterface(mSystem, inLockBodies).ActivateBodies(mBodyIDs.data(), (int)mBodyIDs.size());
  393. }
  394. void Ragdoll::SetGroupID(CollisionGroup::GroupID inGroupID, bool inLockBodies)
  395. {
  396. // Lock the bodies
  397. int body_count = (int)mBodyIDs.size();
  398. BodyLockMultiWrite lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count);
  399. // Update group ID
  400. for (int b = 0; b < body_count; ++b)
  401. {
  402. Body *body = lock.GetBody(b);
  403. body->GetCollisionGroup().SetGroupID(inGroupID);
  404. }
  405. }
  406. void Ragdoll::SetPose(const SkeletonPose &inPose, bool inLockBodies)
  407. {
  408. JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton);
  409. SetPose(inPose.GetJointMatrices().data(), inLockBodies);
  410. }
  411. void Ragdoll::SetPose(const Mat44 *inJointMatrices, bool inLockBodies)
  412. {
  413. // Move bodies instantly into the correct position
  414. BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies);
  415. for (int i = 0; i < (int)mBodyIDs.size(); ++i)
  416. {
  417. const Mat44 &joint = inJointMatrices[i];
  418. bi.SetPositionAndRotation(mBodyIDs[i], joint.GetTranslation(), joint.GetRotation().GetQuaternion(), EActivation::DontActivate);
  419. }
  420. }
  421. void Ragdoll::DriveToPoseUsingKinematics(const SkeletonPose &inPose, float inDeltaTime, bool inLockBodies)
  422. {
  423. JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton);
  424. DriveToPoseUsingKinematics(inPose.GetJointMatrices().data(), inDeltaTime, inLockBodies);
  425. }
  426. void Ragdoll::DriveToPoseUsingKinematics(const Mat44 *inJointMatrices, float inDeltaTime, bool inLockBodies)
  427. {
  428. // Move bodies into the correct position using kinematics
  429. BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies);
  430. for (int i = 0; i < (int)mBodyIDs.size(); ++i)
  431. {
  432. const Mat44 &joint = inJointMatrices[i];
  433. bi.MoveKinematic(mBodyIDs[i], joint.GetTranslation(), joint.GetRotation().GetQuaternion(), inDeltaTime);
  434. }
  435. }
  436. void Ragdoll::DriveToPoseUsingMotors(const SkeletonPose &inPose)
  437. {
  438. JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton);
  439. // Move bodies into the correct position using kinematics
  440. for (int i = 0; i < (int)inPose.GetJointMatrices().size(); ++i)
  441. {
  442. int constraint_idx = mRagdollSettings->GetConstraintIndexForBodyIndex(i);
  443. if (constraint_idx >= 0)
  444. {
  445. SwingTwistConstraint *constraint = (SwingTwistConstraint *)mConstraints[constraint_idx].GetPtr();
  446. // Get desired rotation of this body relative to its parent
  447. Quat joint_transform = inPose.GetJoint(i).mRotation;
  448. // Drive constraint to target
  449. constraint->SetSwingMotorState(EMotorState::Position);
  450. constraint->SetTwistMotorState(EMotorState::Position);
  451. constraint->SetTargetOrientationBS(joint_transform);
  452. }
  453. }
  454. }
  455. void Ragdoll::SetLinearAndAngularVelocity(Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, bool inLockBodies)
  456. {
  457. BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies);
  458. for (BodyID body_id : mBodyIDs)
  459. bi.SetLinearAndAngularVelocity(body_id, inLinearVelocity, inAngularVelocity);
  460. }
  461. void Ragdoll::SetLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies)
  462. {
  463. BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies);
  464. for (BodyID body_id : mBodyIDs)
  465. bi.SetLinearVelocity(body_id, inLinearVelocity);
  466. }
  467. void Ragdoll::AddLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies)
  468. {
  469. BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies);
  470. for (BodyID body_id : mBodyIDs)
  471. bi.AddLinearVelocity(body_id, inLinearVelocity);
  472. }
  473. void Ragdoll::AddImpulse(Vec3Arg inImpulse, bool inLockBodies)
  474. {
  475. BodyInterface &bi = sGetBodyInterface(mSystem, inLockBodies);
  476. for (BodyID body_id : mBodyIDs)
  477. bi.AddImpulse(body_id, inImpulse);
  478. }
  479. void Ragdoll::GetRootTransform(Vec3 &outPosition, Quat &outRotation, bool inLockBodies) const
  480. {
  481. BodyLockRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs[0]);
  482. if (lock.Succeeded())
  483. {
  484. const Body &body = lock.GetBody();
  485. outPosition = body.GetPosition();
  486. outRotation = body.GetRotation();
  487. }
  488. else
  489. {
  490. outPosition = Vec3::sZero();
  491. outRotation = Quat::sIdentity();
  492. }
  493. }
  494. AABox Ragdoll::GetWorldSpaceBounds(bool inLockBodies) const
  495. {
  496. // Lock the bodies
  497. int body_count = (int)mBodyIDs.size();
  498. BodyLockMultiRead lock(sGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count);
  499. // Encapsulate all bodies
  500. AABox bounds;
  501. for (int b = 0; b < body_count; ++b)
  502. {
  503. const Body *body = lock.GetBody(b);
  504. if (body != nullptr)
  505. bounds.Encapsulate(body->GetWorldSpaceBounds());
  506. }
  507. return bounds;
  508. }
  509. JPH_NAMESPACE_END