You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
261 lines
7.0 KiB
C#
261 lines
7.0 KiB
C#
/******************************************************************************/
|
|
/*
|
|
Project - MudBun
|
|
Publisher - Long Bunny Labs
|
|
http://LongBunnyLabs.com
|
|
Author - Ming-Lun "Allen" Chou
|
|
http://AllenChou.net
|
|
*/
|
|
/******************************************************************************/
|
|
|
|
using UnityEngine;
|
|
|
|
namespace MudBun
|
|
{
|
|
public class QuaternionUtil
|
|
{
|
|
// basic stuff
|
|
// ------------------------------------------------------------------------
|
|
|
|
public static float Magnitude(Quaternion q)
|
|
{
|
|
return Mathf.Sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
|
|
}
|
|
|
|
public static float MagnitudeSqr(Quaternion q)
|
|
{
|
|
return q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w;
|
|
}
|
|
|
|
public static Quaternion Normalize(Quaternion q)
|
|
{
|
|
float magInv = 1.0f / Magnitude(q);
|
|
return new Quaternion(magInv * q.x, magInv * q.y, magInv * q.z, magInv * q.w);
|
|
}
|
|
|
|
public static Quaternion AngularVector(Vector3 v)
|
|
{
|
|
float len = v.magnitude;
|
|
if (len < MathUtil.Epsilon)
|
|
return Quaternion.identity;
|
|
|
|
v /= len;
|
|
|
|
float h = 0.5f * len;
|
|
float s = Mathf.Sin(h);
|
|
float c = Mathf.Cos(h);
|
|
|
|
return new Quaternion(s * v.x, s * v.y, s * v.z, c);
|
|
}
|
|
|
|
// axis must be normalized
|
|
public static Quaternion AxisAngle(Vector3 axis, float angle)
|
|
{
|
|
float h = 0.5f * angle;
|
|
float s = Mathf.Sin(h);
|
|
float c = Mathf.Cos(h);
|
|
|
|
return new Quaternion(s * axis.x, s * axis.y, s * axis.z, c);
|
|
}
|
|
|
|
public static Vector3 GetAxis(Quaternion q)
|
|
{
|
|
Vector3 v = new Vector3(q.x, q.y, q.z);
|
|
float len = v.magnitude;
|
|
if (len < MathUtil.Epsilon)
|
|
return Vector3.left;
|
|
|
|
return v / len;
|
|
}
|
|
|
|
public static float GetAngle(Quaternion q)
|
|
{
|
|
return 2.0f * Mathf.Acos(Mathf.Clamp(q.w, -1.0f, 1.0f));
|
|
}
|
|
|
|
public static Quaternion Pow(Quaternion q, float exp)
|
|
{
|
|
Vector3 axis = GetAxis(q);
|
|
float angle = GetAngle(q);
|
|
return AxisAngle(axis, angle * exp);
|
|
}
|
|
|
|
// v: derivative of q
|
|
public static Quaternion Integrate(Quaternion q, Quaternion v, float dt)
|
|
{
|
|
return Pow(v, dt) * q;
|
|
}
|
|
|
|
// omega: angular velocity (direction is axis, magnitude is angle)
|
|
// https://www.ashwinnarayan.com/post/how-to-integrate-quaternions/
|
|
// https://gafferongames.com/post/physics_in_3d/
|
|
public static Quaternion Integrate(Quaternion q, Vector3 omega, float dt)
|
|
{
|
|
omega *= 0.5f;
|
|
Quaternion p = (new Quaternion(omega.x, omega.y, omega.z, 0.0f)) * q;
|
|
return Normalize(new Quaternion(q.x + p.x * dt, q.y + p.y * dt, q.z + p.z * dt, q.w + p.w * dt));
|
|
}
|
|
|
|
public static Vector4 ToVector4(Quaternion q)
|
|
{
|
|
return new Vector4(q.x, q.y, q.z, q.w);
|
|
}
|
|
|
|
public static Quaternion FromVector4(Vector4 v, bool normalize = true)
|
|
{
|
|
if (normalize)
|
|
{
|
|
float magSqr = v.sqrMagnitude;
|
|
if (magSqr < MathUtil.Epsilon)
|
|
return Quaternion.identity;
|
|
|
|
v /= Mathf.Sqrt(magSqr);
|
|
}
|
|
|
|
return new Quaternion(v.x, v.y, v.z, v.w);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// end: basic stuff
|
|
|
|
|
|
// swing-twist decomposition & interpolation
|
|
// ------------------------------------------------------------------------
|
|
|
|
public static void DecomposeSwingTwist
|
|
(
|
|
Quaternion q,
|
|
Vector3 twistAxis,
|
|
out Quaternion swing,
|
|
out Quaternion twist
|
|
)
|
|
{
|
|
Vector3 r = new Vector3(q.x, q.y, q.z); // (rotaiton axis) * cos(angle / 2)
|
|
|
|
// singularity: rotation by 180 degree
|
|
if (r.sqrMagnitude < MathUtil.Epsilon)
|
|
{
|
|
Vector3 rotatedTwistAxis = q * twistAxis;
|
|
Vector3 swingAxis = Vector3.Cross(twistAxis, rotatedTwistAxis);
|
|
|
|
if (swingAxis.sqrMagnitude > MathUtil.Epsilon)
|
|
{
|
|
float swingAngle = Vector3.Angle(twistAxis, rotatedTwistAxis);
|
|
swing = Quaternion.AngleAxis(swingAngle, swingAxis);
|
|
}
|
|
else
|
|
{
|
|
// more singularity: rotation axis parallel to twist axis
|
|
swing = Quaternion.identity; // no swing
|
|
}
|
|
|
|
// always twist 180 degree on singularity
|
|
twist = Quaternion.AngleAxis(180.0f, twistAxis);
|
|
return;
|
|
}
|
|
|
|
// formula & proof:
|
|
// http://www.euclideanspace.com/maths/geometry/rotations/for/decomposition/
|
|
Vector3 p = Vector3.Project(r, twistAxis);
|
|
twist = new Quaternion(p.x, p.y, p.z, q.w);
|
|
twist = Normalize(twist);
|
|
swing = q * Quaternion.Inverse(twist);
|
|
}
|
|
|
|
public enum SterpMode
|
|
{
|
|
// non-constant angular velocity, faster
|
|
// use if interpolating across small angles or constant angular velocity is not important
|
|
Nlerp,
|
|
|
|
// constant angular velocity, slower
|
|
// use if interpolating across large angles and constant angular velocity is important
|
|
Slerp,
|
|
};
|
|
|
|
// same swing & twist parameters
|
|
public static Quaternion Sterp
|
|
(
|
|
Quaternion a,
|
|
Quaternion b,
|
|
Vector3 twistAxis,
|
|
float t,
|
|
SterpMode mode = SterpMode.Slerp
|
|
)
|
|
{
|
|
Quaternion swing;
|
|
Quaternion twist;
|
|
return Sterp(a, b, twistAxis, t, out swing, out twist, mode);
|
|
}
|
|
|
|
// same swing & twist parameters with individual interpolated swing & twist outputs
|
|
public static Quaternion Sterp
|
|
(
|
|
Quaternion a,
|
|
Quaternion b,
|
|
Vector3 twistAxis,
|
|
float t,
|
|
out Quaternion swing,
|
|
out Quaternion twist,
|
|
SterpMode mode = SterpMode.Slerp
|
|
)
|
|
{
|
|
return Sterp(a, b, twistAxis, t, t, out swing, out twist, mode);
|
|
}
|
|
|
|
// different swing & twist parameters
|
|
public static Quaternion Sterp
|
|
(
|
|
Quaternion a,
|
|
Quaternion b,
|
|
Vector3 twistAxis,
|
|
float tSwing,
|
|
float tTwist,
|
|
SterpMode mode = SterpMode.Slerp
|
|
)
|
|
{
|
|
Quaternion swing;
|
|
Quaternion twist;
|
|
return Sterp(a, b, twistAxis, tSwing, tTwist, out swing, out twist, mode);
|
|
}
|
|
|
|
// master sterp function
|
|
public static Quaternion Sterp
|
|
(
|
|
Quaternion a,
|
|
Quaternion b,
|
|
Vector3 twistAxis,
|
|
float tSwing,
|
|
float tTwist,
|
|
out Quaternion swing,
|
|
out Quaternion twist,
|
|
SterpMode mode
|
|
)
|
|
{
|
|
Quaternion q = b * Quaternion.Inverse(a);
|
|
Quaternion swingFull;
|
|
Quaternion twistFull;
|
|
QuaternionUtil.DecomposeSwingTwist(q, twistAxis, out swingFull, out twistFull);
|
|
|
|
switch (mode)
|
|
{
|
|
default:
|
|
case SterpMode.Nlerp:
|
|
swing = Quaternion.Lerp(Quaternion.identity, swingFull, tSwing);
|
|
twist = Quaternion.Lerp(Quaternion.identity, twistFull, tTwist);
|
|
break;
|
|
case SterpMode.Slerp:
|
|
swing = Quaternion.Slerp(Quaternion.identity, swingFull, tSwing);
|
|
twist = Quaternion.Slerp(Quaternion.identity, twistFull, tTwist);
|
|
break;
|
|
}
|
|
|
|
return twist * swing;
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// end: swing-twist decomposition & interpolation
|
|
}
|
|
}
|
|
|