190 lines
5.9 KiB
C#
190 lines
5.9 KiB
C#
|
using UnityEngine;
|
||
|
using System.Collections;
|
||
|
|
||
|
namespace RootMotion.FinalIK
|
||
|
{
|
||
|
|
||
|
/// <summary>
|
||
|
/// The base abstract class for all Rotation limits. Contains common functionality and static helper methods
|
||
|
/// </summary>
|
||
|
public abstract class RotationLimit : MonoBehaviour
|
||
|
{
|
||
|
|
||
|
#region Main Interface
|
||
|
|
||
|
/// <summary>
|
||
|
/// The main axis of the rotation limit.
|
||
|
/// </summary>
|
||
|
public Vector3 axis = Vector3.forward;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Map the zero rotation point to the current local rotation of this gameobject.
|
||
|
/// </summary>
|
||
|
public void SetDefaultLocalRotation()
|
||
|
{
|
||
|
defaultLocalRotation = transform.localRotation;
|
||
|
defaultLocalRotationSet = true;
|
||
|
defaultLocalRotationOverride = false;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Map the zero rotation point to the specified rotation.
|
||
|
/// </summary>
|
||
|
public void SetDefaultLocalRotation(Quaternion localRotation)
|
||
|
{
|
||
|
defaultLocalRotation = localRotation;
|
||
|
defaultLocalRotationSet = true;
|
||
|
defaultLocalRotationOverride = true;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the limited local rotation.
|
||
|
/// </summary>
|
||
|
public Quaternion GetLimitedLocalRotation(Quaternion localRotation, out bool changed)
|
||
|
{
|
||
|
// Making sure the Rotation Limit is initiated
|
||
|
if (!initiated) Awake();
|
||
|
|
||
|
// Subtracting defaultLocalRotation
|
||
|
Quaternion rotation = Quaternion.Inverse(defaultLocalRotation) * localRotation;
|
||
|
|
||
|
Quaternion limitedRotation = LimitRotation(rotation);
|
||
|
#if UNITY_2018_3_OR_NEWER
|
||
|
limitedRotation = Quaternion.Normalize(limitedRotation);
|
||
|
#endif
|
||
|
changed = limitedRotation != rotation;
|
||
|
|
||
|
if (!changed) return localRotation;
|
||
|
|
||
|
// Apply defaultLocalRotation back on
|
||
|
return defaultLocalRotation * limitedRotation;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Apply the rotation limit to transform.localRotation. Returns true if the limit has changed the rotation.
|
||
|
/// </summary>
|
||
|
public bool Apply()
|
||
|
{
|
||
|
bool changed = false;
|
||
|
|
||
|
transform.localRotation = GetLimitedLocalRotation(transform.localRotation, out changed);
|
||
|
|
||
|
return changed;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Disable this instance making sure it is initiated. Use this if you intend to manually control the updating of this Rotation Limit.
|
||
|
/// </summary>
|
||
|
public void Disable()
|
||
|
{
|
||
|
if (initiated)
|
||
|
{
|
||
|
enabled = false;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Awake();
|
||
|
enabled = false;
|
||
|
}
|
||
|
|
||
|
#endregion Main Interface
|
||
|
|
||
|
/*
|
||
|
* An arbitrary secondary axis that we get by simply switching the axes
|
||
|
* */
|
||
|
public Vector3 secondaryAxis { get { return new Vector3(axis.y, axis.z, axis.x); } }
|
||
|
|
||
|
/*
|
||
|
* Cross product of axis and secondaryAxis
|
||
|
* */
|
||
|
public Vector3 crossAxis { get { return Vector3.Cross(axis, secondaryAxis); } }
|
||
|
|
||
|
/*
|
||
|
* The default local rotation of the gameobject. By default stored in Awake.
|
||
|
* */
|
||
|
[HideInInspector] public Quaternion defaultLocalRotation;
|
||
|
|
||
|
public bool defaultLocalRotationOverride { get; private set; }
|
||
|
|
||
|
protected abstract Quaternion LimitRotation(Quaternion rotation);
|
||
|
|
||
|
private bool initiated;
|
||
|
private bool applicationQuit;
|
||
|
private bool defaultLocalRotationSet;
|
||
|
|
||
|
/*
|
||
|
* Initiating the Rotation Limit
|
||
|
* */
|
||
|
void Awake()
|
||
|
{
|
||
|
// Store the local rotation to map the zero rotation point to the current rotation
|
||
|
if (!defaultLocalRotationSet) SetDefaultLocalRotation();
|
||
|
|
||
|
if (axis == Vector3.zero) Debug.LogError("Axis is Vector3.zero.");
|
||
|
initiated = true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Using LateUpdate here because you most probably want to apply the limits after animation.
|
||
|
* If you need precise control over the execution order, disable this script and call Apply() whenever you need
|
||
|
* */
|
||
|
void LateUpdate()
|
||
|
{
|
||
|
Apply();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Logs the warning if no other warning has beed logged in this session.
|
||
|
* */
|
||
|
public void LogWarning(string message)
|
||
|
{
|
||
|
Warning.Log(message, transform);
|
||
|
}
|
||
|
|
||
|
#region Static helper methods for all Rotation Limits
|
||
|
|
||
|
/*
|
||
|
* Limits rotation to a single degree of freedom (along axis)
|
||
|
* */
|
||
|
protected static Quaternion Limit1DOF(Quaternion rotation, Vector3 axis)
|
||
|
{
|
||
|
return Quaternion.FromToRotation(rotation * axis, axis) * rotation;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Applies uniform twist limit to the rotation
|
||
|
* */
|
||
|
protected static Quaternion LimitTwist(Quaternion rotation, Vector3 axis, Vector3 orthoAxis, float twistLimit)
|
||
|
{
|
||
|
twistLimit = Mathf.Clamp(twistLimit, 0, 180);
|
||
|
if (twistLimit >= 180) return rotation;
|
||
|
|
||
|
Vector3 normal = rotation * axis;
|
||
|
Vector3 orthoTangent = orthoAxis;
|
||
|
Vector3.OrthoNormalize(ref normal, ref orthoTangent);
|
||
|
|
||
|
Vector3 rotatedOrthoTangent = rotation * orthoAxis;
|
||
|
Vector3.OrthoNormalize(ref normal, ref rotatedOrthoTangent);
|
||
|
|
||
|
Quaternion fixedRotation = Quaternion.FromToRotation(rotatedOrthoTangent, orthoTangent) * rotation;
|
||
|
|
||
|
if (twistLimit <= 0) return fixedRotation;
|
||
|
|
||
|
// Rotate from zero twist to free twist by the limited angle
|
||
|
return Quaternion.RotateTowards(fixedRotation, rotation, twistLimit);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Returns the angle between two vectors on a plane with the specified normal
|
||
|
* */
|
||
|
protected static float GetOrthogonalAngle(Vector3 v1, Vector3 v2, Vector3 normal)
|
||
|
{
|
||
|
Vector3.OrthoNormalize(ref normal, ref v1);
|
||
|
Vector3.OrthoNormalize(ref normal, ref v2);
|
||
|
return Vector3.Angle(v1, v2);
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
}
|
||
|
}
|