Files
beyond/Assets/Scripts/Editor/AnimationHelperInspector.cs
2024-11-20 15:21:28 +01:00

796 lines
27 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(AnimationHelper))]
public class AnimationHelperInspector : Editor
{
public enum CurveType {POS_X, POS_Y, POS_Z, ROT_X, ROT_Y, ROT_Z };
const float segLen = 0.1f;
const float timeStep = 0.02f;
private const float handleSize = 0.04f;
private const float pickSize = 0.06f;
//private Transform trans;
private AnimationHelper aHelper;
class PositionData : IComparable<PositionData>
{
public PositionData()
{
//xKeyId = yKeyId = zKeyId = -1;
point = Vector3.zero;
inTan = Vector3.zero;
outTan = Vector3.zero;
}
public Vector3 point;
public Vector3 inTan;
public Vector3 outTan;
public float time;
//public int xKeyId, yKeyId, zKeyId;
public int CompareTo(PositionData other)
{
if (time < other.time)
return -1;
else if (time > other.time)
return 1;
return 0;
}
}
class AnimationData
{
SortedList<float,PositionData> keys = new SortedList<float, PositionData>();
public Transform trans;
public int Count
{
get { return keys.Count; }
}
public PositionData this[int i]
{
get
{
return keys.Values[i];
}
set
{
keys.Values[i] = value;
}
}
public void GenerateKeys(AnimationCurve curve)
{
for (int i = 0; i < curve.keys.Length; i++)
{
Keyframe k = curve.keys[i];
if (!keys.ContainsKey(k.time))
{
PositionData p = new PositionData();
p.time = k.time;
keys.Add(k.time, p);
}
}
}
public void CopyKeys(AnimationCurve curve, CurveType type)
{
foreach (Keyframe k in curve.keys)
{
PositionData data = keys[k.time];
switch (type)
{
case CurveType.POS_X:
data.point.x = k.value;
data.inTan.x = k.inTangent;
data.outTan.x = k.outTangent;
break;
case CurveType.POS_Y:
data.point.y = k.value;
data.inTan.y = k.inTangent;
data.outTan.y = k.outTangent;
break;
case CurveType.POS_Z:
data.point.z = k.value;
data.inTan.z = k.inTangent;
data.outTan.z = k.outTangent;
break;
}
}
}
public bool AddMissingKeysToCurve(AnimationCurve curve)
{
bool res = false;
//curve.k
KeyTimeComparer cmp = new KeyTimeComparer();
Keyframe k = new Keyframe();
for (int i = 0; i < keys.Values.Count; i++)
{
k.time = keys.Values[i].time;
if (Array.BinarySearch<Keyframe>(curve.keys, k, cmp) < 0)
{
curve.AddKey(k.time, curve.Evaluate(k.time));
res = true;
}
}
return res;
}
public void TransformToWorld()
{
if (trans)
{
foreach (KeyValuePair<float, PositionData> k in keys)
{
k.Value.inTan = trans.TransformDirection(k.Value.inTan);
k.Value.outTan = trans.TransformDirection(k.Value.outTan);
k.Value.point = trans.TransformPoint(k.Value.point);
}
}
for (int i = 0; i < keys.Count - 1; i++)
{
float a = 1f / 3f;
float timeDiff = this[i + 1].time - this[i].time;
a *= timeDiff;
this[i].outTan *= a;
this[i + 1].inTan *= a;
}
}
public Vector3 GetWorldPosition(int idx)
{
return trans ? trans.TransformPoint(this[idx].point) : this[idx].point;
}
public void SetWorldPosition(int idx, Vector3 pos)
{
if (keys.Count <= idx)
return;
this[idx].point = trans ? trans.InverseTransformPoint(pos) : pos;
}
public Vector3 GetWorldTangent(int idx)
{
Vector3 res = Vector3.zero;
return res;
}
public bool InTangentFromWorld(int idx, Vector3 point, out Vector3 outVect)
{
outVect = point;
if (idx - 1 >= 0)
{
point = this[idx].point - point;
float timeDiff = this[idx].time - this[idx - 1].time;
float a = 1f / 3.0f;
a *= timeDiff;
point /= a;
if (trans)
point = trans.transform.InverseTransformDirection(point);
outVect = point;
}
else
{
return false;
}
return true;
}
public bool OutTangentFromWorld(int idx, Vector3 point, out Vector3 outVect)
{
outVect = point;
if (idx + 1 < keys.Count)
{
point = point - this[idx].point;
float timeDiff = this[idx + 1].time - this[idx].time;
float a = 1f / 3.0f;
a *= timeDiff;
point /= a;
if (trans)
point = trans.transform.InverseTransformDirection(point);
outVect = point;
}
else
{
return false;
}
return true;
}
}
AnimationData animData;
// List<PositionData> positionAnim;
AnimationCurve pxCurve;
AnimationCurve pyCurve;
AnimationCurve pzCurve;
Nullable<EditorCurveBinding> pxBind = null;
Nullable<EditorCurveBinding> pyBind = null;
Nullable<EditorCurveBinding> pzBind = null;
AnimationClip anim;
class KeyTimeComparer : System.Collections.Generic.IComparer<UnityEngine.Keyframe>
{
public int Compare(Keyframe x, Keyframe y)
{
return (x).time.CompareTo(y.time);
}
}
private bool AddMissingKeysToCurve(AnimationCurve src, AnimationCurve dest)
{
bool res = false;
KeyTimeComparer cmp = new KeyTimeComparer();
for (int i=0; i<src.length; i++)
{
if (Array.BinarySearch<Keyframe>(dest.keys, src[i], cmp) < 0)
{
dest.AddKey(src[i].time, dest.Evaluate(src[i].time));
res = true;
}
}
return res;
}
private void EnsureKeysIntegrity()
{
/*
bool xChanged = false;
bool yChanged = false;
bool zChanged = false;
yChanged |= AddMissingKeysToCurve(pxCurve, pyCurve);
zChanged |= AddMissingKeysToCurve(pxCurve, pzCurve);
xChanged |= AddMissingKeysToCurve(pyCurve, pxCurve);
zChanged |= AddMissingKeysToCurve(pyCurve, pzCurve);
xChanged |= AddMissingKeysToCurve(pzCurve, pxCurve);
yChanged |= AddMissingKeysToCurve(pzCurve, pyCurve);
if (xChanged)
AnimationUtility.SetEditorCurve(anim, pxBind.Value, pxCurve);
if (yChanged)
AnimationUtility.SetEditorCurve(anim, pyBind.Value, pyCurve);
if (zChanged)
AnimationUtility.SetEditorCurve(anim, pzBind.Value, pzCurve);
*/
if (animData.AddMissingKeysToCurve(pxCurve))
AnimationUtility.SetEditorCurve(anim, pxBind.Value, pxCurve);
if (animData.AddMissingKeysToCurve(pyCurve))
AnimationUtility.SetEditorCurve(anim, pyBind.Value, pzCurve);
if (animData.AddMissingKeysToCurve(pzCurve))
AnimationUtility.SetEditorCurve(anim, pzBind.Value, pzCurve);
}
private bool FindBindings()
{
Animator animator = null;
String path = "";
Transform trans = aHelper.transform;
while (true)
{
animator = trans.GetComponent<Animator>();
if (animator != null || trans.parent == null)
{
break;
}
else
{
path = path == "" ? trans.gameObject.name : trans.gameObject.name + "/" + path;
trans = trans.parent;
}
}
Debug.Log("Current Path: " + path);
if (animator == null)
return false;
//find clip
if (aHelper.currentClip != null)
{
anim = aHelper.currentClip;
}
else
{
AnimatorClipInfo[] info = animator.GetCurrentAnimatorClipInfo(0);
if (info.Length == 0)
animator.GetNextAnimatorClipInfo(0);
if (info.Length == 0)
return false;
anim = info[0].clip;
aHelper.currentClip = anim;
}
EditorCurveBinding[] cb = AnimationUtility.GetCurveBindings(anim);
//AnimationUtility.g
pxBind = null;
pyBind = null;
pzBind = null;
//trans = aHelper.transform;
foreach (EditorCurveBinding b in cb)
{
//Debug.Log("Property: " + b.propertyName + ", Path:" + b.path);
if (b.path != path)
continue;
if (b.propertyName == "m_LocalPosition.x")
{
pxBind = b;
}
else if (b.propertyName == "m_LocalPosition.y")
{
pyBind = b;
}
else if (b.propertyName == "m_LocalPosition.z")
{
pzBind = b;
}
}
return pxBind.HasValue && pyBind.HasValue && pzBind.HasValue;
}
private void OnSceneGUI()
{
aHelper = target as AnimationHelper;
animData = new AnimationData();
//use transformy for points only if parent exists. Otherwise, keys are in global coords.
if (aHelper.transform.parent)
animData.trans = aHelper.transform.parent.transform;
if (!FindBindings())
return;
pxCurve = null;
pyCurve = null;
pzCurve = null;
if (pxBind.HasValue)
{
pxCurve = AnimationUtility.GetEditorCurve(anim, pxBind.Value);
}
if (pyBind.HasValue)
{
pyCurve = AnimationUtility.GetEditorCurve(anim, pyBind.Value);
}
if (pzBind.HasValue)
{
pzCurve = AnimationUtility.GetEditorCurve(anim, pzBind.Value);
}
// anim.
if (pxCurve == null || pyCurve == null || pzCurve == null)
return;
//positionAnim = new List<PositionData>();
animData.GenerateKeys(pxCurve);
animData.GenerateKeys(pyCurve);
animData.GenerateKeys(pzCurve);
if (animData.Count < 2)
return;
EnsureKeysIntegrity();
animData.CopyKeys(pxCurve, CurveType.POS_X);
animData.CopyKeys(pyCurve, CurveType.POS_Y);
animData.CopyKeys(pzCurve, CurveType.POS_Z);
animData.TransformToWorld();
for (int i = 0; i < animData.Count; i++)
{
ShowPoint(i);
}
for (int i = 0; i < animData.Count - 1; i++)
{
Handles.DrawBezier(animData[i].point, animData[i + 1].point, animData[i].point + animData[i].outTan, animData[i + 1].point - animData[i + 1].inTan, Color.white, null, 2f);
}
/*
PositionData tmp;
for (int i = 0; i < pxCurve.keys.Length; i++)
{
Keyframe k = pxCurve.keys[i];
tmp = new PositionData();
tmp.time = k.time;
int idx = positionAnim.BinarySearch(tmp);
if (idx < 0)
{
//tmp.xKeyId = i;
tmp.point.x = k.value;
tmp.inTan.x = k.inTangent;
tmp.outTan.x = k.outTangent;
positionAnim.Add(tmp);
}
else
{
//positionAnim[idx].xKeyId = i;
positionAnim[idx].point.x = k.value;
positionAnim[idx].inTan.x = k.inTangent;
positionAnim[idx].outTan.x = k.outTangent;
}
}
for (int i = 0; i < pyCurve.keys.Length; i++)
{
Keyframe k = pyCurve.keys[i];
tmp = new PositionData();
tmp.time = k.time;
int idx = positionAnim.BinarySearch(tmp);
if (idx < 0)
{
//tmp.yKeyId = i;
tmp.point.y = k.value;
tmp.inTan.y = k.inTangent;
tmp.outTan.y = k.outTangent;
positionAnim.Add(tmp);
}
else
{
//positionAnim[idx].yKeyId = i;
positionAnim[idx].point.y = k.value;
positionAnim[idx].inTan.y = k.inTangent;
positionAnim[idx].outTan.y = k.outTangent;
}
}
for (int i = 0; i < pzCurve.keys.Length; i++)
{
Keyframe k = pzCurve.keys[i];
tmp = new PositionData();
tmp.time = k.time;
int idx = positionAnim.BinarySearch(tmp);
if (idx < 0)
{
//tmp.zKeyId = i;
tmp.point.z = k.value;
tmp.inTan.z = k.inTangent;
tmp.outTan.z = k.outTangent;
positionAnim.Add(tmp);
}
else
{
//positionAnim[idx].zKeyId = i;
positionAnim[idx].point.z = k.value;
positionAnim[idx].inTan.z = k.inTangent;
positionAnim[idx].outTan.z = k.outTangent;
}
}
if (positionAnim.Count < 2)
return;
for (int i = 0; i < positionAnim.Count; i++)
{
if (trans.parent)
{
positionAnim[i].point = trans.parent.transform.TransformPoint(positionAnim[i].point);
positionAnim[i].inTan = trans.parent.transform.TransformDirection(positionAnim[i].inTan);
positionAnim[i].outTan = trans.parent.transform.TransformDirection(positionAnim[i].outTan);
}
ShowPoint(i);
}
// Debug.Log(anim.frameRate);
//adjust tangents
for (int i = 0; i < positionAnim.Count - 1; i++)
{
float a = 1f/3f;
float timeDiff = positionAnim[i + 1].time - positionAnim[i].time;
a *= timeDiff;
positionAnim[i].outTan *= a;
positionAnim[i + 1].inTan *= a;
}
for (int i = 0; i < positionAnim.Count - 1; i++)
{
Handles.DrawBezier(positionAnim[i].point, positionAnim[i + 1].point, positionAnim[i].point + positionAnim[i].outTan, positionAnim[i + 1].point - positionAnim[i + 1].inTan, Color.white, null, 2f);
}
float timeEnd = positionAnim[positionAnim.Count - 1].time;
Vector3 p0 = positionAnim[0].point;
Vector3 p1 = p0;
for (float t = positionAnim[0].time; t < timeEnd; t += timeStep)
{
p1.x = pxCurve.Evaluate(t);
p1.y = pyCurve.Evaluate(t);
p1.z = pzCurve.Evaluate(t);
// p1 = trans.TransformPoint(p1);
Handles.DrawLine(p0, p1);
p0 = p1;
}
*/
}
private Vector3 ShowPoint(int index)
{
PositionData pData = animData[index];
Vector3 point = pData.point;
float size = HandleUtility.GetHandleSize(point);
if (index == 0)
{
size *= 2f;
}
Handles.color = Color.gray;
Quaternion handleRot = Tools.pivotRotation == PivotRotation.Local ?
aHelper.transform.rotation : Quaternion.identity;
//Handles.DrawBezier(positionAnim[i].point, positionAnim[i + 1].point, positionAnim[i].point + positionAnim[i].outTan, positionAnim[i + 1].point - positionAnim[i + 1].inTan, Color.white, null, 2f);
Handles.DrawLine(point, point - pData.inTan);
Handles.DrawLine(point, point + pData.outTan);
if (Handles.Button(point, handleRot, size * handleSize, size * pickSize, Handles.DotHandleCap))
{
aHelper.selectedKeyIndex = index;
aHelper.selectionType = AnimationHelper.SelectionType.KEY;
Repaint();
}
else if (Handles.Button(point + pData.outTan, handleRot, size * handleSize, size * pickSize, Handles.DotHandleCap))
{
aHelper.selectedKeyIndex = index;
aHelper.selectionType = AnimationHelper.SelectionType.OUT_TANGENT;
Repaint();
}
else if (Handles.Button(point - pData.inTan, handleRot, size * handleSize, size * pickSize, Handles.DotHandleCap))
{
aHelper.selectedKeyIndex = index;
aHelper.selectionType = AnimationHelper.SelectionType.IN_TANGENT;
Repaint();
}
if (aHelper.selectedKeyIndex == index)
{
EditorGUI.BeginChangeCheck();
switch (aHelper.selectionType)
{
case AnimationHelper.SelectionType.KEY:
point = Handles.DoPositionHandle(point, handleRot);
break;
case AnimationHelper.SelectionType.IN_TANGENT:
point = Handles.DoPositionHandle(point - pData.inTan, handleRot);
break;
case AnimationHelper.SelectionType.OUT_TANGENT:
point = Handles.DoPositionHandle(point + pData.outTan, handleRot);
break;
}
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(aHelper, "Move Point");
EditorUtility.SetDirty(aHelper);
Keyframe[] xkeys, ykeys, zkeys;
xkeys = pxCurve.keys;
ykeys = pyCurve.keys;
zkeys = pzCurve.keys;
switch (aHelper.selectionType)
{
case AnimationHelper.SelectionType.KEY:
animData.SetWorldPosition(index, point);
point = animData[index].point;
xkeys[index].value = point.x;
ykeys[index].value = point.y;
zkeys[index].value = point.z;
break;
case AnimationHelper.SelectionType.IN_TANGENT:
if (animData.InTangentFromWorld(index, point, out point))
{
xkeys[index].inTangent = point.x;
ykeys[index].inTangent = point.y;
zkeys[index].inTangent = point.z;
//
if (aHelper.tangentMode == AnimationHelper.TangentMode.SMOOTH)
{
xkeys[index].outTangent = point.x;
ykeys[index].outTangent = point.y;
zkeys[index].outTangent = point.z;
}
}
break;
case AnimationHelper.SelectionType.OUT_TANGENT:
if (animData.OutTangentFromWorld(index, point, out point))
{
xkeys[index].outTangent = point.x;
ykeys[index].outTangent = point.y;
zkeys[index].outTangent = point.z;
//
if (aHelper.tangentMode == AnimationHelper.TangentMode.SMOOTH)
{
xkeys[index].inTangent = point.x;
ykeys[index].inTangent = point.y;
zkeys[index].inTangent = point.z;
}
}
break;
}
pxCurve.keys = xkeys;
pyCurve.keys = ykeys;
pzCurve.keys = zkeys;
AnimationUtility.SetEditorCurve(anim, pxBind.Value, pxCurve);
AnimationUtility.SetEditorCurve(anim, pyBind.Value, pyCurve);
AnimationUtility.SetEditorCurve(anim, pzBind.Value, pzCurve);
}
}
return point;
}
/*
private Vector3 ShowPoint(int index)
{
PositionData pData = positionAnim[index];
Vector3 point = pData.point;
float size = HandleUtility.GetHandleSize(point);
if (index == 0)
{
size *= 2f;
}
Handles.color = Color.gray;
//Handles.DrawBezier(positionAnim[i].point, positionAnim[i + 1].point, positionAnim[i].point + positionAnim[i].outTan, positionAnim[i + 1].point - positionAnim[i + 1].inTan, Color.white, null, 2f);
Handles.DrawLine(point, point - pData.inTan);
Handles.DrawLine(point, point + pData.outTan);
if (Handles.Button(point, trans.localRotation, size * handleSize, size * pickSize, Handles.DotCap))
{
aHelper.selectedKeyIndex = index;
aHelper.selectionType = AnimationHelper.SelectionType.KEY;
Repaint();
}
else if (Handles.Button(point + pData.outTan, trans.localRotation, size * handleSize, size * pickSize, Handles.DotCap))
{
aHelper.selectedKeyIndex = index;
aHelper.selectionType = AnimationHelper.SelectionType.OUT_TANGENT;
Repaint();
}
else if (Handles.Button(point - pData.inTan, trans.localRotation, size * handleSize, size * pickSize, Handles.DotCap))
{
aHelper.selectedKeyIndex = index;
aHelper.selectionType = AnimationHelper.SelectionType.IN_TANGENT;
Repaint();
}
if (aHelper.selectedKeyIndex == index)
{
EditorGUI.BeginChangeCheck();
switch (aHelper.selectionType)
{
case AnimationHelper.SelectionType.KEY:
point = Handles.DoPositionHandle(point, trans.localRotation);
break;
case AnimationHelper.SelectionType.IN_TANGENT:
point = Handles.DoPositionHandle(point - pData.inTan, trans.localRotation);
break;
case AnimationHelper.SelectionType.OUT_TANGENT:
point = Handles.DoPositionHandle(point + pData.outTan, trans.localRotation);
break;
}
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(aHelper, "Move Point");
EditorUtility.SetDirty(aHelper);
Keyframe[] xkeys, ykeys, zkeys;
xkeys = pxCurve.keys;
ykeys = pyCurve.keys;
zkeys = pzCurve.keys;
switch (aHelper.selectionType)
{
case AnimationHelper.SelectionType.KEY:
if (trans.parent)
point = trans.parent.transform.InverseTransformPoint(point);
xkeys[index].value = point.x;
ykeys[index].value = point.y;
zkeys[index].value = point.z;
break;
case AnimationHelper.SelectionType.IN_TANGENT:
point = pData.point - point;
if (index -1 >= 0)
{
float timeDiff = pData.time - positionAnim[index-1].time;
float a = 1f/3.0f;
a *= timeDiff;
point /= a;
if (trans.parent)
point = trans.parent.transform.InverseTransformDirection(point);
xkeys[index].inTangent = point.x;
ykeys[index].inTangent = point.y;
zkeys[index].inTangent = point.z;
//
if (aHelper.tangentMode == AnimationHelper.TangentMode.SMOOTH)
{
xkeys[index].outTangent = point.x;
ykeys[index].outTangent = point.y;
zkeys[index].outTangent = point.z;
}
}
break;
case AnimationHelper.SelectionType.OUT_TANGENT:
point = point - pData.point;
if (index + 1 < positionAnim.Count)
{
float timeDiff = positionAnim[index + 1].time - pData.time;
float a = 1f/3f;
a *= timeDiff;
point /= a;
if (trans.parent)
point = trans.parent.transform.InverseTransformDirection(point);
xkeys[index].outTangent = point.x;
ykeys[index].outTangent = point.y;
zkeys[index].outTangent = point.z;
//
if (aHelper.tangentMode == AnimationHelper.TangentMode.SMOOTH)
{
xkeys[index].inTangent = point.x;
ykeys[index].inTangent = point.y;
zkeys[index].inTangent = point.z;
}
}
break;
}
pxCurve.keys = xkeys;
pyCurve.keys = ykeys;
pzCurve.keys = zkeys;
AnimationUtility.SetEditorCurve(anim, pxBind.Value, pxCurve);
AnimationUtility.SetEditorCurve(anim, pyBind.Value, pyCurve);
AnimationUtility.SetEditorCurve(anim, pzBind.Value, pzCurve);
}
}
return point;
}
*/
private float GetBezierValue(float t, float p0, float p1, float p2, float p3)
{
t = Mathf.Clamp01(t); float oneMinusT = 1.0f - t;
float oneMinusT2 = oneMinusT * oneMinusT;
float oneMinusT3 = oneMinusT2 * oneMinusT;
float t2 = t * t;
float t3 = t2 * t;
return oneMinusT3 * p0 + 3.0f * oneMinusT2 * t * p1 + 3.0f * oneMinusT * t2 * p2 + t3 * p3;
}
private float GetBezierDerivative(float t, float p0, float p1, float p2, float p3)
{
t = Mathf.Clamp01(t);
float oneMinusT = 1f - t;
return
3f * oneMinusT * oneMinusT * (p1 - p0) +
6f * oneMinusT * t * (p2 - p1) +
3f * t * t * (p3 - p2);
}
}