Body.cpp 14 KB

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