forked from LeenkxTeam/LNXSDK
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;
|
||
|
}
|
||
|
}
|