Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Character/Character.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/Character/Character.h>7#include <Jolt/Physics/Body/BodyCreationSettings.h>8#include <Jolt/Physics/Body/BodyLock.h>9#include <Jolt/Physics/Collision/CollideShape.h>10#include <Jolt/Physics/PhysicsSystem.h>11#include <Jolt/ObjectStream/TypeDeclarations.h>1213JPH_NAMESPACE_BEGIN1415static inline const BodyLockInterface &sCharacterGetBodyLockInterface(const PhysicsSystem *inSystem, bool inLockBodies)16{17return inLockBodies? static_cast<const BodyLockInterface &>(inSystem->GetBodyLockInterface()) : static_cast<const BodyLockInterface &>(inSystem->GetBodyLockInterfaceNoLock());18}1920static inline BodyInterface &sCharacterGetBodyInterface(PhysicsSystem *inSystem, bool inLockBodies)21{22return inLockBodies? inSystem->GetBodyInterface() : inSystem->GetBodyInterfaceNoLock();23}2425static inline const NarrowPhaseQuery &sCharacterGetNarrowPhaseQuery(const PhysicsSystem *inSystem, bool inLockBodies)26{27return inLockBodies? inSystem->GetNarrowPhaseQuery() : inSystem->GetNarrowPhaseQueryNoLock();28}2930Character::Character(const CharacterSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, uint64 inUserData, PhysicsSystem *inSystem) :31CharacterBase(inSettings, inSystem),32mLayer(inSettings->mLayer)33{34// Construct rigid body35BodyCreationSettings settings(mShape, inPosition, inRotation, EMotionType::Dynamic, mLayer);36settings.mAllowedDOFs = inSettings->mAllowedDOFs;37settings.mEnhancedInternalEdgeRemoval = inSettings->mEnhancedInternalEdgeRemoval;38settings.mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided;39settings.mMassPropertiesOverride.mMass = inSettings->mMass;40settings.mFriction = inSettings->mFriction;41settings.mGravityFactor = inSettings->mGravityFactor;42settings.mUserData = inUserData;43const Body *body = mSystem->GetBodyInterface().CreateBody(settings);44if (body != nullptr)45mBodyID = body->GetID();46}4748Character::~Character()49{50// Destroy the body51mSystem->GetBodyInterface().DestroyBody(mBodyID);52}5354void Character::AddToPhysicsSystem(EActivation inActivationMode, bool inLockBodies)55{56sCharacterGetBodyInterface(mSystem, inLockBodies).AddBody(mBodyID, inActivationMode);57}5859void Character::RemoveFromPhysicsSystem(bool inLockBodies)60{61sCharacterGetBodyInterface(mSystem, inLockBodies).RemoveBody(mBodyID);62}6364void Character::Activate(bool inLockBodies)65{66sCharacterGetBodyInterface(mSystem, inLockBodies).ActivateBody(mBodyID);67}6869void Character::CheckCollision(RMat44Arg inCenterOfMassTransform, Vec3Arg inMovementDirection, float inMaxSeparationDistance, const Shape *inShape, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies) const70{71// Create query broadphase layer filter72DefaultBroadPhaseLayerFilter broadphase_layer_filter = mSystem->GetDefaultBroadPhaseLayerFilter(mLayer);7374// Create query object layer filter75DefaultObjectLayerFilter object_layer_filter = mSystem->GetDefaultLayerFilter(mLayer);7677// Ignore sensors and my own body78class CharacterBodyFilter : public IgnoreSingleBodyFilter79{80public:81using IgnoreSingleBodyFilter::IgnoreSingleBodyFilter;8283virtual bool ShouldCollideLocked(const Body &inBody) const override84{85return !inBody.IsSensor();86}87};88CharacterBodyFilter body_filter(mBodyID);8990// Settings for collide shape91CollideShapeSettings settings;92settings.mMaxSeparationDistance = inMaxSeparationDistance;93settings.mActiveEdgeMode = EActiveEdgeMode::CollideOnlyWithActive;94settings.mActiveEdgeMovementDirection = inMovementDirection;95settings.mBackFaceMode = EBackFaceMode::IgnoreBackFaces;9697sCharacterGetNarrowPhaseQuery(mSystem, inLockBodies).CollideShape(inShape, Vec3::sOne(), inCenterOfMassTransform, settings, inBaseOffset, ioCollector, broadphase_layer_filter, object_layer_filter, body_filter);98}99100void Character::CheckCollision(RVec3Arg inPosition, QuatArg inRotation, Vec3Arg inMovementDirection, float inMaxSeparationDistance, const Shape *inShape, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies) const101{102// Calculate center of mass transform103RMat44 center_of_mass = RMat44::sRotationTranslation(inRotation, inPosition).PreTranslated(inShape->GetCenterOfMass());104105CheckCollision(center_of_mass, inMovementDirection, inMaxSeparationDistance, inShape, inBaseOffset, ioCollector, inLockBodies);106}107108void Character::CheckCollision(const Shape *inShape, float inMaxSeparationDistance, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector, bool inLockBodies) const109{110// Determine position and velocity of body111RMat44 query_transform;112Vec3 velocity;113{114BodyLockRead lock(sCharacterGetBodyLockInterface(mSystem, inLockBodies), mBodyID);115if (!lock.Succeeded())116return;117118const Body &body = lock.GetBody();119120// Correct the center of mass transform for the difference between the old and new center of mass shape121query_transform = body.GetCenterOfMassTransform().PreTranslated(inShape->GetCenterOfMass() - mShape->GetCenterOfMass());122velocity = body.GetLinearVelocity();123}124125CheckCollision(query_transform, velocity, inMaxSeparationDistance, inShape, inBaseOffset, ioCollector, inLockBodies);126}127128void Character::PostSimulation(float inMaxSeparationDistance, bool inLockBodies)129{130// Get character position, rotation and velocity131RVec3 char_pos;132Quat char_rot;133Vec3 char_vel;134{135BodyLockRead lock(sCharacterGetBodyLockInterface(mSystem, inLockBodies), mBodyID);136if (!lock.Succeeded())137return;138const Body &body = lock.GetBody();139char_pos = body.GetPosition();140char_rot = body.GetRotation();141char_vel = body.GetLinearVelocity();142}143144// Collector that finds the hit with the normal that is the most 'up'145class MyCollector : public CollideShapeCollector146{147public:148// Constructor149explicit MyCollector(Vec3Arg inUp, RVec3 inBaseOffset) : mBaseOffset(inBaseOffset), mUp(inUp) { }150151// See: CollectorType::AddHit152virtual void AddHit(const CollideShapeResult &inResult) override153{154Vec3 normal = -inResult.mPenetrationAxis.Normalized();155float dot = normal.Dot(mUp);156if (dot > mBestDot) // Find the hit that is most aligned with the up vector157{158mGroundBodyID = inResult.mBodyID2;159mGroundBodySubShapeID = inResult.mSubShapeID2;160mGroundPosition = mBaseOffset + inResult.mContactPointOn2;161mGroundNormal = normal;162mBestDot = dot;163}164}165166BodyID mGroundBodyID;167SubShapeID mGroundBodySubShapeID;168RVec3 mGroundPosition = RVec3::sZero();169Vec3 mGroundNormal = Vec3::sZero();170171private:172RVec3 mBaseOffset;173Vec3 mUp;174float mBestDot = -FLT_MAX;175};176177// Collide shape178MyCollector collector(mUp, char_pos);179CheckCollision(char_pos, char_rot, char_vel, inMaxSeparationDistance, mShape, char_pos, collector, inLockBodies);180181// Copy results182mGroundBodyID = collector.mGroundBodyID;183mGroundBodySubShapeID = collector.mGroundBodySubShapeID;184mGroundPosition = collector.mGroundPosition;185mGroundNormal = collector.mGroundNormal;186187// Get additional data from body188BodyLockRead lock(sCharacterGetBodyLockInterface(mSystem, inLockBodies), mGroundBodyID);189if (lock.Succeeded())190{191const Body &body = lock.GetBody();192193// Update ground state194RMat44 inv_transform = RMat44::sInverseRotationTranslation(char_rot, char_pos);195if (mSupportingVolume.SignedDistance(Vec3(inv_transform * mGroundPosition)) > 0.0f)196mGroundState = EGroundState::NotSupported;197else if (IsSlopeTooSteep(mGroundNormal))198mGroundState = EGroundState::OnSteepGround;199else200mGroundState = EGroundState::OnGround;201202// Copy other body properties203mGroundMaterial = body.GetShape()->GetMaterial(mGroundBodySubShapeID);204mGroundVelocity = body.GetPointVelocity(mGroundPosition);205mGroundUserData = body.GetUserData();206}207else208{209mGroundState = EGroundState::InAir;210mGroundMaterial = PhysicsMaterial::sDefault;211mGroundVelocity = Vec3::sZero();212mGroundUserData = 0;213}214}215216void Character::SetLinearAndAngularVelocity(Vec3Arg inLinearVelocity, Vec3Arg inAngularVelocity, bool inLockBodies)217{218sCharacterGetBodyInterface(mSystem, inLockBodies).SetLinearAndAngularVelocity(mBodyID, inLinearVelocity, inAngularVelocity);219}220221Vec3 Character::GetLinearVelocity(bool inLockBodies) const222{223return sCharacterGetBodyInterface(mSystem, inLockBodies).GetLinearVelocity(mBodyID);224}225226void Character::SetLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies)227{228sCharacterGetBodyInterface(mSystem, inLockBodies).SetLinearVelocity(mBodyID, inLinearVelocity);229}230231void Character::AddLinearVelocity(Vec3Arg inLinearVelocity, bool inLockBodies)232{233sCharacterGetBodyInterface(mSystem, inLockBodies).AddLinearVelocity(mBodyID, inLinearVelocity);234}235236void Character::AddImpulse(Vec3Arg inImpulse, bool inLockBodies)237{238sCharacterGetBodyInterface(mSystem, inLockBodies).AddImpulse(mBodyID, inImpulse);239}240241void Character::GetPositionAndRotation(RVec3 &outPosition, Quat &outRotation, bool inLockBodies) const242{243sCharacterGetBodyInterface(mSystem, inLockBodies).GetPositionAndRotation(mBodyID, outPosition, outRotation);244}245246void Character::SetPositionAndRotation(RVec3Arg inPosition, QuatArg inRotation, EActivation inActivationMode, bool inLockBodies) const247{248sCharacterGetBodyInterface(mSystem, inLockBodies).SetPositionAndRotation(mBodyID, inPosition, inRotation, inActivationMode);249}250251RVec3 Character::GetPosition(bool inLockBodies) const252{253return sCharacterGetBodyInterface(mSystem, inLockBodies).GetPosition(mBodyID);254}255256void Character::SetPosition(RVec3Arg inPosition, EActivation inActivationMode, bool inLockBodies)257{258sCharacterGetBodyInterface(mSystem, inLockBodies).SetPosition(mBodyID, inPosition, inActivationMode);259}260261Quat Character::GetRotation(bool inLockBodies) const262{263return sCharacterGetBodyInterface(mSystem, inLockBodies).GetRotation(mBodyID);264}265266void Character::SetRotation(QuatArg inRotation, EActivation inActivationMode, bool inLockBodies)267{268sCharacterGetBodyInterface(mSystem, inLockBodies).SetRotation(mBodyID, inRotation, inActivationMode);269}270271RVec3 Character::GetCenterOfMassPosition(bool inLockBodies) const272{273return sCharacterGetBodyInterface(mSystem, inLockBodies).GetCenterOfMassPosition(mBodyID);274}275276RMat44 Character::GetWorldTransform(bool inLockBodies) const277{278return sCharacterGetBodyInterface(mSystem, inLockBodies).GetWorldTransform(mBodyID);279}280281void Character::SetLayer(ObjectLayer inLayer, bool inLockBodies)282{283mLayer = inLayer;284285sCharacterGetBodyInterface(mSystem, inLockBodies).SetObjectLayer(mBodyID, inLayer);286}287288bool Character::SetShape(const Shape *inShape, float inMaxPenetrationDepth, bool inLockBodies)289{290if (inMaxPenetrationDepth < FLT_MAX)291{292// Collector that checks if there is anything in the way while switching to inShape293class MyCollector : public CollideShapeCollector294{295public:296// Constructor297explicit MyCollector(float inMaxPenetrationDepth) : mMaxPenetrationDepth(inMaxPenetrationDepth) { }298299// See: CollectorType::AddHit300virtual void AddHit(const CollideShapeResult &inResult) override301{302if (inResult.mPenetrationDepth > mMaxPenetrationDepth)303{304mHadCollision = true;305ForceEarlyOut();306}307}308309float mMaxPenetrationDepth;310bool mHadCollision = false;311};312313// Test if anything is in the way of switching314RVec3 char_pos = GetPosition(inLockBodies);315MyCollector collector(inMaxPenetrationDepth);316CheckCollision(inShape, 0.0f, char_pos, collector, inLockBodies);317if (collector.mHadCollision)318return false;319}320321// Switch the shape322mShape = inShape;323sCharacterGetBodyInterface(mSystem, inLockBodies).SetShape(mBodyID, mShape, false, EActivation::Activate);324return true;325}326327TransformedShape Character::GetTransformedShape(bool inLockBodies) const328{329return sCharacterGetBodyInterface(mSystem, inLockBodies).GetTransformedShape(mBodyID);330}331332JPH_NAMESPACE_END333334335