// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. // ----------------------------------------------------------------------------- // Original code from SharpDX project. https://github.com/sharpdx/SharpDX/ // Greetings to Alexandre Mutel. Original code published with the following license: // ----------------------------------------------------------------------------- // Copyright (c) 2010-2014 SharpDX - Alexandre Mutel // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // ----------------------------------------------------------------------------- // Original code from SlimMath project. http://code.google.com/p/slimmath/ // Greetings to SlimDX Group. Original code published with the following license: // ----------------------------------------------------------------------------- /* * Copyright (c) 2007-2011 SlimDX Group * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ using System; using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace FlaxEngine { [Serializable] #if FLAX_EDITOR [System.ComponentModel.TypeConverter(typeof(TypeConverters.QuaternionConverter))] #endif partial struct Quaternion : IEquatable, IFormattable { private static readonly string _formatString = "X:{0:F2} Y:{1:F2} Z:{2:F2} W:{3:F2}"; /// /// The size of the type, in bytes. /// public static readonly int SizeInBytes = Marshal.SizeOf(typeof(Quaternion)); /// /// A with all of its components set to zero. /// public static readonly Quaternion Zero; /// /// A with all of its components set to one. /// public static readonly Quaternion One = new Quaternion(1.0f, 1.0f, 1.0f, 1.0f); /// /// The identity (0, 0, 0, 1). /// public static readonly Quaternion Identity = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f); /// /// Initializes a new instance of the struct. /// /// A vector containing the values with which to initialize the components. public Quaternion(Float4 value) { X = value.X; Y = value.Y; Z = value.Z; W = value.W; } /// /// Initializes a new instance of the struct. /// /// A vector containing the values with which to initialize the components. public Quaternion(Vector4 value) { X = (float)value.X; Y = (float)value.Y; Z = (float)value.Z; W = (float)value.W; } /// /// Initializes a new instance of the struct. /// /// A vector containing the values with which to initialize the X, Y, and Z components. /// Initial value for the W component of the quaternion. public Quaternion(Float3 value, float w) { X = value.X; Y = value.Y; Z = value.Z; W = w; } /// /// Initializes a new instance of the struct. /// /// Initial value for the X component of the quaternion. /// Initial value for the Y component of the quaternion. /// Initial value for the Z component of the quaternion. /// Initial value for the W component of the quaternion. public Quaternion(float x, float y, float z, float w) { X = x; Y = y; Z = z; W = w; } /// /// Initializes a new instance of the struct. /// /// The values to assign to the X, Y, Z, and W components of the quaternion. This must be an array with four elements. /// Thrown when is null. /// Thrown when contains more or less than four elements. public Quaternion(float[] values) { if (values == null) throw new ArgumentNullException(nameof(values)); if (values.Length != 4) throw new ArgumentOutOfRangeException(nameof(values), "There must be four and only four input values for Quaternion."); X = values[0]; Y = values[1]; Z = values[2]; W = values[3]; } /// /// Gets a value indicating whether this instance is equivalent to the identity quaternion. /// public bool IsIdentity => Equals(Identity); /// /// Gets a value indicting whether this instance is normalized. /// public bool IsNormalized => Mathf.Abs((X * X + Y * Y + Z * Z + W * W) - 1.0f) < 1e-4f; /// /// Gets the euler angle (pitch, yaw, roll) in degrees. /// public Float3 EulerAngles { get { Float3 result; float sqw = W * W; float sqx = X * X; float sqy = Y * Y; float sqz = Z * Z; float unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor float test = X * W - Y * Z; if (test > 0.499995f * unit) { // singularity at north pole // yaw pitch roll result.Y = 2.0f * Mathf.Atan2(Y, X); result.X = Mathf.PiOverTwo; result.Z = 0; } else if (test < -0.499995f * unit) { // singularity at south pole // yaw pitch roll result.Y = -2.0f * Mathf.Atan2(Y, X); result.X = -Mathf.PiOverTwo; result.Z = 0; } else { // yaw pitch roll var q = new Quaternion(W, Z, X, Y); result.Y = Mathf.Atan2(2.0f * q.X * q.W + 2.0f * q.Y * q.Z, 1 - 2.0f * (q.Z * q.Z + q.W * q.W)); result.X = Mathf.Asin(2.0f * (q.X * q.Z - q.W * q.Y)); result.Z = Mathf.Atan2(2.0f * q.X * q.Y + 2.0f * q.Z * q.W, 1 - 2.0f * (q.Y * q.Y + q.Z * q.Z)); } result *= Mathf.RadiansToDegrees; return new Float3(Mathf.UnwindDegrees(result.X), Mathf.UnwindDegrees(result.Y), Mathf.UnwindDegrees(result.Z)); } } /// /// Gets the angle of the quaternion. /// public float Angle { get { float length = X * X + Y * Y + Z * Z; if (Mathf.IsZero(length)) return 0.0f; return (float)(2.0 * Math.Acos(Mathf.Clamp(W, -1f, 1f))); } } /// /// Gets the axis components of the quaternion. /// public Float3 Axis { get { float length = X * X + Y * Y + Z * Z; if (Mathf.IsZero(length)) return Float3.UnitX; float inv = 1.0f / (float)Math.Sqrt(length); return new Float3(X * inv, Y * inv, Z * inv); } } /// /// Gets or sets the component at the specified index. /// /// The value of the X, Y, Z, or W component, depending on the index. /// The index of the component to access. Use 0 for the X component, 1 for the Y component, 2 for the Z component, and 3 for the W component. /// The value of the component at the specified index. /// Thrown when the is out of the range [0, 3]. public float this[int index] { get { switch (index) { case 0: return X; case 1: return Y; case 2: return Z; case 3: return W; } throw new ArgumentOutOfRangeException(nameof(index), "Indices for Quaternion run from 0 to 3, inclusive."); } set { switch (index) { case 0: X = value; break; case 1: Y = value; break; case 2: Z = value; break; case 3: W = value; break; default: throw new ArgumentOutOfRangeException(nameof(index), "Indices for Quaternion run from 0 to 3, inclusive."); } } } /// /// Conjugates the quaternion. /// public void Conjugate() { X = -X; Y = -Y; Z = -Z; } /// /// Gets the conjugated quaternion. /// public Quaternion Conjugated() { return new Quaternion(-X, -Y, -Z, W); } /// /// Conjugates and renormalizes the quaternion. /// public void Invert() { float lengthSq = LengthSquared; if (!Mathf.IsZero(lengthSq)) { lengthSq = 1.0f / lengthSq; X = -X * lengthSq; Y = -Y * lengthSq; Z = -Z * lengthSq; W *= lengthSq; } } /// /// Calculates the length of the quaternion. /// /// The length of the quaternion. /// may be preferred when only the relative length is needed and speed is of the essence. public float Length => (float)Math.Sqrt(X * X + Y * Y + Z * Z + W * W); /// /// Calculates the squared length of the quaternion. /// /// The squared length of the quaternion. /// This method may be preferred to when only a relative length is needed and speed is of the essence. public float LengthSquared => X * X + Y * Y + Z * Z + W * W; /// /// Converts the quaternion into a unit quaternion. /// public void Normalize() { float length = Length; if (length >= Mathf.Epsilon) { float inverse = 1.0f / length; X *= inverse; Y *= inverse; Z *= inverse; W *= inverse; } } /// /// Creates an array containing the elements of the quaternion. /// /// A four-element array containing the components of the quaternion. public float[] ToArray() { return new[] { X, Y, Z, W }; } /// /// Adds two quaternions. /// /// The first quaternion to add. /// The second quaternion to add. /// When the method completes, contains the sum of the two quaternions. public static void Add(ref Quaternion left, ref Quaternion right, out Quaternion result) { result.X = left.X + right.X; result.Y = left.Y + right.Y; result.Z = left.Z + right.Z; result.W = left.W + right.W; } /// /// Adds two quaternions. /// /// The first quaternion to add. /// The second quaternion to add. /// The sum of the two quaternions. public static Quaternion Add(Quaternion left, Quaternion right) { Add(ref left, ref right, out var result); return result; } /// /// Subtracts two quaternions. /// /// The first quaternion to subtract. /// The second quaternion to subtract. /// When the method completes, contains the difference of the two quaternions. public static void Subtract(ref Quaternion left, ref Quaternion right, out Quaternion result) { result.X = left.X - right.X; result.Y = left.Y - right.Y; result.Z = left.Z - right.Z; result.W = left.W - right.W; } /// /// Subtracts two quaternions. /// /// The first quaternion to subtract. /// The second quaternion to subtract. /// The difference of the two quaternions. public static Quaternion Subtract(Quaternion left, Quaternion right) { Subtract(ref left, ref right, out var result); return result; } /// /// Scales a quaternion by the given value. /// /// The quaternion to scale. /// The amount by which to scale the quaternion. /// When the method completes, contains the scaled quaternion. public static void Multiply(ref Quaternion value, float scale, out Quaternion result) { result.X = value.X * scale; result.Y = value.Y * scale; result.Z = value.Z * scale; result.W = value.W * scale; } /// /// Scales a quaternion by the given value. /// /// The quaternion to scale. /// The amount by which to scale the quaternion. /// The scaled quaternion. public static Quaternion Multiply(Quaternion value, float scale) { Multiply(ref value, scale, out var result); return result; } /// /// Multiplies a quaternion by another. /// /// The first quaternion to multiply. /// The second quaternion to multiply. /// When the method completes, contains the multiplied quaternion. public static void Multiply(ref Quaternion left, ref Quaternion right, out Quaternion result) { float a = left.Y * right.Z - left.Z * right.Y; float b = left.Z * right.X - left.X * right.Z; float c = left.X * right.Y - left.Y * right.X; float d = left.X * right.X + left.Y * right.Y + left.Z * right.Z; result.X = left.X * right.W + right.X * left.W + a; result.Y = left.Y * right.W + right.Y * left.W + b; result.Z = left.Z * right.W + right.Z * left.W + c; result.W = left.W * right.W - d; } /// /// Multiplies a quaternion by another. /// /// The first quaternion to multiply. /// The second quaternion to multiply. /// The multiplied quaternion. public static Quaternion Multiply(Quaternion left, Quaternion right) { Multiply(ref left, ref right, out var result); return result; } /// /// Reverses the direction of a given quaternion. /// /// The quaternion to negate. /// When the method completes, contains a quaternion facing in the opposite direction. public static void Negate(ref Quaternion value, out Quaternion result) { result.X = -value.X; result.Y = -value.Y; result.Z = -value.Z; result.W = -value.W; } /// /// Reverses the direction of a given quaternion. /// /// The quaternion to negate. /// A quaternion facing in the opposite direction. public static Quaternion Negate(Quaternion value) { Negate(ref value, out var result); return result; } /// /// Returns a containing the 4D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 2D triangle. /// /// A containing the 4D Cartesian coordinates of vertex 1 of the triangle. /// A containing the 4D Cartesian coordinates of vertex 2 of the triangle. /// A containing the 4D Cartesian coordinates of vertex 3 of the triangle. /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). /// When the method completes, contains a new containing the 4D Cartesian coordinates of the specified point. public static void Barycentric(ref Quaternion value1, ref Quaternion value2, ref Quaternion value3, float amount1, float amount2, out Quaternion result) { Slerp(ref value1, ref value2, amount1 + amount2, out var start); Slerp(ref value1, ref value3, amount1 + amount2, out var end); Slerp(ref start, ref end, amount2 / (amount1 + amount2), out result); } /// /// Returns a containing the 4D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 2D triangle. /// /// A containing the 4D Cartesian coordinates of vertex 1 of the triangle. /// A containing the 4D Cartesian coordinates of vertex 2 of the triangle. /// A containing the 4D Cartesian coordinates of vertex 3 of the triangle. /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). /// A new containing the 4D Cartesian coordinates of the specified point. public static Quaternion Barycentric(Quaternion value1, Quaternion value2, Quaternion value3, float amount1, float amount2) { Barycentric(ref value1, ref value2, ref value3, amount1, amount2, out var result); return result; } /// /// Conjugates a quaternion. /// /// The quaternion to conjugate. /// When the method completes, contains the conjugated quaternion. public static void Conjugate(ref Quaternion value, out Quaternion result) { result.X = -value.X; result.Y = -value.Y; result.Z = -value.Z; result.W = value.W; } /// /// Conjugates a quaternion. /// /// The quaternion to conjugate. /// The conjugated quaternion. public static Quaternion Conjugate(Quaternion value) { Conjugate(ref value, out var result); return result; } /// /// Calculates the dot product of two quaternions. /// /// First source quaternion. /// Second source quaternion. /// The dot product of the two quaternions. public static float Dot(ref Quaternion left, ref Quaternion right) { return left.X * right.X + left.Y * right.Y + left.Z * right.Z + left.W * right.W; } /// /// Calculates the dot product of two quaternions. /// /// First source quaternion. /// Second source quaternion. /// The dot product of the two quaternions. public static float Dot(Quaternion left, Quaternion right) { return left.X * right.X + left.Y * right.Y + left.Z * right.Z + left.W * right.W; } /// /// Calculates the angle between two quaternions. /// /// First source quaternion. /// Second source quaternion. /// Returns the angle in degrees between two rotations a and b. public static float AngleBetween(Quaternion a, Quaternion b) { float num = Dot(a, b); return num > 0.9999999f ? 0 : Mathf.Acos(Mathf.Min(Mathf.Abs(num), 1f)) * 2f * 57.29578f; } /// /// Exponentiates a quaternion. /// /// The quaternion to exponentiate. /// When the method completes, contains the exponentiated quaternion. public static void Exponential(ref Quaternion value, out Quaternion result) { var angle = (float)Math.Sqrt(value.X * value.X + value.Y * value.Y + value.Z * value.Z); var sin = (float)Math.Sin(angle); if (!Mathf.IsZero(sin)) { float coeff = sin / angle; result.X = coeff * value.X; result.Y = coeff * value.Y; result.Z = coeff * value.Z; } else { result = value; } result.W = (float)Math.Cos(angle); } /// /// Exponentiates a quaternion. /// /// The quaternion to exponentiate. /// The exponentiated quaternion. public static Quaternion Exponential(Quaternion value) { Exponential(ref value, out var result); return result; } /// /// Conjugates and renormalizes the quaternion. /// /// The quaternion to conjugate and renormalize. /// When the method completes, contains the conjugated and renormalized quaternion. public static void Invert(ref Quaternion value, out Quaternion result) { result = value; result.Invert(); } /// /// Conjugates and renormalizes the quaternion. /// /// The quaternion to conjugate and renormalize. /// The conjugated and renormalized quaternion. public static Quaternion Invert(Quaternion value) { var result = value; result.Invert(); return result; } /// /// Calculates the orientation from the direction vector. /// /// The direction vector (normalized). /// The orientation. public static Quaternion FromDirection(Float3 direction) { Quaternion orientation; if (Float3.Dot(direction, Float3.Up) >= 0.999f) { orientation = RotationAxis(Float3.Left, Mathf.PiOverTwo); } else if (Float3.Dot(direction, Float3.Down) >= 0.999f) { orientation = RotationAxis(Float3.Right, Mathf.PiOverTwo); } else { var right = Float3.Cross(direction, Float3.Up); var up = Float3.Cross(right, direction); orientation = LookRotation(direction, up); } return orientation; } /// /// Performs a linear interpolation between two quaternions. /// /// Start quaternion. /// End quaternion. /// Value between 0 and 1 indicating the weight of . /// When the method completes, contains the linear interpolation of the two quaternions. /// /// This method performs the linear interpolation based on the following formula. /// start + (end - start) * amount /// Passing a value of 0 will cause to be returned; a value of 1 /// will cause to be returned. /// public static void Lerp(ref Quaternion start, ref Quaternion end, float amount, out Quaternion result) { float inverse = 1.0f - amount; if (Dot(start, end) >= 0.0f) { result.X = inverse * start.X + amount * end.X; result.Y = inverse * start.Y + amount * end.Y; result.Z = inverse * start.Z + amount * end.Z; result.W = inverse * start.W + amount * end.W; } else { result.X = inverse * start.X - amount * end.X; result.Y = inverse * start.Y - amount * end.Y; result.Z = inverse * start.Z - amount * end.Z; result.W = inverse * start.W - amount * end.W; } result.Normalize(); } /// /// Performs a linear interpolation between two quaternion. /// /// Start quaternion. /// End quaternion. /// Value between 0 and 1 indicating the weight of . /// The linear interpolation of the two quaternions. /// /// This method performs the linear interpolation based on the following formula. /// start + (end - start) * amount /// Passing a value of 0 will cause to be returned; a value of 1 /// will cause to be returned. /// public static Quaternion Lerp(Quaternion start, Quaternion end, float amount) { Lerp(ref start, ref end, amount, out var result); return result; } /// /// Calculates the natural logarithm of the specified quaternion. /// /// The quaternion whose logarithm will be calculated. /// When the method completes, contains the natural logarithm of the quaternion. public static void Logarithm(ref Quaternion value, out Quaternion result) { if (Math.Abs(value.W) < 1.0) { var angle = (float)Math.Acos(value.W); var sin = (float)Math.Sin(angle); if (!Mathf.IsZero(sin)) { float coeff = angle / sin; result.X = value.X * coeff; result.Y = value.Y * coeff; result.Z = value.Z * coeff; } else { result = value; } } else { result = value; } result.W = 0.0f; } /// /// Calculates the natural logarithm of the specified quaternion. /// /// The quaternion whose logarithm will be calculated. /// The natural logarithm of the quaternion. public static Quaternion Logarithm(Quaternion value) { Logarithm(ref value, out var result); return result; } /// /// Converts the quaternion into a unit quaternion. /// /// The quaternion to normalize. /// When the method completes, contains the normalized quaternion. public static void Normalize(ref Quaternion value, out Quaternion result) { Quaternion temp = value; result = temp; result.Normalize(); } /// /// Converts the quaternion into a unit quaternion. /// /// The quaternion to normalize. /// The normalized quaternion. public static Quaternion Normalize(Quaternion value) { value.Normalize(); return value; } /// /// Creates a quaternion given a rotation and an axis. /// /// The axis of rotation. /// The angle of rotation (in radians). /// When the method completes, contains the newly created quaternion. public static void RotationAxis(ref Float3 axis, float angle, out Quaternion result) { Float3.Normalize(ref axis, out var normalized); float half = angle * 0.5f; var sin = (float)Math.Sin(half); var cos = (float)Math.Cos(half); result.X = normalized.X * sin; result.Y = normalized.Y * sin; result.Z = normalized.Z * sin; result.W = cos; } /// /// Creates a quaternion given a rotation and an axis. /// /// The axis of rotation. /// The angle of rotation (in radians). /// The newly created quaternion. public static Quaternion RotationAxis(Float3 axis, float angle) { RotationAxis(ref axis, angle, out var result); return result; } /// /// Creates a quaternion given a rotation matrix. /// /// The rotation matrix. /// When the method completes, contains the newly created quaternion. public static void RotationMatrix(ref Matrix matrix, out Quaternion result) { float sqrt; float half; float scale = matrix.M11 + matrix.M22 + matrix.M33; if (scale > 0.0f) { sqrt = (float)Math.Sqrt(scale + 1.0f); result.W = sqrt * 0.5f; sqrt = 0.5f / sqrt; result.X = (matrix.M23 - matrix.M32) * sqrt; result.Y = (matrix.M31 - matrix.M13) * sqrt; result.Z = (matrix.M12 - matrix.M21) * sqrt; } else if (matrix.M11 >= matrix.M22 && matrix.M11 >= matrix.M33) { sqrt = (float)Math.Sqrt(1.0f + matrix.M11 - matrix.M22 - matrix.M33); half = 0.5f / sqrt; result.X = 0.5f * sqrt; result.Y = (matrix.M12 + matrix.M21) * half; result.Z = (matrix.M13 + matrix.M31) * half; result.W = (matrix.M23 - matrix.M32) * half; } else if (matrix.M22 > matrix.M33) { sqrt = (float)Math.Sqrt(1.0f + matrix.M22 - matrix.M11 - matrix.M33); half = 0.5f / sqrt; result.X = (matrix.M21 + matrix.M12) * half; result.Y = 0.5f * sqrt; result.Z = (matrix.M32 + matrix.M23) * half; result.W = (matrix.M31 - matrix.M13) * half; } else { sqrt = (float)Math.Sqrt(1.0f + matrix.M33 - matrix.M11 - matrix.M22); half = 0.5f / sqrt; result.X = (matrix.M31 + matrix.M13) * half; result.Y = (matrix.M32 + matrix.M23) * half; result.Z = 0.5f * sqrt; result.W = (matrix.M12 - matrix.M21) * half; } } /// /// Creates a quaternion given a rotation matrix. /// /// The rotation matrix. /// When the method completes, contains the newly created quaternion. public static void RotationMatrix(ref Matrix3x3 matrix, out Quaternion result) { float sqrt; float half; float scale = matrix.M11 + matrix.M22 + matrix.M33; if (scale > 0.0f) { sqrt = (float)Math.Sqrt(scale + 1.0f); result.W = sqrt * 0.5f; sqrt = 0.5f / sqrt; result.X = (matrix.M23 - matrix.M32) * sqrt; result.Y = (matrix.M31 - matrix.M13) * sqrt; result.Z = (matrix.M12 - matrix.M21) * sqrt; } else if (matrix.M11 >= matrix.M22 && matrix.M11 >= matrix.M33) { sqrt = (float)Math.Sqrt(1.0f + matrix.M11 - matrix.M22 - matrix.M33); half = 0.5f / sqrt; result.X = 0.5f * sqrt; result.Y = (matrix.M12 + matrix.M21) * half; result.Z = (matrix.M13 + matrix.M31) * half; result.W = (matrix.M23 - matrix.M32) * half; } else if (matrix.M22 > matrix.M33) { sqrt = (float)Math.Sqrt(1.0f + matrix.M22 - matrix.M11 - matrix.M33); half = 0.5f / sqrt; result.X = (matrix.M21 + matrix.M12) * half; result.Y = 0.5f * sqrt; result.Z = (matrix.M32 + matrix.M23) * half; result.W = (matrix.M31 - matrix.M13) * half; } else { sqrt = (float)Math.Sqrt(1.0f + matrix.M33 - matrix.M11 - matrix.M22); half = 0.5f / sqrt; result.X = (matrix.M31 + matrix.M13) * half; result.Y = (matrix.M32 + matrix.M23) * half; result.Z = 0.5f * sqrt; result.W = (matrix.M12 - matrix.M21) * half; } } /// /// Creates a left-handed, look-at quaternion. /// /// The position of the viewer's eye. /// The camera look-at target. /// The camera's up vector. /// When the method completes, contains the created look-at quaternion. public static void LookAt(ref Float3 eye, ref Float3 target, ref Float3 up, out Quaternion result) { Matrix3x3.LookAt(ref eye, ref target, ref up, out var matrix); RotationMatrix(ref matrix, out result); } /// /// Creates a left-handed, look-at quaternion. /// /// The position of the viewer's eye. /// The camera look-at target. /// The created look-at quaternion. public static Quaternion LookAt(Float3 eye, Float3 target) { return LookAt(eye, target, Float3.Up); } /// /// Creates a left-handed, look-at quaternion. /// /// The position of the viewer's eye. /// The camera look-at target. /// The camera's up vector. /// The created look-at quaternion. public static Quaternion LookAt(Float3 eye, Float3 target, Float3 up) { LookAt(ref eye, ref target, ref up, out var result); return result; } /// /// Creates a left-handed, look-at quaternion. /// /// The camera's forward direction. /// The camera's up vector. /// When the method completes, contains the created look-at quaternion. public static void RotationLookAt(ref Float3 forward, ref Float3 up, out Quaternion result) { var eye = Float3.Zero; LookAt(ref eye, ref forward, ref up, out result); } /// /// Creates a left-handed, look-at quaternion. /// /// The camera's forward direction. /// The created look-at quaternion. public static Quaternion RotationLookAt(Float3 forward) { return RotationLookAt(forward, Float3.Up); } /// /// Creates a left-handed, look-at quaternion. /// /// The camera's forward direction. /// The camera's up vector. /// The created look-at quaternion. public static Quaternion RotationLookAt(Float3 forward, Float3 up) { RotationLookAt(ref forward, ref up, out var result); return result; } /// /// Creates a rotation with the specified forward and upwards directions. /// /// The forward direction. Direction to orient towards. /// The calculated quaternion. public static Quaternion LookRotation(Float3 forward) { return LookRotation(forward, Float3.Up); } /// /// Creates a rotation with the specified forward and upwards directions. /// /// The forward direction. Direction to orient towards. /// Up direction. Constrains y axis orientation to a plane this vector lies on. This rule might be broken if forward and up direction are nearly parallel. /// The calculated quaternion. public static Quaternion LookRotation(Float3 forward, Float3 up) { LookRotation(ref forward, ref up, out var result); return result; } /// /// Creates a rotation with the specified forward and upwards directions. /// /// The forward direction. Direction to orient towards. /// The up direction. Constrains y axis orientation to a plane this vector lies on. This rule might be broken if forward and up direction are nearly parallel. /// The calculated quaternion. public static void LookRotation(ref Float3 forward, ref Float3 up, out Quaternion result) { Float3 forwardNorm = forward; forwardNorm.Normalize(); Float3.Cross(ref up, ref forwardNorm, out var rightNorm); rightNorm.Normalize(); Float3.Cross(ref forwardNorm, ref rightNorm, out var upNorm); float m00 = rightNorm.X; float m01 = rightNorm.Y; float m02 = rightNorm.Z; float m10 = upNorm.X; float m11 = upNorm.Y; float m12 = upNorm.Z; float m20 = forwardNorm.X; float m21 = forwardNorm.Y; float m22 = forwardNorm.Z; float sum = m00 + m11 + m22; if (sum > 0) { float num = Mathf.Sqrt(sum + 1); float invNumHalf = 0.5f / num; result.X = (m12 - m21) * invNumHalf; result.Y = (m20 - m02) * invNumHalf; result.Z = (m01 - m10) * invNumHalf; result.W = num * 0.5f; } else if (m00 >= m11 && m00 >= m22) { float num = Mathf.Sqrt(1 + m00 - m11 - m22); float invNumHalf = 0.5f / num; result.X = 0.5f * num; result.Y = (m01 + m10) * invNumHalf; result.Z = (m02 + m20) * invNumHalf; result.W = (m12 - m21) * invNumHalf; } else if (m11 > m22) { float num = Mathf.Sqrt(1 + m11 - m00 - m22); float invNumHalf = 0.5f / num; result.X = (m10 + m01) * invNumHalf; result.Y = 0.5f * num; result.Z = (m21 + m12) * invNumHalf; result.W = (m20 - m02) * invNumHalf; } else { float num = Mathf.Sqrt(1 + m22 - m00 - m11); float invNumHalf = 0.5f / num; result.X = (m20 + m02) * invNumHalf; result.Y = (m21 + m12) * invNumHalf; result.Z = 0.5f * num; result.W = (m01 - m10) * invNumHalf; } } /// /// Gets the shortest arc quaternion to rotate this vector to the destination vector. /// /// The source vector. /// The destination vector. /// The result. /// The fallback axis. public static void GetRotationFromTo(ref Float3 from, ref Float3 to, out Quaternion result, ref Float3 fallbackAxis) { // Based on Stan Melax's article in Game Programming Gems Float3 v0 = from; Float3 v1 = to; v0.Normalize(); v1.Normalize(); // If dot == 1, vectors are the same float d = Float3.Dot(ref v0, ref v1); if (d >= 1.0f) { result = Identity; return; } if (d < 1e-6f - 1.0f) { if (fallbackAxis != Float3.Zero) { // Rotate 180 degrees about the fallback axis RotationAxis(ref fallbackAxis, Mathf.Pi, out result); } else { // Generate an axis Float3 axis = Float3.Cross(Float3.UnitX, from); if (axis.LengthSquared < Mathf.Epsilon) // Pick another if colinear axis = Float3.Cross(Float3.UnitY, from); axis.Normalize(); RotationAxis(ref axis, Mathf.Pi, out result); } } else { float s = Mathf.Sqrt((1 + d) * 2); float invS = 1 / s; Float3.Cross(ref v0, ref v1, out var c); result.X = c.X * invS; result.Y = c.Y * invS; result.Z = c.Z * invS; result.W = s * 0.5f; result.Normalize(); } } /// /// Gets the shortest arc quaternion to rotate this vector to the destination vector. /// /// The source vector. /// The destination vector. /// The fallback axis. /// The rotation. public static Quaternion GetRotationFromTo(Float3 from, Float3 to, Float3 fallbackAxis) { GetRotationFromTo(ref from, ref to, out var result, ref fallbackAxis); return result; } /// /// Gets the quaternion that will rotate vector from into vector to, around their plan perpendicular axis.The input vectors don't need to be normalized. /// /// The source vector. /// The destination vector. /// The result. public static void FindBetween(ref Float3 from, ref Float3 to, out Quaternion result) { // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final float normFromNormTo = Mathf.Sqrt(from.LengthSquared * to.LengthSquared); if (normFromNormTo < Mathf.Epsilon) { result = Identity; return; } float w = normFromNormTo + Float3.Dot(from, to); if (w < 1.0-6f * normFromNormTo) { result = Mathf.Abs(from.X) > Mathf.Abs(from.Z) ? new Quaternion(-from.Y, from.X, 0.0f, 0.0f) : new Quaternion(0.0f, -from.Z, from.Y, 0.0f); } else { Float3 cross = Float3.Cross(from, to); result = new Quaternion(cross.X, cross.Y, cross.Z, w); } result.Normalize(); } /// /// Gets the quaternion that will rotate vector from into vector to, around their plan perpendicular axis.The input vectors don't need to be normalized. /// /// The source vector. /// The destination vector. /// The rotation. public static Quaternion FindBetween(Float3 from, Float3 to) { FindBetween(ref from, ref to, out var result); return result; } /// /// Creates a left-handed spherical billboard that rotates around a specified object position. /// /// The position of the object around which the billboard will rotate. /// The position of the camera. /// The up vector of the camera. /// The forward vector of the camera. /// When the method completes, contains the created billboard quaternion. public static void Billboard(ref Float3 objectPosition, ref Float3 cameraPosition, ref Float3 cameraUpVector, ref Float3 cameraForwardVector, out Quaternion result) { Matrix3x3.Billboard(ref objectPosition, ref cameraPosition, ref cameraUpVector, ref cameraForwardVector, out var matrix); RotationMatrix(ref matrix, out result); } /// /// Creates a left-handed spherical billboard that rotates around a specified object position. /// /// The position of the object around which the billboard will rotate. /// The position of the camera. /// The up vector of the camera. /// The forward vector of the camera. /// The created billboard quaternion. public static Quaternion Billboard(Float3 objectPosition, Float3 cameraPosition, Float3 cameraUpVector, Float3 cameraForwardVector) { Billboard(ref objectPosition, ref cameraPosition, ref cameraUpVector, ref cameraForwardVector, out var result); return result; } /// /// Creates a quaternion given a rotation matrix. /// /// The rotation matrix. /// The newly created quaternion. public static Quaternion RotationMatrix(Matrix matrix) { RotationMatrix(ref matrix, out var result); return result; } /// /// Creates a quaternion that rotates around the x-axis. /// /// Angle of rotation in radians. /// When the method completes, contains the newly created quaternion. public static void RotationX(float angle, out Quaternion result) { float halfAngle = angle * 0.5f; result = new Quaternion((float)Math.Sin(halfAngle), 0.0f, 0.0f, (float)Math.Cos(halfAngle)); } /// /// Creates a quaternion that rotates around the x-axis. /// /// Angle of rotation in radians. /// The created rotation quaternion. public static Quaternion RotationX(float angle) { RotationX(angle, out var result); return result; } /// /// Creates a quaternion that rotates around the y-axis. /// /// Angle of rotation in radians. /// When the method completes, contains the newly created quaternion. public static void RotationY(float angle, out Quaternion result) { float halfAngle = angle * 0.5f; result = new Quaternion(0.0f, (float)Math.Sin(halfAngle), 0.0f, (float)Math.Cos(halfAngle)); } /// /// Creates a quaternion that rotates around the y-axis. /// /// Angle of rotation in radians. /// The created rotation quaternion. public static Quaternion RotationY(float angle) { RotationY(angle, out var result); return result; } /// /// Creates a quaternion that rotates around the z-axis. /// /// Angle of rotation in radians. /// When the method completes, contains the newly created quaternion. public static void RotationZ(float angle, out Quaternion result) { float halfAngle = angle * 0.5f; result = new Quaternion(0.0f, 0.0f, (float)Math.Sin(halfAngle), (float)Math.Cos(halfAngle)); } /// /// Creates a quaternion that rotates around the z-axis. /// /// Angle of rotation in radians. /// The created rotation quaternion. public static Quaternion RotationZ(float angle) { RotationZ(angle, out var result); return result; } /// /// Creates a quaternion given a pitch, yaw and roll values. Angles are in degrees. /// /// The pitch, yaw and roll angles of rotation. /// When the method completes, contains the newly created quaternion. public static Quaternion Euler(Float3 eulerAngles) { RotationYawPitchRoll(eulerAngles.Y * Mathf.DegreesToRadians, eulerAngles.X * Mathf.DegreesToRadians, eulerAngles.Z * Mathf.DegreesToRadians, out var result); return result; } /// /// Creates a quaternion given a pitch, yaw and roll values. Angles are in degrees. /// /// The pitch, yaw and roll angles of rotation. /// When the method completes, contains the newly created quaternion. public static void Euler(ref Float3 eulerAngles, out Quaternion result) { RotationYawPitchRoll(eulerAngles.Y * Mathf.DegreesToRadians, eulerAngles.X * Mathf.DegreesToRadians, eulerAngles.Z * Mathf.DegreesToRadians, out result); } /// /// Creates a quaternion given a pitch, yaw and roll values. Angles are in degrees. /// /// The pitch of rotation (in degrees). /// The yaw of rotation (in degrees). /// The roll of rotation (in degrees). /// When the method completes, contains the newly created quaternion. public static Quaternion Euler(float x, float y, float z) { RotationYawPitchRoll(y * Mathf.DegreesToRadians, x * Mathf.DegreesToRadians, z * Mathf.DegreesToRadians, out var result); return result; } /// /// Creates a quaternion given a pitch, yaw and roll values. Angles are in degrees. /// /// The pitch of rotation (in degrees). /// The yaw of rotation (in degrees). /// The roll of rotation (in degrees). /// When the method completes, contains the newly created quaternion. public static void Euler(float x, float y, float z, out Quaternion result) { RotationYawPitchRoll(y * Mathf.DegreesToRadians, x * Mathf.DegreesToRadians, z * Mathf.DegreesToRadians, out result); } /// /// Creates a quaternion given a yaw, pitch, and roll value. Angles are in radians. Use to convert degrees to radians. /// /// The yaw of rotation (in radians). /// The pitch of rotation (in radians). /// The roll of rotation (in radians). /// When the method completes, contains the newly created quaternion. public static void RotationYawPitchRoll(float yaw, float pitch, float roll, out Quaternion result) { float halfRoll = roll * 0.5f; float halfPitch = pitch * 0.5f; float halfYaw = yaw * 0.5f; var sinRoll = (float)Math.Sin(halfRoll); var cosRoll = (float)Math.Cos(halfRoll); var sinPitch = (float)Math.Sin(halfPitch); var cosPitch = (float)Math.Cos(halfPitch); var sinYaw = (float)Math.Sin(halfYaw); var cosYaw = (float)Math.Cos(halfYaw); result.X = cosYaw * sinPitch * cosRoll + sinYaw * cosPitch * sinRoll; result.Y = sinYaw * cosPitch * cosRoll - cosYaw * sinPitch * sinRoll; result.Z = cosYaw * cosPitch * sinRoll - sinYaw * sinPitch * cosRoll; result.W = cosYaw * cosPitch * cosRoll + sinYaw * sinPitch * sinRoll; } /// /// Creates a quaternion given a yaw, pitch, and roll value. Angles are in radians. /// /// The yaw of rotation (in radians). /// The pitch of rotation (in radians). /// The roll of rotation (in radians). /// The newly created quaternion (in radians). public static Quaternion RotationYawPitchRoll(float yaw, float pitch, float roll) { RotationYawPitchRoll(yaw, pitch, roll, out var result); return result; } /// /// Interpolates between two quaternions, using spherical linear interpolation. /// /// Start quaternion. /// End quaternion. /// Value between 0 and 1 indicating the weight of . /// When the method completes, contains the spherical linear interpolation of the two quaternions. public static void Slerp(ref Quaternion start, ref Quaternion end, float amount, out Quaternion result) { float opposite; float inverse; float dot = Dot(start, end); if (Math.Abs(dot) > 1.0f - Mathf.Epsilon) { inverse = 1.0f - amount; opposite = amount * Math.Sign(dot); } else { var acos = (float)Math.Acos(Math.Abs(dot)); var invSin = (float)(1.0 / Math.Sin(acos)); inverse = (float)Math.Sin((1.0f - amount) * acos) * invSin; opposite = (float)Math.Sin(amount * acos) * invSin * Math.Sign(dot); } result.X = inverse * start.X + opposite * end.X; result.Y = inverse * start.Y + opposite * end.Y; result.Z = inverse * start.Z + opposite * end.Z; result.W = inverse * start.W + opposite * end.W; } /// /// Interpolates between two quaternions, using spherical linear interpolation. /// /// Start quaternion. /// End quaternion. /// Value between 0 and 1 indicating the weight of . /// The spherical linear interpolation of the two quaternions. public static Quaternion Slerp(Quaternion start, Quaternion end, float amount) { Slerp(ref start, ref end, amount, out var result); return result; } /// /// Interpolates between quaternions, using spherical quadrangle interpolation. /// /// First source quaternion. /// Second source quaternion. /// Third source quaternion. /// Fourth source quaternion. /// Value between 0 and 1 indicating the weight of interpolation. /// When the method completes, contains the spherical quadrangle interpolation of the quaternions. public static void Squad(ref Quaternion value1, ref Quaternion value2, ref Quaternion value3, ref Quaternion value4, float amount, out Quaternion result) { Slerp(ref value1, ref value4, amount, out var start); Slerp(ref value2, ref value3, amount, out var end); Slerp(ref start, ref end, 2.0f * amount * (1.0f - amount), out result); } /// /// Interpolates between quaternions, using spherical quadrangle interpolation. /// /// First source quaternion. /// Second source quaternion. /// Third source quaternion. /// Fourth source quaternion. /// Value between 0 and 1 indicating the weight of interpolation. /// The spherical quadrangle interpolation of the quaternions. public static Quaternion Squad(Quaternion value1, Quaternion value2, Quaternion value3, Quaternion value4, float amount) { Squad(ref value1, ref value2, ref value3, ref value4, amount, out var result); return result; } /// /// Sets up control points for spherical quadrangle interpolation. /// /// First source quaternion. /// Second source quaternion. /// Third source quaternion. /// Fourth source quaternion. /// An array of three quaternions that represent control points for spherical quadrangle interpolation. public static Quaternion[] SquadSetup(Quaternion value1, Quaternion value2, Quaternion value3, Quaternion value4) { Quaternion q0 = (value1 + value2).LengthSquared < (value1 - value2).LengthSquared ? -value1 : value1; Quaternion q2 = (value2 + value3).LengthSquared < (value2 - value3).LengthSquared ? -value3 : value3; Quaternion q3 = (value3 + value4).LengthSquared < (value3 - value4).LengthSquared ? -value4 : value4; Quaternion q1 = value2; Exponential(ref q1, out var q1Exp); Exponential(ref q2, out var q2Exp); var results = new Quaternion[3]; results[0] = q1 * Exponential(-0.25f * (Logarithm(q1Exp * q2) + Logarithm(q1Exp * q0))); results[1] = q2 * Exponential(-0.25f * (Logarithm(q2Exp * q3) + Logarithm(q2Exp * q1))); results[2] = q2; return results; } /// /// Gets rotation from a normal in relation to a transform.
/// This function is especially useful for axis aligned faces, /// and with . /// /// Example code: /// /// GetRotationFromNormalExample :
/// RayOrigin;
/// SomeObject;
///
/// {
/// (.RayCast(RayOrigin.Position, RayOrigin.Transform.Forward, out hit) /// {
/// position = hit.Collider.Position; /// transform = hit.Collider.Transform; /// point = hit.Point; /// normal = hit.Normal; /// rot = .GetRotationFromNormal(normal,transform); /// SomeObject.Position = point; /// SomeObject.Orientation = rot; /// } /// } /// } ///
///
///
/// The normal vector. /// The reference transform. /// The rotation from the normal vector. public static Quaternion GetRotationFromNormal(Vector3 normal, Transform reference) { Float3 up = reference.Up; var dot = Vector3.Dot(normal, up); if (Mathf.NearEqual(Math.Abs(dot), 1)) up = reference.Right; return LookRotation(normal, up); } /// /// Adds two quaternions. /// /// The first quaternion to add. /// The second quaternion to add. /// The sum of the two quaternions. public static Quaternion operator +(Quaternion left, Quaternion right) { Add(ref left, ref right, out var result); return result; } /// /// Subtracts two quaternions. /// /// The first quaternion to subtract. /// The second quaternion to subtract. /// The difference of the two quaternions. public static Quaternion operator -(Quaternion left, Quaternion right) { Subtract(ref left, ref right, out var result); return result; } /// /// Reverses the direction of a given quaternion. /// /// The quaternion to negate. /// A quaternion facing in the opposite direction. public static Quaternion operator -(Quaternion value) { Negate(ref value, out var result); return result; } /// /// Scales a quaternion by the given value. /// /// The quaternion to scale. /// The amount by which to scale the quaternion. /// The scaled quaternion. public static Quaternion operator *(float scale, Quaternion value) { Multiply(ref value, scale, out var result); return result; } /// /// Scales a quaternion by the given value. /// /// The quaternion to scale. /// The amount by which to scale the quaternion. /// The scaled quaternion. public static Quaternion operator *(Quaternion value, float scale) { Multiply(ref value, scale, out var result); return result; } /// /// Multiplies a quaternion by another. /// /// The first quaternion to multiply. /// The second quaternion to multiply. /// The multiplied quaternion. public static Quaternion operator *(Quaternion left, Quaternion right) { Multiply(ref left, ref right, out var result); return result; } /// /// Tests for equality between two objects. /// /// The first value to compare. /// The second value to compare. /// true if has the same value as ; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Quaternion left, Quaternion right) { return Dot(ref left, ref right) > 0.9999999f; } /// /// Tests for inequality between two objects. /// /// The first value to compare. /// The second value to compare. /// true if has a different value than ; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Quaternion left, Quaternion right) { return Dot(ref left, ref right) <= 0.9999999f; } /// /// Returns a that represents this instance. /// /// A that represents this instance. public override string ToString() { return string.Format(CultureInfo.CurrentCulture, _formatString, X, Y, Z, W); } /// /// Returns a that represents this instance. /// /// The format. /// A that represents this instance. public string ToString(string format) { if (format == null) return ToString(); return string.Format(CultureInfo.CurrentCulture, _formatString, X.ToString(format, CultureInfo.CurrentCulture), Y.ToString(format, CultureInfo.CurrentCulture), Z.ToString(format, CultureInfo.CurrentCulture), W.ToString(format, CultureInfo.CurrentCulture)); } /// /// Returns a that represents this instance. /// /// The format provider. /// A that represents this instance. public string ToString(IFormatProvider formatProvider) { return string.Format(formatProvider, _formatString, X, Y, Z, W); } /// /// Returns a that represents this instance. /// /// The format. /// The format provider. /// A that represents this instance. public string ToString(string format, IFormatProvider formatProvider) { if (format == null) return ToString(formatProvider); return string.Format(formatProvider, _formatString, X.ToString(format, formatProvider), Y.ToString(format, formatProvider), Z.ToString(format, formatProvider), W.ToString(format, formatProvider)); } /// /// Returns a hash code for this instance. /// /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. public override int GetHashCode() { unchecked { int hashCode = X.GetHashCode(); hashCode = (hashCode * 397) ^ Y.GetHashCode(); hashCode = (hashCode * 397) ^ Z.GetHashCode(); hashCode = (hashCode * 397) ^ W.GetHashCode(); return hashCode; } } /// /// Tests whether one quaternion is near another quaternion. /// /// The left quaternion. /// The right quaternion. /// The epsilon. /// true if left and right are near another, false otherwise public static bool NearEqual(Quaternion left, Quaternion right, float epsilon = Mathf.Epsilon) { return NearEqual(ref left, ref right, epsilon); } /// /// Tests whether one quaternion is near another quaternion. /// /// The left quaternion. /// The right quaternion. /// The epsilon. /// true if left and right are near another, false otherwise public static bool NearEqual(ref Quaternion left, ref Quaternion right, float epsilon = Mathf.Epsilon) { //return Dot(ref left, ref right) > 1.0f - epsilon; return Mathf.WithinEpsilon(left.X, right.X, epsilon) && Mathf.WithinEpsilon(left.Y, right.Y, epsilon) && Mathf.WithinEpsilon(left.Z, right.Z, epsilon) && Mathf.WithinEpsilon(left.W, right.W, epsilon); } /// /// Determines whether the specified is equal to this instance. /// /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ref Quaternion other) { //return Dot(ref this, ref other) > 0.9999999f; return Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y) && Mathf.NearEqual(other.Z, Z) && Mathf.NearEqual(other.W, W); } /// /// Determines whether the specified is equal to this instance. /// /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Quaternion other) { return Equals(ref other); } /// /// Determines whether the specified is equal to this instance. /// /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object value) { if (!(value is Quaternion)) return false; var strongValue = (Quaternion)value; return Equals(ref strongValue); } } }