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;
 | 
						|
	}
 | 
						|
}
 |