Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Ragdoll/Ragdoll.cpp
9912 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#include <Jolt/Jolt.h>56#include <Jolt/Physics/Ragdoll/Ragdoll.h>7#include <Jolt/Physics/Constraints/SwingTwistConstraint.h>8#include <Jolt/Physics/PhysicsSystem.h>9#include <Jolt/Physics/Body/BodyLockMulti.h>10#include <Jolt/Physics/Collision/GroupFilterTable.h>11#include <Jolt/Physics/Collision/CollisionCollectorImpl.h>12#include <Jolt/Physics/Collision/CollideShape.h>13#include <Jolt/Physics/Collision/CollisionDispatch.h>14#include <Jolt/ObjectStream/TypeDeclarations.h>15#include <Jolt/Core/StreamIn.h>16#include <Jolt/Core/StreamOut.h>1718JPH_NAMESPACE_BEGIN1920JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(RagdollSettings::Part)21{22JPH_ADD_BASE_CLASS(RagdollSettings::Part, BodyCreationSettings)2324JPH_ADD_ATTRIBUTE(RagdollSettings::Part, mToParent)25}2627JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(RagdollSettings::AdditionalConstraint)28{29JPH_ADD_ATTRIBUTE(RagdollSettings::AdditionalConstraint, mBodyIdx)30JPH_ADD_ATTRIBUTE(RagdollSettings::AdditionalConstraint, mConstraint)31}3233JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(RagdollSettings)34{35JPH_ADD_ATTRIBUTE(RagdollSettings, mSkeleton)36JPH_ADD_ATTRIBUTE(RagdollSettings, mParts)37JPH_ADD_ATTRIBUTE(RagdollSettings, mAdditionalConstraints)38}3940static inline BodyInterface &sRagdollGetBodyInterface(PhysicsSystem *inSystem, bool inLockBodies)41{42return inLockBodies? inSystem->GetBodyInterface() : inSystem->GetBodyInterfaceNoLock();43}4445static inline const BodyLockInterface &sRagdollGetBodyLockInterface(const PhysicsSystem *inSystem, bool inLockBodies)46{47return inLockBodies? static_cast<const BodyLockInterface &>(inSystem->GetBodyLockInterface()) : static_cast<const BodyLockInterface &>(inSystem->GetBodyLockInterfaceNoLock());48}4950bool RagdollSettings::Stabilize()51{52// Based on: Stop my Constraints from Blowing Up! - Oliver Strunk (Havok)53// Do 2 things:54// 1. Limit the mass ratios between parents and children (slide 16)55// 2. Increase the inertia of parents so that they're bigger or equal to the sum of their children (slide 34)5657// If we don't have any joints there's nothing to stabilize58if (mSkeleton->GetJointCount() == 0)59return true;6061// 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.62// This array keeps track of which joints have been processed.63Array<bool> visited;64visited.resize(mSkeleton->GetJointCount());65for (size_t v = 0; v < visited.size(); ++v)66{67// Mark static bodies as visited so we won't process these68Part &p = mParts[v];69bool has_mass_properties = p.HasMassProperties();70visited[v] = !has_mass_properties;7172if (has_mass_properties && p.mOverrideMassProperties != EOverrideMassProperties::MassAndInertiaProvided)73{74// Mass properties not yet calculated, do it now75p.mMassPropertiesOverride = p.GetMassProperties();76p.mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided;77}78}7980// Find first unvisited part that either has no parent or that has a parent that was visited81for (int first_idx = 0; first_idx < mSkeleton->GetJointCount(); ++first_idx)82{83int first_idx_parent = mSkeleton->GetJoint(first_idx).mParentJointIndex;84if (!visited[first_idx] && (first_idx_parent == -1 || visited[first_idx_parent]))85{86// Find all children of first_idx and their children up to the next static part87int next_to_process = 0;88Array<int> indices;89indices.reserve(mSkeleton->GetJointCount());90visited[first_idx] = true;91indices.push_back(first_idx);92do93{94int parent_idx = indices[next_to_process++];95for (int child_idx = 0; child_idx < mSkeleton->GetJointCount(); ++child_idx)96if (!visited[child_idx] && mSkeleton->GetJoint(child_idx).mParentJointIndex == parent_idx)97{98visited[child_idx] = true;99indices.push_back(child_idx);100}101} while (next_to_process < (int)indices.size());102103// If there's only 1 body, we can't redistribute mass104if (indices.size() == 1)105continue;106107const float cMinMassRatio = 0.8f;108const float cMaxMassRatio = 1.2f;109110// Ensure that the mass ratio from parent to child is within a range111float total_mass_ratio = 1.0f;112Array<float> mass_ratios;113mass_ratios.resize(mSkeleton->GetJointCount());114mass_ratios[indices[0]] = 1.0f;115for (int i = 1; i < (int)indices.size(); ++i)116{117int child_idx = indices[i];118int parent_idx = mSkeleton->GetJoint(child_idx).mParentJointIndex;119float ratio = mParts[child_idx].mMassPropertiesOverride.mMass / mParts[parent_idx].mMassPropertiesOverride.mMass;120mass_ratios[child_idx] = mass_ratios[parent_idx] * Clamp(ratio, cMinMassRatio, cMaxMassRatio);121total_mass_ratio += mass_ratios[child_idx];122}123124// Calculate total mass of this chain125float total_mass = 0.0f;126for (int idx : indices)127total_mass += mParts[idx].mMassPropertiesOverride.mMass;128129// Calculate how much mass belongs to a ratio of 1130float ratio_to_mass = total_mass / total_mass_ratio;131132// Adjust all masses and inertia tensors for the new mass133for (int i : indices)134{135Part &p = mParts[i];136float old_mass = p.mMassPropertiesOverride.mMass;137float new_mass = mass_ratios[i] * ratio_to_mass;138p.mMassPropertiesOverride.mMass = new_mass;139p.mMassPropertiesOverride.mInertia *= new_mass / old_mass;140p.mMassPropertiesOverride.mInertia.SetColumn4(3, Vec4(0, 0, 0, 1));141}142143const float cMaxInertiaIncrease = 2.0f;144145// Get the principal moments of inertia for all parts146struct Principal147{148Mat44 mRotation;149Vec3 mDiagonal;150float mChildSum = 0.0f;151};152Array<Principal> principals;153principals.resize(mParts.size());154for (int i : indices)155if (!mParts[i].mMassPropertiesOverride.DecomposePrincipalMomentsOfInertia(principals[i].mRotation, principals[i].mDiagonal))156{157JPH_ASSERT(false, "Failed to decompose the inertia tensor!");158return false;159}160161// Calculate sum of child inertias162// Walk backwards so we sum the leaves first163for (int i = (int)indices.size() - 1; i > 0; --i)164{165int child_idx = indices[i];166int parent_idx = mSkeleton->GetJoint(child_idx).mParentJointIndex;167principals[parent_idx].mChildSum += principals[child_idx].mDiagonal[0] + principals[child_idx].mChildSum;168}169170// Adjust inertia tensors for all parts171for (int i : indices)172{173Part &p = mParts[i];174Principal &principal = principals[i];175if (principal.mChildSum != 0.0f)176{177// Calculate minimum inertia this object should have based on it children178float minimum = min(cMaxInertiaIncrease * principal.mDiagonal[0], principal.mChildSum);179principal.mDiagonal = Vec3::sMax(principal.mDiagonal, Vec3::sReplicate(minimum));180181// Recalculate moment of inertia in body space182p.mMassPropertiesOverride.mInertia = principal.mRotation * Mat44::sScale(principal.mDiagonal) * principal.mRotation.Inversed3x3();183}184}185}186}187188return true;189}190191void RagdollSettings::DisableParentChildCollisions(const Mat44 *inJointMatrices, float inMinSeparationDistance)192{193int joint_count = mSkeleton->GetJointCount();194JPH_ASSERT(joint_count == (int)mParts.size());195196// Create a group filter table that disables collisions between parent and child197Ref<GroupFilterTable> group_filter = new GroupFilterTable(joint_count);198for (int joint_idx = 0; joint_idx < joint_count; ++joint_idx)199{200int parent_joint = mSkeleton->GetJoint(joint_idx).mParentJointIndex;201if (parent_joint >= 0)202group_filter->DisableCollision(joint_idx, parent_joint);203}204205// If joint matrices are provided206if (inJointMatrices != nullptr)207{208// Loop over all joints209for (int j1 = 0; j1 < joint_count; ++j1)210{211// Shape and transform for joint 1212const Part &part1 = mParts[j1];213const Shape *shape1 = part1.GetShape();214Vec3 scale1;215Mat44 com1 = (inJointMatrices[j1].PreTranslated(shape1->GetCenterOfMass())).Decompose(scale1);216217// Loop over all other joints218for (int j2 = j1 + 1; j2 < joint_count; ++j2)219if (group_filter->IsCollisionEnabled(j1, j2)) // Only if collision is still enabled we need to test220{221// Shape and transform for joint 2222const Part &part2 = mParts[j2];223const Shape *shape2 = part2.GetShape();224Vec3 scale2;225Mat44 com2 = (inJointMatrices[j2].PreTranslated(shape2->GetCenterOfMass())).Decompose(scale2);226227// Collision settings228CollideShapeSettings settings;229settings.mActiveEdgeMode = EActiveEdgeMode::CollideWithAll;230settings.mBackFaceMode = EBackFaceMode::CollideWithBackFaces;231settings.mMaxSeparationDistance = inMinSeparationDistance;232233// Only check if one of the two bodies can become dynamic234if (part1.HasMassProperties() || part2.HasMassProperties())235{236// If there is a collision, disable the collision between the joints237AnyHitCollisionCollector<CollideShapeCollector> collector;238if (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)239CollisionDispatch::sCollideShapeVsShape(shape1, shape2, scale1, scale2, com1, com2, SubShapeIDCreator(), SubShapeIDCreator(), settings, collector);240else241CollisionDispatch::sCollideShapeVsShape(shape2, shape1, scale2, scale1, com2, com1, SubShapeIDCreator(), SubShapeIDCreator(), settings, collector);242if (collector.HadHit())243group_filter->DisableCollision(j1, j2);244}245}246}247}248249// Loop over the body parts and assign them a sub group ID and the group filter250for (int joint_idx = 0; joint_idx < joint_count; ++joint_idx)251{252Part &part = mParts[joint_idx];253part.mCollisionGroup.SetSubGroupID(joint_idx);254part.mCollisionGroup.SetGroupFilter(group_filter);255}256}257258void RagdollSettings::SaveBinaryState(StreamOut &inStream, bool inSaveShapes, bool inSaveGroupFilter) const259{260BodyCreationSettings::ShapeToIDMap shape_to_id;261BodyCreationSettings::MaterialToIDMap material_to_id;262BodyCreationSettings::GroupFilterToIDMap group_filter_to_id;263264// Save skeleton265mSkeleton->SaveBinaryState(inStream);266267// Save parts268inStream.Write((uint32)mParts.size());269for (const Part &p : mParts)270{271// Write body creation settings272p.SaveWithChildren(inStream, inSaveShapes? &shape_to_id : nullptr, inSaveShapes? &material_to_id : nullptr, inSaveGroupFilter? &group_filter_to_id : nullptr);273274// Save constraint275inStream.Write(p.mToParent != nullptr);276if (p.mToParent != nullptr)277p.mToParent->SaveBinaryState(inStream);278}279280// Save additional constraints281inStream.Write((uint32)mAdditionalConstraints.size());282for (const AdditionalConstraint &c : mAdditionalConstraints)283{284// Save bodies indices285inStream.Write(c.mBodyIdx);286287// Save constraint288c.mConstraint->SaveBinaryState(inStream);289}290}291292RagdollSettings::RagdollResult RagdollSettings::sRestoreFromBinaryState(StreamIn &inStream)293{294RagdollResult result;295296// Restore skeleton297Skeleton::SkeletonResult skeleton_result = Skeleton::sRestoreFromBinaryState(inStream);298if (skeleton_result.HasError())299{300result.SetError(skeleton_result.GetError());301return result;302}303304// Create ragdoll305Ref<RagdollSettings> ragdoll = new RagdollSettings();306ragdoll->mSkeleton = skeleton_result.Get();307308BodyCreationSettings::IDToShapeMap id_to_shape;309BodyCreationSettings::IDToMaterialMap id_to_material;310BodyCreationSettings::IDToGroupFilterMap id_to_group_filter;311312// Reserve some memory to avoid frequent reallocations313id_to_shape.reserve(1024);314id_to_material.reserve(128);315id_to_group_filter.reserve(128);316317// Read parts318uint32 len = 0;319inStream.Read(len);320ragdoll->mParts.resize(len);321for (Part &p : ragdoll->mParts)322{323// Read creation settings324BodyCreationSettings::BCSResult bcs_result = BodyCreationSettings::sRestoreWithChildren(inStream, id_to_shape, id_to_material, id_to_group_filter);325if (bcs_result.HasError())326{327result.SetError(bcs_result.GetError());328return result;329}330static_cast<BodyCreationSettings &>(p) = bcs_result.Get();331332// Read constraint333bool has_constraint = false;334inStream.Read(has_constraint);335if (has_constraint)336{337ConstraintSettings::ConstraintResult constraint_result = ConstraintSettings::sRestoreFromBinaryState(inStream);338if (constraint_result.HasError())339{340result.SetError(constraint_result.GetError());341return result;342}343p.mToParent = DynamicCast<TwoBodyConstraintSettings>(constraint_result.Get());344}345}346347// Read additional constraints348len = 0;349inStream.Read(len);350ragdoll->mAdditionalConstraints.resize(len);351for (AdditionalConstraint &c : ragdoll->mAdditionalConstraints)352{353// Read body indices354inStream.Read(c.mBodyIdx);355356// Read constraint357ConstraintSettings::ConstraintResult constraint_result = ConstraintSettings::sRestoreFromBinaryState(inStream);358if (constraint_result.HasError())359{360result.SetError(constraint_result.GetError());361return result;362}363c.mConstraint = DynamicCast<TwoBodyConstraintSettings>(constraint_result.Get());364}365366// Create mapping tables367ragdoll->CalculateBodyIndexToConstraintIndex();368ragdoll->CalculateConstraintIndexToBodyIdxPair();369370result.Set(ragdoll);371return result;372}373374Ragdoll *RagdollSettings::CreateRagdoll(CollisionGroup::GroupID inCollisionGroup, uint64 inUserData, PhysicsSystem *inSystem) const375{376Ragdoll *r = new Ragdoll(inSystem);377r->mRagdollSettings = this;378r->mBodyIDs.reserve(mParts.size());379r->mConstraints.reserve(mParts.size() + mAdditionalConstraints.size());380381// Create bodies and constraints382BodyInterface &bi = inSystem->GetBodyInterface();383Body **bodies = (Body **)JPH_STACK_ALLOC(mParts.size() * sizeof(Body *));384int joint_idx = 0;385for (const Part &p : mParts)386{387Body *body2 = bi.CreateBody(p);388if (body2 == nullptr)389{390// Out of bodies, failed to create ragdoll391delete r;392return nullptr;393}394body2->GetCollisionGroup().SetGroupID(inCollisionGroup);395body2->SetUserData(inUserData);396397// Temporarily store body pointer for hooking up constraints398bodies[joint_idx] = body2;399400// Create constraint401if (p.mToParent != nullptr)402{403Body *body1 = bodies[mSkeleton->GetJoint(joint_idx).mParentJointIndex];404r->mConstraints.push_back(p.mToParent->Create(*body1, *body2));405}406407// Store body ID and constraint in parallel arrays408r->mBodyIDs.push_back(body2->GetID());409410++joint_idx;411}412413// Add additional constraints414for (const AdditionalConstraint &c : mAdditionalConstraints)415{416Body *body1 = bodies[c.mBodyIdx[0]];417Body *body2 = bodies[c.mBodyIdx[1]];418r->mConstraints.push_back(c.mConstraint->Create(*body1, *body2));419}420421return r;422}423424void RagdollSettings::CalculateBodyIndexToConstraintIndex()425{426mBodyIndexToConstraintIndex.clear();427mBodyIndexToConstraintIndex.reserve(mParts.size());428429int constraint_index = 0;430for (const Part &p : mParts)431{432if (p.mToParent != nullptr)433mBodyIndexToConstraintIndex.push_back(constraint_index++);434else435mBodyIndexToConstraintIndex.push_back(-1);436}437}438439void RagdollSettings::CalculateConstraintIndexToBodyIdxPair()440{441mConstraintIndexToBodyIdxPair.clear();442mConstraintIndexToBodyIdxPair.reserve(mParts.size() + mAdditionalConstraints.size());443444// Add constraints between parts445int joint_idx = 0;446for (const Part &p : mParts)447{448if (p.mToParent != nullptr)449{450int parent_joint_idx = mSkeleton->GetJoint(joint_idx).mParentJointIndex;451mConstraintIndexToBodyIdxPair.emplace_back(parent_joint_idx, joint_idx);452}453454++joint_idx;455}456457// Add additional constraints458for (const AdditionalConstraint &c : mAdditionalConstraints)459mConstraintIndexToBodyIdxPair.emplace_back(c.mBodyIdx[0], c.mBodyIdx[1]);460}461462Ragdoll::~Ragdoll()463{464// Destroy all bodies465mSystem->GetBodyInterface().DestroyBodies(mBodyIDs.data(), (int)mBodyIDs.size());466}467468void Ragdoll::AddToPhysicsSystem(EActivation inActivationMode, bool inLockBodies)469{470// Scope for JPH_STACK_ALLOC471{472// Create copy of body ids since they will be shuffled473int num_bodies = (int)mBodyIDs.size();474BodyID *bodies = (BodyID *)JPH_STACK_ALLOC(num_bodies * sizeof(BodyID));475memcpy(bodies, mBodyIDs.data(), num_bodies * sizeof(BodyID));476477// Insert bodies as a batch478BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);479BodyInterface::AddState add_state = bi.AddBodiesPrepare(bodies, num_bodies);480bi.AddBodiesFinalize(bodies, num_bodies, add_state, inActivationMode);481}482483// Add all constraints484mSystem->AddConstraints((Constraint **)mConstraints.data(), (int)mConstraints.size());485}486487void Ragdoll::RemoveFromPhysicsSystem(bool inLockBodies)488{489// Remove all constraints before removing the bodies490mSystem->RemoveConstraints((Constraint **)mConstraints.data(), (int)mConstraints.size());491492// Scope for JPH_STACK_ALLOC493{494// Create copy of body ids since they will be shuffled495int num_bodies = (int)mBodyIDs.size();496BodyID *bodies = (BodyID *)JPH_STACK_ALLOC(num_bodies * sizeof(BodyID));497memcpy(bodies, mBodyIDs.data(), num_bodies * sizeof(BodyID));498499// Remove all bodies as a batch500sRagdollGetBodyInterface(mSystem, inLockBodies).RemoveBodies(bodies, num_bodies);501}502}503504void Ragdoll::Activate(bool inLockBodies)505{506sRagdollGetBodyInterface(mSystem, inLockBodies).ActivateBodies(mBodyIDs.data(), (int)mBodyIDs.size());507}508509bool Ragdoll::IsActive(bool inLockBodies) const510{511// Lock the bodies512int body_count = (int)mBodyIDs.size();513BodyLockMultiRead lock(sRagdollGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count);514515// Test if any body is active516for (int b = 0; b < body_count; ++b)517{518const Body *body = lock.GetBody(b);519if (body->IsActive())520return true;521}522523return false;524}525526void Ragdoll::SetGroupID(CollisionGroup::GroupID inGroupID, bool inLockBodies)527{528// Lock the bodies529int body_count = (int)mBodyIDs.size();530BodyLockMultiWrite lock(sRagdollGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count);531532// Update group ID533for (int b = 0; b < body_count; ++b)534{535Body *body = lock.GetBody(b);536body->GetCollisionGroup().SetGroupID(inGroupID);537}538}539540void Ragdoll::SetPose(const SkeletonPose &inPose, bool inLockBodies)541{542JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton);543544SetPose(inPose.GetRootOffset(), inPose.GetJointMatrices().data(), inLockBodies);545}546547void Ragdoll::SetPose(RVec3Arg inRootOffset, const Mat44 *inJointMatrices, bool inLockBodies)548{549// Move bodies instantly into the correct position550BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);551for (int i = 0; i < (int)mBodyIDs.size(); ++i)552{553const Mat44 &joint = inJointMatrices[i];554bi.SetPositionAndRotation(mBodyIDs[i], inRootOffset + joint.GetTranslation(), joint.GetQuaternion(), EActivation::DontActivate);555}556}557558void Ragdoll::GetPose(SkeletonPose &outPose, bool inLockBodies)559{560JPH_ASSERT(outPose.GetSkeleton() == mRagdollSettings->mSkeleton);561562RVec3 root_offset;563GetPose(root_offset, outPose.GetJointMatrices().data(), inLockBodies);564outPose.SetRootOffset(root_offset);565}566567void Ragdoll::GetPose(RVec3 &outRootOffset, Mat44 *outJointMatrices, bool inLockBodies)568{569// Lock the bodies570int body_count = (int)mBodyIDs.size();571if (body_count == 0)572return;573BodyLockMultiRead lock(sRagdollGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count);574575// Get root matrix576const Body *root = lock.GetBody(0);577RMat44 root_transform = root->GetWorldTransform();578outRootOffset = root_transform.GetTranslation();579outJointMatrices[0] = Mat44(root_transform.GetColumn4(0), root_transform.GetColumn4(1), root_transform.GetColumn4(2), Vec4(0, 0, 0, 1));580581// Get other matrices582for (int b = 1; b < body_count; ++b)583{584const Body *body = lock.GetBody(b);585RMat44 transform = body->GetWorldTransform();586outJointMatrices[b] = Mat44(transform.GetColumn4(0), transform.GetColumn4(1), transform.GetColumn4(2), Vec4(Vec3(transform.GetTranslation() - outRootOffset), 1));587}588}589590void Ragdoll::ResetWarmStart()591{592for (TwoBodyConstraint *c : mConstraints)593c->ResetWarmStart();594}595596void Ragdoll::DriveToPoseUsingKinematics(const SkeletonPose &inPose, float inDeltaTime, bool inLockBodies)597{598JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton);599600DriveToPoseUsingKinematics(inPose.GetRootOffset(), inPose.GetJointMatrices().data(), inDeltaTime, inLockBodies);601}602603void Ragdoll::DriveToPoseUsingKinematics(RVec3Arg inRootOffset, const Mat44 *inJointMatrices, float inDeltaTime, bool inLockBodies)604{605// Move bodies into the correct position using kinematics606BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);607for (int i = 0; i < (int)mBodyIDs.size(); ++i)608{609const Mat44 &joint = inJointMatrices[i];610bi.MoveKinematic(mBodyIDs[i], inRootOffset + joint.GetTranslation(), joint.GetQuaternion(), inDeltaTime);611}612}613614void Ragdoll::DriveToPoseUsingMotors(const SkeletonPose &inPose)615{616JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton);617618// Move bodies into the correct position using constraints619for (int i = 0; i < (int)inPose.GetJointMatrices().size(); ++i)620{621int constraint_idx = mRagdollSettings->GetConstraintIndexForBodyIndex(i);622if (constraint_idx >= 0)623{624// Get desired rotation of this body relative to its parent625const SkeletalAnimation::JointState &joint_state = inPose.GetJoint(i);626627// Drive constraint to target628TwoBodyConstraint *constraint = mConstraints[constraint_idx];629EConstraintSubType sub_type = constraint->GetSubType();630if (sub_type == EConstraintSubType::SwingTwist)631{632SwingTwistConstraint *st_constraint = static_cast<SwingTwistConstraint *>(constraint);633st_constraint->SetSwingMotorState(EMotorState::Position);634st_constraint->SetTwistMotorState(EMotorState::Position);635st_constraint->SetTargetOrientationBS(joint_state.mRotation);636}637else638JPH_ASSERT(false, "Constraint type not implemented!");639}640}641}642643void Ragdoll::SetLinearAndAngularVelocity(Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, bool inLockBodies)644{645BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);646for (BodyID body_id : mBodyIDs)647bi.SetLinearAndAngularVelocity(body_id, inLinearVelocity, inAngularVelocity);648}649650void Ragdoll::SetLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies)651{652BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);653for (BodyID body_id : mBodyIDs)654bi.SetLinearVelocity(body_id, inLinearVelocity);655}656657void Ragdoll::AddLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies)658{659BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);660for (BodyID body_id : mBodyIDs)661bi.AddLinearVelocity(body_id, inLinearVelocity);662}663664void Ragdoll::AddImpulse(Vec3Arg inImpulse, bool inLockBodies)665{666BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);667for (BodyID body_id : mBodyIDs)668bi.AddImpulse(body_id, inImpulse);669}670671void Ragdoll::GetRootTransform(RVec3 &outPosition, Quat &outRotation, bool inLockBodies) const672{673BodyLockRead lock(sRagdollGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs[0]);674if (lock.Succeeded())675{676const Body &body = lock.GetBody();677outPosition = body.GetPosition();678outRotation = body.GetRotation();679}680else681{682outPosition = RVec3::sZero();683outRotation = Quat::sIdentity();684}685}686687AABox Ragdoll::GetWorldSpaceBounds(bool inLockBodies) const688{689// Lock the bodies690int body_count = (int)mBodyIDs.size();691BodyLockMultiRead lock(sRagdollGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count);692693// Encapsulate all bodies694AABox bounds;695for (int b = 0; b < body_count; ++b)696{697const Body *body = lock.GetBody(b);698if (body != nullptr)699bounds.Encapsulate(body->GetWorldSpaceBounds());700}701return bounds;702}703704JPH_NAMESPACE_END705706707