Pixel-Composer/scripts/BBMOD_DualQuaternion/BBMOD_DualQuaternion.gml
2023-04-23 16:47:33 +02:00

518 lines
16 KiB
Plaintext

/// @func BBMOD_DualQuaternion([_x, _y, _z, _w, _dx, _dy, _dz, _dw])
///
/// @desc A dual quaternion.
///
/// @param {Real} [_x] The first component of the real part of the dual
/// quaternion. Defaults to 0.
/// @param {Real} [_y] The second component of the real part of the dual
/// quaternion. Defaults to 0.
/// @param {Real} [_z] The third component of the real part of the dual
/// quaternion. Defaults to 0.
/// @param {Real} [_w] The fourth component of the real part of the dual
/// quaternion. Defaults to 1.
/// @param {Real} [_dx] The first component of the dual part of the dual
/// quaternion. Defaults to 0.
/// @param {Real} [_dy] The second component of the dual part of the dual
/// quaternion. Defaults to 0.
/// @param {Real} [_dz] The third component of the dual part of the dual
/// quaternion. Defaults to 0.
/// @param {Real} [_dw] The fourth component of the dual part of the dual
/// quaternion. Defaults to 0.
///
/// @note If you leave all the arguments to their default values, an identity
/// dual quaternion is created.
function BBMOD_DualQuaternion(
_x=0.0, _y=0.0, _z=0.0, _w=1.0,
_dx=0.0, _dy=0.0, _dz=0.0, _dw=0.0) constructor
{
/// @var {Struct.BBMOD_Quaternion} The real part of the dual quaternion.
Real = new BBMOD_Quaternion(_x, _y, _z, _w);
/// @var {Struct.BBMOD_Quaternion} The dual part of the dual quaternion.
Dual = new BBMOD_Quaternion(_dx, _dy, _dz, _dw);
/// @func Add(_dq)
///
/// @desc Adds dual quaternions and returns the result as a new dual
/// quaternion.
///
/// @param {Struct.BBMOD_DualQuaternion} _dq The other dual quaternion.
///
/// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion.
static Add = function (_dq) {
gml_pragma("forceinline");
return new BBMOD_DualQuaternion()
.FromRealDual(
Real.Add(_dq.Real),
Dual.Add(_dq.Dual));
};
/// @func Clone()
///
/// @desc Creates a clone of the dual quaternion.
///
/// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion.
static Clone = function () {
gml_pragma("forceinline");
var _dq = new BBMOD_DualQuaternion();
_dq.Real = Real;
_dq.Dual = Dual;
return _dq;
};
/// @func Conjugate()
///
/// @desc Conjugates the dual quaternion and returns the result as a new
/// dual quaternion.
///
/// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion.
static Conjugate = function () {
gml_pragma("forceinline");
return new BBMOD_DualQuaternion()
.FromRealDual(
Real.Conjugate(),
Dual.Conjugate());
};
/// @func Copy(_dq)
///
/// @desc Copies components of the dual quaternion into other dual
/// quaternion.
///
/// @param {Struct.BBMOD_DualQuaternion} _dest The destination dual
/// quaternion.
///
/// @return {Struct.BBMOD_DualQuaternion} Returns `self`.
static Copy = function (_dest) {
gml_pragma("forceinline");
_dest.Real = Real;
_dest.Dual = Dual;
return self;
};
/// @func Dot(_dq)
///
/// @desc Computes a dot product of two dual quaternions.
///
/// @param {Struct.BBMOD_DualQuaternion} _dq The other dual quaternion.
///
/// @return {Real} The dot product of the dual quaternions.
static Dot = function (_dq) {
gml_pragma("forceinline");
return Real.Dot(_dq.Real);
};
/// @func Exp()
///
/// @desc Computes an exponential map of the dual quaternion and returns
/// the result as a new dual quaternion.
///
/// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion.
static Exp = function () {
gml_pragma("forceinline");
var _real = Real.Exp();
return new BBMOD_DualQuaternion()
.FromRealDual(
_real,
_real.Mul(Dual));
};
/// @func FromArray(_array[, _index])
///
/// @desc Loads dual quaternion components `(rX, rY, rZ, rW, dX, dY, dZ, dW)`
/// from an array.
///
/// @param {Array<Real>} _array The array to read the dual quaternion
/// components from.
/// @param {Real} [_index] The index to start reading the dual quaternion
/// components from. Defaults to 0.
///
/// @return {Struct.BBMOD_DualQuaternion} Returns `self`.
static FromArray = function (_array, _index=0) {
gml_pragma("forceinline");
Real = Real.FromArray(_array, _index);
Dual = Dual.FromArray(_array, _index + 4);
return self;
};
/// @func FromBuffer(_buffer, _type)
///
/// @desc Loads dual quaternion components `(rX, rY, rZ, rW, dX, dY, dZ, dW)`
/// from a buffer.
///
/// @param {Id.Buffer} _buffer The buffer to read the dual quaternion
/// components from.
///
/// @param {Constant.BufferDataType} [_type] The type of each component.
///
/// @return {Struct.BBMOD_DualQuaternion} Returns `self`.
static FromBuffer = function (_buffer, _type) {
gml_pragma("forceinline");
Real = Real.FromBuffer(_buffer, _type);
Dual = Dual.FromBuffer(_buffer, _type);
return self;
};
/// @func FromRealDual(_real, _dual)
///
/// @desc Initializes the dual quaternion using real and dual part.
///
/// @param {Struct.BBMOD_Quaternion} _real The real part of the dual
/// quaternion.
/// @param {Struct.BBMOD_Quaternion} _dual The dual part of the dual
/// quaternion.
///
/// @return {Struct.BBMOD_DualQuaternion} Returns `self`.
static FromRealDual = function (_real, _dual) {
gml_pragma("forceinline");
Real = _real.Normalize();
Dual = _dual;
return self;
};
/// @func FromTranslationRotation(_t, _r)
///
/// @desc Initializes the dual quaternion from translation and rotation
/// (quaternion).
///
/// @param {Struct.BBMOD_Vec3} _t The translation.
///
/// @param {Struct.BBMOD_Quaternion} _r The rotation.
///
/// @return {Struct.BBMOD_DualQuaternion} Returns `self`.
static FromTranslationRotation = function (_t, _r) {
gml_pragma("forceinline");
Real = _r.Normalize();
//Dual = new BBMOD_Quaternion(_t.X, _t.Y, _t.Z, 0).Mul(Real).Scale(0.5);
var _realX = Real.X;
var _realY = Real.Y;
var _realZ = Real.Z;
var _realW = Real.W;
var _tX = _t.X;
var _tY = _t.Y;
var _tZ = _t.Z;
// var _tW = 0;
Dual.X = (_tY * _realZ - _tZ * _realY
/*+ _tW * _realX*/ + _tX * _realW) * 0.5;
Dual.Y = (_tZ * _realX - _tX * _realZ
/*+ _tW * _realY*/ + _tY * _realW) * 0.5;
Dual.Z = (_tX * _realY - _tY * _realX
/*+ _tW * _realZ*/ + _tZ * _realW) * 0.5;
Dual.W = (/*_tW * _realW*/ - _tX * _realX
- _tY * _realY - _tZ * _realZ) * 0.5;
return self;
};
/// @func GetRotation()
///
/// @desc Extracts rotation (quaternion) from dual quaternion.
///
/// @return {Struct.BBMOD_Quaternion} The created quaternion.
static GetRotation = function () {
gml_pragma("forceinline");
return Real.Clone();
};
/// @func GetTranslation()
///
/// @desc Extracts translation (vec3) from dual quaternion.
///
/// @return {Struct.BBMOD_Vec3} The created vector.
static GetTranslation = function () {
gml_pragma("forceinline");
// Dual.Scale(2.0)
var _q10 = Dual.X * 2.0;
var _q11 = Dual.Y * 2.0;
var _q12 = Dual.Z * 2.0;
var _q13 = Dual.W * 2.0;
// Real.Conjugate()
var _q20 = -Real.X;
var _q21 = -Real.Y;
var _q22 = -Real.Z;
var _q23 = Real.W;
//return Dual.Scale(2.0).Mul(Real.Conjugate());
return new BBMOD_Vec3(
_q13 * _q20 + _q10 * _q23 + _q11 * _q22 - _q12 * _q21,
_q13 * _q21 + _q11 * _q23 + _q12 * _q20 - _q10 * _q22,
_q13 * _q22 + _q12 * _q23 + _q10 * _q21 - _q11 * _q20
);
};
/// @func Log()
///
/// @desc Computes the logarithm map of the dual quaternion and returns the
/// result as a new dual quaternion.
///
/// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion.
static Log = function () {
gml_pragma("forceinline");
var _scale = 1.0 / Real.Length();
return new BBMOD_DualQuaternion()
.FromRealDual(
Real.Log(),
Real.Conjugate().Mul(Dual).Scale(_scale * _scale));
};
/// @func Mul(_dq)
///
/// @desc Multiplies two dual quaternions and returns the result as a new
/// dual quaternion.
///
/// @param {Struct.BBMOD_DualQuaternion} _dq The other dual quaternion.
///
/// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion.
static Mul = function (_dq) {
gml_pragma("forceinline");
var _dq1r0 = Real.X;
var _dq1r1 = Real.Y;
var _dq1r2 = Real.Z;
var _dq1r3 = Real.W;
var _dq1d0 = Dual.X;
var _dq1d1 = Dual.Y;
var _dq1d2 = Dual.Z;
var _dq1d3 = Dual.W;
var _dq2r0 = _dq.Real.X;
var _dq2r1 = _dq.Real.Y;
var _dq2r2 = _dq.Real.Z;
var _dq2r3 = _dq.Real.W;
var _dq2d0 = _dq.Dual.X;
var _dq2d1 = _dq.Dual.Y;
var _dq2d2 = _dq.Dual.Z;
var _dq2d3 = _dq.Dual.W;
var _res = new BBMOD_DualQuaternion();
_res.Real.X = (_dq2r3 * _dq1r0 + _dq2r0 * _dq1r3 + _dq2r1 * _dq1r2 - _dq2r2 * _dq1r1);
_res.Real.Y = (_dq2r3 * _dq1r1 + _dq2r1 * _dq1r3 + _dq2r2 * _dq1r0 - _dq2r0 * _dq1r2);
_res.Real.Z = (_dq2r3 * _dq1r2 + _dq2r2 * _dq1r3 + _dq2r0 * _dq1r1 - _dq2r1 * _dq1r0);
_res.Real.W = (_dq2r3 * _dq1r3 - _dq2r0 * _dq1r0 - _dq2r1 * _dq1r1 - _dq2r2 * _dq1r2);
_res.Dual.X = (_dq2d3 * _dq1r0 + _dq2d0 * _dq1r3 + _dq2d1 * _dq1r2 - _dq2d2 * _dq1r1)
+ (_dq2r3 * _dq1d0 + _dq2r0 * _dq1d3 + _dq2r1 * _dq1d2 - _dq2r2 * _dq1d1);
_res.Dual.Y = (_dq2d3 * _dq1r1 + _dq2d1 * _dq1r3 + _dq2d2 * _dq1r0 - _dq2d0 * _dq1r2)
+ (_dq2r3 * _dq1d1 + _dq2r1 * _dq1d3 + _dq2r2 * _dq1d0 - _dq2r0 * _dq1d2);
_res.Dual.Z = (_dq2d3 * _dq1r2 + _dq2d2 * _dq1r3 + _dq2d0 * _dq1r1 - _dq2d1 * _dq1r0)
+ (_dq2r3 * _dq1d2 + _dq2r2 * _dq1d3 + _dq2r0 * _dq1d1 - _dq2r1 * _dq1d0);
_res.Dual.W = (_dq2d3 * _dq1r3 - _dq2d0 * _dq1r0 - _dq2d1 * _dq1r1 - _dq2d2 * _dq1r2)
+ (_dq2r3 * _dq1d3 - _dq2r0 * _dq1d0 - _dq2r1 * _dq1d1 - _dq2r2 * _dq1d2);
return _res;
};
/// @func Normalize()
///
/// @desc Normalizes the dual quaternion and returns the result as a new
/// dual quaternion.
///
/// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion.
static Normalize = function () {
gml_pragma("forceinline");
var _dq = Clone();
var _mag = Real.Dot(Real);
if (_mag > math_get_epsilon())
{
_dq.Real = _dq.Real.Scale(1.0 / _mag);
_dq.Dual = _dq.Dual.Scale(1.0 / _mag);
}
return _dq;
};
/// @func Pow(_p)
///
/// @desc Computes the power of the dual quaternion raised to a real number
/// and returns the result as a new dual quaternion.
///
/// @param {Real} _p The power value.
///
/// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion.
static Pow = function (_p) {
gml_pragma("forceinline");
return Log().Scale(_p).Exp();
};
/// @func Rotate(_v)
///
/// @desc Rotates a vector using the dual quaternion and returns the result
/// as a new vector.
///
/// @param {Struct.BBMOD_Vec3} _v The vector to rotate.
///
/// @return {Struct.BBMOD_Vec3} The created vector.
static Rotate = function (_v) {
gml_pragma("forceinline");
return Real.Rotate(_v);
};
/// @func Scale(_s)
///
/// @desc Scales each component of the dual quaternion by a real value and
/// returns the result as a new dual quaternion.
///
/// @param {Real} _s The value to scale the dual quaternion by.
///
/// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion.
static Scale = function (_s) {
gml_pragma("forceinline");
var _dq = new BBMOD_DualQuaternion();
_dq.Real = Real.Scale(_s);
_dq.Dual = Dual.Scale(_s);
return _dq;
};
/// @func Sclerp(_dq, _s)
///
/// @desc Computes a screw linear interpolation of two dual quaternions
/// and returns the result as a new dual quaternion.
///
/// @param {Struct.BBMOD_DualQuaternion} _dq The other dual quaternion.
/// @param {Real} _s The interpolation factor.
///
/// @return {Struct.BBMOD_DualQuaternion} The created dual quaternion.
static Sclerp = function (_dq, _s) {
gml_pragma("forceinline");
return _dq.Mul(Conjugate()).Pow(_s).Mul(self).Normalize();
};
/// @func ToArray([_array[, _index]])
///
/// @desc Writes components `(rX, rY, rZ, rW, dX, dY, dZ, dW)` of the dual
/// quaternion into an array.
///
/// @param {Array<Real>} [_array] The destination array. If not defined, a
/// new one is created.
///
/// @param {Real} [_index] The index to start writing to. Defaults to 0.
///
/// @return {Array<Real>} Returns the destination array.
static ToArray = function (_array=undefined, _index=0) {
gml_pragma("forceinline");
_array ??= array_create(8, 0.0);
Real.ToArray(_array, _index);
Dual.ToArray(_array, _index + 4);
return _array;
};
/// @func ToBuffer(_buffer, _type)
///
/// @desc Writes components `(rX, rY, rZ, rW, dX, dY, dZ, dW)` of the dual
/// quaternion into a buffer.
///
/// @param {Id.Buffer} _buffer The destination buffer.
/// @param {Constant.BufferDataType} _type The type of each component.
///
/// @return {Struct.BBMOD_DualQuaternion} Returns `self`.
static ToBuffer = function (_buffer, _type) {
gml_pragma("forceinline");
Real.ToBuffer(_buffer, _type);
Dual.ToBuffer(_buffer, _type);
return self;
};
/// @func ToMatrix([_dest[, _index]])
///
/// @desc Converts dual quaternion into a matrix.
///
/// @param {Array<Real>} [_dest] The destination array. If not specified,
/// a new one is created.
/// @param {Real} [_index] The starting index in the destination array.
/// Defaults to 0.
///
/// @return {Array<Real>} Returns the destination array.
static ToMatrix = function (_dest=undefined, _index=0) {
gml_pragma("forceinline");
_dest ??= array_create(16, 0.0);
// Rotation
Real.ToMatrix(_dest, _index);
_dest[@ _index + 3] = 0.0;
_dest[@ _index + 7] = 0.0;
_dest[@ _index + 11] = 0.0;
// Translation
var _translation = GetTranslation();
_dest[@ _index + 12] = _translation.X;
_dest[@ _index + 13] = _translation.Y;
_dest[@ _index + 14] = _translation.Z;
_dest[@ _index + 15] = 1.0;
return _dest;
};
/// @func Transform(_v)
///
/// @desc Translates and rotates a vector using the dual quaternion
/// and returns the result as a new vector.
///
/// @param {Struct.BBMOD_Vec3} _v The vector to transform.
///
/// @return {Struct.BBMOD_Vec3} The created vector.
static Transform = function (_v) {
gml_pragma("forceinline");
return GetTranslation().Add(Real.Rotate(_v));
};
}
/// @func __bbmod_dual_quaternion_array_multiply(_dq1, _dq1Index, _dq2, _dq2Index, _dest, _destIndex)
///
/// @desc Multiplies two dual quaternions stored in arrays and writes the result
/// into the destination array.
///
/// @param {Array<Real>} _dq1 An array containing the first dual quaternion.
/// @param {Real} _dq1Index The starting index of the first dual quaternion.
/// @param {Array<Real>} _dq2 An array containing the second dual quaternion.
/// @param {Real} _dq2Index The starting index of the second dual quaternion.
/// @param {Array<Real>} _dest The destination array.
/// @param {Real} _destIndex The index to start writing to within the
/// destination array.
///
/// @note The arguments can overlap, as the input values are stored into local
/// variables before the multiplication.
///
/// @private
function __bbmod_dual_quaternion_array_multiply(
_dq1, _dq1Index, _dq2, _dq2Index, _dest, _destIndex)
{
gml_pragma("forceinline");
var _dq1r0 = _dq1[_dq1Index + 0];
var _dq1r1 = _dq1[_dq1Index + 1];
var _dq1r2 = _dq1[_dq1Index + 2];
var _dq1r3 = _dq1[_dq1Index + 3];
var _dq1d0 = _dq1[_dq1Index + 4];
var _dq1d1 = _dq1[_dq1Index + 5];
var _dq1d2 = _dq1[_dq1Index + 6];
var _dq1d3 = _dq1[_dq1Index + 7];
var _dq2r0 = _dq2[_dq2Index + 0];
var _dq2r1 = _dq2[_dq2Index + 1];
var _dq2r2 = _dq2[_dq2Index + 2];
var _dq2r3 = _dq2[_dq2Index + 3];
var _dq2d0 = _dq2[_dq2Index + 4];
var _dq2d1 = _dq2[_dq2Index + 5];
var _dq2d2 = _dq2[_dq2Index + 6];
var _dq2d3 = _dq2[_dq2Index + 7];
_dest[@ _destIndex + 0] = (_dq2r3 * _dq1r0 + _dq2r0 * _dq1r3 + _dq2r1 * _dq1r2 - _dq2r2 * _dq1r1);
_dest[@ _destIndex + 1] = (_dq2r3 * _dq1r1 + _dq2r1 * _dq1r3 + _dq2r2 * _dq1r0 - _dq2r0 * _dq1r2);
_dest[@ _destIndex + 2] = (_dq2r3 * _dq1r2 + _dq2r2 * _dq1r3 + _dq2r0 * _dq1r1 - _dq2r1 * _dq1r0);
_dest[@ _destIndex + 3] = (_dq2r3 * _dq1r3 - _dq2r0 * _dq1r0 - _dq2r1 * _dq1r1 - _dq2r2 * _dq1r2);
_dest[@ _destIndex + 4] = (_dq2d3 * _dq1r0 + _dq2d0 * _dq1r3 + _dq2d1 * _dq1r2 - _dq2d2 * _dq1r1)
+ (_dq2r3 * _dq1d0 + _dq2r0 * _dq1d3 + _dq2r1 * _dq1d2 - _dq2r2 * _dq1d1);
_dest[@ _destIndex + 5] = (_dq2d3 * _dq1r1 + _dq2d1 * _dq1r3 + _dq2d2 * _dq1r0 - _dq2d0 * _dq1r2)
+ (_dq2r3 * _dq1d1 + _dq2r1 * _dq1d3 + _dq2r2 * _dq1d0 - _dq2r0 * _dq1d2);
_dest[@ _destIndex + 6] = (_dq2d3 * _dq1r2 + _dq2d2 * _dq1r3 + _dq2d0 * _dq1r1 - _dq2d1 * _dq1r0)
+ (_dq2r3 * _dq1d2 + _dq2r2 * _dq1d3 + _dq2r0 * _dq1d1 - _dq2r1 * _dq1d0);
_dest[@ _destIndex + 7] = (_dq2d3 * _dq1r3 - _dq2d0 * _dq1r0 - _dq2d1 * _dq1r1 - _dq2d2 * _dq1r2)
+ (_dq2r3 * _dq1d3 - _dq2r0 * _dq1d0 - _dq2r1 * _dq1d1 - _dq2r2 * _dq1d2);
}