MotionProperties.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  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/MotionProperties.h>
  6. #include <Jolt/Physics/StateRecorder.h>
  7. JPH_NAMESPACE_BEGIN
  8. void MotionProperties::SetMassProperties(EAllowedDOFs inAllowedDOFs, const MassProperties &inMassProperties)
  9. {
  10. // Store allowed DOFs
  11. mAllowedDOFs = inAllowedDOFs;
  12. // Decompose DOFs
  13. uint allowed_translation_axis = uint(inAllowedDOFs) & 0b111;
  14. uint allowed_rotation_axis = (uint(inAllowedDOFs) >> 3) & 0b111;
  15. // Set inverse mass
  16. if (allowed_translation_axis == 0)
  17. {
  18. // No translation possible
  19. mInvMass = 0.0f;
  20. }
  21. else
  22. {
  23. JPH_ASSERT(inMassProperties.mMass > 0.0f);
  24. mInvMass = 1.0f / inMassProperties.mMass;
  25. }
  26. if (allowed_rotation_axis == 0b111)
  27. {
  28. // Set inverse inertia
  29. Mat44 rotation;
  30. Vec3 diagonal;
  31. if (inMassProperties.DecomposePrincipalMomentsOfInertia(rotation, diagonal)
  32. && !diagonal.IsNearZero())
  33. {
  34. mInvInertiaDiagonal = diagonal.Reciprocal();
  35. mInertiaRotation = rotation.GetQuaternion();
  36. }
  37. else
  38. {
  39. // Failed! Fall back to inertia tensor of sphere with radius 1.
  40. mInvInertiaDiagonal = Vec3::sReplicate(2.5f * mInvMass);
  41. mInertiaRotation = Quat::sIdentity();
  42. }
  43. }
  44. else if (allowed_rotation_axis == 0)
  45. {
  46. // No rotation possible
  47. mInvInertiaDiagonal = Vec3::sZero();
  48. mInertiaRotation = Quat::sIdentity();
  49. }
  50. else
  51. {
  52. uint num_allowed_rotation_axis = CountBits(allowed_rotation_axis);
  53. if (num_allowed_rotation_axis == 1)
  54. {
  55. // We can only rotate around one axis so the inverse inertia is trivial to calculate
  56. mInertiaRotation = Quat::sIdentity();
  57. mInvInertiaDiagonal = Vec3::sZero();
  58. for (int axis = 0; axis < 3; ++axis)
  59. if ((allowed_rotation_axis & (1 << axis)) != 0)
  60. mInvInertiaDiagonal.SetComponent(axis, 1.0f / inMassProperties.mInertia(axis, axis));
  61. }
  62. else
  63. {
  64. JPH_ASSERT(num_allowed_rotation_axis == 2);
  65. uint locked_axis = CountTrailingZeros(~allowed_rotation_axis);
  66. // Copy the mass properties so we can modify it
  67. MassProperties copy = inMassProperties;
  68. Mat44 &inertia = copy.mInertia;
  69. // Set the locked row and column to 0
  70. for (uint axis = 0; axis < 3; ++axis)
  71. {
  72. inertia(axis, locked_axis) = 0.0f;
  73. inertia(locked_axis, axis) = 0.0f;
  74. }
  75. // Set the diagonal entry to 1
  76. inertia(locked_axis, locked_axis) = 1.0f;
  77. // Decompose the inertia matrix, note that using a 2x2 matrix would have been more efficient
  78. // but we happen to have a 3x3 matrix version lying around so we use that.
  79. Mat44 rotation;
  80. Vec3 diagonal;
  81. if (copy.DecomposePrincipalMomentsOfInertia(rotation, diagonal))
  82. {
  83. mInvInertiaDiagonal = diagonal.Reciprocal();
  84. mInertiaRotation = rotation.GetQuaternion();
  85. // Now set the diagonal entry corresponding to the locked axis to 0
  86. for (uint axis = 0; axis < 3; ++axis)
  87. if (abs(inertia.GetColumn3(locked_axis).Dot(rotation.GetColumn3(axis))) > 0.999f)
  88. {
  89. mInvInertiaDiagonal.SetComponent(axis, 0.0f);
  90. break;
  91. }
  92. // Check that we placed a zero
  93. JPH_ASSERT(Vec3::sEquals(mInvInertiaDiagonal, Vec3::sZero()).TestAnyXYZTrue());
  94. }
  95. else
  96. {
  97. // Failed! Fall back to inaccurate version.
  98. mInertiaRotation = Quat::sIdentity();
  99. mInvInertiaDiagonal = Vec3::sZero();
  100. for (uint axis = 0; axis < 3; ++axis)
  101. if (axis != locked_axis)
  102. mInvInertiaDiagonal.SetComponent(axis, 1.0f / inertia.GetColumn3(axis).Length());
  103. }
  104. }
  105. }
  106. JPH_ASSERT(mInvMass != 0.0f || mInvInertiaDiagonal != Vec3::sZero(), "Can't lock all axes, use a static body for this. This will crash with a division by zero later!");
  107. }
  108. void MotionProperties::SaveState(StateRecorder &inStream) const
  109. {
  110. // Only write properties that can change at runtime
  111. inStream.Write(mLinearVelocity);
  112. inStream.Write(mAngularVelocity);
  113. inStream.Write(mForce);
  114. inStream.Write(mTorque);
  115. #ifdef JPH_DOUBLE_PRECISION
  116. inStream.Write(mSleepTestOffset);
  117. #endif // JPH_DOUBLE_PRECISION
  118. inStream.Write(mSleepTestSpheres);
  119. inStream.Write(mSleepTestTimer);
  120. inStream.Write(mAllowSleeping);
  121. }
  122. void MotionProperties::RestoreState(StateRecorder &inStream)
  123. {
  124. inStream.Read(mLinearVelocity);
  125. inStream.Read(mAngularVelocity);
  126. inStream.Read(mForce);
  127. inStream.Read(mTorque);
  128. #ifdef JPH_DOUBLE_PRECISION
  129. inStream.Read(mSleepTestOffset);
  130. #endif // JPH_DOUBLE_PRECISION
  131. inStream.Read(mSleepTestSpheres);
  132. inStream.Read(mSleepTestTimer);
  133. inStream.Read(mAllowSleeping);
  134. }
  135. JPH_NAMESPACE_END