using UnityEngine; using System.Collections; namespace RootMotion { /// /// Helper methods for dealing with Quaternions. /// public static class QuaTools { /// /// Optimized Quaternion.Lerp /// public static Quaternion Lerp(Quaternion fromRotation, Quaternion toRotation, float weight) { if (weight <= 0f) return fromRotation; if (weight >= 1f) return toRotation; return Quaternion.Lerp(fromRotation, toRotation, weight); } /// /// Optimized Quaternion.Slerp /// public static Quaternion Slerp(Quaternion fromRotation, Quaternion toRotation, float weight) { if (weight <= 0f) return fromRotation; if (weight >= 1f) return toRotation; return Quaternion.Slerp(fromRotation, toRotation, weight); } /// /// Returns the rotation from identity Quaternion to "q", interpolated linearily by "weight". /// public static Quaternion LinearBlend(Quaternion q, float weight) { if (weight <= 0f) return Quaternion.identity; if (weight >= 1f) return q; return Quaternion.Lerp(Quaternion.identity, q, weight); } /// /// Returns the rotation from identity Quaternion to "q", interpolated spherically by "weight". /// public static Quaternion SphericalBlend(Quaternion q, float weight) { if (weight <= 0f) return Quaternion.identity; if (weight >= 1f) return q; return Quaternion.Slerp(Quaternion.identity, q, weight); } /// /// Creates a FromToRotation, but makes sure it's axis remains fixed near to the Quaternion singularity point. /// /// /// The from to rotation around an axis. /// /// /// From direction. /// /// /// To direction. /// /// /// Axis. Should be normalized before passing into this method. /// public static Quaternion FromToAroundAxis(Vector3 fromDirection, Vector3 toDirection, Vector3 axis) { Quaternion fromTo = Quaternion.FromToRotation(fromDirection, toDirection); float angle = 0; Vector3 freeAxis = Vector3.zero; fromTo.ToAngleAxis(out angle, out freeAxis); float dot = Vector3.Dot(freeAxis, axis); if (dot < 0) angle = -angle; return Quaternion.AngleAxis(angle, axis); } /// /// Gets the rotation that can be used to convert a rotation from one axis space to another. /// public static Quaternion RotationToLocalSpace(Quaternion space, Quaternion rotation) { return Quaternion.Inverse(Quaternion.Inverse(space) * rotation); } /// /// Gets the Quaternion from rotation "from" to rotation "to". /// public static Quaternion FromToRotation(Quaternion from, Quaternion to) { if (to == from) return Quaternion.identity; return to * Quaternion.Inverse(from); } /// /// Gets the closest direction axis to a vector. Input vector must be normalized! /// public static Vector3 GetAxis(Vector3 v) { Vector3 closest = Vector3.right; bool neg = false; float x = Vector3.Dot(v, Vector3.right); float maxAbsDot = Mathf.Abs(x); if (x < 0f) neg = true; float y = Vector3.Dot(v, Vector3.up); float absDot = Mathf.Abs(y); if (absDot > maxAbsDot) { maxAbsDot = absDot; closest = Vector3.up; neg = y < 0f; } float z = Vector3.Dot(v, Vector3.forward); absDot = Mathf.Abs(z); if (absDot > maxAbsDot) { closest = Vector3.forward; neg = z < 0f; } if (neg) closest = -closest; return closest; } /// /// Clamps the rotation similar to V3Tools.ClampDirection. /// public static Quaternion ClampRotation(Quaternion rotation, float clampWeight, int clampSmoothing) { if (clampWeight >= 1f) return Quaternion.identity; if (clampWeight <= 0f) return rotation; float angle = Quaternion.Angle(Quaternion.identity, rotation); float dot = 1f - (angle / 180f); float targetClampMlp = Mathf.Clamp(1f - ((clampWeight - dot) / (1f - dot)), 0f, 1f); float clampMlp = Mathf.Clamp(dot / clampWeight, 0f, 1f); // Sine smoothing iterations for (int i = 0; i < clampSmoothing; i++) { float sinF = clampMlp * Mathf.PI * 0.5f; clampMlp = Mathf.Sin(sinF); } return Quaternion.Slerp(Quaternion.identity, rotation, clampMlp * targetClampMlp); } /// /// Clamps an angular value. /// public static float ClampAngle(float angle, float clampWeight, int clampSmoothing) { if (clampWeight >= 1f) return 0f; if (clampWeight <= 0f) return angle; float dot = 1f - (Mathf.Abs(angle) / 180f); float targetClampMlp = Mathf.Clamp(1f - ((clampWeight - dot) / (1f - dot)), 0f, 1f); float clampMlp = Mathf.Clamp(dot / clampWeight, 0f, 1f); // Sine smoothing iterations for (int i = 0; i < clampSmoothing; i++) { float sinF = clampMlp * Mathf.PI * 0.5f; clampMlp = Mathf.Sin(sinF); } return Mathf.Lerp(0f, angle, clampMlp * targetClampMlp); } /// /// Used for matching the rotations of objects that have different orientations. /// public static Quaternion MatchRotation(Quaternion targetRotation, Vector3 targetforwardAxis, Vector3 targetUpAxis, Vector3 forwardAxis, Vector3 upAxis) { Quaternion f = Quaternion.LookRotation(forwardAxis, upAxis); Quaternion fTarget = Quaternion.LookRotation(targetforwardAxis, targetUpAxis); Quaternion d = targetRotation * fTarget; return d * Quaternion.Inverse(f); } /// /// Converts an Euler rotation from 0 to 360 representation to -180 to 180. /// public static Vector3 ToBiPolar(Vector3 euler) { return new Vector3(ToBiPolar(euler.x), ToBiPolar(euler.y), ToBiPolar(euler.z)); } /// /// Converts an angular value from 0 to 360 representation to -180 to 180. /// public static float ToBiPolar(float angle) { angle = angle % 360f; if (angle >= 180f) return angle - 360f; if (angle <= -180f) return angle + 360f; return angle; } } }