Body.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <Jolt/Jolt.h>
  5. #include <Jolt/Physics/Body/Body.h>
  6. #include <Jolt/Physics/Body/BodyCreationSettings.h>
  7. #include <Jolt/Physics/SoftBody/SoftBodyCreationSettings.h>
  8. #include <Jolt/Physics/SoftBody/SoftBodyMotionProperties.h>
  9. #include <Jolt/Physics/PhysicsSettings.h>
  10. #include <Jolt/Physics/StateRecorder.h>
  11. #include <Jolt/Physics/Collision/Shape/EmptyShape.h>
  12. #include <Jolt/Core/StringTools.h>
  13. #include <Jolt/Core/Profiler.h>
  14. #ifdef JPH_DEBUG_RENDERER
  15. #include <Jolt/Renderer/DebugRenderer.h>
  16. #endif // JPH_DEBUG_RENDERER
  17. JPH_NAMESPACE_BEGIN
  18. static const EmptyShape sFixedToWorldShape;
  19. Body Body::sFixedToWorld(false);
  20. Body::Body(bool) :
  21. mPosition(Vec3::sZero()),
  22. mRotation(Quat::sIdentity()),
  23. mShape(&sFixedToWorldShape), // Dummy shape
  24. mFriction(0.0f),
  25. mRestitution(0.0f),
  26. mObjectLayer(cObjectLayerInvalid),
  27. mMotionType(EMotionType::Static)
  28. {
  29. sFixedToWorldShape.SetEmbedded();
  30. }
  31. void Body::SetMotionType(EMotionType inMotionType)
  32. {
  33. if (mMotionType == inMotionType)
  34. return;
  35. JPH_ASSERT(inMotionType == EMotionType::Static || mMotionProperties != nullptr, "Body needs to be created with mAllowDynamicOrKinematic set to true");
  36. JPH_ASSERT(inMotionType != EMotionType::Static || !IsActive(), "Deactivate body first");
  37. JPH_ASSERT(inMotionType == EMotionType::Dynamic || !IsSoftBody(), "Soft bodies can only be dynamic, you can make individual vertices kinematic by setting their inverse mass to 0");
  38. // Store new motion type
  39. mMotionType = inMotionType;
  40. if (mMotionProperties != nullptr)
  41. {
  42. // Update cache
  43. JPH_IF_ENABLE_ASSERTS(mMotionProperties->mCachedMotionType = inMotionType;)
  44. switch (inMotionType)
  45. {
  46. case EMotionType::Static:
  47. // Stop the object
  48. mMotionProperties->mLinearVelocity = Vec3::sZero();
  49. mMotionProperties->mAngularVelocity = Vec3::sZero();
  50. [[fallthrough]];
  51. case EMotionType::Kinematic:
  52. // Cancel forces
  53. mMotionProperties->ResetForce();
  54. mMotionProperties->ResetTorque();
  55. break;
  56. case EMotionType::Dynamic:
  57. break;
  58. }
  59. }
  60. }
  61. void Body::SetAllowSleeping(bool inAllow)
  62. {
  63. mMotionProperties->mAllowSleeping = inAllow;
  64. if (inAllow)
  65. ResetSleepTimer();
  66. }
  67. void Body::MoveKinematic(RVec3Arg inTargetPosition, QuatArg inTargetRotation, float inDeltaTime)
  68. {
  69. JPH_ASSERT(IsRigidBody()); // Only valid for rigid bodies
  70. JPH_ASSERT(!IsStatic());
  71. JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess(), BodyAccess::EAccess::Read));
  72. // Calculate center of mass at end situation
  73. RVec3 new_com = inTargetPosition + inTargetRotation * mShape->GetCenterOfMass();
  74. // Calculate delta position and rotation
  75. Vec3 delta_pos = Vec3(new_com - mPosition);
  76. Quat delta_rotation = inTargetRotation * mRotation.Conjugated();
  77. mMotionProperties->MoveKinematic(delta_pos, delta_rotation, inDeltaTime);
  78. }
  79. void Body::CalculateWorldSpaceBoundsInternal()
  80. {
  81. mBounds = mShape->GetWorldSpaceBounds(GetCenterOfMassTransform(), Vec3::sOne());
  82. }
  83. void Body::SetPositionAndRotationInternal(RVec3Arg inPosition, QuatArg inRotation, bool inResetSleepTimer)
  84. {
  85. JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess(), BodyAccess::EAccess::ReadWrite));
  86. mPosition = inPosition + inRotation * mShape->GetCenterOfMass();
  87. mRotation = inRotation;
  88. // Initialize bounding box
  89. CalculateWorldSpaceBoundsInternal();
  90. // Reset sleeping test
  91. if (inResetSleepTimer && mMotionProperties != nullptr)
  92. ResetSleepTimer();
  93. }
  94. void Body::UpdateCenterOfMassInternal(Vec3Arg inPreviousCenterOfMass, bool inUpdateMassProperties)
  95. {
  96. // Update center of mass position so the world position for this body stays the same
  97. mPosition += mRotation * (mShape->GetCenterOfMass() - inPreviousCenterOfMass);
  98. // Recalculate mass and inertia if requested
  99. if (inUpdateMassProperties && mMotionProperties != nullptr)
  100. mMotionProperties->SetMassProperties(mMotionProperties->GetAllowedDOFs(), mShape->GetMassProperties());
  101. }
  102. void Body::SetShapeInternal(const Shape *inShape, bool inUpdateMassProperties)
  103. {
  104. JPH_ASSERT(IsRigidBody()); // Only valid for rigid bodies
  105. JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess(), BodyAccess::EAccess::ReadWrite));
  106. // Get the old center of mass
  107. Vec3 old_com = mShape->GetCenterOfMass();
  108. // Update the shape
  109. mShape = inShape;
  110. // Update center of mass
  111. UpdateCenterOfMassInternal(old_com, inUpdateMassProperties);
  112. // Recalculate bounding box
  113. CalculateWorldSpaceBoundsInternal();
  114. }
  115. ECanSleep Body::UpdateSleepStateInternal(float inDeltaTime, float inMaxMovement, float inTimeBeforeSleep)
  116. {
  117. // Check override & sensors will never go to sleep (they would stop detecting collisions with sleeping bodies)
  118. if (!mMotionProperties->mAllowSleeping || IsSensor())
  119. return ECanSleep::CannotSleep;
  120. // Get the points to test
  121. RVec3 points[3];
  122. GetSleepTestPoints(points);
  123. #ifdef JPH_DOUBLE_PRECISION
  124. // Get base offset for spheres
  125. DVec3 offset = mMotionProperties->GetSleepTestOffset();
  126. #endif // JPH_DOUBLE_PRECISION
  127. for (int i = 0; i < 3; ++i)
  128. {
  129. Sphere &sphere = mMotionProperties->mSleepTestSpheres[i];
  130. // Make point relative to base offset
  131. #ifdef JPH_DOUBLE_PRECISION
  132. Vec3 p = Vec3(points[i] - offset);
  133. #else
  134. Vec3 p = points[i];
  135. #endif // JPH_DOUBLE_PRECISION
  136. // Encapsulate the point in a sphere
  137. sphere.EncapsulatePoint(p);
  138. // Test if it exceeded the max movement
  139. if (sphere.GetRadius() > inMaxMovement)
  140. {
  141. // Body is not sleeping, reset test
  142. mMotionProperties->ResetSleepTestSpheres(points);
  143. return ECanSleep::CannotSleep;
  144. }
  145. }
  146. return mMotionProperties->AccumulateSleepTime(inDeltaTime, inTimeBeforeSleep);
  147. }
  148. void Body::GetSubmergedVolume(RVec3Arg inSurfacePosition, Vec3Arg inSurfaceNormal, float &outTotalVolume, float &outSubmergedVolume, Vec3 &outRelativeCenterOfBuoyancy) const
  149. {
  150. // For GetSubmergedVolume we transform the surface relative to the body position for increased precision
  151. Mat44 rotation = Mat44::sRotation(mRotation);
  152. Plane surface_relative_to_body = Plane::sFromPointAndNormal(inSurfacePosition - mPosition, inSurfaceNormal);
  153. // Calculate amount of volume that is submerged and what the center of buoyancy is
  154. mShape->GetSubmergedVolume(rotation, Vec3::sOne(), surface_relative_to_body, outTotalVolume, outSubmergedVolume, outRelativeCenterOfBuoyancy JPH_IF_DEBUG_RENDERER(, mPosition));
  155. }
  156. bool Body::ApplyBuoyancyImpulse(float inTotalVolume, float inSubmergedVolume, Vec3Arg inRelativeCenterOfBuoyancy, float inBuoyancy, float inLinearDrag, float inAngularDrag, Vec3Arg inFluidVelocity, Vec3Arg inGravity, float inDeltaTime)
  157. {
  158. JPH_ASSERT(IsRigidBody()); // Only implemented for rigid bodies currently
  159. // We follow the approach from 'Game Programming Gems 6' 2.5 Exact Buoyancy for Polyhedra
  160. // All quantities below are in world space
  161. // If we're not submerged, there's no point in doing the rest of the calculations
  162. if (inSubmergedVolume > 0.0f)
  163. {
  164. #ifdef JPH_DEBUG_RENDERER
  165. // Draw submerged volume properties
  166. if (Shape::sDrawSubmergedVolumes)
  167. {
  168. RVec3 center_of_buoyancy = mPosition + inRelativeCenterOfBuoyancy;
  169. DebugRenderer::sInstance->DrawMarker(center_of_buoyancy, Color::sWhite, 2.0f);
  170. DebugRenderer::sInstance->DrawText3D(center_of_buoyancy, StringFormat("%.3f / %.3f", (double)inSubmergedVolume, (double)inTotalVolume));
  171. }
  172. #endif // JPH_DEBUG_RENDERER
  173. // 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.
  174. // Buoyancy > 1 should make the object float, < 1 should make it sink.
  175. float inverse_mass = mMotionProperties->GetInverseMass();
  176. float fluid_density = inBuoyancy / (inTotalVolume * inverse_mass);
  177. // Buoyancy force = Density of Fluid * Submerged volume * Magnitude of gravity * Up direction (eq 2.5.1)
  178. // Impulse = Force * Delta time
  179. // We should apply this at the center of buoyancy (= center of mass of submerged volume)
  180. Vec3 buoyancy_impulse = -fluid_density * inSubmergedVolume * mMotionProperties->GetGravityFactor() * inGravity * inDeltaTime;
  181. // Calculate the velocity of the center of buoyancy relative to the fluid
  182. Vec3 linear_velocity = mMotionProperties->GetLinearVelocity();
  183. Vec3 angular_velocity = mMotionProperties->GetAngularVelocity();
  184. Vec3 center_of_buoyancy_velocity = linear_velocity + angular_velocity.Cross(inRelativeCenterOfBuoyancy);
  185. Vec3 relative_center_of_buoyancy_velocity = inFluidVelocity - center_of_buoyancy_velocity;
  186. // 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%29
  187. // Drag force = 0.5 * Fluid Density * (Velocity of fluid - Velocity of center of buoyancy)^2 * Linear Drag * Area Facing the Relative Fluid Velocity
  188. // Again Impulse = Force * Delta Time
  189. // We should apply this at the center of buoyancy (= center of mass for submerged volume with no center of mass offset)
  190. // Get size of local bounding box
  191. Vec3 size = mShape->GetLocalBounds().GetSize();
  192. // Determine area of the local space bounding box in the direction of the relative velocity between the fluid and the center of buoyancy
  193. float area = 0.0f;
  194. float relative_center_of_buoyancy_velocity_len_sq = relative_center_of_buoyancy_velocity.LengthSq();
  195. if (relative_center_of_buoyancy_velocity_len_sq > 1.0e-12f)
  196. {
  197. Vec3 local_relative_center_of_buoyancy_velocity = GetRotation().Conjugated() * relative_center_of_buoyancy_velocity;
  198. area = 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);
  199. }
  200. // Calculate the impulse
  201. Vec3 drag_impulse = (0.5f * fluid_density * inLinearDrag * area * inDeltaTime) * relative_center_of_buoyancy_velocity * relative_center_of_buoyancy_velocity.Length();
  202. // Clamp magnitude against current linear velocity to prevent overshoot
  203. float linear_velocity_len_sq = linear_velocity.LengthSq();
  204. float drag_delta_linear_velocity_len_sq = (drag_impulse * inverse_mass).LengthSq();
  205. if (drag_delta_linear_velocity_len_sq > linear_velocity_len_sq)
  206. drag_impulse *= sqrt(linear_velocity_len_sq / drag_delta_linear_velocity_len_sq);
  207. // Calculate the resulting delta linear velocity due to buoyancy and drag
  208. Vec3 delta_linear_velocity = (drag_impulse + buoyancy_impulse) * inverse_mass;
  209. mMotionProperties->AddLinearVelocityStep(delta_linear_velocity);
  210. // Determine average width of the body (across the three axis)
  211. float l = (size.GetX() + size.GetY() + size.GetZ()) / 3.0f;
  212. // Drag torque = -Angular Drag * Mass * Submerged volume / Total volume * (Average width of body)^2 * Angular velocity (eq 2.5.15)
  213. Vec3 drag_angular_impulse = (-inAngularDrag * inSubmergedVolume / inTotalVolume * inDeltaTime * Square(l) / inverse_mass) * angular_velocity;
  214. Mat44 inv_inertia = GetInverseInertia();
  215. Vec3 drag_delta_angular_velocity = inv_inertia * drag_angular_impulse;
  216. // Clamp magnitude against the current angular velocity to prevent overshoot
  217. float angular_velocity_len_sq = angular_velocity.LengthSq();
  218. float drag_delta_angular_velocity_len_sq = drag_delta_angular_velocity.LengthSq();
  219. if (drag_delta_angular_velocity_len_sq > angular_velocity_len_sq)
  220. drag_delta_angular_velocity *= sqrt(angular_velocity_len_sq / drag_delta_angular_velocity_len_sq);
  221. // Calculate total delta angular velocity due to drag and buoyancy
  222. Vec3 delta_angular_velocity = drag_delta_angular_velocity + inv_inertia * inRelativeCenterOfBuoyancy.Cross(buoyancy_impulse + drag_impulse);
  223. mMotionProperties->AddAngularVelocityStep(delta_angular_velocity);
  224. return true;
  225. }
  226. return false;
  227. }
  228. bool Body::ApplyBuoyancyImpulse(RVec3Arg inSurfacePosition, Vec3Arg inSurfaceNormal, float inBuoyancy, float inLinearDrag, float inAngularDrag, Vec3Arg inFluidVelocity, Vec3Arg inGravity, float inDeltaTime)
  229. {
  230. JPH_PROFILE_FUNCTION();
  231. float total_volume, submerged_volume;
  232. Vec3 relative_center_of_buoyancy;
  233. GetSubmergedVolume(inSurfacePosition, inSurfaceNormal, total_volume, submerged_volume, relative_center_of_buoyancy);
  234. return ApplyBuoyancyImpulse(total_volume, submerged_volume, relative_center_of_buoyancy, inBuoyancy, inLinearDrag, inAngularDrag, inFluidVelocity, inGravity, inDeltaTime);
  235. }
  236. void Body::SaveState(StateRecorder &inStream) const
  237. {
  238. // Only write properties that can change at runtime
  239. inStream.Write(mPosition);
  240. inStream.Write(mRotation);
  241. if (mMotionProperties != nullptr)
  242. {
  243. if (IsSoftBody())
  244. static_cast<const SoftBodyMotionProperties *>(mMotionProperties)->SaveState(inStream);
  245. else
  246. mMotionProperties->SaveState(inStream);
  247. }
  248. }
  249. void Body::RestoreState(StateRecorder &inStream)
  250. {
  251. inStream.Read(mPosition);
  252. inStream.Read(mRotation);
  253. if (mMotionProperties != nullptr)
  254. {
  255. if (IsSoftBody())
  256. static_cast<SoftBodyMotionProperties *>(mMotionProperties)->RestoreState(inStream);
  257. else
  258. mMotionProperties->RestoreState(inStream);
  259. JPH_IF_ENABLE_ASSERTS(mMotionProperties->mCachedMotionType = mMotionType);
  260. }
  261. // Initialize bounding box
  262. CalculateWorldSpaceBoundsInternal();
  263. }
  264. BodyCreationSettings Body::GetBodyCreationSettings() const
  265. {
  266. JPH_ASSERT(IsRigidBody());
  267. BodyCreationSettings result;
  268. result.mPosition = GetPosition();
  269. result.mRotation = GetRotation();
  270. result.mLinearVelocity = mMotionProperties != nullptr? mMotionProperties->GetLinearVelocity() : Vec3::sZero();
  271. result.mAngularVelocity = mMotionProperties != nullptr? mMotionProperties->GetAngularVelocity() : Vec3::sZero();
  272. result.mObjectLayer = GetObjectLayer();
  273. result.mUserData = mUserData;
  274. result.mCollisionGroup = GetCollisionGroup();
  275. result.mMotionType = GetMotionType();
  276. result.mAllowedDOFs = mMotionProperties != nullptr? mMotionProperties->GetAllowedDOFs() : EAllowedDOFs::All;
  277. result.mAllowDynamicOrKinematic = mMotionProperties != nullptr;
  278. result.mIsSensor = IsSensor();
  279. result.mCollideKinematicVsNonDynamic = GetCollideKinematicVsNonDynamic();
  280. result.mUseManifoldReduction = GetUseManifoldReduction();
  281. result.mApplyGyroscopicForce = GetApplyGyroscopicForce();
  282. result.mMotionQuality = mMotionProperties != nullptr? mMotionProperties->GetMotionQuality() : EMotionQuality::Discrete;
  283. result.mEnhancedInternalEdgeRemoval = GetEnhancedInternalEdgeRemoval();
  284. result.mAllowSleeping = mMotionProperties != nullptr? GetAllowSleeping() : true;
  285. result.mFriction = GetFriction();
  286. result.mRestitution = GetRestitution();
  287. result.mLinearDamping = mMotionProperties != nullptr? mMotionProperties->GetLinearDamping() : 0.0f;
  288. result.mAngularDamping = mMotionProperties != nullptr? mMotionProperties->GetAngularDamping() : 0.0f;
  289. result.mMaxLinearVelocity = mMotionProperties != nullptr? mMotionProperties->GetMaxLinearVelocity() : 0.0f;
  290. result.mMaxAngularVelocity = mMotionProperties != nullptr? mMotionProperties->GetMaxAngularVelocity() : 0.0f;
  291. result.mGravityFactor = mMotionProperties != nullptr? mMotionProperties->GetGravityFactor() : 1.0f;
  292. result.mNumVelocityStepsOverride = mMotionProperties != nullptr? mMotionProperties->GetNumVelocityStepsOverride() : 0;
  293. result.mNumPositionStepsOverride = mMotionProperties != nullptr? mMotionProperties->GetNumPositionStepsOverride() : 0;
  294. result.mOverrideMassProperties = EOverrideMassProperties::MassAndInertiaProvided;
  295. // Invert inertia and mass
  296. if (mMotionProperties != nullptr)
  297. {
  298. float inv_mass = mMotionProperties->GetInverseMassUnchecked();
  299. Mat44 inv_inertia = mMotionProperties->GetLocalSpaceInverseInertiaUnchecked();
  300. // Get mass
  301. result.mMassPropertiesOverride.mMass = inv_mass != 0.0f? 1.0f / inv_mass : FLT_MAX;
  302. // Get inertia
  303. Mat44 inertia;
  304. if (inertia.SetInversed3x3(inv_inertia))
  305. {
  306. // Inertia was invertible, we can use it
  307. result.mMassPropertiesOverride.mInertia = inertia;
  308. }
  309. else
  310. {
  311. // Prevent division by zero
  312. Vec3 diagonal = Vec3::sMax(inv_inertia.GetDiagonal3(), Vec3::sReplicate(FLT_MIN));
  313. result.mMassPropertiesOverride.mInertia = Mat44::sScale(diagonal.Reciprocal());
  314. }
  315. }
  316. else
  317. {
  318. result.mMassPropertiesOverride.mMass = FLT_MAX;
  319. result.mMassPropertiesOverride.mInertia = Mat44::sScale(Vec3::sReplicate(FLT_MAX));
  320. }
  321. result.SetShape(GetShape());
  322. return result;
  323. }
  324. SoftBodyCreationSettings Body::GetSoftBodyCreationSettings() const
  325. {
  326. JPH_ASSERT(IsSoftBody());
  327. SoftBodyCreationSettings result;
  328. result.mPosition = GetPosition();
  329. result.mRotation = GetRotation();
  330. result.mUserData = mUserData;
  331. result.mObjectLayer = GetObjectLayer();
  332. result.mCollisionGroup = GetCollisionGroup();
  333. result.mFriction = GetFriction();
  334. result.mRestitution = GetRestitution();
  335. const SoftBodyMotionProperties *mp = static_cast<const SoftBodyMotionProperties *>(mMotionProperties);
  336. result.mNumIterations = mp->GetNumIterations();
  337. result.mLinearDamping = mp->GetLinearDamping();
  338. result.mMaxLinearVelocity = mp->GetMaxLinearVelocity();
  339. result.mGravityFactor = mp->GetGravityFactor();
  340. result.mPressure = mp->GetPressure();
  341. result.mUpdatePosition = mp->GetUpdatePosition();
  342. result.mVertexRadius = mp->GetVertexRadius();
  343. result.mAllowSleeping = mp->GetAllowSleeping();
  344. result.mFacesDoubleSided = mp->GetFacesDoubleSided();
  345. result.mSettings = mp->GetSettings();
  346. return result;
  347. }
  348. JPH_NAMESPACE_END