avatar/Assets/VRCFury/Scripts/Editor/VF/Builder/ClipCopier.cs

172 lines
7.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using VF.Builder.Exceptions;
using Object = UnityEngine.Object;
namespace VF.Builder {
public class ClipCopier {
public static void Copy(
AnimationClip from,
AnimationClip to
) {
var fromC = new ControllerManager.MutableClip(from);
var toC = new ControllerManager.MutableClip(to);
foreach (var binding in fromC.GetFloatBindings())
toC.SetFloatCurve(binding, fromC.GetFloatCurve(binding));
foreach (var binding in fromC.GetObjectBindings())
toC.SetObjectCurve(binding, fromC.GetObjectCurve(binding));
}
public static void Rewrite(
AnimationClip clip_,
GameObject fromObj = null,
GameObject fromRoot = null,
List<string> removePrefixes = null,
string addPrefix = null,
bool rootBindingsApplyToAvatar = false,
Func<string,string> rewriteParam = null
) {
var clip = new ControllerManager.MutableClip(clip_);
string prefix;
if (fromObj == null) {
prefix = "";
} else if (fromRoot == null) {
throw new VRCFBuilderException("fromRoot != null && fromBase == null");
} else if (fromObj == fromRoot) {
prefix = "";
} else if (!fromObj.transform.IsChildOf(fromRoot.transform)) {
throw new VRCFBuilderException("fromRoot not child of fromBase");
} else {
prefix = AnimationUtility.CalculateTransformPath(fromObj.transform, fromRoot.transform);
}
string RewritePath(string path) {
if (removePrefixes != null) {
foreach (var removePrefix in removePrefixes) {
if (path.StartsWith(removePrefix + "/")) {
path = path.Substring(removePrefix.Length + 1);
} else if (path.StartsWith(removePrefix)) {
path = path.Substring(removePrefix.Length);
}
}
}
if (path == "" && rootBindingsApplyToAvatar) {
return "";
}
if (!string.IsNullOrWhiteSpace(addPrefix)) {
path = Join(addPrefix, path);
}
path = Join(prefix, path);
return path;
}
foreach (var originalBinding in clip.GetFloatBindings()) {
var rewrittenBinding = originalBinding;
rewrittenBinding.path = RewritePath(rewrittenBinding.path);
var curve = clip.GetFloatCurve(originalBinding);
var bindingToUse = rewrittenBinding;
if (originalBinding.path == "" && originalBinding.type == typeof(Animator)) {
bindingToUse = originalBinding;
var propName = originalBinding.propertyName;
if (GetIsMuscle(propName)) {
// Use the muscle
} else if (rewriteParam != null) {
//Debug.LogWarning("Rewritten prop found: " + bindingToUse.propertyName);
bindingToUse.propertyName = rewriteParam(bindingToUse.propertyName);
}
} else if (
rewrittenBinding.path == ""
&& rewrittenBinding.type == typeof(Transform)
&& rewrittenBinding.propertyName.StartsWith("m_LocalScale.")
&& fromRoot
&& GetFloatFromAvatar(fromRoot, originalBinding, out var avatarScale)
) {
curve.keys = curve.keys.Select(k => {
k.value *= avatarScale;
k.inTangent *= avatarScale;
k.outTangent *= avatarScale;
return k;
}).ToArray();
} else if (fromRoot) {
var existsOnProp = GetFloatFromAvatar(fromRoot, rewrittenBinding, out _);
var existsOnAvatar = GetFloatFromAvatar(fromRoot, originalBinding, out _);
if (existsOnAvatar && !existsOnProp)
bindingToUse = originalBinding;
}
if (originalBinding != bindingToUse) {
clip.SetFloatCurve(originalBinding, null);
clip.SetFloatCurve(bindingToUse, curve);
}
}
foreach (var originalBinding in clip.GetObjectBindings()) {
var rewrittenBinding = originalBinding;
rewrittenBinding.path = RewritePath(rewrittenBinding.path);
var curve = clip.GetObjectCurve(originalBinding);
var bindingToUse = rewrittenBinding;
if (fromRoot) {
var existsOnProp = GetObjectFromAvatar(fromRoot, rewrittenBinding, out _);
var existsOnAvatar = GetObjectFromAvatar(fromRoot, originalBinding, out _);
if (existsOnAvatar && !existsOnProp) {
bindingToUse = originalBinding;
}
}
if (originalBinding != bindingToUse) {
clip.SetObjectCurve(originalBinding, null);
clip.SetObjectCurve(bindingToUse, curve);
}
}
}
private static bool GetFloatFromAvatar(GameObject avatar, EditorCurveBinding binding, out float output) {
return AnimationUtility.GetFloatValue(avatar, binding, out output);
}
private static bool GetObjectFromAvatar(GameObject avatar, EditorCurveBinding binding, out Object output) {
return AnimationUtility.GetObjectReferenceValue(avatar, binding, out output);
}
private static string Join(params string[] paths) {
var ret = new List<string>();
foreach (var path in paths) {
if (path.StartsWith("/")) {
ret.Clear();
}
foreach (var part in path.Split('/')) {
if (part.Equals("..") && ret.Count > 0 && !"..".Equals(ret[ret.Count - 1])) {
ret.RemoveAt(ret.Count - 1);
} else if (part == "." || part == "") {
// omit this chunk
} else {
ret.Add(part);
}
}
}
return string.Join("/", ret);
}
private static HashSet<string> _humanMuscleList;
private static HashSet<string> GetHumanMuscleList() {
if (_humanMuscleList != null) return _humanMuscleList;
_humanMuscleList = new HashSet<string>();
_humanMuscleList.UnionWith(HumanTrait.MuscleName);
return _humanMuscleList;
}
private static bool GetIsMuscle(string name) {
return GetHumanMuscleList().Contains(name)
|| name.EndsWith(" Stretched")
|| name.EndsWith(".Spread")
|| name.EndsWith(".x")
|| name.EndsWith(".y")
|| name.EndsWith(".z")
|| name.EndsWith(".w");
}
}
}