Skip to content

Instantly share code, notes, and snippets.

@phi16
Last active November 13, 2022 12:56
Show Gist options
  • Save phi16/fb81ce82ea075c012fd8beaf3cc5860e to your computer and use it in GitHub Desktop.
Save phi16/fb81ce82ea075c012fd8beaf3cc5860e to your computer and use it in GitHub Desktop.
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