1 module prova.math.quaternion; 2 3 import prova.math, 4 std.math; 5 6 /// 7 struct Quaternion 8 { 9 /// 10 float x = 0; 11 /// 12 float y = 0; 13 /// 14 float z = 0; 15 /// 16 float w = 1; 17 18 /// 19 this(float x, float y, float z, float w) 20 { 21 set(x, y, z, w); 22 } 23 24 /// Sets the values of x, y, z, and w in a single statement 25 void set(float x, float y, float z, float w) 26 { 27 this.x = x; 28 this.y = y; 29 this.z = z; 30 this.w = w; 31 } 32 33 /// angle is in degrees 34 static Quaternion fromAxisAngle(Vector3 axis, float angle) 35 { 36 return fromAxisAngle(axis.x, axis.y, axis.z, angle); 37 } 38 39 /// angle is in degrees 40 static Quaternion fromAxisAngle(float x, float y, float z, float angle) 41 { 42 angle *= PI / 180 / 2; 43 44 float sinAngle = sin(angle); 45 float cosAngle = cos(angle); 46 47 Quaternion result; 48 result.x = x * sinAngle; 49 result.y = y * sinAngle; 50 result.z = z * sinAngle; 51 result.w = cosAngle; 52 result.normalize(); 53 54 return result; 55 } 56 57 /// Create a quaternion from euler angles in degrees 58 static Quaternion fromEuler(Vector3 euler) 59 { 60 return fromEuler(euler.x, euler.y, euler.z); 61 } 62 63 /// Create a quaternion from euler angles in degrees 64 static Quaternion fromEuler(float x, float y, float z) 65 { 66 x *= PI / 180 / 2; 67 y *= PI / 180 / 2; 68 z *= PI / 180 / 2; 69 70 float cosX = cos(x); 71 float sinX = sin(x); 72 float cosY = cos(y); 73 float sinY = sin(y); 74 float cosZ = cos(z); 75 float sinZ = sin(z); 76 77 Quaternion result; 78 result.x = cosZ * sinX * cosY - sinZ * cosX * sinY; 79 result.y = cosZ * cosX * sinY + sinZ * sinX * cosY; 80 result.z = sinZ * cosX * cosY - cosZ * sinX * sinY; 81 result.w = cosZ * cosX * cosY + sinZ * sinX * sinY; 82 83 return result; 84 } 85 86 /// Creates a normalized quaternion with a random rotation and axis 87 static Quaternion random() 88 { 89 Quaternion result = Quaternion( 90 randomF(1), 91 randomF(1), 92 randomF(1), 93 randomF(1) 94 ); 95 96 result.normalize(); 97 98 return result; 99 } 100 101 /// 102 @property Vector3 xyz() const 103 { 104 return Vector3(x, y, z); 105 } 106 107 /// Returns a normalized copy of this quaternion 108 Quaternion getNormalized() const 109 { 110 const float magnitude = getMagnitude(); 111 112 Quaternion result; 113 114 if(magnitude != 0) { 115 result.x = x / magnitude; 116 result.y = y / magnitude; 117 result.z = z / magnitude; 118 result.w = w / magnitude; 119 } 120 121 return result; 122 } 123 124 /// Normalizes the quaternion 125 void normalize() 126 { 127 const float magnitude = getMagnitude(); 128 129 if(magnitude == 0) 130 return; 131 132 x = x / magnitude; 133 y = y / magnitude; 134 z = z / magnitude; 135 w = w / magnitude; 136 } 137 138 /// Returns the magnitude of the quaternion 139 float getMagnitude() const 140 { 141 return sqrt(x * x + y * y + z * z + w * w); 142 } 143 144 /** 145 * Sets the magnitude of this quaternion 146 * 147 * If the previous magnitude is zero, the x value 148 * of the quaternion will be set to the magnitude 149 */ 150 void setMagnitude(float magnitude) 151 { 152 if(getMagnitude() == 0) { 153 x = magnitude; 154 return; 155 } 156 157 normalize(); 158 159 x *= magnitude; 160 y *= magnitude; 161 z *= magnitude; 162 w *= magnitude; 163 } 164 165 /// 166 Quaternion getConjugate() const 167 { 168 Quaternion result; 169 result.x = -x; 170 result.y = -y; 171 result.z = -z; 172 result.w = w; 173 174 return result; 175 } 176 177 178 // assignment overloading 179 Quaternion opAddAssign(Quaternion quaternion) 180 { 181 x += quaternion.x; 182 y += quaternion.y; 183 z += quaternion.z; 184 w += quaternion.w; 185 186 return this; 187 } 188 189 Quaternion opSubAssign(Quaternion quaternion) 190 { 191 x -= quaternion.x; 192 y -= quaternion.y; 193 z -= quaternion.z; 194 w -= quaternion.w; 195 196 return this; 197 } 198 199 Quaternion opMulAssign(float a) 200 { 201 x *= a; 202 y *= a; 203 z *= a; 204 w *= a; 205 206 return this; 207 } 208 209 Quaternion opDivAssign(float a) 210 { 211 x /= a; 212 y /= a; 213 z /= a; 214 w /= a; 215 216 return this; 217 } 218 219 220 // arithmetic overloading 221 Quaternion opAdd(Quaternion quaternion) const 222 { 223 Quaternion result; 224 result.x = x + quaternion.x; 225 result.y = y + quaternion.y; 226 result.z = z + quaternion.z; 227 result.w = w + quaternion.w; 228 229 return result; 230 } 231 232 Quaternion opSub(Quaternion quaternion) const 233 { 234 Quaternion result; 235 result.x = x - quaternion.x; 236 result.y = y - quaternion.y; 237 result.z = z - quaternion.z; 238 result.w = w - quaternion.w; 239 240 return result; 241 } 242 243 Quaternion opUnary(string s)() const if (s == "-") 244 { 245 Quaternion result; 246 result.x = -x; 247 result.y = -y; 248 result.z = -z; 249 result.w = -w; 250 251 return result; 252 } 253 254 Quaternion opMul(float a) const 255 { 256 Quaternion result; 257 result.x = x * a; 258 result.y = y * a; 259 result.z = z * a; 260 result.w = w * a; 261 262 return result; 263 } 264 265 Vector3 opMul(Vector3 vector) const 266 { 267 // Solution from Laurent Couvidou https://gamedev.stackexchange.com/users/14808/laurent-couvidou 268 // https://gamedev.stackexchange.com/questions/28395/rotating-vector3-by-a-quaternion 269 270 Vector3 xyz = this.xyz; 271 272 return 2 * xyz.dot(vector) * xyz + 273 (w * w - xyz.dot(xyz)) * vector + 274 2 * w * xyz.cross(vector); 275 } 276 277 Quaternion opMul(Quaternion quaternion) const 278 { 279 Vector3 crossProduct = xyz.cross(quaternion.xyz); 280 float dotProduct = xyz.dot(quaternion.xyz); 281 282 Vector3 axis = xyz * quaternion.w + quaternion.xyz * w + crossProduct; 283 284 Quaternion result; 285 result.x = axis.x; 286 result.y = axis.y; 287 result.z = axis.z; 288 result.w = (w * quaternion.w) - dotProduct; 289 290 return result; 291 } 292 293 Quaternion opDiv(float a) const 294 { 295 Quaternion result; 296 result.x = x / a; 297 result.y = y / a; 298 result.z = z / a; 299 result.w = w / a; 300 301 return result; 302 } 303 }