Quaternions.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. using System;
  2. using UnityEngine;
  3. namespace FirstGearGames.Utilities.Networks
  4. {
  5. public static class Quaternions
  6. {
  7. /// <summary>
  8. /// Credit to this man for converting gaffer games c code to c#
  9. /// https://gist.github.com/fversnel/0497ad7ab3b81e0dc1dd
  10. /// </summary>
  11. private enum ComponentType : uint
  12. {
  13. X = 0,
  14. Y = 1,
  15. Z = 2,
  16. W = 3
  17. }
  18. // note: 1.0f / sqrt(2)
  19. private const float Maximum = +1.0f / 1.414214f;
  20. private const int BitsPerAxis = 10;
  21. private const int LargestComponentShift = BitsPerAxis * 3;
  22. private const int AShift = BitsPerAxis * 2;
  23. private const int BShift = BitsPerAxis * 1;
  24. private const int IntScale = (1 << (BitsPerAxis - 1)) - 1;
  25. private const int IntMask = (1 << BitsPerAxis) - 1;
  26. internal static uint Compress(Quaternion quaternion)
  27. {
  28. float absX = Mathf.Abs(quaternion.x);
  29. float absY = Mathf.Abs(quaternion.y);
  30. float absZ = Mathf.Abs(quaternion.z);
  31. float absW = Mathf.Abs(quaternion.w);
  32. ComponentType largestComponent = ComponentType.X;
  33. float largestAbs = absX;
  34. float largest = quaternion.x;
  35. if (absY > largestAbs)
  36. {
  37. largestAbs = absY;
  38. largestComponent = ComponentType.Y;
  39. largest = quaternion.y;
  40. }
  41. if (absZ > largestAbs)
  42. {
  43. largestAbs = absZ;
  44. largestComponent = ComponentType.Z;
  45. largest = quaternion.z;
  46. }
  47. if (absW > largestAbs)
  48. {
  49. largestComponent = ComponentType.W;
  50. largest = quaternion.w;
  51. }
  52. float a = 0;
  53. float b = 0;
  54. float c = 0;
  55. switch (largestComponent)
  56. {
  57. case ComponentType.X:
  58. a = quaternion.y;
  59. b = quaternion.z;
  60. c = quaternion.w;
  61. break;
  62. case ComponentType.Y:
  63. a = quaternion.x;
  64. b = quaternion.z;
  65. c = quaternion.w;
  66. break;
  67. case ComponentType.Z:
  68. a = quaternion.x;
  69. b = quaternion.y;
  70. c = quaternion.w;
  71. break;
  72. case ComponentType.W:
  73. a = quaternion.x;
  74. b = quaternion.y;
  75. c = quaternion.z;
  76. break;
  77. }
  78. if (largest < 0)
  79. {
  80. a = -a;
  81. b = -b;
  82. c = -c;
  83. }
  84. uint integerA = ScaleToUint(a);
  85. uint integerB = ScaleToUint(b);
  86. uint integerC = ScaleToUint(c);
  87. return (((uint)largestComponent) << LargestComponentShift) | (integerA << AShift) | (integerB << BShift) | integerC;
  88. }
  89. private static uint ScaleToUint(float v)
  90. {
  91. float normalized = v / Maximum;
  92. return (uint)Mathf.RoundToInt(normalized * IntScale) & IntMask;
  93. }
  94. private static float ScaleToFloat(uint v)
  95. {
  96. float unscaled = v * Maximum / IntScale;
  97. if (unscaled > Maximum)
  98. unscaled -= Maximum * 2;
  99. return unscaled;
  100. }
  101. internal static Quaternion Decompress(uint compressed)
  102. {
  103. var largestComponentType = (ComponentType)(compressed >> LargestComponentShift);
  104. uint integerA = (compressed >> AShift) & IntMask;
  105. uint integerB = (compressed >> BShift) & IntMask;
  106. uint integerC = compressed & IntMask;
  107. float a = ScaleToFloat(integerA);
  108. float b = ScaleToFloat(integerB);
  109. float c = ScaleToFloat(integerC);
  110. Quaternion rotation;
  111. switch (largestComponentType)
  112. {
  113. case ComponentType.X:
  114. // (?) y z w
  115. rotation.y = a;
  116. rotation.z = b;
  117. rotation.w = c;
  118. rotation.x = Mathf.Sqrt(1 - rotation.y * rotation.y
  119. - rotation.z * rotation.z
  120. - rotation.w * rotation.w);
  121. break;
  122. case ComponentType.Y:
  123. // x (?) z w
  124. rotation.x = a;
  125. rotation.z = b;
  126. rotation.w = c;
  127. rotation.y = Mathf.Sqrt(1 - rotation.x * rotation.x
  128. - rotation.z * rotation.z
  129. - rotation.w * rotation.w);
  130. break;
  131. case ComponentType.Z:
  132. // x y (?) w
  133. rotation.x = a;
  134. rotation.y = b;
  135. rotation.w = c;
  136. rotation.z = Mathf.Sqrt(1 - rotation.x * rotation.x
  137. - rotation.y * rotation.y
  138. - rotation.w * rotation.w);
  139. break;
  140. case ComponentType.W:
  141. // x y z (?)
  142. rotation.x = a;
  143. rotation.y = b;
  144. rotation.z = c;
  145. rotation.w = Mathf.Sqrt(1 - rotation.x * rotation.x
  146. - rotation.y * rotation.y
  147. - rotation.z * rotation.z);
  148. break;
  149. default:
  150. // Should never happen!
  151. throw new ArgumentOutOfRangeException("Unknown rotation component type: " +
  152. largestComponentType);
  153. }
  154. return rotation;
  155. }
  156. }
  157. }