1 /** 2 Quaternion. 3 4 Copyright: 5 Copyright (c) 2007 Juan Linietsky, Ariel Manzur. 6 Copyright (c) 2014 Godot Engine contributors (cf. AUTHORS.md) 7 Copyright (c) 2017 Godot-D contributors 8 Copyright (c) 2022 Godot-DLang contributors 9 10 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License) 11 12 13 */ 14 module godot.quat; 15 16 import godot.api.types; 17 import godot.vector3; 18 import godot.basis; 19 20 import std.math; 21 22 /** 23 A 4-dimensional vector representing a rotation. 24 25 The vector represents a 4 dimensional complex number where multiplication of the basis elements is not commutative (multiplying i with j gives a different result than multiplying j with i). 26 27 Multiplying quaternions reproduces rotation sequences. However quaternions need to be often renormalized, or else they suffer from precision issues. 28 29 It can be used to perform SLERP (spherical-linear interpolation) between two rotations. 30 */ 31 struct Quaternion { 32 @nogc nothrow: 33 34 real_t x = 0; 35 real_t y = 0; 36 real_t z = 0; 37 real_t w = 1; 38 39 void set(real_t p_x, real_t p_y, real_t p_z, real_t p_w) { 40 x = p_x; 41 y = p_y; 42 z = p_z; 43 w = p_w; 44 } 45 46 this(real_t p_x, real_t p_y, real_t p_z, real_t p_w) { 47 x = p_x; 48 y = p_y; 49 z = p_z; 50 w = p_w; 51 } 52 53 real_t length() const { 54 return sqrt(lengthSquared()); 55 } 56 57 void normalize() { 58 this /= length(); 59 } 60 61 Quaternion normalized() const { 62 return this / length(); 63 } 64 65 Quaternion inverse() const { 66 return Quaternion(-x, -y, -z, w); 67 } 68 69 void setEuler(in Vector3 p_euler) { 70 real_t half_a1 = p_euler.x * 0.5; 71 real_t half_a2 = p_euler.y * 0.5; 72 real_t half_a3 = p_euler.z * 0.5; 73 74 // R = X(a1).Y(a2).Z(a3) convention for Euler angles. 75 // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-2) 76 // a3 is the angle of the first rotation, following the notation in this reference. 77 78 real_t cos_a1 = cos(half_a1); 79 real_t sin_a1 = sin(half_a1); 80 real_t cos_a2 = cos(half_a2); 81 real_t sin_a2 = sin(half_a2); 82 real_t cos_a3 = cos(half_a3); 83 real_t sin_a3 = sin(half_a3); 84 85 set(sin_a1 * cos_a2 * cos_a3 + sin_a2 * sin_a3 * cos_a1, 86 -sin_a1 * sin_a3 * cos_a2 + sin_a2 * cos_a1 * cos_a3, 87 sin_a1 * sin_a2 * cos_a3 + sin_a3 * cos_a1 * cos_a2, 88 -sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3); 89 } 90 91 Quaternion slerp(in Quaternion q, in real_t t) const { 92 Quaternion to1; 93 real_t omega, cosom, sinom, scale0, scale1; 94 // calc cosine 95 cosom = dot(q); 96 97 // adjust signs (if necessary) 98 if (cosom < 0.0) { 99 cosom = -cosom; 100 to1.x = -q.x; 101 to1.y = -q.y; 102 to1.z = -q.z; 103 to1.w = -q.w; 104 } else { 105 to1.x = q.x; 106 to1.y = q.y; 107 to1.z = q.z; 108 to1.w = q.w; 109 } 110 // calculate coefficients 111 if ((1.0 - cosom) > CMP_EPSILON) { 112 // standard case (slerp) 113 omega = acos(cosom); 114 sinom = sin(omega); 115 scale0 = sin((1.0 - t) * omega) / sinom; 116 scale1 = sin(t * omega) / sinom; 117 } else { 118 // "from" and "to" quaternions are very close 119 // ... so we can do a linear interpolation 120 scale0 = 1.0 - t; 121 scale1 = t; 122 } 123 // calculate final values 124 return Quaternion( 125 scale0 * x + scale1 * to1.x, 126 scale0 * y + scale1 * to1.y, 127 scale0 * z + scale1 * to1.z, 128 scale0 * w + scale1 * to1.w 129 ); 130 } 131 132 Quaternion slerpni(in Quaternion q, in real_t t) const { 133 Quaternion from = this; 134 135 real_t dot = from.dot(q); 136 137 if (fabs(dot) > 0.9999) 138 return from; 139 140 real_t theta = acos(dot), 141 sinT = 1.0 / sin(theta), 142 newFactor = sin(t * theta) * sinT, 143 invFactor = sin((1.0 - t) * theta) * sinT; 144 145 return Quaternion(invFactor * from.x + newFactor * q.x, 146 invFactor * from.y + newFactor * q.y, 147 invFactor * from.z + newFactor * q.z, 148 invFactor * from.w + newFactor * q.w); 149 } 150 151 Quaternion cubicSlerp(in Quaternion q, in Quaternion prep, in Quaternion postq, in real_t t) const { 152 //the only way to do slerp :| 153 real_t t2 = (1.0 - t) * t * 2; 154 Quaternion sp = this.slerp(q, t); 155 Quaternion sq = prep.slerpni(postq, t); 156 return sp.slerpni(sq, t2); 157 } 158 159 void getAxisAndAngle(out Vector3 r_axis, out real_t r_angle) const { 160 r_angle = 2 * acos(w); 161 r_axis.x = x / sqrt(1 - w * w); 162 r_axis.y = y / sqrt(1 - w * w); 163 r_axis.z = z / sqrt(1 - w * w); 164 } 165 166 Quaternion opBinary(string op : "*")(in Vector3 v) const { 167 return Quaternion(w * v.x + y * v.z - z * v.y, 168 w * v.y + z * v.x - x * v.z, 169 w * v.z + x * v.y - y * v.x, 170 -x * v.x - y * v.y - z * v.z); 171 } 172 173 Vector3 xform(in Vector3 v) const { 174 Quaternion q = this * v; 175 q *= this.inverse(); 176 return Vector3(q.x, q.y, q.z); 177 } 178 179 this(in Vector3 axis, in real_t angle) { 180 real_t d = axis.length(); 181 if (d == 0) 182 set(0, 0, 0, 0); 183 else { 184 real_t sin_angle = sin(angle * 0.5); 185 real_t cos_angle = cos(angle * 0.5); 186 real_t s = sin_angle / d; 187 set(axis.x * s, axis.y * s, axis.z * s, 188 cos_angle); 189 } 190 } 191 192 this(in Vector3 v0, in Vector3 v1) // shortest arc 193 { 194 Vector3 c = v0.cross(v1); 195 real_t d = v0.dot(v1); 196 197 if (d < -1.0 + CMP_EPSILON) { 198 x = 0; 199 y = 1; 200 z = 0; 201 w = 0; 202 } else { 203 real_t s = sqrt((1.0 + d) * 2.0); 204 real_t rs = 1.0 / s; 205 206 x = c.x * rs; 207 y = c.y * rs; 208 z = c.z * rs; 209 w = s * 0.5; 210 } 211 } 212 213 real_t dot(in Quaternion q) const { 214 return x * q.x + y * q.y + z * q.z + w * q.w; 215 } 216 217 real_t lengthSquared() const { 218 return dot(this); 219 } 220 221 void opOpAssign(string op : "+")(in Quaternion q) { 222 x += q.x; 223 y += q.y; 224 z += q.z; 225 w += q.w; 226 } 227 228 void opOpAssign(string op : "-")(in Quaternion q) { 229 x -= q.x; 230 y -= q.y; 231 z -= q.z; 232 w -= q.w; 233 } 234 235 void opOpAssign(string op : "*")(in Quaternion q) { 236 x *= q.x; 237 y *= q.y; 238 z *= q.z; 239 w *= q.w; 240 } 241 242 void opOpAssign(string op : "*")(in real_t s) { 243 x *= s; 244 y *= s; 245 z *= s; 246 w *= s; 247 } 248 249 void opOpAssign(string op : "/")(in real_t s) { 250 this *= 1.0 / s; 251 } 252 253 Quaternion opBinary(string op : "+")(in Quaternion q2) const { 254 Quaternion q1 = this; 255 return Quaternion(q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w); 256 } 257 258 Quaternion opBinary(string op : "-")(in Quaternion q2) const { 259 Quaternion q1 = this; 260 return Quaternion(q1.x - q2.x, q1.y - q2.y, q1.z - q2.z, q1.w - q2.w); 261 } 262 263 Quaternion opBinary(string op : "*")(in Quaternion q2) const { 264 Quaternion q1 = this; 265 q1 *= q2; 266 return q1; 267 } 268 269 Quaternion opUnary(string op : "-")() const { 270 return Quaternion(-x, -y, -z, -w); 271 } 272 273 Quaternion opBinary(string op : "*")(in real_t s) const { 274 return Quaternion(x * s, y * s, z * s, w * s); 275 } 276 277 Quaternion opBinary(string op : "/")(in real_t s) const { 278 return this * (1.0 / s); 279 } 280 281 Vector3 getEuler() const { 282 Basis m = Basis(this); 283 return m.getEuler(); 284 } 285 }