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; private readonly VRCAvatarDescriptor.AnimLayerType type; private readonly Func currentFeatureNumProvider; private readonly Func currentFeatureNameProvider; private readonly string tmpDir; // These can't use AnimatorControllerLayer, because AnimatorControllerLayer is generated on request, not consistent private readonly HashSet managedLayers = new HashSet(); private readonly Dictionary layerOwners = new Dictionary(); private readonly ClipStorageManager clipStorage; private readonly VFAController _controller; public ControllerManager( AnimatorController ctrl, Func paramManager, VRCAvatarDescriptor.AnimLayerType type, Func currentFeatureNumProvider, Func 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 GetLayers() { return ctrl.layers.Select(l => l.stateMachine); } public IEnumerable GetManagedLayers() { return GetLayers().Where(IsManaged); } public IEnumerable 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 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 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); } } } }