Body.inl 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #pragma once
  5. JPH_NAMESPACE_BEGIN
  6. RMat44 Body::GetWorldTransform() const
  7. {
  8. JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read));
  9. return RMat44::sRotationTranslation(mRotation, mPosition).PreTranslated(-mShape->GetCenterOfMass());
  10. }
  11. RMat44 Body::GetCenterOfMassTransform() const
  12. {
  13. JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read));
  14. return RMat44::sRotationTranslation(mRotation, mPosition);
  15. }
  16. RMat44 Body::GetInverseCenterOfMassTransform() const
  17. {
  18. JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read));
  19. return RMat44::sInverseRotationTranslation(mRotation, mPosition);
  20. }
  21. inline static bool sIsValidSensorBodyPair(const Body &inSensor, const Body &inOther)
  22. {
  23. // If the sensor is not an actual sensor then this is not a valid pair
  24. if (!inSensor.IsSensor())
  25. return false;
  26. if (inSensor.SensorDetectsStatic())
  27. return !inOther.IsDynamic(); // If the other body is dynamic, the pair will be handled when the bodies are swapped, otherwise we'll detect the collision twice
  28. else
  29. return inOther.IsKinematic(); // Only kinematic bodies are valid
  30. }
  31. inline bool Body::sFindCollidingPairsCanCollide(const Body &inBody1, const Body &inBody2)
  32. {
  33. // First body should never be a soft body
  34. JPH_ASSERT(!inBody1.IsSoftBody());
  35. // One of these conditions must be true
  36. // - One of the bodies must be dynamic to collide
  37. // - A sensor can collide with non-dynamic bodies
  38. if ((!inBody1.IsDynamic() && !inBody2.IsDynamic())
  39. && !sIsValidSensorBodyPair(inBody1, inBody2)
  40. && !sIsValidSensorBodyPair(inBody2, inBody1))
  41. return false;
  42. // Check that body 1 is active
  43. uint32 body1_index_in_active_bodies = inBody1.GetIndexInActiveBodiesInternal();
  44. JPH_ASSERT(!inBody1.IsStatic() && body1_index_in_active_bodies != Body::cInactiveIndex, "This function assumes that Body 1 is active");
  45. // If the pair A, B collides we need to ensure that the pair B, A does not collide or else we will handle the collision twice.
  46. // If A is the same body as B we don't want to collide (1)
  47. // If A is dynamic and B is static we should collide (2)
  48. // If A is dynamic / kinematic and B is dynamic / kinematic we should only collide if (kinematic vs kinematic is ruled out by the if above)
  49. // - A is active and B is not yet active (3)
  50. // - A is active and B will become active during this simulation step (4)
  51. // - A is active and B is active, we require a condition that makes A, B collide and B, A not (5)
  52. //
  53. // In order to implement this we use the index in the active body list and make use of the fact that
  54. // a body not in the active list has Body.Index = 0xffffffff which is the highest possible value for an uint32.
  55. //
  56. // Because we know that A is active we know that A.Index != 0xffffffff:
  57. // (1) Because A.Index != 0xffffffff, if A.Index = B.Index then A = B, so to collide A.Index != B.Index
  58. // (2) A.Index != 0xffffffff, B.Index = 0xffffffff (because it's static and cannot be in the active list), so to collide A.Index != B.Index
  59. // (3) A.Index != 0xffffffff, B.Index = 0xffffffff (because it's not yet active), so to collide A.Index != B.Index
  60. // (4) A.Index != 0xffffffff, B.Index = 0xffffffff currently. But it can activate during the Broad/NarrowPhase step at which point it
  61. // will be added to the end of the active list which will make B.Index > A.Index (this holds only true when we don't deactivate
  62. // bodies during the Broad/NarrowPhase step), so to collide A.Index < B.Index.
  63. // (5) As tie breaker we can use the same condition A.Index < B.Index to collide, this means that if A, B collides then B, A won't
  64. static_assert(Body::cInactiveIndex == 0xffffffff, "The algorithm below uses this value");
  65. if (!inBody2.IsSoftBody() && body1_index_in_active_bodies >= inBody2.GetIndexInActiveBodiesInternal())
  66. return false;
  67. JPH_ASSERT(inBody1.GetID() != inBody2.GetID(), "Read the comment above, A and B are the same body which should not be possible!");
  68. // Bodies in the same group don't collide
  69. if (!inBody1.GetCollisionGroup().CanCollide(inBody2.GetCollisionGroup()))
  70. return false;
  71. return true;
  72. }
  73. void Body::AddRotationStep(Vec3Arg inAngularVelocityTimesDeltaTime)
  74. {
  75. JPH_ASSERT(IsRigidBody());
  76. JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::ReadWrite));
  77. // This used to use the equation: d/dt R(t) = 1/2 * w(t) * R(t) so that R(t + dt) = R(t) + 1/2 * w(t) * R(t) * dt
  78. // See: Appendix B of An Introduction to Physically Based Modeling: Rigid Body Simulation II-Nonpenetration Constraints
  79. // URL: https://www.cs.cmu.edu/~baraff/sigcourse/notesd2.pdf
  80. // But this is a first order approximation and does not work well for kinematic ragdolls that are driven to a new
  81. // pose if the poses differ enough. So now we split w(t) * dt into an axis and angle part and create a quaternion with it.
  82. // Note that the resulting quaternion is normalized since otherwise numerical drift will eventually make the rotation non-normalized.
  83. float len = inAngularVelocityTimesDeltaTime.Length();
  84. if (len > 1.0e-6f)
  85. {
  86. mRotation = (Quat::sRotation(inAngularVelocityTimesDeltaTime / len, len) * mRotation).Normalized();
  87. JPH_ASSERT(!mRotation.IsNaN());
  88. }
  89. }
  90. void Body::SubRotationStep(Vec3Arg inAngularVelocityTimesDeltaTime)
  91. {
  92. JPH_ASSERT(IsRigidBody());
  93. JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::ReadWrite));
  94. // See comment at Body::AddRotationStep
  95. float len = inAngularVelocityTimesDeltaTime.Length();
  96. if (len > 1.0e-6f)
  97. {
  98. mRotation = (Quat::sRotation(inAngularVelocityTimesDeltaTime / len, -len) * mRotation).Normalized();
  99. JPH_ASSERT(!mRotation.IsNaN());
  100. }
  101. }
  102. Vec3 Body::GetWorldSpaceSurfaceNormal(const SubShapeID &inSubShapeID, RVec3Arg inPosition) const
  103. {
  104. RMat44 inv_com = GetInverseCenterOfMassTransform();
  105. return inv_com.Multiply3x3Transposed(mShape->GetSurfaceNormal(inSubShapeID, Vec3(inv_com * inPosition))).Normalized();
  106. }
  107. Mat44 Body::GetInverseInertia() const
  108. {
  109. JPH_ASSERT(IsDynamic());
  110. return GetMotionProperties()->GetInverseInertiaForRotation(Mat44::sRotation(mRotation));
  111. }
  112. void Body::AddForce(Vec3Arg inForce, RVec3Arg inPosition)
  113. {
  114. AddForce(inForce);
  115. AddTorque(Vec3(inPosition - mPosition).Cross(inForce));
  116. }
  117. void Body::AddImpulse(Vec3Arg inImpulse)
  118. {
  119. JPH_ASSERT(IsDynamic());
  120. SetLinearVelocityClamped(mMotionProperties->GetLinearVelocity() + inImpulse * mMotionProperties->GetInverseMass());
  121. }
  122. void Body::AddImpulse(Vec3Arg inImpulse, RVec3Arg inPosition)
  123. {
  124. JPH_ASSERT(IsDynamic());
  125. SetLinearVelocityClamped(mMotionProperties->GetLinearVelocity() + inImpulse * mMotionProperties->GetInverseMass());
  126. SetAngularVelocityClamped(mMotionProperties->GetAngularVelocity() + mMotionProperties->MultiplyWorldSpaceInverseInertiaByVector(mRotation, Vec3(inPosition - mPosition).Cross(inImpulse)));
  127. }
  128. void Body::AddAngularImpulse(Vec3Arg inAngularImpulse)
  129. {
  130. JPH_ASSERT(IsDynamic());
  131. SetAngularVelocityClamped(mMotionProperties->GetAngularVelocity() + mMotionProperties->MultiplyWorldSpaceInverseInertiaByVector(mRotation, inAngularImpulse));
  132. }
  133. void Body::GetSleepTestPoints(RVec3 *outPoints) const
  134. {
  135. JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read));
  136. // Center of mass is the first position
  137. outPoints[0] = mPosition;
  138. // The second and third position are on the largest axis of the bounding box
  139. Vec3 extent = mShape->GetLocalBounds().GetExtent();
  140. int lowest_component = extent.GetLowestComponentIndex();
  141. Mat44 rotation = Mat44::sRotation(mRotation);
  142. switch (lowest_component)
  143. {
  144. case 0:
  145. outPoints[1] = mPosition + extent.GetY() * rotation.GetColumn3(1);
  146. outPoints[2] = mPosition + extent.GetZ() * rotation.GetColumn3(2);
  147. break;
  148. case 1:
  149. outPoints[1] = mPosition + extent.GetX() * rotation.GetColumn3(0);
  150. outPoints[2] = mPosition + extent.GetZ() * rotation.GetColumn3(2);
  151. break;
  152. case 2:
  153. outPoints[1] = mPosition + extent.GetX() * rotation.GetColumn3(0);
  154. outPoints[2] = mPosition + extent.GetY() * rotation.GetColumn3(1);
  155. break;
  156. default:
  157. JPH_ASSERT(false);
  158. break;
  159. }
  160. }
  161. void Body::ResetSleepTestSpheres()
  162. {
  163. RVec3 points[3];
  164. GetSleepTestPoints(points);
  165. mMotionProperties->ResetSleepTestSpheres(points);
  166. }
  167. JPH_NAMESPACE_END