avatar/Assets/VRCFury/Scripts/Editor/VF/Builder/Manager/ControllerManager.cs

315 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;
using VF.Builder.Exceptions;
using VRC.SDK3.Avatars.Components;
using VRC.SDK3.Avatars.ScriptableObjects;
namespace VF.Builder {
public class ControllerManager {
private readonly AnimatorController ctrl;
private readonly Func<ParamManager> paramManager;
private readonly VRCAvatarDescriptor.AnimLayerType type;
private readonly Func<int> currentFeatureNumProvider;
private readonly Func<string> currentFeatureNameProvider;
private readonly string tmpDir;
// These can't use AnimatorControllerLayer, because AnimatorControllerLayer is generated on request, not consistent
private readonly HashSet<AnimatorStateMachine> managedLayers = new HashSet<AnimatorStateMachine>();
private readonly Dictionary<AnimatorStateMachine, string> layerOwners =
new Dictionary<AnimatorStateMachine, string>();
private readonly ClipStorageManager clipStorage;
private readonly VFAController _controller;
public ControllerManager(
AnimatorController ctrl,
Func<ParamManager> paramManager,
VRCAvatarDescriptor.AnimLayerType type,
Func<int> currentFeatureNumProvider,
Func<string> currentFeatureNameProvider,
string tmpDir,
ClipStorageManager clipStorage
) {
this.ctrl = ctrl;
this.paramManager = paramManager;
this.type = type;
this.currentFeatureNumProvider = currentFeatureNumProvider;
this.currentFeatureNameProvider = currentFeatureNameProvider;
this.tmpDir = tmpDir;
this.clipStorage = clipStorage;
this._controller = new VFAController(ctrl, type);
if (ctrl.layers.Length == 0) {
// There was no base layer, just make one
GetController().NewLayer("Base Mask");
}
for (var i = 1; i < ctrl.layers.Length; i++) {
layerOwners[ctrl.layers[i].stateMachine] = "Base Avatar";
}
if (type == VRCAvatarDescriptor.AnimLayerType.Gesture && GetMask(0) == null) {
var mask = new AvatarMask();
for (AvatarMaskBodyPart bodyPart = 0; bodyPart < AvatarMaskBodyPart.LastBodyPart; bodyPart++) {
mask.SetHumanoidBodyPartActive(bodyPart, false);
}
VRCFuryAssetDatabase.SaveAsset(mask, tmpDir, "gestureMask");
SetMask(0, mask);
}
}
public AnimatorController GetRaw() {
return ctrl;
}
public new VRCAvatarDescriptor.AnimLayerType GetType() {
return type;
}
private VFAController GetController() {
return _controller;
}
public VFALayer NewLayer(string name, int insertAt = -1) {
var newLayer = GetController().NewLayer(NewLayerName(name), insertAt);
managedLayers.Add(newLayer.GetRawStateMachine());
layerOwners[newLayer.GetRawStateMachine()] = currentFeatureNameProvider();
return newLayer;
}
public void RemoveLayer(AnimatorStateMachine sm) {
var id = GetLayerId(sm);
managedLayers.Remove(sm);
layerOwners.Remove(sm);
GetController().RemoveLayer(id);
}
public int GetLayerId(AnimatorStateMachine sm) {
return GetLayers()
.Select((s, i) => (s, i))
.Where(tuple => tuple.Item1 == sm)
.Select(tuple => tuple.Item2)
.First();
}
public void TakeLayersFrom(AnimatorController other) {
other.layers = other.layers.Select((layer, i) => {
if (i == 0) layer.defaultWeight = 1;
layer.name = NewLayerName(layer.name);
managedLayers.Add(layer.stateMachine);
layerOwners[layer.stateMachine] = currentFeatureNameProvider();
return layer;
}).ToArray();
ctrl.layers = ctrl.layers.Concat(other.layers).ToArray();
}
public string NewLayerName(string name) {
return "[VF" + currentFeatureNumProvider() + "] " + name;
}
public IEnumerable<AnimatorStateMachine> GetLayers() {
return ctrl.layers.Select(l => l.stateMachine);
}
public IEnumerable<AnimatorStateMachine> GetManagedLayers() {
return GetLayers().Where(IsManaged);
}
public IEnumerable<AnimatorStateMachine> GetUnmanagedLayers() {
return GetLayers().Where(l => !IsManaged(l));
}
private bool IsManaged(AnimatorStateMachine layer) {
return managedLayers.Contains(layer);
}
public VFABool NewTrigger(string name, bool usePrefix = true) {
if (usePrefix) name = NewParamName(name);
return GetController().NewTrigger(name);
}
private ParamManager GetParamManager() {
return paramManager.Invoke();
}
public VFABool NewBool(string name, bool synced = false, bool def = false, bool saved = false, bool usePrefix = true, bool defTrueInEditor = false) {
if (usePrefix) name = NewParamName(name);
if (synced) {
var param = new VRCExpressionParameters.Parameter();
param.name = name;
param.valueType = VRCExpressionParameters.ValueType.Bool;
param.saved = saved;
param.defaultValue = def ? 1 : 0;
GetParamManager().addSyncedParam(param);
}
return GetController().NewBool(name, def || defTrueInEditor);
}
public VFANumber NewInt(string name, bool synced = false, int def = 0, bool saved = false, bool usePrefix = true) {
if (usePrefix) name = NewParamName(name);
if (synced) {
var param = new VRCExpressionParameters.Parameter();
param.name = name;
param.valueType = VRCExpressionParameters.ValueType.Int;
param.saved = saved;
param.defaultValue = def;
GetParamManager().addSyncedParam(param);
}
return GetController().NewInt(name, def);
}
public VFANumber NewFloat(string name, bool synced = false, float def = 0, bool saved = false, bool usePrefix = true) {
if (usePrefix) name = NewParamName(name);
if (synced) {
var param = new VRCExpressionParameters.Parameter();
param.name = name;
param.valueType = VRCExpressionParameters.ValueType.Float;
param.saved = saved;
param.defaultValue = def;
GetParamManager().addSyncedParam(param);
}
return GetController().NewFloat(name, def);
}
private string NewParamName(string name) {
return NewParamName(name, currentFeatureNumProvider.Invoke());
}
public static string NewParamName(string name, int modelNum) {
return "VF" + modelNum + "_" + name;
}
public VFABool True() {
return NewBool("VF_True", def: true, usePrefix: false);
}
public VFACondition Always() {
return True().IsTrue();
}
public VFACondition Never() {
return True().IsFalse();
}
public VFANumber GestureLeft() {
return NewInt("GestureLeft", usePrefix: false);
}
public VFANumber GestureRight() {
return NewInt("GestureRight", usePrefix: false);
}
public VFANumber GestureLeftWeight() {
return NewFloat("GestureLeftWeight", usePrefix: false);
}
public VFANumber GestureRightWeight() {
return NewFloat("GestureRightWeight", usePrefix: false);
}
public VFANumber Viseme() {
return NewInt("Viseme", usePrefix: false);
}
public VFABool IsLocal() {
return NewBool("IsLocal", usePrefix: false);
}
public void UnionBaseMask(AvatarMask sourceMask) {
if (sourceMask == null) return;
var mask = GetMask(0);
if (mask == null) return;
for (AvatarMaskBodyPart bodyPart = 0; bodyPart < AvatarMaskBodyPart.LastBodyPart; bodyPart++) {
if (sourceMask.GetHumanoidBodyPartActive(bodyPart))
mask.SetHumanoidBodyPartActive(bodyPart, true);
}
for (var i = 0; i < sourceMask.transformCount; i++) {
if (sourceMask.GetTransformActive(i)) {
mask.transformCount++;
mask.SetTransformPath(mask.transformCount-1, sourceMask.GetTransformPath(i));
mask.SetTransformActive(mask.transformCount-1, true);
}
}
}
public AvatarMask GetMask(int layerId) {
if (layerId < 0 || layerId >= ctrl.layers.Length) return null;
return ctrl.layers[layerId].avatarMask;
}
public void SetMask(int layerId, AvatarMask mask) {
if (layerId < 0 || layerId >= ctrl.layers.Length) return;
var layers = ctrl.layers;
layers[layerId].avatarMask = mask;
ctrl.layers = layers;
EditorUtility.SetDirty(ctrl);
}
public void SetName(int layerId, string name) {
if (layerId < 0 || layerId >= ctrl.layers.Length) return;
var layers = ctrl.layers;
layers[layerId].name = name;
ctrl.layers = layers;
EditorUtility.SetDirty(ctrl);
}
public ISet<string> GetLayerOwners() {
return layerOwners.Values.Distinct().ToImmutableHashSet();
}
public string GetLayerOwner(AnimatorStateMachine stateMachine) {
if (!layerOwners.TryGetValue(stateMachine, out var layerOwner)) {
return null;
}
return layerOwner;
}
public float GetWeight(AnimatorStateMachine sm) {
return GetWeight(GetLayerId(sm));
}
public float GetWeight(int layerId) {
return ctrl.layers[layerId].defaultWeight;
}
public void SetWeight(int layerId, float weight) {
var layers = ctrl.layers;
var layer = layers[layerId];
layer.defaultWeight = weight;
ctrl.layers = layers;
}
public void SetWeight(AnimatorStateMachine stateMachine, float weight) {
var layers = ctrl.layers;
var layer = layers.FirstOrDefault(l => l.stateMachine == stateMachine);
if (layer == null) throw new VRCFBuilderException("Failed to find layer for stateMachine");
layer.defaultWeight = weight;
ctrl.layers = layers;
}
public void ForEachClip(Action<MutableClip> action) {
foreach (var l in GetLayers()) {
AnimatorIterator.ForEachClip(l, clip => {
action(new MutableClip(clip));
});
}
}
public class MutableClip {
private AnimationClip clip;
public MutableClip(AnimationClip clip) {
this.clip = clip;
}
public EditorCurveBinding[] GetFloatBindings() {
return AnimationUtility.GetCurveBindings(clip);
}
public EditorCurveBinding[] GetObjectBindings() {
return AnimationUtility.GetObjectReferenceCurveBindings(clip);
}
public AnimationCurve GetFloatCurve(EditorCurveBinding binding) {
return AnimationUtility.GetEditorCurve(clip, binding);
}
public ObjectReferenceKeyframe[] GetObjectCurve(EditorCurveBinding binding) {
return AnimationUtility.GetObjectReferenceCurve(clip, binding);
}
public void SetFloatCurve(EditorCurveBinding binding, AnimationCurve curve) {
AnimationUtility.SetEditorCurve(clip, binding, curve);
}
public void SetObjectCurve(EditorCurveBinding binding, ObjectReferenceKeyframe[] curve) {
AnimationUtility.SetObjectReferenceCurve(clip, binding, curve);
}
}
}
}