Last active
November 13, 2022 12:56
-
-
Save phi16/fb81ce82ea075c012fd8beaf3cc5860e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEngine.Playables; | |
#if UNITY_EDITOR | |
using UnityEditor; | |
#endif | |
// Original: https://styly.cc/ja/tips/aigaalince001_jojomon_particlelive1/ CreateBoneAnimeClip.cs | |
public class BakeBoneRotations : MonoBehaviour | |
{ | |
[SerializeField] private PlayableDirector director; | |
[SerializeField] private Transform rootPath; | |
[SerializeField] private List<Transform> rootBones; | |
#if UNITY_EDITOR | |
List<AnimCurve> curves; | |
class AnimCurve { | |
public string relativePath; | |
public Type type; | |
public string property; | |
public Func<float> value; | |
public AnimationCurve curve; | |
public int count; | |
public bool first; | |
public bool skipped; | |
public float lastTime1; | |
public float lastTime0; | |
public float lastValue1; | |
public float lastValue0; | |
public float timeCand; | |
public float valueCand; | |
public float prevTime; | |
public float prevValue; | |
public bool smooth; | |
public bool degree; | |
public AnimCurve(string relativePath, Type type, string property, Func<float> value, bool smooth = true, bool degree = false) { | |
this.relativePath = relativePath; | |
this.type = type; | |
this.property = property; | |
this.value = value; | |
this.smooth = smooth; | |
this.degree = degree; | |
curve = new AnimationCurve(); | |
count = 0; | |
first = true; | |
skipped = false; | |
lastTime1 = 0; | |
lastTime0 = 0; | |
lastValue1 = 0; | |
lastValue0 = 0; | |
timeCand = 0; | |
valueCand = 0; | |
prevTime = 0; | |
prevValue = 0; | |
} | |
private void AddKey(float t, float v) { | |
if(smooth) { | |
if(t == prevTime) { | |
curve.AddKey(t, v); | |
} else { | |
float g = (v - prevValue) / (t - prevTime); | |
Keyframe f = new Keyframe(t, v); | |
f.weightedMode = WeightedMode.Both; | |
f.inTangent = f.outTangent = g; | |
curve.AddKey(f); | |
} | |
} else { | |
Keyframe f = new Keyframe(t, v); | |
f.weightedMode = WeightedMode.Both; | |
f.inWeight = f.outWeight = 0; // discrete | |
// f.inWeight = f.outWeight = 1; // linear | |
curve.AddKey(f); | |
} | |
count++; | |
lastTime1 = lastTime0; | |
lastTime0 = t; | |
lastValue1 = lastValue0; | |
lastValue0 = v; | |
} | |
private float PickValue(float p, float c) { | |
if(degree) { | |
c -= p; | |
c = (((c + 180) % 360 + 360) % 360) - 180; | |
c += p; | |
} | |
return c; | |
} | |
private float calcT; | |
private float calcV; | |
public bool Tick(float t) { | |
if(first) { | |
first = false; | |
return false; | |
} | |
float v = PickValue(lastValue0, value()); | |
bool accept = false; | |
if(count < 2) { | |
accept = true; | |
} else { | |
float le = (t - lastTime1) / (lastTime0 - lastTime1); | |
float pr = lastValue1 + (lastValue0 - lastValue1) * le; | |
float diff = Mathf.Abs(pr - v); | |
if(diff > 1.0f) { | |
accept = true; | |
} | |
} | |
calcT = t; | |
calcV = v; | |
return accept; | |
} | |
public void Proc(bool accept) { | |
float t = calcT; | |
float v = calcV; | |
if(accept) { | |
AddKey(t, v); | |
skipped = false; | |
} else { | |
timeCand = t; | |
valueCand = v; | |
skipped = true; | |
} | |
prevTime = t; | |
prevValue = v; | |
} | |
public void Done() { | |
if(skipped) { | |
AddKey(timeCand, valueCand); | |
} | |
} | |
} | |
bool recording = true; | |
private void Start() { | |
curves = new List<AnimCurve>(); | |
foreach(Transform rootBone in rootBones) { | |
Transform[] children = rootBone.GetComponentsInChildren<Transform>(); | |
foreach(Transform t in children) { | |
string path = AnimationUtility.CalculateTransformPath(t, rootPath); | |
curves.Add(new AnimCurve(path, typeof(Transform), "localEulerAnglesRaw.x", ()=>t.localEulerAngles.x, true, true)); | |
curves.Add(new AnimCurve(path, typeof(Transform), "localEulerAnglesRaw.y", ()=>t.localEulerAngles.y, true, true)); | |
curves.Add(new AnimCurve(path, typeof(Transform), "localEulerAnglesRaw.z", ()=>t.localEulerAngles.z, true, true)); | |
} | |
} | |
} | |
private void Update() { | |
if(recording) { | |
float t = (float) director.time; | |
for(int i=0;i<curves.Count;i+=3) { | |
AnimCurve c0 = curves[i+0]; | |
AnimCurve c1 = curves[i+1]; | |
AnimCurve c2 = curves[i+2]; | |
bool a = false; | |
a = c0.Tick(t) || a; | |
a = c1.Tick(t) || a; | |
a = c2.Tick(t) || a; | |
c0.Proc(a); | |
c1.Proc(a); | |
c2.Proc(a); | |
} | |
foreach(AnimCurve curve in curves) { | |
curve.Tick((float) director.time); | |
} | |
} | |
if(Input.GetKeyDown(KeyCode.Space)) { | |
recording = false; | |
AnimationClip clip = new AnimationClip(); | |
foreach(AnimCurve c in curves) { | |
c.Done(); | |
AnimationCurve cu = c.curve; | |
clip.SetCurve(c.relativePath, c.type, c.property, c.curve); | |
} | |
string path = AssetDatabase.GenerateUniqueAssetPath($"Assets/{rootPath.name}_clip.anim"); | |
AssetDatabase.CreateAsset(clip, path); | |
AssetDatabase.SaveAssets(); | |
AssetDatabase.Refresh(); | |
} | |
} | |
#endif | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment