318 lines
8.5 KiB
C#
318 lines
8.5 KiB
C#
using System;
|
|
using UnityEngine;
|
|
|
|
namespace UnityEditor.U2D.Animation
|
|
{
|
|
[Serializable]
|
|
internal struct Pose
|
|
{
|
|
public Vector3 position;
|
|
public Quaternion rotation;
|
|
public Matrix4x4 matrix => Matrix4x4.TRS(position, rotation, Vector3.one);
|
|
|
|
public static Pose Create(Vector3 p, Quaternion r)
|
|
{
|
|
var pose = new Pose()
|
|
{
|
|
position = p,
|
|
rotation = r
|
|
};
|
|
|
|
return pose;
|
|
}
|
|
|
|
public override bool Equals(object other)
|
|
{
|
|
return other is Pose && this == (Pose)other;
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return position.GetHashCode() ^ rotation.GetHashCode();
|
|
}
|
|
|
|
public static bool operator==(Pose p1, Pose p2)
|
|
{
|
|
return p1.position == p2.position && p1.rotation == p2.rotation;
|
|
}
|
|
|
|
public static bool operator!=(Pose p1, Pose p2)
|
|
{
|
|
return !(p1 == p2);
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
internal struct BonePose
|
|
{
|
|
public Pose pose;
|
|
public float length;
|
|
public static BonePose Create(Pose p, float l)
|
|
{
|
|
var pose = new BonePose()
|
|
{
|
|
pose = p,
|
|
length = l
|
|
};
|
|
|
|
return pose;
|
|
}
|
|
|
|
public override bool Equals(object other)
|
|
{
|
|
return other is BonePose && this == (BonePose)other;
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return pose.GetHashCode() ^ length.GetHashCode();
|
|
}
|
|
|
|
public static bool operator==(BonePose p1, BonePose p2)
|
|
{
|
|
return p1.pose == p2.pose && Mathf.Abs(p1.length - p2.length) < Mathf.Epsilon;
|
|
}
|
|
|
|
public static bool operator!=(BonePose p1, BonePose p2)
|
|
{
|
|
return !(p1 == p2);
|
|
}
|
|
}
|
|
|
|
internal class BoneCache : TransformCache
|
|
{
|
|
[SerializeField]
|
|
Color32 m_BindPoseColor;
|
|
[SerializeField]
|
|
Pose m_BindPose;
|
|
[SerializeField]
|
|
BonePose m_DefaultPose;
|
|
[SerializeField]
|
|
BoneCache m_ChainedChild;
|
|
[SerializeField]
|
|
float m_Depth;
|
|
[SerializeField]
|
|
float m_LocalLength = 1f;
|
|
[SerializeField]
|
|
bool m_IsVisible = true;
|
|
[SerializeField]
|
|
string m_Guid;
|
|
public bool NotInDefaultPose()
|
|
{
|
|
return localPosition != m_DefaultPose.pose.position
|
|
|| localRotation != m_DefaultPose.pose.rotation
|
|
|| Mathf.Abs(localLength - m_DefaultPose.length) > Mathf.Epsilon;
|
|
}
|
|
|
|
public bool isVisible
|
|
{
|
|
get => m_IsVisible;
|
|
set => m_IsVisible = value;
|
|
}
|
|
|
|
public Color bindPoseColor
|
|
{
|
|
get => m_BindPoseColor;
|
|
set => m_BindPoseColor = value;
|
|
}
|
|
|
|
public virtual BoneCache parentBone => parent as BoneCache;
|
|
|
|
public SkeletonCache skeleton
|
|
{
|
|
get
|
|
{
|
|
var parentSkeleton = parent as SkeletonCache;
|
|
if (parentSkeleton != null)
|
|
return parentSkeleton;
|
|
|
|
return parentBone != null ? parentBone.skeleton : null;
|
|
}
|
|
}
|
|
|
|
public virtual BoneCache chainedChild
|
|
{
|
|
get
|
|
{
|
|
if (m_ChainedChild != null && m_ChainedChild.parentBone == this)
|
|
return m_ChainedChild;
|
|
|
|
return null;
|
|
}
|
|
set
|
|
{
|
|
if (m_ChainedChild != value)
|
|
{
|
|
if (value == null || value.parentBone == this)
|
|
{
|
|
m_ChainedChild = value;
|
|
if(m_ChainedChild != null)
|
|
OrientToChainedChild(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Vector3 localEndPosition => Vector3.right * localLength;
|
|
|
|
public Vector3 endPosition
|
|
{
|
|
get => localToWorldMatrix.MultiplyPoint3x4(localEndPosition);
|
|
set
|
|
{
|
|
if (chainedChild != null)
|
|
return;
|
|
|
|
var direction = value - position;
|
|
right = direction;
|
|
length = direction.magnitude;
|
|
}
|
|
}
|
|
|
|
public BonePose localPose
|
|
{
|
|
get => BonePose.Create(Pose.Create(localPosition, localRotation), localLength);
|
|
set
|
|
{
|
|
localPosition = value.pose.position;
|
|
localRotation = value.pose.rotation;
|
|
localLength = value.length;
|
|
}
|
|
}
|
|
|
|
public BonePose worldPose
|
|
{
|
|
get => BonePose.Create(Pose.Create(position, rotation), length);
|
|
set
|
|
{
|
|
position = value.pose.position;
|
|
rotation = value.pose.rotation;
|
|
length = value.length;
|
|
}
|
|
}
|
|
|
|
public Pose bindPose => m_BindPose;
|
|
|
|
public string guid
|
|
{
|
|
get => m_Guid;
|
|
set => m_Guid = value;
|
|
}
|
|
|
|
public float depth
|
|
{
|
|
get => m_Depth;
|
|
set => m_Depth = value;
|
|
}
|
|
public float localLength
|
|
{
|
|
get => m_LocalLength;
|
|
set => m_LocalLength = Mathf.Max(0f, value);
|
|
}
|
|
|
|
public float length
|
|
{
|
|
get => localToWorldMatrix.MultiplyVector(localEndPosition).magnitude;
|
|
set => m_LocalLength = worldToLocalMatrix.MultiplyVector(right * Mathf.Max(0f, value)).magnitude;
|
|
}
|
|
|
|
internal Pose[] GetChildrenWoldPose()
|
|
{
|
|
return Array.ConvertAll(children, c => Pose.Create(c.position, c.rotation));
|
|
}
|
|
|
|
internal void SetChildrenWorldPose(Pose[] worldPoses)
|
|
{
|
|
var childrenArray = children;
|
|
|
|
Debug.Assert(childrenArray.Length == worldPoses.Length);
|
|
|
|
for (var i = 0; i < childrenArray.Length; ++i)
|
|
{
|
|
var child = childrenArray[i];
|
|
var pose= worldPoses[i];
|
|
|
|
child.position = pose.position;
|
|
child.rotation = pose.rotation;
|
|
}
|
|
}
|
|
|
|
internal override void OnDestroy()
|
|
{
|
|
base.OnDestroy();
|
|
m_ChainedChild = null;
|
|
}
|
|
|
|
public new void SetParent(TransformCache newParent, bool worldPositionStays = true)
|
|
{
|
|
if (parentBone != null && parentBone.chainedChild == this)
|
|
parentBone.chainedChild = null;
|
|
|
|
base.SetParent(newParent, worldPositionStays);
|
|
|
|
if (parentBone != null && parentBone.chainedChild == null && (parentBone.endPosition - position).sqrMagnitude < 0.001f)
|
|
parentBone.chainedChild = this;
|
|
}
|
|
|
|
public void OrientToChainedChild(bool freezeChildren)
|
|
{
|
|
Debug.Assert(chainedChild != null);
|
|
|
|
var childPosition = chainedChild.position;
|
|
var childRotation = chainedChild.rotation;
|
|
|
|
Pose[] childrenWorldPose = null;
|
|
|
|
if (freezeChildren)
|
|
childrenWorldPose = GetChildrenWoldPose();
|
|
|
|
right = childPosition - position;
|
|
|
|
if (freezeChildren)
|
|
{
|
|
SetChildrenWorldPose(childrenWorldPose);
|
|
}
|
|
else
|
|
{
|
|
chainedChild.position = childPosition;
|
|
chainedChild.rotation = childRotation;
|
|
}
|
|
|
|
length = (childPosition - position).magnitude;
|
|
}
|
|
|
|
public void SetDefaultPose()
|
|
{
|
|
m_DefaultPose = localPose;
|
|
|
|
if (IsUnscaled())
|
|
m_BindPose = worldPose.pose;
|
|
else
|
|
throw new Exception("BindPose cannot be set under global scale");
|
|
}
|
|
|
|
public void RestoreDefaultPose()
|
|
{
|
|
localPose = m_DefaultPose;
|
|
}
|
|
|
|
bool IsUnscaled()
|
|
{
|
|
var currentTransform = this as TransformCache;
|
|
|
|
while (currentTransform != null)
|
|
{
|
|
var scale = currentTransform.localScale;
|
|
var isUnscaled = Mathf.Approximately(scale.x, 1f) && Mathf.Approximately(scale.y, 1f) && Mathf.Approximately(scale.z, 1f);
|
|
|
|
if (!isUnscaled)
|
|
return false;
|
|
|
|
currentTransform = currentTransform.parent;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|