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; } } }