2
0

MotionProperties.inl 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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. void MotionProperties::MoveKinematic(Vec3Arg inDeltaPosition, QuatArg inDeltaRotation, float inDeltaTime)
  7. {
  8. JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess(), BodyAccess::EAccess::ReadWrite));
  9. JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess(), BodyAccess::EAccess::Read));
  10. JPH_ASSERT(mCachedBodyType == EBodyType::RigidBody);
  11. JPH_ASSERT(mCachedMotionType != EMotionType::Static);
  12. // Calculate required linear velocity
  13. mLinearVelocity = LockTranslation(inDeltaPosition / inDeltaTime);
  14. // Calculate required angular velocity
  15. Vec3 axis;
  16. float angle;
  17. inDeltaRotation.GetAxisAngle(axis, angle);
  18. mAngularVelocity = LockAngular(axis * (angle / inDeltaTime));
  19. }
  20. void MotionProperties::ClampLinearVelocity()
  21. {
  22. JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess(), BodyAccess::EAccess::ReadWrite));
  23. float len_sq = mLinearVelocity.LengthSq();
  24. JPH_ASSERT(isfinite(len_sq));
  25. if (len_sq > Square(mMaxLinearVelocity))
  26. mLinearVelocity *= mMaxLinearVelocity / sqrt(len_sq);
  27. }
  28. void MotionProperties::ClampAngularVelocity()
  29. {
  30. JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess(), BodyAccess::EAccess::ReadWrite));
  31. float len_sq = mAngularVelocity.LengthSq();
  32. JPH_ASSERT(isfinite(len_sq));
  33. if (len_sq > Square(mMaxAngularVelocity))
  34. mAngularVelocity *= mMaxAngularVelocity / sqrt(len_sq);
  35. }
  36. inline Mat44 MotionProperties::GetLocalSpaceInverseInertiaUnchecked() const
  37. {
  38. Mat44 rotation = Mat44::sRotation(mInertiaRotation);
  39. Mat44 rotation_mul_scale_transposed(mInvInertiaDiagonal.SplatX() * rotation.GetColumn4(0), mInvInertiaDiagonal.SplatY() * rotation.GetColumn4(1), mInvInertiaDiagonal.SplatZ() * rotation.GetColumn4(2), Vec4(0, 0, 0, 1));
  40. return rotation.Multiply3x3RightTransposed(rotation_mul_scale_transposed);
  41. }
  42. inline void MotionProperties::ScaleToMass(float inMass)
  43. {
  44. JPH_ASSERT(mInvMass > 0.0f, "Body must have finite mass");
  45. JPH_ASSERT(inMass > 0.0f, "New mass cannot be zero");
  46. float new_inv_mass = 1.0f / inMass;
  47. mInvInertiaDiagonal *= new_inv_mass / mInvMass;
  48. mInvMass = new_inv_mass;
  49. }
  50. inline Mat44 MotionProperties::GetLocalSpaceInverseInertia() const
  51. {
  52. JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic);
  53. return GetLocalSpaceInverseInertiaUnchecked();
  54. }
  55. Mat44 MotionProperties::GetInverseInertiaForRotation(Mat44Arg inRotation) const
  56. {
  57. JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic);
  58. Mat44 rotation = inRotation.Multiply3x3(Mat44::sRotation(mInertiaRotation));
  59. Mat44 rotation_mul_scale_transposed(mInvInertiaDiagonal.SplatX() * rotation.GetColumn4(0), mInvInertiaDiagonal.SplatY() * rotation.GetColumn4(1), mInvInertiaDiagonal.SplatZ() * rotation.GetColumn4(2), Vec4(0, 0, 0, 1));
  60. Mat44 inverse_inertia = rotation.Multiply3x3RightTransposed(rotation_mul_scale_transposed);
  61. // We need to mask out both the rows and columns of DOFs that are not allowed
  62. Vec4 angular_dofs_mask = GetAngularDOFsMask().ReinterpretAsFloat();
  63. inverse_inertia.SetColumn4(0, Vec4::sAnd(inverse_inertia.GetColumn4(0), Vec4::sAnd(angular_dofs_mask, angular_dofs_mask.SplatX())));
  64. inverse_inertia.SetColumn4(1, Vec4::sAnd(inverse_inertia.GetColumn4(1), Vec4::sAnd(angular_dofs_mask, angular_dofs_mask.SplatY())));
  65. inverse_inertia.SetColumn4(2, Vec4::sAnd(inverse_inertia.GetColumn4(2), Vec4::sAnd(angular_dofs_mask, angular_dofs_mask.SplatZ())));
  66. return inverse_inertia;
  67. }
  68. Vec3 MotionProperties::MultiplyWorldSpaceInverseInertiaByVector(QuatArg inBodyRotation, Vec3Arg inV) const
  69. {
  70. JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic);
  71. // Mask out columns of DOFs that are not allowed
  72. Vec3 angular_dofs_mask = Vec3(GetAngularDOFsMask().ReinterpretAsFloat());
  73. Vec3 v = Vec3::sAnd(inV, angular_dofs_mask);
  74. // Multiply vector by inverse inertia
  75. Mat44 rotation = Mat44::sRotation(inBodyRotation * mInertiaRotation);
  76. Vec3 result = rotation.Multiply3x3(mInvInertiaDiagonal * rotation.Multiply3x3Transposed(v));
  77. // Mask out rows of DOFs that are not allowed
  78. return Vec3::sAnd(result, angular_dofs_mask);
  79. }
  80. void MotionProperties::ApplyGyroscopicForceInternal(QuatArg inBodyRotation, float inDeltaTime)
  81. {
  82. JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess(), BodyAccess::EAccess::ReadWrite));
  83. JPH_ASSERT(mCachedBodyType == EBodyType::RigidBody);
  84. JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic);
  85. // Calculate local space inertia tensor (a diagonal in local space)
  86. UVec4 is_zero = Vec3::sEquals(mInvInertiaDiagonal, Vec3::sZero());
  87. Vec3 denominator = Vec3::sSelect(mInvInertiaDiagonal, Vec3::sOne(), is_zero);
  88. Vec3 nominator = Vec3::sSelect(Vec3::sOne(), Vec3::sZero(), is_zero);
  89. Vec3 local_inertia = nominator / denominator; // Avoid dividing by zero, inertia in this axis will be zero
  90. // Calculate local space angular momentum
  91. Quat inertia_space_to_world_space = inBodyRotation * mInertiaRotation;
  92. Vec3 local_angular_velocity = inertia_space_to_world_space.InverseRotate(mAngularVelocity);
  93. Vec3 local_momentum = local_inertia * local_angular_velocity;
  94. // The gyroscopic force applies a torque: T = -w x I w where w is angular velocity and I the inertia tensor
  95. // Calculate the new angular momentum by applying the gyroscopic force and make sure the new magnitude is the same as the old one
  96. // to avoid introducing energy into the system due to the Euler step
  97. Vec3 new_local_momentum = local_momentum - inDeltaTime * local_angular_velocity.Cross(local_momentum);
  98. float new_local_momentum_len_sq = new_local_momentum.LengthSq();
  99. new_local_momentum = new_local_momentum_len_sq > 0.0f? new_local_momentum * sqrt(local_momentum.LengthSq() / new_local_momentum_len_sq) : Vec3::sZero();
  100. // Convert back to world space angular velocity
  101. mAngularVelocity = inertia_space_to_world_space * (mInvInertiaDiagonal * new_local_momentum);
  102. }
  103. void MotionProperties::ApplyForceTorqueAndDragInternal(QuatArg inBodyRotation, Vec3Arg inGravity, float inDeltaTime)
  104. {
  105. JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess(), BodyAccess::EAccess::ReadWrite));
  106. JPH_ASSERT(mCachedBodyType == EBodyType::RigidBody);
  107. JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic);
  108. // Update linear velocity
  109. mLinearVelocity = LockTranslation(mLinearVelocity + inDeltaTime * (mGravityFactor * inGravity + mInvMass * GetAccumulatedForce()));
  110. // Update angular velocity
  111. mAngularVelocity += inDeltaTime * MultiplyWorldSpaceInverseInertiaByVector(inBodyRotation, GetAccumulatedTorque());
  112. // Linear damping: dv/dt = -c * v
  113. // Solution: v(t) = v(0) * e^(-c * t) or v2 = v1 * e^(-c * dt)
  114. // Taylor expansion of e^(-c * dt) = 1 - c * dt + ...
  115. // Since dt is usually in the order of 1/60 and c is a low number too this approximation is good enough
  116. mLinearVelocity *= max(0.0f, 1.0f - mLinearDamping * inDeltaTime);
  117. mAngularVelocity *= max(0.0f, 1.0f - mAngularDamping * inDeltaTime);
  118. // Clamp velocities
  119. ClampLinearVelocity();
  120. ClampAngularVelocity();
  121. }
  122. void MotionProperties::ResetSleepTestSpheres(const RVec3 *inPoints)
  123. {
  124. #ifdef JPH_DOUBLE_PRECISION
  125. // Make spheres relative to the first point and initialize them to zero radius
  126. DVec3 offset = inPoints[0];
  127. offset.StoreDouble3(&mSleepTestOffset);
  128. mSleepTestSpheres[0] = Sphere(Vec3::sZero(), 0.0f);
  129. for (int i = 1; i < 3; ++i)
  130. mSleepTestSpheres[i] = Sphere(Vec3(inPoints[i] - offset), 0.0f);
  131. #else
  132. // Initialize the spheres to zero radius around the supplied points
  133. for (int i = 0; i < 3; ++i)
  134. mSleepTestSpheres[i] = Sphere(inPoints[i], 0.0f);
  135. #endif
  136. mSleepTestTimer = 0.0f;
  137. }
  138. ECanSleep MotionProperties::AccumulateSleepTime(float inDeltaTime, float inTimeBeforeSleep)
  139. {
  140. mSleepTestTimer += inDeltaTime;
  141. return mSleepTestTimer >= inTimeBeforeSleep? ECanSleep::CanSleep : ECanSleep::CannotSleep;
  142. }
  143. JPH_NAMESPACE_END