Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/Body/Body.cpp
9913 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/Body/Body.h>7#include <Jolt/Physics/Body/BodyCreationSettings.h>8#include <Jolt/Physics/SoftBody/SoftBodyCreationSettings.h>9#include <Jolt/Physics/SoftBody/SoftBodyMotionProperties.h>10#include <Jolt/Physics/PhysicsSettings.h>11#include <Jolt/Physics/StateRecorder.h>12#include <Jolt/Physics/Collision/Shape/EmptyShape.h>13#include <Jolt/Core/StringTools.h>14#include <Jolt/Core/Profiler.h>15#ifdef JPH_DEBUG_RENDERER16#include <Jolt/Renderer/DebugRenderer.h>17#endif // JPH_DEBUG_RENDERER1819JPH_NAMESPACE_BEGIN2021static const EmptyShape sFixedToWorldShape;22Body Body::sFixedToWorld(false);2324Body::Body(bool) :25mPosition(Vec3::sZero()),26mRotation(Quat::sIdentity()),27mShape(&sFixedToWorldShape), // Dummy shape28mFriction(0.0f),29mRestitution(0.0f),30mObjectLayer(cObjectLayerInvalid),31mMotionType(EMotionType::Static)32{33sFixedToWorldShape.SetEmbedded();34}3536void Body::SetMotionType(EMotionType inMotionType)37{38if (mMotionType == inMotionType)39return;4041JPH_ASSERT(inMotionType == EMotionType::Static || mMotionProperties != nullptr, "Body needs to be created with mAllowDynamicOrKinematic set to true");42JPH_ASSERT(inMotionType != EMotionType::Static || !IsActive(), "Deactivate body first");43JPH_ASSERT(inMotionType == EMotionType::Dynamic || !IsSoftBody(), "Soft bodies can only be dynamic, you can make individual vertices kinematic by setting their inverse mass to 0");4445// Store new motion type46mMotionType = inMotionType;4748if (mMotionProperties != nullptr)49{50// Update cache51JPH_IF_ENABLE_ASSERTS(mMotionProperties->mCachedMotionType = inMotionType;)5253switch (inMotionType)54{55case EMotionType::Static:56// Stop the object57mMotionProperties->mLinearVelocity = Vec3::sZero();58mMotionProperties->mAngularVelocity = Vec3::sZero();59[[fallthrough]];6061case EMotionType::Kinematic:62// Cancel forces63mMotionProperties->ResetForce();64mMotionProperties->ResetTorque();65break;6667case EMotionType::Dynamic:68break;69}70}71}7273void Body::SetAllowSleeping(bool inAllow)74{75mMotionProperties->mAllowSleeping = inAllow;76if (inAllow)77ResetSleepTimer();78}7980void Body::MoveKinematic(RVec3Arg inTargetPosition, QuatArg inTargetRotation, float inDeltaTime)81{82JPH_ASSERT(IsRigidBody()); // Only valid for rigid bodies83JPH_ASSERT(!IsStatic());84JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess(), BodyAccess::EAccess::Read));8586// Calculate center of mass at end situation87RVec3 new_com = inTargetPosition + inTargetRotation * mShape->GetCenterOfMass();8889// Calculate delta position and rotation90Vec3 delta_pos = Vec3(new_com - mPosition);91Quat delta_rotation = inTargetRotation * mRotation.Conjugated();9293mMotionProperties->MoveKinematic(delta_pos, delta_rotation, inDeltaTime);94}9596void Body::CalculateWorldSpaceBoundsInternal()97{98mBounds = mShape->GetWorldSpaceBounds(GetCenterOfMassTransform(), Vec3::sOne());99}100101void Body::SetPositionAndRotationInternal(RVec3Arg inPosition, QuatArg inRotation, bool inResetSleepTimer)102{103JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess(), BodyAccess::EAccess::ReadWrite));104105mPosition = inPosition + inRotation * mShape->GetCenterOfMass();106mRotation = inRotation;107108// Initialize bounding box109CalculateWorldSpaceBoundsInternal();110111// Reset sleeping test112if (inResetSleepTimer && mMotionProperties != nullptr)113ResetSleepTimer();114}115116void Body::UpdateCenterOfMassInternal(Vec3Arg inPreviousCenterOfMass, bool inUpdateMassProperties)117{118// Update center of mass position so the world position for this body stays the same119mPosition += mRotation * (mShape->GetCenterOfMass() - inPreviousCenterOfMass);120121// Recalculate mass and inertia if requested122if (inUpdateMassProperties && mMotionProperties != nullptr)123mMotionProperties->SetMassProperties(mMotionProperties->GetAllowedDOFs(), mShape->GetMassProperties());124}125126void Body::SetShapeInternal(const Shape *inShape, bool inUpdateMassProperties)127{128JPH_ASSERT(IsRigidBody()); // Only valid for rigid bodies129JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess(), BodyAccess::EAccess::ReadWrite));130131// Get the old center of mass132Vec3 old_com = mShape->GetCenterOfMass();133134// Update the shape135mShape = inShape;136137// Update center of mass138UpdateCenterOfMassInternal(old_com, inUpdateMassProperties);139140// Recalculate bounding box141CalculateWorldSpaceBoundsInternal();142}143144ECanSleep Body::UpdateSleepStateInternal(float inDeltaTime, float inMaxMovement, float inTimeBeforeSleep)145{146// Check override & sensors will never go to sleep (they would stop detecting collisions with sleeping bodies)147if (!mMotionProperties->mAllowSleeping || IsSensor())148return ECanSleep::CannotSleep;149150// Get the points to test151RVec3 points[3];152GetSleepTestPoints(points);153154#ifdef JPH_DOUBLE_PRECISION155// Get base offset for spheres156DVec3 offset = mMotionProperties->GetSleepTestOffset();157#endif // JPH_DOUBLE_PRECISION158159for (int i = 0; i < 3; ++i)160{161Sphere &sphere = mMotionProperties->mSleepTestSpheres[i];162163// Make point relative to base offset164#ifdef JPH_DOUBLE_PRECISION165Vec3 p = Vec3(points[i] - offset);166#else167Vec3 p = points[i];168#endif // JPH_DOUBLE_PRECISION169170// Encapsulate the point in a sphere171sphere.EncapsulatePoint(p);172173// Test if it exceeded the max movement174if (sphere.GetRadius() > inMaxMovement)175{176// Body is not sleeping, reset test177mMotionProperties->ResetSleepTestSpheres(points);178return ECanSleep::CannotSleep;179}180}181182return mMotionProperties->AccumulateSleepTime(inDeltaTime, inTimeBeforeSleep);183}184185void Body::GetSubmergedVolume(RVec3Arg inSurfacePosition, Vec3Arg inSurfaceNormal, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outRelativeCenterOfBuoyancy) const186{187// For GetSubmergedVolume we transform the surface relative to the body position for increased precision188Mat44 rotation = Mat44::sRotation(mRotation);189Plane surface_relative_to_body = Plane::sFromPointAndNormal(inSurfacePosition - mPosition, inSurfaceNormal);190191// Calculate amount of volume that is submerged and what the center of buoyancy is192mShape->GetSubmergedVolume(rotation, Vec3::sOne(), surface_relative_to_body, outTotalVolume, outSubmergedVolume, outRelativeCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, mPosition));193}194195bool Body::ApplyBuoyancyImpulse(float inTotalVolume, float inSubmergedVolume, Vec3Arg inRelativeCenterOfBuoyancy, float inBuoyancy, float inLinearDrag, float inAngularDrag, Vec3Arg inFluidVelocity, Vec3Arg inGravity, float inDeltaTime)196{197JPH_ASSERT(IsRigidBody()); // Only implemented for rigid bodies currently198199// We follow the approach from 'Game Programming Gems 6' 2.5 Exact Buoyancy for Polyhedra200// All quantities below are in world space201202// If we're not submerged, there's no point in doing the rest of the calculations203if (inSubmergedVolume > 0.0f)204{205#ifdef JPH_DEBUG_RENDERER206// Draw submerged volume properties207if (Shape::sDrawSubmergedVolumes)208{209RVec3 center_of_buoyancy = mPosition + inRelativeCenterOfBuoyancy;210DebugRenderer::sInstance->DrawMarker(center_of_buoyancy, Color::sWhite, 2.0f);211DebugRenderer::sInstance->DrawText3D(center_of_buoyancy, StringFormat("%.3f / %.3f", (double)inSubmergedVolume, (double)inTotalVolume));212}213#endif // JPH_DEBUG_RENDERER214215// When buoyancy is 1 we want neutral buoyancy, this means that the density of the liquid is the same as the density of the body at that point.216// Buoyancy > 1 should make the object float, < 1 should make it sink.217float inverse_mass = mMotionProperties->GetInverseMass();218float fluid_density = inBuoyancy / (inTotalVolume * inverse_mass);219220// Buoyancy force = Density of Fluid * Submerged volume * Magnitude of gravity * Up direction (eq 2.5.1)221// Impulse = Force * Delta time222// We should apply this at the center of buoyancy (= center of mass of submerged volume)223Vec3 buoyancy_impulse = -fluid_density * inSubmergedVolume * mMotionProperties->GetGravityFactor() * inGravity * inDeltaTime;224225// Calculate the velocity of the center of buoyancy relative to the fluid226Vec3 linear_velocity = mMotionProperties->GetLinearVelocity();227Vec3 angular_velocity = mMotionProperties->GetAngularVelocity();228Vec3 center_of_buoyancy_velocity = linear_velocity + angular_velocity.Cross(inRelativeCenterOfBuoyancy);229Vec3 relative_center_of_buoyancy_velocity = inFluidVelocity - center_of_buoyancy_velocity;230231// Here we deviate from the article, instead of eq 2.5.14 we use a quadratic drag formula: https://en.wikipedia.org/wiki/Drag_%28physics%29232// Drag force = 0.5 * Fluid Density * (Velocity of fluid - Velocity of center of buoyancy)^2 * Linear Drag * Area Facing the Relative Fluid Velocity233// Again Impulse = Force * Delta Time234// We should apply this at the center of buoyancy (= center of mass for submerged volume with no center of mass offset)235236// Get size of local bounding box237Vec3 size = mShape->GetLocalBounds().GetSize();238239// Determine area of the local space bounding box in the direction of the relative velocity between the fluid and the center of buoyancy240float area = 0.0f;241float relative_center_of_buoyancy_velocity_len_sq = relative_center_of_buoyancy_velocity.LengthSq();242if (relative_center_of_buoyancy_velocity_len_sq > 1.0e-12f)243{244Vec3 local_relative_center_of_buoyancy_velocity = GetRotation().Conjugated() * relative_center_of_buoyancy_velocity;245area = local_relative_center_of_buoyancy_velocity.Abs().Dot(size.Swizzle<SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_X>() * size.Swizzle<SWIZZLE_Z, SWIZZLE_X, SWIZZLE_Y>()) / sqrt(relative_center_of_buoyancy_velocity_len_sq);246}247248// Calculate the impulse249Vec3 drag_impulse = (0.5f * fluid_density * inLinearDrag * area * inDeltaTime) * relative_center_of_buoyancy_velocity * relative_center_of_buoyancy_velocity.Length();250251// Clamp magnitude against current linear velocity to prevent overshoot252float linear_velocity_len_sq = linear_velocity.LengthSq();253float drag_delta_linear_velocity_len_sq = (drag_impulse * inverse_mass).LengthSq();254if (drag_delta_linear_velocity_len_sq > linear_velocity_len_sq)255drag_impulse *= sqrt(linear_velocity_len_sq / drag_delta_linear_velocity_len_sq);256257// Calculate the resulting delta linear velocity due to buoyancy and drag258Vec3 delta_linear_velocity = (drag_impulse + buoyancy_impulse) * inverse_mass;259mMotionProperties->AddLinearVelocityStep(delta_linear_velocity);260261// Determine average width of the body (across the three axis)262float l = (size.GetX() + size.GetY() + size.GetZ()) / 3.0f;263264// Drag torque = -Angular Drag * Mass * Submerged volume / Total volume * (Average width of body)^2 * Angular velocity (eq 2.5.15)265Vec3 drag_angular_impulse = (-inAngularDrag * inSubmergedVolume / inTotalVolume * inDeltaTime * Square(l) / inverse_mass) * angular_velocity;266Mat44 inv_inertia = GetInverseInertia();267Vec3 drag_delta_angular_velocity = inv_inertia * drag_angular_impulse;268269// Clamp magnitude against the current angular velocity to prevent overshoot270float angular_velocity_len_sq = angular_velocity.LengthSq();271float drag_delta_angular_velocity_len_sq = drag_delta_angular_velocity.LengthSq();272if (drag_delta_angular_velocity_len_sq > angular_velocity_len_sq)273drag_delta_angular_velocity *= sqrt(angular_velocity_len_sq / drag_delta_angular_velocity_len_sq);274275// Calculate total delta angular velocity due to drag and buoyancy276Vec3 delta_angular_velocity = drag_delta_angular_velocity + inv_inertia * inRelativeCenterOfBuoyancy.Cross(buoyancy_impulse + drag_impulse);277mMotionProperties->AddAngularVelocityStep(delta_angular_velocity);278return true;279}280281return false;282}283284bool Body::ApplyBuoyancyImpulse(RVec3Arg inSurfacePosition, Vec3Arg inSurfaceNormal, float inBuoyancy, float inLinearDrag, float inAngularDrag, Vec3Arg inFluidVelocity, Vec3Arg inGravity, float inDeltaTime)285{286JPH_PROFILE_FUNCTION();287288float total_volume, submerged_volume;289Vec3 relative_center_of_buoyancy;290GetSubmergedVolume(inSurfacePosition, inSurfaceNormal, total_volume, submerged_volume, relative_center_of_buoyancy);291292return ApplyBuoyancyImpulse(total_volume, submerged_volume, relative_center_of_buoyancy, inBuoyancy, inLinearDrag, inAngularDrag, inFluidVelocity, inGravity, inDeltaTime);293}294295void Body::SaveState(StateRecorder &inStream) const296{297// Only write properties that can change at runtime298inStream.Write(mPosition);299inStream.Write(mRotation);300301if (mMotionProperties != nullptr)302{303if (IsSoftBody())304static_cast<const SoftBodyMotionProperties *>(mMotionProperties)->SaveState(inStream);305else306mMotionProperties->SaveState(inStream);307}308}309310void Body::RestoreState(StateRecorder &inStream)311{312inStream.Read(mPosition);313inStream.Read(mRotation);314315if (mMotionProperties != nullptr)316{317if (IsSoftBody())318static_cast<SoftBodyMotionProperties *>(mMotionProperties)->RestoreState(inStream);319else320mMotionProperties->RestoreState(inStream);321322JPH_IF_ENABLE_ASSERTS(mMotionProperties->mCachedMotionType = mMotionType);323}324325// Initialize bounding box326CalculateWorldSpaceBoundsInternal();327}328329BodyCreationSettings Body::GetBodyCreationSettings() const330{331JPH_ASSERT(IsRigidBody());332333BodyCreationSettings result;334335result.mPosition = GetPosition();336result.mRotation = GetRotation();337result.mLinearVelocity = mMotionProperties != nullptr? mMotionProperties->GetLinearVelocity() : Vec3::sZero();338result.mAngularVelocity = mMotionProperties != nullptr? mMotionProperties->GetAngularVelocity() : Vec3::sZero();339result.mObjectLayer = GetObjectLayer();340result.mUserData = mUserData;341result.mCollisionGroup = GetCollisionGroup();342result.mMotionType = GetMotionType();343result.mAllowedDOFs = mMotionProperties != nullptr? mMotionProperties->GetAllowedDOFs() : EAllowedDOFs::All;344result.mAllowDynamicOrKinematic = mMotionProperties != nullptr;345result.mIsSensor = IsSensor();346result.mCollideKinematicVsNonDynamic = GetCollideKinematicVsNonDynamic();347result.mUseManifoldReduction = GetUseManifoldReduction();348result.mApplyGyroscopicForce = GetApplyGyroscopicForce();349result.mMotionQuality = mMotionProperties != nullptr? mMotionProperties->GetMotionQuality() : EMotionQuality::Discrete;350result.mEnhancedInternalEdgeRemoval = GetEnhancedInternalEdgeRemoval();351result.mAllowSleeping = mMotionProperties != nullptr? GetAllowSleeping() : true;352result.mFriction = GetFriction();353result.mRestitution = GetRestitution();354result.mLinearDamping = mMotionProperties != nullptr? mMotionProperties->GetLinearDamping() : 0.0f;355result.mAngularDamping = mMotionProperties != nullptr? mMotionProperties->GetAngularDamping() : 0.0f;356result.mMaxLinearVelocity = mMotionProperties != nullptr? mMotionProperties->GetMaxLinearVelocity() : 0.0f;357result.mMaxAngularVelocity = mMotionProperties != nullptr? mMotionProperties->GetMaxAngularVelocity() : 0.0f;358result.mGravityFactor = mMotionProperties != nullptr? mMotionProperties->GetGravityFactor() : 1.0f;359result.mNumVelocityStepsOverride = mMotionProperties != nullptr? mMotionProperties->GetNumVelocityStepsOverride() : 0;360result.mNumPositionStepsOverride = mMotionProperties != nullptr? mMotionProperties->GetNumPositionStepsOverride() : 0;361result.mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided;362363// Invert inertia and mass364if (mMotionProperties != nullptr)365{366float inv_mass = mMotionProperties->GetInverseMassUnchecked();367Mat44 inv_inertia = mMotionProperties->GetLocalSpaceInverseInertiaUnchecked();368369// Get mass370result.mMassPropertiesOverride.mMass = inv_mass != 0.0f? 1.0f / inv_mass : FLT_MAX;371372// Get inertia373Mat44 inertia;374if (inertia.SetInversed3x3(inv_inertia))375{376// Inertia was invertible, we can use it377result.mMassPropertiesOverride.mInertia = inertia;378}379else380{381// Prevent division by zero382Vec3 diagonal = Vec3::sMax(inv_inertia.GetDiagonal3(), Vec3::sReplicate(FLT_MIN));383result.mMassPropertiesOverride.mInertia = Mat44::sScale(diagonal.Reciprocal());384}385}386else387{388result.mMassPropertiesOverride.mMass = FLT_MAX;389result.mMassPropertiesOverride.mInertia = Mat44::sScale(Vec3::sReplicate(FLT_MAX));390}391392result.SetShape(GetShape());393394return result;395}396397SoftBodyCreationSettings Body::GetSoftBodyCreationSettings() const398{399JPH_ASSERT(IsSoftBody());400401SoftBodyCreationSettings result;402403result.mPosition = GetPosition();404result.mRotation = GetRotation();405result.mUserData = mUserData;406result.mObjectLayer = GetObjectLayer();407result.mCollisionGroup = GetCollisionGroup();408result.mFriction = GetFriction();409result.mRestitution = GetRestitution();410const SoftBodyMotionProperties *mp = static_cast<const SoftBodyMotionProperties *>(mMotionProperties);411result.mNumIterations = mp->GetNumIterations();412result.mLinearDamping = mp->GetLinearDamping();413result.mMaxLinearVelocity = mp->GetMaxLinearVelocity();414result.mGravityFactor = mp->GetGravityFactor();415result.mPressure = mp->GetPressure();416result.mUpdatePosition = mp->GetUpdatePosition();417result.mSettings = mp->GetSettings();418419return result;420}421422JPH_NAMESPACE_END423424425