Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Ragdoll/Ragdoll.cpp
22060 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/Constraints/HingeConstraint.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>1819JPH_NAMESPACE_BEGIN2021JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(RagdollSettings::Part)22{23JPH_ADD_BASE_CLASS(RagdollSettings::Part, BodyCreationSettings)2425JPH_ADD_ATTRIBUTE(RagdollSettings::Part, mToParent)26}2728JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(RagdollSettings::AdditionalConstraint)29{30JPH_ADD_ATTRIBUTE(RagdollSettings::AdditionalConstraint, mBodyIdx)31JPH_ADD_ATTRIBUTE(RagdollSettings::AdditionalConstraint, mConstraint)32}3334JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(RagdollSettings)35{36JPH_ADD_ATTRIBUTE(RagdollSettings, mSkeleton)37JPH_ADD_ATTRIBUTE(RagdollSettings, mParts)38JPH_ADD_ATTRIBUTE(RagdollSettings, mAdditionalConstraints)39}4041static inline BodyInterface &sRagdollGetBodyInterface(PhysicsSystem *inSystem, bool inLockBodies)42{43return inLockBodies? inSystem->GetBodyInterface() : inSystem->GetBodyInterfaceNoLock();44}4546static inline const BodyLockInterface &sRagdollGetBodyLockInterface(const PhysicsSystem *inSystem, bool inLockBodies)47{48return inLockBodies? static_cast<const BodyLockInterface &>(inSystem->GetBodyLockInterface()) : static_cast<const BodyLockInterface &>(inSystem->GetBodyLockInterfaceNoLock());49}5051bool 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)5758// If we don't have any joints there's nothing to stabilize59if (mSkeleton->GetJointCount() == 0)60return true;6162// 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.64Array<bool> visited;65visited.resize(mSkeleton->GetJointCount());66for (size_t v = 0; v < visited.size(); ++v)67{68// Mark static bodies as visited so we won't process these69Part &p = mParts[v];70bool has_mass_properties = p.HasMassProperties();71visited[v] = !has_mass_properties;7273if (has_mass_properties && p.mOverrideMassProperties != EOverrideMassProperties::MassAndInertiaProvided)74{75// Mass properties not yet calculated, do it now76p.mMassPropertiesOverride = p.GetMassProperties();77p.mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided;78}79}8081// Find first unvisited part that either has no parent or that has a parent that was visited82for (int first_idx = 0; first_idx < mSkeleton->GetJointCount(); ++first_idx)83{84int first_idx_parent = mSkeleton->GetJoint(first_idx).mParentJointIndex;85if (!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 part88int next_to_process = 0;89Array<int> indices;90indices.reserve(mSkeleton->GetJointCount());91visited[first_idx] = true;92indices.push_back(first_idx);93do94{95int parent_idx = indices[next_to_process++];96for (int child_idx = 0; child_idx < mSkeleton->GetJointCount(); ++child_idx)97if (!visited[child_idx] && mSkeleton->GetJoint(child_idx).mParentJointIndex == parent_idx)98{99visited[child_idx] = true;100indices.push_back(child_idx);101}102} while (next_to_process < (int)indices.size());103104// If there's only 1 body, we can't redistribute mass105if (indices.size() == 1)106continue;107108const float cMinMassRatio = 0.8f;109const float cMaxMassRatio = 1.2f;110111// Ensure that the mass ratio from parent to child is within a range112float total_mass_ratio = 1.0f;113Array<float> mass_ratios;114mass_ratios.resize(mSkeleton->GetJointCount());115mass_ratios[indices[0]] = 1.0f;116for (int i = 1; i < (int)indices.size(); ++i)117{118int child_idx = indices[i];119int parent_idx = mSkeleton->GetJoint(child_idx).mParentJointIndex;120float ratio = mParts[child_idx].mMassPropertiesOverride.mMass / mParts[parent_idx].mMassPropertiesOverride.mMass;121mass_ratios[child_idx] = mass_ratios[parent_idx] * Clamp(ratio, cMinMassRatio, cMaxMassRatio);122total_mass_ratio += mass_ratios[child_idx];123}124125// Calculate total mass of this chain126float total_mass = 0.0f;127for (int idx : indices)128total_mass += mParts[idx].mMassPropertiesOverride.mMass;129130// Calculate how much mass belongs to a ratio of 1131float ratio_to_mass = total_mass / total_mass_ratio;132133// Adjust all masses and inertia tensors for the new mass134for (int i : indices)135{136Part &p = mParts[i];137float old_mass = p.mMassPropertiesOverride.mMass;138float new_mass = mass_ratios[i] * ratio_to_mass;139p.mMassPropertiesOverride.mMass = new_mass;140p.mMassPropertiesOverride.mInertia *= new_mass / old_mass;141p.mMassPropertiesOverride.mInertia.SetColumn4(3, Vec4(0, 0, 0, 1));142}143144const float cMaxInertiaIncrease = 2.0f;145146// Get the principal moments of inertia for all parts147struct Principal148{149Mat44 mRotation;150Vec3 mDiagonal;151float mChildSum = 0.0f;152};153Array<Principal> principals;154principals.resize(mParts.size());155for (int i : indices)156if (!mParts[i].mMassPropertiesOverride.DecomposePrincipalMomentsOfInertia(principals[i].mRotation, principals[i].mDiagonal))157{158JPH_ASSERT(false, "Failed to decompose the inertia tensor!");159return false;160}161162// Calculate sum of child inertias163// Walk backwards so we sum the leaves first164for (int i = (int)indices.size() - 1; i > 0; --i)165{166int child_idx = indices[i];167int parent_idx = mSkeleton->GetJoint(child_idx).mParentJointIndex;168principals[parent_idx].mChildSum += principals[child_idx].mDiagonal[0] + principals[child_idx].mChildSum;169}170171// Adjust inertia tensors for all parts172for (int i : indices)173{174Part &p = mParts[i];175Principal &principal = principals[i];176if (principal.mChildSum != 0.0f)177{178// Calculate minimum inertia this object should have based on it children179float minimum = min(cMaxInertiaIncrease * principal.mDiagonal[0], principal.mChildSum);180principal.mDiagonal = Vec3::sMax(principal.mDiagonal, Vec3::sReplicate(minimum));181182// Recalculate moment of inertia in body space183p.mMassPropertiesOverride.mInertia = principal.mRotation * Mat44::sScale(principal.mDiagonal) * principal.mRotation.Inversed3x3();184}185}186}187}188189return true;190}191192void RagdollSettings::DisableParentChildCollisions(const Mat44 *inJointMatrices, float inMinSeparationDistance)193{194int joint_count = mSkeleton->GetJointCount();195JPH_ASSERT(joint_count == (int)mParts.size());196197// Create a group filter table that disables collisions between parent and child198Ref<GroupFilterTable> group_filter = new GroupFilterTable(joint_count);199for (int joint_idx = 0; joint_idx < joint_count; ++joint_idx)200{201int parent_joint = mSkeleton->GetJoint(joint_idx).mParentJointIndex;202if (parent_joint >= 0)203group_filter->DisableCollision(joint_idx, parent_joint);204}205206// If joint matrices are provided207if (inJointMatrices != nullptr)208{209// Loop over all joints210for (int j1 = 0; j1 < joint_count; ++j1)211{212// Shape and transform for joint 1213const Part &part1 = mParts[j1];214const Shape *shape1 = part1.GetShape();215Vec3 scale1;216Mat44 com1 = (inJointMatrices[j1].PreTranslated(shape1->GetCenterOfMass())).Decompose(scale1);217218// Loop over all other joints219for (int j2 = j1 + 1; j2 < joint_count; ++j2)220if (group_filter->IsCollisionEnabled(j1, j2)) // Only if collision is still enabled we need to test221{222// Shape and transform for joint 2223const Part &part2 = mParts[j2];224const Shape *shape2 = part2.GetShape();225Vec3 scale2;226Mat44 com2 = (inJointMatrices[j2].PreTranslated(shape2->GetCenterOfMass())).Decompose(scale2);227228// Collision settings229CollideShapeSettings settings;230settings.mActiveEdgeMode = EActiveEdgeMode::CollideWithAll;231settings.mBackFaceMode = EBackFaceMode::CollideWithBackFaces;232settings.mMaxSeparationDistance = inMinSeparationDistance;233234// Only check if one of the two bodies can become dynamic235if (part1.HasMassProperties() || part2.HasMassProperties())236{237// If there is a collision, disable the collision between the joints238AnyHitCollisionCollector<CollideShapeCollector> collector;239if (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)240CollisionDispatch::sCollideShapeVsShape(shape1, shape2, scale1, scale2, com1, com2, SubShapeIDCreator(), SubShapeIDCreator(), settings, collector);241else242CollisionDispatch::sCollideShapeVsShape(shape2, shape1, scale2, scale1, com2, com1, SubShapeIDCreator(), SubShapeIDCreator(), settings, collector);243if (collector.HadHit())244group_filter->DisableCollision(j1, j2);245}246}247}248}249250// Loop over the body parts and assign them a sub group ID and the group filter251for (int joint_idx = 0; joint_idx < joint_count; ++joint_idx)252{253Part &part = mParts[joint_idx];254part.mCollisionGroup.SetSubGroupID(joint_idx);255part.mCollisionGroup.SetGroupFilter(group_filter);256}257}258259void RagdollSettings::SaveBinaryState(StreamOut &inStream, bool inSaveShapes, bool inSaveGroupFilter) const260{261BodyCreationSettings::ShapeToIDMap shape_to_id;262BodyCreationSettings::MaterialToIDMap material_to_id;263BodyCreationSettings::GroupFilterToIDMap group_filter_to_id;264265// Save skeleton266mSkeleton->SaveBinaryState(inStream);267268// Save parts269inStream.Write((uint32)mParts.size());270for (const Part &p : mParts)271{272// Write body creation settings273p.SaveWithChildren(inStream, inSaveShapes? &shape_to_id : nullptr, inSaveShapes? &material_to_id : nullptr, inSaveGroupFilter? &group_filter_to_id : nullptr);274275// Save constraint276inStream.Write(p.mToParent != nullptr);277if (p.mToParent != nullptr)278p.mToParent->SaveBinaryState(inStream);279}280281// Save additional constraints282inStream.Write((uint32)mAdditionalConstraints.size());283for (const AdditionalConstraint &c : mAdditionalConstraints)284{285// Save bodies indices286inStream.Write(c.mBodyIdx);287288// Save constraint289c.mConstraint->SaveBinaryState(inStream);290}291}292293RagdollSettings::RagdollResult RagdollSettings::sRestoreFromBinaryState(StreamIn &inStream)294{295RagdollResult result;296297// Restore skeleton298Skeleton::SkeletonResult skeleton_result = Skeleton::sRestoreFromBinaryState(inStream);299if (skeleton_result.HasError())300{301result.SetError(skeleton_result.GetError());302return result;303}304305// Create ragdoll306Ref<RagdollSettings> ragdoll = new RagdollSettings();307ragdoll->mSkeleton = skeleton_result.Get();308309BodyCreationSettings::IDToShapeMap id_to_shape;310BodyCreationSettings::IDToMaterialMap id_to_material;311BodyCreationSettings::IDToGroupFilterMap id_to_group_filter;312313// Reserve some memory to avoid frequent reallocations314id_to_shape.reserve(1024);315id_to_material.reserve(128);316id_to_group_filter.reserve(128);317318// Read parts319uint32 len = 0;320inStream.Read(len);321ragdoll->mParts.resize(len);322for (Part &p : ragdoll->mParts)323{324// Read creation settings325BodyCreationSettings::BCSResult bcs_result = BodyCreationSettings::sRestoreWithChildren(inStream, id_to_shape, id_to_material, id_to_group_filter);326if (bcs_result.HasError())327{328result.SetError(bcs_result.GetError());329return result;330}331static_cast<BodyCreationSettings &>(p) = bcs_result.Get();332333// Read constraint334bool has_constraint = false;335inStream.Read(has_constraint);336if (has_constraint)337{338ConstraintSettings::ConstraintResult constraint_result = ConstraintSettings::sRestoreFromBinaryState(inStream);339if (constraint_result.HasError())340{341result.SetError(constraint_result.GetError());342return result;343}344p.mToParent = DynamicCast<TwoBodyConstraintSettings>(constraint_result.Get());345}346}347348// Read additional constraints349len = 0;350inStream.Read(len);351ragdoll->mAdditionalConstraints.resize(len);352for (AdditionalConstraint &c : ragdoll->mAdditionalConstraints)353{354// Read body indices355inStream.Read(c.mBodyIdx);356357// Read constraint358ConstraintSettings::ConstraintResult constraint_result = ConstraintSettings::sRestoreFromBinaryState(inStream);359if (constraint_result.HasError())360{361result.SetError(constraint_result.GetError());362return result;363}364c.mConstraint = DynamicCast<TwoBodyConstraintSettings>(constraint_result.Get());365}366367// Create mapping tables368ragdoll->CalculateBodyIndexToConstraintIndex();369ragdoll->CalculateConstraintIndexToBodyIdxPair();370371result.Set(ragdoll);372return result;373}374375Ragdoll *RagdollSettings::CreateRagdoll(CollisionGroup::GroupID inCollisionGroup, uint64 inUserData, PhysicsSystem *inSystem) const376{377Ragdoll *r = new Ragdoll(inSystem);378r->mRagdollSettings = this;379r->mBodyIDs.reserve(mParts.size());380r->mConstraints.reserve(mParts.size() + mAdditionalConstraints.size());381382// Create bodies and constraints383BodyInterface &bi = inSystem->GetBodyInterface();384Body **bodies = (Body **)JPH_STACK_ALLOC(mParts.size() * sizeof(Body *));385int joint_idx = 0;386for (const Part &p : mParts)387{388Body *body2 = bi.CreateBody(p);389if (body2 == nullptr)390{391// Out of bodies, failed to create ragdoll392delete r;393return nullptr;394}395body2->GetCollisionGroup().SetGroupID(inCollisionGroup);396body2->SetUserData(inUserData);397398// Temporarily store body pointer for hooking up constraints399bodies[joint_idx] = body2;400401// Create constraint402if (p.mToParent != nullptr)403{404Body *body1 = bodies[mSkeleton->GetJoint(joint_idx).mParentJointIndex];405r->mConstraints.push_back(p.mToParent->Create(*body1, *body2));406}407408// Store body ID and constraint in parallel arrays409r->mBodyIDs.push_back(body2->GetID());410411++joint_idx;412}413414// Add additional constraints415for (const AdditionalConstraint &c : mAdditionalConstraints)416{417Body *body1 = bodies[c.mBodyIdx[0]];418Body *body2 = bodies[c.mBodyIdx[1]];419r->mConstraints.push_back(c.mConstraint->Create(*body1, *body2));420}421422return r;423}424425void RagdollSettings::CalculateBodyIndexToConstraintIndex()426{427mBodyIndexToConstraintIndex.clear();428mBodyIndexToConstraintIndex.reserve(mParts.size());429430int constraint_index = 0;431for (const Part &p : mParts)432{433if (p.mToParent != nullptr)434mBodyIndexToConstraintIndex.push_back(constraint_index++);435else436mBodyIndexToConstraintIndex.push_back(-1);437}438}439440void RagdollSettings::CalculateConstraintIndexToBodyIdxPair()441{442mConstraintIndexToBodyIdxPair.clear();443mConstraintIndexToBodyIdxPair.reserve(mParts.size() + mAdditionalConstraints.size());444445// Add constraints between parts446int joint_idx = 0;447for (const Part &p : mParts)448{449if (p.mToParent != nullptr)450{451int parent_joint_idx = mSkeleton->GetJoint(joint_idx).mParentJointIndex;452mConstraintIndexToBodyIdxPair.emplace_back(parent_joint_idx, joint_idx);453}454455++joint_idx;456}457458// Add additional constraints459for (const AdditionalConstraint &c : mAdditionalConstraints)460mConstraintIndexToBodyIdxPair.emplace_back(c.mBodyIdx[0], c.mBodyIdx[1]);461}462463Ragdoll::~Ragdoll()464{465// Destroy all bodies466mSystem->GetBodyInterface().DestroyBodies(mBodyIDs.data(), (int)mBodyIDs.size());467}468469void Ragdoll::AddToPhysicsSystem(EActivation inActivationMode, bool inLockBodies)470{471// Scope for JPH_STACK_ALLOC472{473// Create copy of body ids since they will be shuffled474int num_bodies = (int)mBodyIDs.size();475BodyID *bodies = (BodyID *)JPH_STACK_ALLOC(num_bodies * sizeof(BodyID));476memcpy(bodies, mBodyIDs.data(), num_bodies * sizeof(BodyID));477478// Insert bodies as a batch479BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);480BodyInterface::AddState add_state = bi.AddBodiesPrepare(bodies, num_bodies);481bi.AddBodiesFinalize(bodies, num_bodies, add_state, inActivationMode);482}483484// Add all constraints485mSystem->AddConstraints((Constraint **)mConstraints.data(), (int)mConstraints.size());486}487488void Ragdoll::RemoveFromPhysicsSystem(bool inLockBodies)489{490// Remove all constraints before removing the bodies491mSystem->RemoveConstraints((Constraint **)mConstraints.data(), (int)mConstraints.size());492493// Scope for JPH_STACK_ALLOC494{495// Create copy of body ids since they will be shuffled496int num_bodies = (int)mBodyIDs.size();497BodyID *bodies = (BodyID *)JPH_STACK_ALLOC(num_bodies * sizeof(BodyID));498memcpy(bodies, mBodyIDs.data(), num_bodies * sizeof(BodyID));499500// Remove all bodies as a batch501sRagdollGetBodyInterface(mSystem, inLockBodies).RemoveBodies(bodies, num_bodies);502}503}504505void Ragdoll::Activate(bool inLockBodies)506{507sRagdollGetBodyInterface(mSystem, inLockBodies).ActivateBodies(mBodyIDs.data(), (int)mBodyIDs.size());508}509510bool Ragdoll::IsActive(bool inLockBodies) const511{512// Lock the bodies513int body_count = (int)mBodyIDs.size();514BodyLockMultiRead lock(sRagdollGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count);515516// Test if any body is active517for (int b = 0; b < body_count; ++b)518{519const Body *body = lock.GetBody(b);520if (body->IsActive())521return true;522}523524return false;525}526527void Ragdoll::SetGroupID(CollisionGroup::GroupID inGroupID, bool inLockBodies)528{529// Lock the bodies530int body_count = (int)mBodyIDs.size();531BodyLockMultiWrite lock(sRagdollGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count);532533// Update group ID534for (int b = 0; b < body_count; ++b)535{536Body *body = lock.GetBody(b);537body->GetCollisionGroup().SetGroupID(inGroupID);538}539}540541void Ragdoll::SetPose(const SkeletonPose &inPose, bool inLockBodies)542{543JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton);544545SetPose(inPose.GetRootOffset(), inPose.GetJointMatrices().data(), inLockBodies);546}547548void Ragdoll::SetPose(RVec3Arg inRootOffset, const Mat44 *inJointMatrices, bool inLockBodies)549{550// Move bodies instantly into the correct position551BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);552for (int i = 0; i < (int)mBodyIDs.size(); ++i)553{554const Mat44 &joint = inJointMatrices[i];555bi.SetPositionAndRotation(mBodyIDs[i], inRootOffset + joint.GetTranslation(), joint.GetQuaternion(), EActivation::DontActivate);556}557}558559void Ragdoll::GetPose(SkeletonPose &outPose, bool inLockBodies)560{561JPH_ASSERT(outPose.GetSkeleton() == mRagdollSettings->mSkeleton);562563RVec3 root_offset;564GetPose(root_offset, outPose.GetJointMatrices().data(), inLockBodies);565outPose.SetRootOffset(root_offset);566}567568void Ragdoll::GetPose(RVec3 &outRootOffset, Mat44 *outJointMatrices, bool inLockBodies)569{570// Lock the bodies571int body_count = (int)mBodyIDs.size();572if (body_count == 0)573return;574BodyLockMultiRead lock(sRagdollGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count);575576// Get root matrix577const Body *root = lock.GetBody(0);578RMat44 root_transform = root->GetWorldTransform();579outRootOffset = root_transform.GetTranslation();580outJointMatrices[0] = Mat44(root_transform.GetColumn4(0), root_transform.GetColumn4(1), root_transform.GetColumn4(2), Vec4(0, 0, 0, 1));581582// Get other matrices583for (int b = 1; b < body_count; ++b)584{585const Body *body = lock.GetBody(b);586RMat44 transform = body->GetWorldTransform();587outJointMatrices[b] = Mat44(transform.GetColumn4(0), transform.GetColumn4(1), transform.GetColumn4(2), Vec4(Vec3(transform.GetTranslation() - outRootOffset), 1));588}589}590591void Ragdoll::ResetWarmStart()592{593for (TwoBodyConstraint *c : mConstraints)594c->ResetWarmStart();595}596597void Ragdoll::DriveToPoseUsingKinematics(const SkeletonPose &inPose, float inDeltaTime, bool inLockBodies)598{599JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton);600601DriveToPoseUsingKinematics(inPose.GetRootOffset(), inPose.GetJointMatrices().data(), inDeltaTime, inLockBodies);602}603604void Ragdoll::DriveToPoseUsingKinematics(RVec3Arg inRootOffset, const Mat44 *inJointMatrices, float inDeltaTime, bool inLockBodies)605{606// Move bodies into the correct position using kinematics607BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);608for (int i = 0; i < (int)mBodyIDs.size(); ++i)609{610const Mat44 &joint = inJointMatrices[i];611bi.MoveKinematic(mBodyIDs[i], inRootOffset + joint.GetTranslation(), joint.GetQuaternion(), inDeltaTime);612}613}614615void Ragdoll::DriveToPoseUsingMotors(const SkeletonPose &inPose)616{617JPH_ASSERT(inPose.GetSkeleton() == mRagdollSettings->mSkeleton);618619// Move bodies into the correct position using constraints620for (int i = 0; i < (int)inPose.GetJointMatrices().size(); ++i)621{622int constraint_idx = mRagdollSettings->GetConstraintIndexForBodyIndex(i);623if (constraint_idx >= 0)624{625// Get desired rotation of this body relative to its parent626const SkeletalAnimation::JointState &joint_state = inPose.GetJoint(i);627628// Drive constraint to target629TwoBodyConstraint *constraint = mConstraints[constraint_idx];630EConstraintSubType sub_type = constraint->GetSubType();631if (sub_type == EConstraintSubType::SwingTwist)632{633SwingTwistConstraint *st_constraint = static_cast<SwingTwistConstraint *>(constraint);634st_constraint->SetSwingMotorState(EMotorState::Position);635st_constraint->SetTwistMotorState(EMotorState::Position);636st_constraint->SetTargetOrientationBS(joint_state.mRotation);637}638else if (sub_type == EConstraintSubType::Hinge)639{640HingeConstraint *h_constraint = static_cast<HingeConstraint *>(constraint);641h_constraint->SetMotorState(EMotorState::Position);642h_constraint->SetTargetOrientationBS(joint_state.mRotation);643}644else645JPH_ASSERT(false, "Constraint type not implemented!");646}647}648}649650void Ragdoll::SetLinearAndAngularVelocity(Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, bool inLockBodies)651{652BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);653for (BodyID body_id : mBodyIDs)654bi.SetLinearAndAngularVelocity(body_id, inLinearVelocity, inAngularVelocity);655}656657void Ragdoll::SetLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies)658{659BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);660for (BodyID body_id : mBodyIDs)661bi.SetLinearVelocity(body_id, inLinearVelocity);662}663664void Ragdoll::AddLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies)665{666BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);667for (BodyID body_id : mBodyIDs)668bi.AddLinearVelocity(body_id, inLinearVelocity);669}670671void Ragdoll::AddImpulse(Vec3Arg inImpulse, bool inLockBodies)672{673BodyInterface &bi = sRagdollGetBodyInterface(mSystem, inLockBodies);674for (BodyID body_id : mBodyIDs)675bi.AddImpulse(body_id, inImpulse);676}677678void Ragdoll::GetRootTransform(RVec3 &outPosition, Quat &outRotation, bool inLockBodies) const679{680BodyLockRead lock(sRagdollGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs[0]);681if (lock.Succeeded())682{683const Body &body = lock.GetBody();684outPosition = body.GetPosition();685outRotation = body.GetRotation();686}687else688{689outPosition = RVec3::sZero();690outRotation = Quat::sIdentity();691}692}693694AABox Ragdoll::GetWorldSpaceBounds(bool inLockBodies) const695{696// Lock the bodies697int body_count = (int)mBodyIDs.size();698BodyLockMultiRead lock(sRagdollGetBodyLockInterface(mSystem, inLockBodies), mBodyIDs.data(), body_count);699700// Encapsulate all bodies701AABox bounds;702for (int b = 0; b < body_count; ++b)703{704const Body *body = lock.GetBody(b);705if (body != nullptr)706bounds.Encapsulate(body->GetWorldSpaceBounds());707}708return bounds;709}710711JPH_NAMESPACE_END712713714