Quat.hx 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. package h3d;
  2. using hxd.Math;
  3. @:noDebug
  4. class Quat {
  5. public var x : Float;
  6. public var y : Float;
  7. public var z : Float;
  8. public var w : Float;
  9. public inline function new( x = 0., y = 0., z = 0., w = 1. ) {
  10. this.x = x;
  11. this.y = y;
  12. this.z = z;
  13. this.w = w;
  14. }
  15. public inline function set(x, y, z, w) {
  16. this.x = x;
  17. this.y = y;
  18. this.z = z;
  19. this.w = w;
  20. }
  21. public inline function identity() {
  22. x = y = z = 0;
  23. w = 1;
  24. }
  25. public inline function lengthSq() {
  26. return x * x + y * y + z * z + w * w;
  27. }
  28. public inline function length() {
  29. return lengthSq().sqrt();
  30. }
  31. public inline function load( q : Quat ) {
  32. this.x = q.x;
  33. this.y = q.y;
  34. this.z = q.z;
  35. this.w = q.w;
  36. }
  37. public function clone() {
  38. return new Quat(x, y, z, w);
  39. }
  40. public function initMoveTo( from : Vector, to : Vector ) {
  41. // H = Normalize(From + To)
  42. // Q = (From ^ H, From . H)
  43. //
  44. // We have an issue when From.To moves towards -1, a small tilt on From ^ To will result in big tilt.
  45. var hx = from.x + to.x;
  46. var hy = from.y + to.y;
  47. var hz = from.z + to.z;
  48. var h = Math.invSqrt(hx * hx + hy * hy + hz * hz);
  49. x = from.y * hz - from.z * hy;
  50. y = from.z * hx - from.x * hz;
  51. z = from.x * hy - from.y * hx;
  52. w = from.x * hx + from.y * hy + from.z * hz;
  53. normalize();
  54. }
  55. public function initNormal( dir : h3d.col.Point ) {
  56. var dir = dir.normalized();
  57. if( dir.x*dir.x+dir.y*dir.y < Math.EPSILON )
  58. initDirection(new h3d.Vector(1,0,0));
  59. else {
  60. var ay = new h3d.col.Point(dir.x, dir.y, 0).normalized();
  61. var az = dir.cross(ay);
  62. initDirection(dir.cross(az).toVector());
  63. }
  64. }
  65. public function initDirection( dir : Vector, ?up : Vector ) {
  66. // inlined version of initRotationMatrix(Matrix.lookAtX(dir))
  67. var ax = dir.clone().normalized();
  68. var ay = new Vector(-ax.y, ax.x, 0);
  69. if( up != null )
  70. ay.load(up.cross(ax));
  71. ay.normalize();
  72. if( ay.lengthSq() < Math.EPSILON ) {
  73. ay.x = ax.y;
  74. ay.y = ax.z;
  75. ay.z = ax.x;
  76. }
  77. var az = ax.cross(ay);
  78. var tr = ax.x + ay.y + az.z;
  79. if( tr > 0 ) {
  80. var s = (tr + 1.0).sqrt() * 2;
  81. var ins = 1 / s;
  82. x = (ay.z - az.y) * ins;
  83. y = (az.x - ax.z) * ins;
  84. z = (ax.y - ay.x) * ins;
  85. w = 0.25 * s;
  86. } else if( ax.x > ay.y && ax.x > az.z ) {
  87. var s = (1.0 + ax.x - ay.y - az.z).sqrt() * 2;
  88. var ins = 1 / s;
  89. x = 0.25 * s;
  90. y = (ay.x + ax.y) * ins;
  91. z = (az.x + ax.z) * ins;
  92. w = (ay.z - az.y) * ins;
  93. } else if( ay.y > az.z ) {
  94. var s = (1.0 + ay.y - ax.x - az.z).sqrt() * 2;
  95. var ins = 1 / s;
  96. x = (ay.x + ax.y) * ins;
  97. y = 0.25 * s;
  98. z = (az.y + ay.z) * ins;
  99. w = (az.x - ax.z) * ins;
  100. } else {
  101. var s = (1.0 + az.z - ax.x - ay.y).sqrt() * 2;
  102. var ins = 1 / s;
  103. x = (az.x + ax.z) * ins;
  104. y = (az.y + ay.z) * ins;
  105. z = 0.25 * s;
  106. w = (ax.y - ay.x) * ins;
  107. }
  108. }
  109. public function initRotateAxis( x : Float, y : Float, z : Float, a : Float ) {
  110. var sin = (a / 2).sin();
  111. var cos = (a / 2).cos();
  112. this.x = x * sin;
  113. this.y = y * sin;
  114. this.z = z * sin;
  115. this.w = cos * (x * x + y * y + z * z).sqrt(); // allow not normalized axis
  116. normalize();
  117. }
  118. public function initRotateMatrix( m : Matrix ) {
  119. var tr = m._11 + m._22 + m._33;
  120. if( tr > 0 ) {
  121. var s = (tr + 1.0).sqrt() * 2;
  122. var ins = 1 / s;
  123. x = (m._23 - m._32) * ins;
  124. y = (m._31 - m._13) * ins;
  125. z = (m._12 - m._21) * ins;
  126. w = 0.25 * s;
  127. } else if( m._11 > m._22 && m._11 > m._33 ) {
  128. var s = (1.0 + m._11 - m._22 - m._33).sqrt() * 2;
  129. var ins = 1 / s;
  130. x = 0.25 * s;
  131. y = (m._21 + m._12) * ins;
  132. z = (m._31 + m._13) * ins;
  133. w = (m._23 - m._32) * ins;
  134. } else if( m._22 > m._33 ) {
  135. var s = (1.0 + m._22 - m._11 - m._33).sqrt() * 2;
  136. var ins = 1 / s;
  137. x = (m._21 + m._12) * ins;
  138. y = 0.25 * s;
  139. z = (m._32 + m._23) * ins;
  140. w = (m._31 - m._13) * ins;
  141. } else {
  142. var s = (1.0 + m._33 - m._11 - m._22).sqrt() * 2;
  143. var ins = 1 / s;
  144. x = (m._31 + m._13) * ins;
  145. y = (m._32 + m._23) * ins;
  146. z = 0.25 * s;
  147. w = (m._12 - m._21) * ins;
  148. }
  149. }
  150. public function normalize() {
  151. var len = x * x + y * y + z * z + w * w;
  152. if( len < hxd.Math.EPSILON ) {
  153. x = y = z = 0;
  154. w = 1;
  155. } else {
  156. var m = len.invSqrt();
  157. x *= m;
  158. y *= m;
  159. z *= m;
  160. w *= m;
  161. }
  162. }
  163. public function initRotation( ax : Float, ay : Float, az : Float ) {
  164. var sinX = ( ax * 0.5 ).sin();
  165. var cosX = ( ax * 0.5 ).cos();
  166. var sinY = ( ay * 0.5 ).sin();
  167. var cosY = ( ay * 0.5 ).cos();
  168. var sinZ = ( az * 0.5 ).sin();
  169. var cosZ = ( az * 0.5 ).cos();
  170. var cosYZ = cosY * cosZ;
  171. var sinYZ = sinY * sinZ;
  172. x = sinX * cosYZ - cosX * sinYZ;
  173. y = cosX * sinY * cosZ + sinX * cosY * sinZ;
  174. z = cosX * cosY * sinZ - sinX * sinY * cosZ;
  175. w = cosX * cosYZ + sinX * sinYZ;
  176. }
  177. public function multiply( q1 : Quat, q2 : Quat ) {
  178. var x2 = q1.x * q2.w + q1.w * q2.x + q1.y * q2.z - q1.z * q2.y;
  179. var y2 = q1.w * q2.y - q1.x * q2.z + q1.y * q2.w + q1.z * q2.x;
  180. var z2 = q1.w * q2.z + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w;
  181. var w2 = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z;
  182. x = x2;
  183. y = y2;
  184. z = z2;
  185. w = w2;
  186. }
  187. public function toEuler() {
  188. return toMatrix().getEulerAngles();
  189. }
  190. public inline function lerp( q1 : Quat, q2 : Quat, v : Float, nearest = false ) {
  191. var v2 = 1 - v;
  192. if( nearest && q1.dot(q2) < 0 )
  193. v = -v;
  194. var x = q1.x * v2 + q2.x * v;
  195. var y = q1.y * v2 + q2.y * v;
  196. var z = q1.z * v2 + q2.z * v;
  197. var w = q1.w * v2 + q2.w * v;
  198. this.x = x;
  199. this.y = y;
  200. this.z = z;
  201. this.w = w;
  202. }
  203. public function slerp( q1 : Quat, q2 : Quat, v : Float ) {
  204. var cosHalfTheta = q1.dot(q2);
  205. if( cosHalfTheta.abs() >= 1 ) {
  206. this.x = q1.x;
  207. this.y = q1.y;
  208. this.z = q1.z;
  209. this.w = q1.w;
  210. return;
  211. }
  212. var halfTheta = cosHalfTheta.acos();
  213. var invSinHalfTheta = (1 - cosHalfTheta * cosHalfTheta).invSqrt();
  214. if( invSinHalfTheta.abs() > 1e3 ) {
  215. this.lerp(q1, q2, 0.5, true);
  216. return;
  217. }
  218. var a = ((1 - v) * halfTheta).sin() * invSinHalfTheta;
  219. var b = (v * halfTheta).sin() * invSinHalfTheta * (cosHalfTheta < 0 ? -1 : 1);
  220. this.x = q1.x * a + q2.x * b;
  221. this.y = q1.y * a + q2.y * b;
  222. this.z = q1.z * a + q2.z * b;
  223. this.w = q1.w * a + q2.w * b;
  224. }
  225. public inline function conjugate() {
  226. x = -x;
  227. y = -y;
  228. z = -z;
  229. }
  230. /**
  231. Makes a unit quaternion to the power of the value.
  232. **/
  233. public inline function pow( v : Float ) {
  234. // ln()
  235. var r = Math.sqrt(x*x+y*y+z*z);
  236. var t = r > Math.EPSILON ? Math.atan2(r,w)/r : 0;
  237. w = 0.5 * std.Math.log(w*w+x*x+y*y+z*z);
  238. x *= t;
  239. y *= t;
  240. z *= t;
  241. // scale
  242. x *= v;
  243. y *= v;
  244. z *= v;
  245. w *= v;
  246. // exp
  247. var r = Math.sqrt(x*x+y*y+z*z);
  248. var et = std.Math.exp(w);
  249. var s = r > Math.EPSILON ? et *Math.sin(r)/r : 0;
  250. w = et * Math.cos(r);
  251. x *= s;
  252. y *= s;
  253. z *= s;
  254. }
  255. /**
  256. Negate the quaternion: this will not change the actual angle, use `conjugate` for that.
  257. **/
  258. public inline function negate() {
  259. x = -x;
  260. y = -y;
  261. z = -z;
  262. w = -w;
  263. }
  264. public inline function dot( q : Quat ) {
  265. return x * q.x + y * q.y + z * q.z + w * q.w;
  266. }
  267. public inline function getDirection() {
  268. return new h3d.Vector(1 - 2 * ( y * y + z * z ), 2 * ( x * y + z * w ), 2 * ( x * z - y * w ));
  269. }
  270. public inline function getUpAxis() {
  271. return new h3d.Vector(2 * ( x*z + y*w ),2 * ( y*z - x*w ), 1 - 2 * ( x*x + y*y ));
  272. }
  273. /**
  274. Save to a Left-Handed matrix
  275. **/
  276. public function toMatrix( ?m : h3d.Matrix ) {
  277. if( m == null ) m = new h3d.Matrix();
  278. var xx = x * x;
  279. var xy = x * y;
  280. var xz = x * z;
  281. var xw = x * w;
  282. var yy = y * y;
  283. var yz = y * z;
  284. var yw = y * w;
  285. var zz = z * z;
  286. var zw = z * w;
  287. m._11 = 1 - 2 * ( yy + zz );
  288. m._12 = 2 * ( xy + zw );
  289. m._13 = 2 * ( xz - yw );
  290. m._14 = 0;
  291. m._21 = 2 * ( xy - zw );
  292. m._22 = 1 - 2 * ( xx + zz );
  293. m._23 = 2 * ( yz + xw );
  294. m._24 = 0;
  295. m._31 = 2 * ( xz + yw );
  296. m._32 = 2 * ( yz - xw );
  297. m._33 = 1 - 2 * ( xx + yy );
  298. m._34 = 0;
  299. m._41 = 0;
  300. m._42 = 0;
  301. m._43 = 0;
  302. m._44 = 1;
  303. return m;
  304. }
  305. public function toString() {
  306. return '{${x.fmt()},${y.fmt()},${z.fmt()},${w.fmt()}}';
  307. }
  308. }