256 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			256 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
| 
								 | 
							
								package kha.math;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import kha.math.Vector3;
							 | 
						||
| 
								 | 
							
								import kha.math.Matrix4;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@:structInit
							 | 
						||
| 
								 | 
							
								class Quaternion {
							 | 
						||
| 
								 | 
							
									var values: Array<Float>;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public inline function new(x: Float = 0, y: Float = 0, z: Float = 0, w: Float = 1): Void {
							 | 
						||
| 
								 | 
							
										values = new Array<Float>();
							 | 
						||
| 
								 | 
							
										values.push(x);
							 | 
						||
| 
								 | 
							
										values.push(y);
							 | 
						||
| 
								 | 
							
										values.push(z);
							 | 
						||
| 
								 | 
							
										values.push(w);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Axis has to be normalized
							 | 
						||
| 
								 | 
							
									public inline static function fromAxisAngle(axis: Vector3, radians: Float): Quaternion {
							 | 
						||
| 
								 | 
							
										var q: Quaternion = new Quaternion();
							 | 
						||
| 
								 | 
							
										q.w = Math.cos(radians / 2.0);
							 | 
						||
| 
								 | 
							
										q.x = q.y = q.z = Math.sin(radians / 2.0);
							 | 
						||
| 
								 | 
							
										q.x *= axis.x;
							 | 
						||
| 
								 | 
							
										q.y *= axis.y;
							 | 
						||
| 
								 | 
							
										q.z *= axis.z;
							 | 
						||
| 
								 | 
							
										return q;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function slerp(t: Float, q: Quaternion) {
							 | 
						||
| 
								 | 
							
										var epsilon: Float = 0.0005;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var dot = dot(q);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (dot > 1 - epsilon) {
							 | 
						||
| 
								 | 
							
											var result: Quaternion = q.add((this.sub(q)).scaled(t));
							 | 
						||
| 
								 | 
							
											result.normalize();
							 | 
						||
| 
								 | 
							
											return result;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if (dot < 0)
							 | 
						||
| 
								 | 
							
											dot = 0;
							 | 
						||
| 
								 | 
							
										if (dot > 1)
							 | 
						||
| 
								 | 
							
											dot = 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var theta0: Float = Math.acos(dot);
							 | 
						||
| 
								 | 
							
										var theta: Float = theta0 * t;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var q2: Quaternion = q.sub(scaled(dot));
							 | 
						||
| 
								 | 
							
										q2.normalize();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var result: Quaternion = scaled(Math.cos(theta)).add(q2.scaled(Math.sin(theta)));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										result.normalize();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return result;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// TODO: This should be multiplication
							 | 
						||
| 
								 | 
							
									public inline function rotated(b: Quaternion): Quaternion {
							 | 
						||
| 
								 | 
							
										var q: Quaternion = new Quaternion();
							 | 
						||
| 
								 | 
							
										q.w = w * b.w - x * b.x - y * b.y - z * b.z;
							 | 
						||
| 
								 | 
							
										q.x = w * b.x + x * b.w + y * b.z - z * b.y;
							 | 
						||
| 
								 | 
							
										q.y = w * b.y + y * b.w + z * b.x - x * b.z;
							 | 
						||
| 
								 | 
							
										q.z = w * b.z + z * b.w + x * b.y - y * b.x;
							 | 
						||
| 
								 | 
							
										q.normalize();
							 | 
						||
| 
								 | 
							
										return q;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public inline function scaled(scale: Float): Quaternion {
							 | 
						||
| 
								 | 
							
										return new Quaternion(x * scale, y * scale, z * scale, w * scale);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public inline function scale(scale: Float) {
							 | 
						||
| 
								 | 
							
										x = x * scale;
							 | 
						||
| 
								 | 
							
										y = y * scale;
							 | 
						||
| 
								 | 
							
										z = z * scale;
							 | 
						||
| 
								 | 
							
										w = w * scale;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public inline function matrix(): Matrix4 {
							 | 
						||
| 
								 | 
							
										var s: Float = 2.0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var xs: Float = x * s;
							 | 
						||
| 
								 | 
							
										var ys: Float = y * s;
							 | 
						||
| 
								 | 
							
										var zs: Float = z * s;
							 | 
						||
| 
								 | 
							
										var wx: Float = w * xs;
							 | 
						||
| 
								 | 
							
										var wy: Float = w * ys;
							 | 
						||
| 
								 | 
							
										var wz: Float = w * zs;
							 | 
						||
| 
								 | 
							
										var xx: Float = x * xs;
							 | 
						||
| 
								 | 
							
										var xy: Float = x * ys;
							 | 
						||
| 
								 | 
							
										var xz: Float = x * zs;
							 | 
						||
| 
								 | 
							
										var yy: Float = y * ys;
							 | 
						||
| 
								 | 
							
										var yz: Float = y * zs;
							 | 
						||
| 
								 | 
							
										var zz: Float = z * zs;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return new Matrix4(1 - (yy + zz), xy - wz, xz + wy, 0, xy + wz, 1 - (xx + zz), yz - wx, 0, xz - wy, yz + wx, 1 - (xx + yy), 0, 0, 0, 0, 1);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public inline function get(index: Int): Float {
							 | 
						||
| 
								 | 
							
										return values[index];
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public inline function set(index: Int, value: Float): Void {
							 | 
						||
| 
								 | 
							
										values[index] = value;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public var x(get, set): Float;
							 | 
						||
| 
								 | 
							
									public var y(get, set): Float;
							 | 
						||
| 
								 | 
							
									public var z(get, set): Float;
							 | 
						||
| 
								 | 
							
									public var w(get, set): Float;
							 | 
						||
| 
								 | 
							
									public var length(get, set): Float;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function get_x(): Float {
							 | 
						||
| 
								 | 
							
										return values[0];
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function set_x(value: Float): Float {
							 | 
						||
| 
								 | 
							
										return values[0] = value;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function get_y(): Float {
							 | 
						||
| 
								 | 
							
										return values[1];
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function set_y(value: Float): Float {
							 | 
						||
| 
								 | 
							
										return values[1] = value;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function get_z(): Float {
							 | 
						||
| 
								 | 
							
										return values[2];
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function set_z(value: Float): Float {
							 | 
						||
| 
								 | 
							
										return values[2] = value;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function get_w(): Float {
							 | 
						||
| 
								 | 
							
										return values[3];
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function set_w(value: Float): Float {
							 | 
						||
| 
								 | 
							
										return values[3] = value;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// TODO: Isn't this code wrong? Is wrong in Vector4 for sure! (Missing w in the length)
							 | 
						||
| 
								 | 
							
									function get_length(): Float {
							 | 
						||
| 
								 | 
							
										return Math.sqrt(x * x + y * y + z * z + w * w);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function set_length(length: Float): Float {
							 | 
						||
| 
								 | 
							
										if (get_length() == 0)
							 | 
						||
| 
								 | 
							
											return 0;
							 | 
						||
| 
								 | 
							
										var mul = length / get_length();
							 | 
						||
| 
								 | 
							
										x *= mul;
							 | 
						||
| 
								 | 
							
										y *= mul;
							 | 
						||
| 
								 | 
							
										z *= mul;
							 | 
						||
| 
								 | 
							
										return length;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// For adding a (scaled) axis-angle representation of a quaternion
							 | 
						||
| 
								 | 
							
									public inline function addVector(vec: Vector3): Quaternion {
							 | 
						||
| 
								 | 
							
										var result: Quaternion = new Quaternion(x, y, z, w);
							 | 
						||
| 
								 | 
							
										var q1: Quaternion = new Quaternion(0, vec.x, vec.y, vec.z);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										q1 = q1.mult(result);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										result.x += q1.x * 0.5;
							 | 
						||
| 
								 | 
							
										result.y += q1.y * 0.5;
							 | 
						||
| 
								 | 
							
										result.z += q1.z * 0.5;
							 | 
						||
| 
								 | 
							
										result.w += q1.w * 0.5;
							 | 
						||
| 
								 | 
							
										return result;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public inline function add(q: Quaternion): Quaternion {
							 | 
						||
| 
								 | 
							
										return new Quaternion(x + q.x, y + q.y, z + q.z, w + q.w);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public inline function sub(q: Quaternion): Quaternion {
							 | 
						||
| 
								 | 
							
										return new Quaternion(x - q.x, y - q.y, z - q.z, w - q.w);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// TODO: Check again, but I think the code in Kore is wrong
							 | 
						||
| 
								 | 
							
									public inline function mult(r: Quaternion): Quaternion {
							 | 
						||
| 
								 | 
							
										var q: Quaternion = new Quaternion();
							 | 
						||
| 
								 | 
							
										q.x = w * r.x + x * r.w + y * r.z - z * r.y;
							 | 
						||
| 
								 | 
							
										q.y = w * r.y - x * r.z + y * r.w + z * r.x;
							 | 
						||
| 
								 | 
							
										q.z = w * r.z + x * r.y - y * r.x + z * r.w;
							 | 
						||
| 
								 | 
							
										q.w = w * r.w - x * r.x - y * r.y - z * r.z;
							 | 
						||
| 
								 | 
							
										return q;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public inline function normalize() {
							 | 
						||
| 
								 | 
							
										scale(1.0 / length);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public inline function dot(q: Quaternion) {
							 | 
						||
| 
								 | 
							
										return x * q.x + y * q.y + z * q.z + w * q.w;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// GetEulerAngles extracts Euler angles from the quaternion, in the specified order of
							 | 
						||
| 
								 | 
							
									// axis rotations and the specified coordinate system. Right-handed coordinate system
							 | 
						||
| 
								 | 
							
									// is the default, with CCW rotations while looking in the negative axis direction.
							 | 
						||
| 
								 | 
							
									// Here a,b,c, are the Yaw/Pitch/Roll angles to be returned.
							 | 
						||
| 
								 | 
							
									// rotation a around axis A1
							 | 
						||
| 
								 | 
							
									// is followed by rotation b around axis A2
							 | 
						||
| 
								 | 
							
									// is followed by rotation c around axis A3
							 | 
						||
| 
								 | 
							
									// rotations are CCW or CW (D) in LH or RH coordinate system (S)
							 | 
						||
| 
								 | 
							
									public static inline var AXIS_X: Int = 0;
							 | 
						||
| 
								 | 
							
									public static inline var AXIS_Y: Int = 1;
							 | 
						||
| 
								 | 
							
									public static inline var AXIS_Z: Int = 2;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									public function getEulerAngles(A1: Int, A2: Int, A3: Int, S: Int = 1, D: Int = 1): Vector3 {
							 | 
						||
| 
								 | 
							
										var result: Vector3 = new Vector3();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var Q: Array<Float> = new Array<Float>();
							 | 
						||
| 
								 | 
							
										Q[0] = x;
							 | 
						||
| 
								 | 
							
										Q[1] = y;
							 | 
						||
| 
								 | 
							
										Q[2] = z;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var ww: Float = w * w;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var Q11: Float = Q[A1] * Q[A1];
							 | 
						||
| 
								 | 
							
										var Q22: Float = Q[A2] * Q[A2];
							 | 
						||
| 
								 | 
							
										var Q33: Float = Q[A3] * Q[A3];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var psign: Float = -1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var SingularityRadius: Float = 0.0000001;
							 | 
						||
| 
								 | 
							
										var PiOver2: Float = Math.PI / 2.0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Determine whether even permutation
							 | 
						||
| 
								 | 
							
										if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) {
							 | 
						||
| 
								 | 
							
											psign = 1;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var s2: Float = psign * 2.0 * (psign * w * Q[A2] + Q[A1] * Q[A3]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (s2 < -1 + SingularityRadius) { // South pole singularity
							 | 
						||
| 
								 | 
							
											result.x = 0;
							 | 
						||
| 
								 | 
							
											result.y = -S * D * PiOver2;
							 | 
						||
| 
								 | 
							
											result.z = S * D * Math.atan2(2 * (psign * Q[A1] * Q[A2] + w * Q[A3]), ww + Q22 - Q11 - Q33);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										else if (s2 > 1 - SingularityRadius) { // North pole singularity
							 | 
						||
| 
								 | 
							
											result.x = 0;
							 | 
						||
| 
								 | 
							
											result.y = S * D * PiOver2;
							 | 
						||
| 
								 | 
							
											result.z = S * D * Math.atan2(2 * (psign * Q[A1] * Q[A2] + w * Q[A3]), ww + Q22 - Q11 - Q33);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										else {
							 | 
						||
| 
								 | 
							
											result.x = -S * D * Math.atan2(-2 * (w * Q[A1] - psign * Q[A2] * Q[A3]), ww + Q33 - Q11 - Q22);
							 | 
						||
| 
								 | 
							
											result.y = S * D * Math.asin(s2);
							 | 
						||
| 
								 | 
							
											result.z = S * D * Math.atan2(2 * (w * Q[A3] - psign * Q[A1] * Q[A2]), ww + Q11 - Q22 - Q33);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return result;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 |