summaryrefslogtreecommitdiff
path: root/VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Scripts/LyumaAv3Runtime.cs
diff options
context:
space:
mode:
authortylermurphy534 <tylermurphy534@gmail.com>2022-11-06 15:12:42 -0500
committertylermurphy534 <tylermurphy534@gmail.com>2022-11-06 15:12:42 -0500
commiteb84bb298d2b95aec7b2ae12cbf25ac64f25379a (patch)
treeefd616a157df06ab661c6d56651853431ac6b08b /VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Scripts/LyumaAv3Runtime.cs
downloadunityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.gz
unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.bz2
unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.zip
move to self host
Diffstat (limited to 'VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Scripts/LyumaAv3Runtime.cs')
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Scripts/LyumaAv3Runtime.cs1591
1 files changed, 1591 insertions, 0 deletions
diff --git a/VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Scripts/LyumaAv3Runtime.cs b/VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Scripts/LyumaAv3Runtime.cs
new file mode 100644
index 00000000..4b05b425
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/Resources/Lyuma/Av3Emulator/Scripts/LyumaAv3Runtime.cs
@@ -0,0 +1,1591 @@
+/* Copyright (c) 2020 Lyuma <xn.lyuma@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE. */
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.Animations;
+using UnityEngine.Playables;
+using VRC.SDK3.Avatars.Components;
+using VRC.SDK3.Avatars.ScriptableObjects;
+
+// [RequireComponent(typeof(Animator))]
+public class LyumaAv3Runtime : MonoBehaviour
+{
+ static public Dictionary<VRCAvatarDescriptor.AnimLayerType, RuntimeAnimatorController> animLayerToDefaultController = new Dictionary<VRCAvatarDescriptor.AnimLayerType, RuntimeAnimatorController>();
+ static public Dictionary<VRCAvatarDescriptor.AnimLayerType, AvatarMask> animLayerToDefaultAvaMask = new Dictionary<VRCAvatarDescriptor.AnimLayerType, AvatarMask>();
+ public delegate void UpdateSelectionFunc(GameObject obj);
+ public static UpdateSelectionFunc updateSelectionDelegate;
+ public delegate void AddRuntime(LyumaAv3Runtime runtime);
+ public static AddRuntime addRuntimeDelegate;
+
+ [Tooltip("Resets avatar state machine instantly")]
+ public bool ResetAvatar;
+ [Tooltip("Resets avatar state machine and waits until you uncheck this to start")]
+ public bool ResetAndHold;
+ [Tooltip("Simulates saving and reloading the avatar")]
+ public bool KeepSavedParametersOnReset = true;
+ [Tooltip("In VRChat, 8-bit float quantization only happens remotely. Check this to test your robustness to quantization locally, too. (example: 0.5 -> 0.503")]
+ public bool locally8bitQuantizedFloats = false;
+ [Tooltip("Selects the playable layer to be visible in Unity's Animator window. Unless this is set to Base, creates duplicate playable layers with weight 0. It hopefully works the same.")] public VRCAvatarDescriptor.AnimLayerType AnimatorToDebug;
+ private char PrevAnimatorToDebug;
+ [HideInInspector] public string SourceObjectPath;
+ [Header("Assign to non-local duplicate")]public LyumaAv3Runtime AvatarSyncSource;
+ private int CloneCount;
+ public bool CreateNonLocalClone;
+ VRCAvatarDescriptor avadesc;
+ Avatar animatorAvatar;
+ Animator animator;
+ private RuntimeAnimatorController origAnimatorController;
+
+ private List<AnimatorControllerPlayable> playables = new List<AnimatorControllerPlayable>();
+ private List<Dictionary<string, int>> playableParamterIds = new List<Dictionary<string, int>>();
+ private List<Dictionary<int, float>> playableParamterFloats = new List<Dictionary<int, float>>();
+ private List<Dictionary<int, int>> playableParamterInts = new List<Dictionary<int, int>>();
+ private List<Dictionary<int, bool>> playableParamterBools = new List<Dictionary<int, bool>>();
+ AnimationLayerMixerPlayable playableMixer;
+ PlayableGraph playableGraph;
+ VRCExpressionsMenu expressionsMenu;
+ VRCExpressionParameters stageParameters;
+ int sittingIndex, tposeIndex, ikposeIndex;
+ int fxIndex, altFXIndex;
+ int actionIndex, altActionIndex;
+ int additiveIndex, altAdditiveIndex;
+ int gestureIndex, altGestureIndex;
+
+ private int mouthOpenBlendShapeIdx;
+ private int[] visemeBlendShapeIdxs;
+
+ public static float ClampFloatOnly(float val) {
+ if (val < -1.0f) {
+ val = -1.0f;
+ }
+ if (val > 1.0f) {
+ val = 1.0f;
+ }
+ return val;
+ }
+ public static float ClampAndQuantizeFloat(float val) {
+ val = ClampFloatOnly(val);
+ val *= 127.00f;
+ // if (val > 127.0f) {
+ // val = 127.0f;
+ // }
+ val = Mathf.Round(val);
+ val = (((sbyte)val) / 127.0f);
+ val = ClampFloatOnly(val);
+ return val;
+ }
+ public static int ClampByte(int val) {
+ if (val < 0) {
+ val = 0;
+ }
+ if (val > 255) {
+ val = 255;
+ }
+ return val;
+ }
+
+ public enum VisemeIndex {
+ sil, PP, FF, TH, DD, kk, CH, SS, nn, RR, aa, E, I, O, U
+ }
+ public enum GestureIndex {
+ Neutral, Fist, HandOpen, Fingerpoint, Victory, RockNRoll, HandGun, ThumbsUp
+ }
+ public enum TrackingTypeIndex {
+ Uninitialized, GenericRig, NoFingers, HeadHands, HeadHandsHip, HeadHandsHipFeet = 6
+ }
+ static HashSet<string> BUILTIN_PARAMETERS = new HashSet<string> {
+ "Viseme", "GestureLeft", "GestureLeftWeight", "GestureRight", "GestureRightWeight", "VelocityX", "VelocityY", "VelocityZ", "LocomotionMode", "Upright", "AngularY", "GroundProximity", "Grounded", "Supine", "FootstepDisable", "Seated", "AFK", "TrackingType", "VRMode", "MuteSelf", "InStation"
+ };
+ [Header("Built-in inputs / Viseme")]
+ public VisemeIndex Viseme;
+ [Range(0, 15)] public int VisemeIdx;
+ private int VisemeInt;
+ [Header("Built-in inputs / Hand Gestures")]
+ public GestureIndex GestureLeft;
+ [Range(0, 9)] public int GestureLeftIdx;
+ private char GestureLeftIdxInt;
+ [Range(0, 1)] public float GestureLeftWeight;
+ public GestureIndex GestureRight;
+ [Range(0, 9)] public int GestureRightIdx;
+ private char GestureRightIdxInt;
+ [Range(0, 1)] public float GestureRightWeight;
+ [Header("Built-in inputs / Locomotion")]
+ public Vector3 Velocity;
+ [Range(-1, 1)] public float AngularY;
+ [Range(0, 1)] public float Upright;
+ [Range(-1, 1)] public float GroundProximity; // Not implemented
+ private int LocomotionMode; // Does not exist.
+ public bool Grounded;
+ private bool PrevSeated, PrevTPoseCalibration, PrevIKPoseCalibration;
+ public bool Seated;
+ public bool AFK;
+ public bool TPoseCalibration;
+ public bool IKPoseCalibration;
+ //TODO:
+ bool Supine; // Not implemented
+ private bool FootstepDisable; // Does not exist.
+ [Header("Built-in inputs / Tracking Setup and Other")]
+ public TrackingTypeIndex TrackingType;
+ [Range(0, 6)] public int TrackingTypeIdx;
+ private char TrackingTypeIdxInt;
+ public bool VRMode;
+ public bool MuteSelf;
+ public bool InStation;
+ [HideInInspector] public int AvatarVersion = 3;
+
+ [Header("Output State (Read-only)")]
+ public bool IsLocal;
+ public bool LocomotionIsDisabled;
+ private Vector3 HeadRelativeViewPosition;
+ public Vector3 ViewPosition;
+ float AvatarScaleFactor;
+ public VRCAnimatorTrackingControl.TrackingType trackingHead;
+ public VRCAnimatorTrackingControl.TrackingType trackingLeftHand;
+ public VRCAnimatorTrackingControl.TrackingType trackingRightHand;
+ public VRCAnimatorTrackingControl.TrackingType trackingHip;
+ public VRCAnimatorTrackingControl.TrackingType trackingLeftFoot;
+ public VRCAnimatorTrackingControl.TrackingType trackingRightFoot;
+ public VRCAnimatorTrackingControl.TrackingType trackingLeftFingers;
+ public VRCAnimatorTrackingControl.TrackingType trackingRightFingers;
+ public VRCAnimatorTrackingControl.TrackingType trackingEyesAndEyelids;
+ public VRCAnimatorTrackingControl.TrackingType trackingMouthAndJaw;
+
+ [Serializable]
+ public class FloatParam
+ {
+ [HideInInspector] public string stageName;
+ public string name;
+ [HideInInspector] public bool synced;
+ [Range(-1, 1)] public float value;
+ [HideInInspector] public float lastValue;
+ }
+ [Header("User-generated inputs")]
+ public List<FloatParam> Floats = new List<FloatParam>();
+ public Dictionary<string, int> FloatToIndex = new Dictionary<string, int>();
+
+ [Serializable]
+ public class IntParam
+ {
+ [HideInInspector] public string stageName;
+ public string name;
+ [HideInInspector] public bool synced;
+ public int value;
+ [HideInInspector] public int lastValue;
+ }
+ public List<IntParam> Ints = new List<IntParam>();
+ public Dictionary<string, int> IntToIndex = new Dictionary<string, int>();
+
+ [Serializable]
+ public class BoolParam
+ {
+ [HideInInspector] public string stageName;
+
+ public string name;
+ [HideInInspector] public bool synced;
+ public bool value;
+ [HideInInspector] public bool lastValue;
+ [HideInInspector] public bool[] hasTrigger;
+ [HideInInspector] public bool[] hasBool;
+ }
+ public List<BoolParam> Bools = new List<BoolParam>();
+ public Dictionary<string, int> BoolToIndex = new Dictionary<string, int>();
+
+ public Dictionary<string, string> StageParamterToBuiltin = new Dictionary<string, string>();
+
+ public LyumaAv3Emulator emulator;
+
+ static public Dictionary<Animator, LyumaAv3Runtime> animatorToTopLevelRuntime = new Dictionary<Animator, LyumaAv3Runtime>();
+ private HashSet<Animator> attachedAnimators;
+ private HashSet<string> duplicateParameterAdds = new HashSet<string>();
+
+ const float BASE_HEIGHT = 1.4f;
+
+ public IEnumerator DelayedEnterPoseSpace(bool setView, float time) {
+ yield return new WaitForSeconds(time);
+ if (setView) {
+ Transform head = animator.GetBoneTransform(HumanBodyBones.Head);
+ if (head != null) {
+ ViewPosition = animator.transform.InverseTransformPoint(head.TransformPoint(HeadRelativeViewPosition));
+ }
+ } else {
+ ViewPosition = avadesc.ViewPosition;
+ }
+ }
+
+ class BlendingState {
+ float startWeight;
+ float goalWeight;
+ float blendStartTime;
+ float blendDuration;
+ public bool blending;
+
+ public float UpdateBlending() {
+ if (blendDuration <= 0) {
+ blending = false;
+ return goalWeight;
+ }
+ float amt = (Time.time - blendStartTime) / blendDuration;
+ if (amt >= 1) {
+ blending = false;
+ return goalWeight;
+ }
+ return Mathf.Lerp(startWeight, goalWeight, amt);
+ }
+ public void StartBlend(float startWeight, float goalWeight, float duration) {
+ this.startWeight = startWeight;
+ this.blendDuration = duration;
+ this.blendStartTime = Time.time;
+ this.goalWeight = goalWeight;
+ this.blending = true;
+ }
+ }
+ class PlayableBlendingState : BlendingState {
+ public List<BlendingState> layerBlends = new List<BlendingState>();
+
+ }
+ List<PlayableBlendingState> playableBlendingStates = new List<PlayableBlendingState>();
+
+ static bool getTopLevelRuntime(string component, Animator innerAnimator, out LyumaAv3Runtime runtime) {
+ if (animatorToTopLevelRuntime.TryGetValue(innerAnimator, out runtime)) {
+ return true;
+ }
+ Transform transform = innerAnimator.transform;
+ while (transform != null && runtime == null) {
+ runtime = transform.GetComponent<LyumaAv3Runtime>();
+ transform = transform.parent;
+ }
+ if (runtime != null) {
+ if (runtime.attachedAnimators != null) {
+ Debug.Log("[" + component + "]: " + innerAnimator + " found parent runtime after it was Awoken! Adding to cache. Did you move me?");
+ animatorToTopLevelRuntime.Add(innerAnimator, runtime);
+ runtime.attachedAnimators.Add(innerAnimator);
+ } else {
+ Debug.Log("[" + component + "]: " + innerAnimator + " found parent runtime without being Awoken! Wakey Wakey...");
+ runtime.Awake();
+ }
+ return true;
+ }
+ Debug.LogError("[" + component + "]: outermost Animator is not known: " + innerAnimator + ". If you changed something, consider resetting avatar", innerAnimator);
+
+ return false;
+ }
+
+ static LyumaAv3Runtime() {
+ VRCAvatarParameterDriver.Initialize += (x) => {
+ x.ApplySettings += (behaviour, animator) =>
+ {
+ LyumaAv3Runtime runtime;
+ if (!getTopLevelRuntime("VRCAvatarParameterDriver", animator, out runtime)) {
+ return;
+ }
+ if (behaviour.debugString != null && behaviour.debugString.Length > 0)
+ {
+ Debug.Log("[VRCAvatarParameterDriver:" + (runtime == null ? "null" : runtime.name) + "]" + behaviour.name + ": " + behaviour.debugString, behaviour);
+ }
+ if (!runtime)
+ {
+ return;
+ }
+ if (animator != runtime.animator && (!runtime.emulator || !runtime.emulator.legacySubAnimatorParameterDriverMode)) {
+ return;
+ }
+ if (!runtime.IsLocal && behaviour.localOnly) {
+ return;
+ }
+ HashSet<string> newParameterAdds = new HashSet<string>();
+ HashSet<string> deleteParameterAdds = new HashSet<string>();
+ foreach (var parameter in behaviour.parameters) {
+ if (parameter.type == VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Add || parameter.type == VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Random) {
+ string dupeKey = parameter.value + ((parameter.type == VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Add) ? "add " : "rand ") + parameter.name;
+ if (!runtime.duplicateParameterAdds.Contains(dupeKey)) {
+ newParameterAdds.Add(dupeKey);
+ continue;
+ }
+ deleteParameterAdds.Add(dupeKey);
+ }
+ string actualName = parameter.name;
+ int idx;
+ if (runtime.IntToIndex.TryGetValue(actualName, out idx)) {
+ switch (parameter.type) {
+ case VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Set:
+ runtime.Ints[idx].value = (int)parameter.value;
+ break;
+ case VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Add:
+ runtime.Ints[idx].value += (int)parameter.value;
+ break;
+ case VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Random:
+ runtime.Ints[idx].value = UnityEngine.Random.Range((int)parameter.valueMin, (int)parameter.valueMax);
+ break;
+ }
+ }
+ if (runtime.FloatToIndex.TryGetValue(actualName, out idx)) {
+ switch (parameter.type) {
+ case VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Set:
+ runtime.Floats[idx].value = parameter.value;
+ break;
+ case VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Add:
+ runtime.Floats[idx].value += parameter.value;
+ break;
+ case VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Random:
+ runtime.Floats[idx].value = UnityEngine.Random.Range(parameter.valueMin, parameter.valueMax);
+ break;
+ }
+ }
+ if (runtime.BoolToIndex.TryGetValue(actualName, out idx)) {
+ bool newValue;
+ BoolParam bp = runtime.Bools[idx];
+ int whichController;
+ // bp.value = parameter.value != 0;
+ switch (parameter.type) {
+ case VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Set:
+ newValue = parameter.value != 0.0f;
+ if (!bp.synced) {
+ // Triggers ignore alue and Set unconditionally.
+ whichController = 0;
+ foreach (var p in runtime.playables) {
+ if (bp.hasBool[whichController]) {
+ p.SetBool(actualName, newValue);
+ }
+ whichController++;
+ }
+ whichController = 0;
+ foreach (var p in runtime.playables) {
+ if (bp.hasTrigger[whichController]) {
+ p.SetTrigger(actualName);
+ }
+ whichController++;
+ }
+ bp.lastValue = newValue;
+ }
+ bp.value = newValue;
+ break;
+ case VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Add:
+ /* editor script treats it as random, but it is its own operation */
+ newValue = ((bp.value ? 1.0 : 0.0) + parameter.value) != 0.0f; // weird but ok...
+ Debug.Log("Add bool " + bp.name + " to " + newValue + ", " + (bp.value ? 1.0 : 0.0) + ", " + parameter.value);
+ if (!bp.synced) {
+ newValue = parameter.value != 0.0f;
+ whichController = 0;
+ // Triggers ignore value and Set unconditionally.
+ foreach (var p in runtime.playables) {
+ if (bp.hasBool[whichController]) {
+ p.SetBool(actualName, newValue);
+ }
+ whichController++;
+ }
+ whichController = 0;
+ foreach (var p in runtime.playables) {
+ if (bp.hasTrigger[whichController]) {
+ p.SetTrigger(actualName);
+ }
+ whichController++;
+ }
+ bp.lastValue = newValue;
+ }
+ bp.value = newValue;
+ break;
+ case VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Random:
+ // random is *not* idempotent.
+ newValue = UnityEngine.Random.Range(0.0f, 1.0f) < parameter.chance;
+ if (!bp.synced) {
+ whichController = 0;
+ foreach (var p in runtime.playables) {
+ if (bp.hasBool[whichController]) {
+ p.SetBool(actualName, newValue);
+ }
+ whichController++;
+ }
+ if (newValue) {
+ whichController = 0;
+ foreach (var p in runtime.playables) {
+ if (bp.hasTrigger[whichController]) {
+ p.SetTrigger(actualName);
+ }
+ whichController++;
+ }
+ }
+ bp.lastValue = newValue;
+ }
+ bp.value = newValue;
+ break;
+ }
+ }
+ }
+ foreach (var key in deleteParameterAdds) {
+ runtime.duplicateParameterAdds.Remove(key);
+ }
+ foreach (var key in newParameterAdds) {
+ runtime.duplicateParameterAdds.Add(key);
+ }
+ };
+ };
+ VRCPlayableLayerControl.Initialize += (x) => {
+ x.ApplySettings += (behaviour, animator) =>
+ {
+ LyumaAv3Runtime runtime;
+ if (!getTopLevelRuntime("VRCPlayableLayerControl", animator, out runtime)) {
+ return;
+ }
+ if (behaviour.debugString != null && behaviour.debugString.Length > 0)
+ {
+ Debug.Log("[VRCPlayableLayerControl:" + (runtime == null ? "null" : runtime.name) + "]" + behaviour.name + ": " + behaviour.debugString, behaviour);
+ }
+ if (!runtime)
+ {
+ return;
+ }
+ int idx = -1;
+ switch (behaviour.layer)
+ {
+ case VRCPlayableLayerControl.BlendableLayer.Action:
+ idx = runtime.actionIndex;
+ break;
+ case VRCPlayableLayerControl.BlendableLayer.Additive:
+ idx = runtime.additiveIndex;
+ break;
+ case VRCPlayableLayerControl.BlendableLayer.FX:
+ idx = runtime.fxIndex;
+ break;
+ case VRCPlayableLayerControl.BlendableLayer.Gesture:
+ idx = runtime.gestureIndex;
+ break;
+ }
+ if (idx >= 0 && idx < runtime.playableBlendingStates.Count)
+ {
+ runtime.playableBlendingStates[idx].StartBlend(runtime.playableMixer.GetInputWeight(idx + 1), behaviour.goalWeight, behaviour.blendDuration);
+ // Debug.Log("Start blend of whole playable " + idx + " from " + runtime.playableMixer.GetInputWeight(idx + 1) + " to " + behaviour.goalWeight);
+ }
+ };
+ };
+ VRCAnimatorLayerControl.Initialize += (x) => {
+ x.ApplySettings += (behaviour, animator) =>
+ {
+ LyumaAv3Runtime runtime;
+ if (!getTopLevelRuntime("VRCAnimatorLayerControl", animator, out runtime)) {
+ return;
+ }
+ if (behaviour.debugString != null && behaviour.debugString.Length > 0)
+ {
+ Debug.Log("[VRCAnimatorLayerControl:" + (runtime == null ? "null" : runtime.name) + "]" + behaviour.name + ": " + behaviour.debugString, behaviour);
+ }
+ if (!runtime)
+ {
+ return;
+ }
+ int idx = -1, altidx = -1;
+ switch (behaviour.playable)
+ {
+ case VRCAnimatorLayerControl.BlendableLayer.Action:
+ idx = runtime.actionIndex;
+ altidx = runtime.altActionIndex;
+ break;
+ case VRCAnimatorLayerControl.BlendableLayer.Additive:
+ idx = runtime.additiveIndex;
+ altidx = runtime.altAdditiveIndex;
+ break;
+ case VRCAnimatorLayerControl.BlendableLayer.FX:
+ idx = runtime.fxIndex;
+ altidx = runtime.altFXIndex;
+ break;
+ case VRCAnimatorLayerControl.BlendableLayer.Gesture:
+ idx = runtime.gestureIndex;
+ altidx = runtime.altGestureIndex;
+ break;
+ }
+ if (idx >= 0 && idx < runtime.playableBlendingStates.Count)
+ {
+ if (behaviour.layer >= 0 && behaviour.layer < runtime.playableBlendingStates[idx].layerBlends.Count)
+ {
+ runtime.playableBlendingStates[idx].layerBlends[behaviour.layer].StartBlend(runtime.playables[idx].GetLayerWeight(behaviour.layer), behaviour.goalWeight, behaviour.blendDuration);
+ // Debug.Log("Start blend of playable " + idx + " layer " + behaviour.layer + " from " + runtime.playables[idx].GetLayerWeight(behaviour.layer) + " to " + behaviour.goalWeight);
+ if (altidx >= 0) {
+ runtime.playableBlendingStates[altidx].layerBlends[behaviour.layer].StartBlend(runtime.playables[altidx].GetLayerWeight(behaviour.layer), behaviour.goalWeight, behaviour.blendDuration);
+ // Debug.Log("Start blend of alt playable " + altidx + " layer " + behaviour.layer + " from " + runtime.playables[altidx].GetLayerWeight(behaviour.layer) + " to " + behaviour.goalWeight);
+ }
+ }
+ }
+ };
+ };
+ VRCAnimatorLocomotionControl.Initialize += (x) => {
+ x.ApplySettings += (behaviour, animator) =>
+ {
+ LyumaAv3Runtime runtime;
+ if (!getTopLevelRuntime("VRCAnimatorLocomotionControl", animator, out runtime)) {
+ return;
+ }
+ if (behaviour.debugString != null && behaviour.debugString.Length > 0)
+ {
+ Debug.Log("[VRCAnimatorLocomotionControl:" + (runtime == null ? "null" : runtime.name) + "]" + behaviour.name + ": " + behaviour.debugString, behaviour);
+ }
+ if (!runtime)
+ {
+ return;
+ }
+ // I legit don't know
+ runtime.LocomotionIsDisabled = behaviour.disableLocomotion;
+ };
+ };
+ VRCAnimatorTemporaryPoseSpace.Initialize += (x) => {
+ x.ApplySettings += (behaviour, animator) =>
+ {
+ LyumaAv3Runtime runtime;
+ if (!getTopLevelRuntime("VRCAnimatorSetView", animator, out runtime)) {
+ return;
+ }
+ if (behaviour.debugString != null && behaviour.debugString.Length > 0)
+ {
+ Debug.Log("[VRCAnimatorSetView:" + (runtime == null ? "null" : runtime.name) + "]" + behaviour.name + ": " + behaviour.debugString, behaviour);
+ }
+ if (!runtime)
+ {
+ return;
+ }
+ // fixedDelay: Is the delay fixed or normalized...
+ // The layerIndex is not passed into the delegate, so we cannot reimplement fixedDelay.
+ runtime.StartCoroutine(runtime.DelayedEnterPoseSpace(behaviour.enterPoseSpace, behaviour.delayTime));
+ };
+ };
+ VRCAnimatorTrackingControl.Initialize += (x) => {
+ x.ApplySettings += (behaviour, animator) =>
+ {
+ LyumaAv3Runtime runtime;
+ if (!getTopLevelRuntime("VRCAnimatorTrackingControl", animator, out runtime)) {
+ return;
+ }
+ if (behaviour.debugString != null && behaviour.debugString.Length > 0)
+ {
+ Debug.Log("[VRCAnimatorTrackingControl:" + (runtime == null ? "null" : runtime.name) + "]" + behaviour.name + ": " + behaviour.debugString, behaviour);
+ }
+ if (!runtime)
+ {
+ return;
+ }
+
+ if (behaviour.trackingMouth != VRCAnimatorTrackingControl.TrackingType.NoChange)
+ {
+ runtime.trackingMouthAndJaw = behaviour.trackingMouth;
+ }
+ if (behaviour.trackingHead != VRCAnimatorTrackingControl.TrackingType.NoChange)
+ {
+ runtime.trackingHead = behaviour.trackingHead;
+ }
+ if (behaviour.trackingRightFingers != VRCAnimatorTrackingControl.TrackingType.NoChange)
+ {
+ runtime.trackingRightFingers = behaviour.trackingRightFingers;
+ }
+ if (behaviour.trackingEyes != VRCAnimatorTrackingControl.TrackingType.NoChange)
+ {
+ runtime.trackingEyesAndEyelids = behaviour.trackingEyes;
+ }
+ if (behaviour.trackingLeftFingers != VRCAnimatorTrackingControl.TrackingType.NoChange)
+ {
+ runtime.trackingLeftFingers = behaviour.trackingLeftFingers;
+ }
+ if (behaviour.trackingLeftFoot != VRCAnimatorTrackingControl.TrackingType.NoChange)
+ {
+ runtime.trackingLeftFoot = behaviour.trackingLeftFoot;
+ }
+ if (behaviour.trackingHip != VRCAnimatorTrackingControl.TrackingType.NoChange)
+ {
+ runtime.trackingHip = behaviour.trackingHip;
+ }
+ if (behaviour.trackingRightHand != VRCAnimatorTrackingControl.TrackingType.NoChange)
+ {
+ runtime.trackingRightHand = behaviour.trackingRightHand;
+ }
+ if (behaviour.trackingLeftHand != VRCAnimatorTrackingControl.TrackingType.NoChange)
+ {
+ runtime.trackingLeftHand = behaviour.trackingLeftHand;
+ }
+ if (behaviour.trackingRightFoot != VRCAnimatorTrackingControl.TrackingType.NoChange)
+ {
+ runtime.trackingRightFoot = behaviour.trackingRightFoot;
+ }
+ };
+ };
+ }
+
+ void OnDestroy () {
+ if (this.playableGraph.IsValid()) {
+ this.playableGraph.Destroy();
+ }
+ foreach (var anim in attachedAnimators) {
+ LyumaAv3Runtime runtime;
+ if (animatorToTopLevelRuntime.TryGetValue(anim, out runtime) && runtime == this)
+ {
+ animatorToTopLevelRuntime.Remove(anim);
+ }
+ }
+ if (animator.playableGraph.IsValid())
+ {
+ animator.playableGraph.Destroy();
+ }
+ animator.runtimeAnimatorController = origAnimatorController;
+ }
+
+ void Awake()
+ {
+ if (attachedAnimators != null) {
+ Debug.Log("Deduplicating Awake() call if we already got awoken by our children.");
+ return;
+ }
+ attachedAnimators = new HashSet<Animator>();
+ if (AvatarSyncSource == null) {
+ Transform transform = this.transform;
+ SourceObjectPath = "";
+ while (transform != null) {
+ SourceObjectPath = "/" + transform.name + SourceObjectPath;
+ transform = transform.parent;
+ }
+ AvatarSyncSource = this;
+ } else {
+ AvatarSyncSource = GameObject.Find(SourceObjectPath).GetComponent<LyumaAv3Runtime>();
+ }
+
+ AnimatorToDebug = VRCAvatarDescriptor.AnimLayerType.Base;
+
+ if (LyumaAv3Emulator.emulatorInstance == null) {
+ Debug.LogError("LyumaAv3Runtime awoken without an LyumaAv3Emulator instance!", this);
+ } else {
+ this.VRMode = LyumaAv3Emulator.emulatorInstance.DefaultToVR;
+ this.TrackingType = LyumaAv3Emulator.emulatorInstance.DefaultTrackingType;
+ this.InStation = LyumaAv3Emulator.emulatorInstance.DefaultTestInStation;
+ this.AnimatorToDebug = LyumaAv3Emulator.emulatorInstance.DefaultAnimatorToDebug;
+ }
+
+ animator = this.gameObject.GetOrAddComponent<Animator>();
+ animatorAvatar = animator.avatar;
+ // Default values.
+ Grounded = true;
+ Upright = 1.0f;
+ if (!animator.isHuman) {
+ TrackingType = TrackingTypeIndex.GenericRig;
+ } else if (!VRMode) {
+ TrackingType = TrackingTypeIndex.HeadHands;
+ }
+ avadesc = this.gameObject.GetComponent<VRCAvatarDescriptor>();
+ if (avadesc.VisemeSkinnedMesh == null) {
+ mouthOpenBlendShapeIdx = -1;
+ visemeBlendShapeIdxs = new int[0];
+ } else {
+ mouthOpenBlendShapeIdx = avadesc.VisemeSkinnedMesh.sharedMesh.GetBlendShapeIndex(avadesc.MouthOpenBlendShapeName);
+ visemeBlendShapeIdxs = new int[avadesc.VisemeBlendShapes == null ? 0 : avadesc.VisemeBlendShapes.Length];
+ if (avadesc.VisemeBlendShapes != null) {
+ for (int i = 0; i < avadesc.VisemeBlendShapes.Length; i++) {
+ visemeBlendShapeIdxs[i] = avadesc.VisemeSkinnedMesh.sharedMesh.GetBlendShapeIndex(avadesc.VisemeBlendShapes[i]);
+ }
+ }
+ }
+ InitializeAnimator();
+ if (addRuntimeDelegate != null) {
+ addRuntimeDelegate(this);
+ }
+ }
+
+ private void InitializeAnimator()
+ {
+ ResetAvatar = false;
+ PrevAnimatorToDebug = (char)(int)AnimatorToDebug;
+
+ animator = this.gameObject.GetOrAddComponent<Animator>();
+ animator.avatar = animatorAvatar;
+ animator.applyRootMotion = false;
+ animator.updateMode = AnimatorUpdateMode.Normal;
+ animator.cullingMode = AnimatorCullingMode.CullCompletely;
+ animator.runtimeAnimatorController = null;
+
+ avadesc = this.gameObject.GetComponent<VRCAvatarDescriptor>();
+ ViewPosition = avadesc.ViewPosition;
+ AvatarScaleFactor = ViewPosition.magnitude / BASE_HEIGHT; // mostly guessing...
+ HeadRelativeViewPosition = ViewPosition;
+ if (animator.avatar != null)
+ {
+ Transform head = animator.GetBoneTransform(HumanBodyBones.Head);
+ if (head != null) {
+ HeadRelativeViewPosition = head.InverseTransformPoint(animator.transform.TransformPoint(ViewPosition));
+ }
+ }
+ expressionsMenu = avadesc.expressionsMenu;
+ if (expressionsMenu != null)
+ {
+ stageParameters = avadesc.expressionParameters;
+ }
+ if (origAnimatorController != null) {
+ origAnimatorController = animator.runtimeAnimatorController;
+ }
+
+ VRCAvatarDescriptor.CustomAnimLayer[] baselayers = avadesc.baseAnimationLayers;
+ VRCAvatarDescriptor.CustomAnimLayer[] speciallayers = avadesc.specialAnimationLayers;
+ List<VRCAvatarDescriptor.CustomAnimLayer> allLayers = new List<VRCAvatarDescriptor.CustomAnimLayer>();
+ // foreach (VRCAvatarDescriptor.CustomAnimLayer cal in baselayers) {
+ // if (AnimatorToDebug == cal.type) {
+ // allLayers.Add(cal);
+ // }
+ // }
+ // foreach (VRCAvatarDescriptor.CustomAnimLayer cal in speciallayers) {
+ // if (AnimatorToDebug == cal.type) {
+ // allLayers.Add(cal);
+ // }
+ // }
+ int i = 0;
+ if (AnimatorToDebug != VRCAvatarDescriptor.AnimLayerType.Base) {
+ foreach (VRCAvatarDescriptor.CustomAnimLayer cal in baselayers) {
+ if (AnimatorToDebug == cal.type) {
+ i++;
+ allLayers.Add(cal);
+ break;
+ }
+ }
+ foreach (VRCAvatarDescriptor.CustomAnimLayer cal in speciallayers) {
+ if (AnimatorToDebug == cal.type) {
+ i++;
+ allLayers.Add(cal);
+ break;
+ }
+ }
+ // WE ADD ALL THE LAYERS A SECOND TIME BECAUSE!
+ // Add and Random Parameter drivers are not idepotent.
+ // To solve this, we ignore every other invocation.
+ // Therefore, we must add all layers twice, not just the one we are debugging...???
+ foreach (VRCAvatarDescriptor.CustomAnimLayer cal in baselayers) {
+ if (AnimatorToDebug != cal.type) {
+ i++;
+ allLayers.Add(cal);
+ }
+ }
+ foreach (VRCAvatarDescriptor.CustomAnimLayer cal in speciallayers) {
+ if (AnimatorToDebug != cal.type) {
+ i++;
+ allLayers.Add(cal);
+ }
+ }
+ }
+ int dupeOffset = i;
+ foreach (VRCAvatarDescriptor.CustomAnimLayer cal in baselayers) {
+ if (cal.type == VRCAvatarDescriptor.AnimLayerType.Base || cal.type == VRCAvatarDescriptor.AnimLayerType.Additive) {
+ i++;
+ allLayers.Add(cal);
+ }
+ }
+ foreach (VRCAvatarDescriptor.CustomAnimLayer cal in speciallayers) {
+ i++;
+ allLayers.Add(cal);
+ }
+ foreach (VRCAvatarDescriptor.CustomAnimLayer cal in baselayers) {
+ if (!(cal.type == VRCAvatarDescriptor.AnimLayerType.Base || cal.type == VRCAvatarDescriptor.AnimLayerType.Additive)) {
+ i++;
+ allLayers.Add(cal);
+ }
+ }
+
+ if (playableGraph.IsValid()) {
+ playableGraph.Destroy();
+ }
+ playables.Clear();
+ playableBlendingStates.Clear();
+
+ for (i = 0; i < allLayers.Count; i++) {
+ playables.Add(new AnimatorControllerPlayable());
+ playableBlendingStates.Add(null);
+ }
+
+ actionIndex = fxIndex = gestureIndex = additiveIndex = sittingIndex = ikposeIndex = tposeIndex = -1;
+ altActionIndex = altFXIndex = altGestureIndex = altAdditiveIndex = -1;
+
+ foreach (var anim in attachedAnimators) {
+ LyumaAv3Runtime runtime;
+ if (animatorToTopLevelRuntime.TryGetValue(anim, out runtime) && runtime == this)
+ {
+ animatorToTopLevelRuntime.Remove(anim);
+ }
+ }
+ attachedAnimators.Clear();
+ Animator[] animators = this.gameObject.GetComponentsInChildren<Animator>(true);
+ Debug.Log("anim len "+animators.Length);
+ foreach (Animator anim in animators)
+ {
+ attachedAnimators.Add(anim);
+ animatorToTopLevelRuntime.Add(anim, this);
+ }
+
+ Dictionary<string, float> stageNameToValue = new Dictionary<string, float>();
+ if (IsLocal) {
+ foreach (var val in Ints) {
+ stageNameToValue[val.stageName] = val.value;
+ }
+ foreach (var val in Floats) {
+ stageNameToValue[val.stageName] = val.value;
+ }
+ foreach (var val in Bools) {
+ stageNameToValue[val.stageName] = val.value ? 1.0f : 0.0f;
+ }
+ }
+ Ints.Clear();
+ Bools.Clear();
+ Floats.Clear();
+ StageParamterToBuiltin.Clear();
+ IntToIndex.Clear();
+ FloatToIndex.Clear();
+ BoolToIndex.Clear();
+ playableParamterFloats.Clear();
+ playableParamterIds.Clear();
+ playableParamterInts.Clear();
+ playableParamterBools.Clear();
+ HashSet<string> usedparams = new HashSet<string>(BUILTIN_PARAMETERS);
+ i = 0;
+ if (stageParameters != null)
+ {
+ int stageId = 0;
+ foreach (var stageParam in stageParameters.parameters)
+ {
+ stageId++; // one-indexed
+ if (stageParam.name == null || stageParam.name.Length == 0) {
+ continue;
+ }
+ string stageName = stageParam.name + (stageParam.saved ? " (saved/SYNCED)" : " (SYNCED)"); //"Stage" + stageId;
+ float lastDefault = (stageParam.saved && KeepSavedParametersOnReset && stageNameToValue.ContainsKey(stageName) ? stageNameToValue[stageName] : stageParam.defaultValue);
+ StageParamterToBuiltin.Add(stageName, stageParam.name);
+ if ((int)stageParam.valueType == 0)
+ {
+ IntParam param = new IntParam();
+ param.stageName = stageName;
+ param.synced = true;
+ param.name = stageParam.name;
+ param.value = (int)lastDefault;
+ param.lastValue = 0;
+ IntToIndex[param.name] = Ints.Count;
+ Ints.Add(param);
+ }
+ else if ((int)stageParam.valueType == 1)
+ {
+ FloatParam param = new FloatParam();
+ param.stageName = stageName;
+ param.synced = true;
+ param.name = stageParam.name;
+ param.value = lastDefault;
+ param.lastValue = 0;
+ FloatToIndex[param.name] = Floats.Count;
+ Floats.Add(param);
+ }
+ else if ((int)stageParam.valueType == 2)
+ {
+ BoolParam param = new BoolParam();
+ param.stageName = stageName;
+ param.synced = true;
+ param.name = stageParam.name;
+ param.value = lastDefault != 0.0;
+ param.lastValue = false;
+ param.hasBool = new bool[playables.Count];
+ param.hasTrigger = new bool[playables.Count];
+ BoolToIndex[param.name] = Bools.Count;
+ Bools.Add(param);
+ }
+ usedparams.Add(stageParam.name);
+ i++;
+ }
+ }
+
+ if (animator.playableGraph.IsValid())
+ {
+ animator.playableGraph.Destroy();
+ }
+ // var director = avadesc.gameObject.GetComponent<PlayableDirector>();
+ playableGraph = PlayableGraph.Create("LyumaAvatarRuntime - " + this.gameObject.name);
+ var externalOutput = AnimationPlayableOutput.Create(playableGraph, "ExternalAnimator", animator);
+ playableMixer = AnimationLayerMixerPlayable.Create(playableGraph, allLayers.Count + 1);
+ externalOutput.SetSourcePlayable(playableMixer);
+ animator.applyRootMotion = false;
+
+ i = 0;
+ // playableMixer.ConnectInput(0, AnimatorControllerPlayable.Create(playableGraph, allLayers[layerToDebug - 1].animatorController), 0, 0);
+ foreach (VRCAvatarDescriptor.CustomAnimLayer vrcAnimLayer in allLayers)
+ {
+ i++; // Ignore zeroth layer.
+ bool additive = (vrcAnimLayer.type == VRCAvatarDescriptor.AnimLayerType.Additive);
+ RuntimeAnimatorController ac = null;
+ AvatarMask mask;
+ if (vrcAnimLayer.isDefault) {
+ ac = animLayerToDefaultController[vrcAnimLayer.type];
+ mask = animLayerToDefaultAvaMask[vrcAnimLayer.type];
+ } else
+ {
+ ac = vrcAnimLayer.animatorController;
+ mask = vrcAnimLayer.mask;
+ if (vrcAnimLayer.type == VRCAvatarDescriptor.AnimLayerType.FX) {
+ mask = animLayerToDefaultAvaMask[vrcAnimLayer.type]; // Force mask to prevent muscle overrides.
+ }
+ }
+ if (ac == null) {
+ // i is not incremented.
+ continue;
+ }
+ AnimatorControllerPlayable humanAnimatorPlayable = AnimatorControllerPlayable.Create(playableGraph, ac);
+ PlayableBlendingState pbs = new PlayableBlendingState();
+ for (int j = 0; j < humanAnimatorPlayable.GetLayerCount(); j++)
+ {
+ humanAnimatorPlayable.SetLayerWeight(j, 1f);
+ pbs.layerBlends.Add(new BlendingState());
+ }
+
+ // If we are debugging a particular layer, we must put that first.
+ // The Animator Controller window only shows the first layer.
+ int effectiveIdx = i;
+
+ playableMixer.ConnectInput((int)effectiveIdx, humanAnimatorPlayable, 0, 1);
+ playables[effectiveIdx - 1] = humanAnimatorPlayable;
+ playableBlendingStates[effectiveIdx - 1] = pbs;
+ if (vrcAnimLayer.type == VRCAvatarDescriptor.AnimLayerType.Sitting) {
+ if (i >= dupeOffset) {
+ sittingIndex = effectiveIdx - 1;
+ }
+ playableMixer.SetInputWeight(effectiveIdx, 0f);
+ }
+ if (vrcAnimLayer.type == VRCAvatarDescriptor.AnimLayerType.IKPose)
+ {
+ if (i >= dupeOffset) {
+ ikposeIndex = effectiveIdx - 1;
+ }
+ playableMixer.SetInputWeight(effectiveIdx, 0f);
+ }
+ if (vrcAnimLayer.type == VRCAvatarDescriptor.AnimLayerType.TPose)
+ {
+ if (i >= dupeOffset) {
+ tposeIndex = effectiveIdx - 1;
+ }
+ playableMixer.SetInputWeight(effectiveIdx, 0f);
+ }
+ if (vrcAnimLayer.type == VRCAvatarDescriptor.AnimLayerType.Action)
+ {
+ playableMixer.SetInputWeight(i, 0f);
+ if (i < dupeOffset) {
+ altActionIndex = effectiveIdx - 1;
+ } else {
+ actionIndex = effectiveIdx - 1;
+ }
+
+ }
+ if (vrcAnimLayer.type == VRCAvatarDescriptor.AnimLayerType.Gesture) {
+ if (i < dupeOffset) {
+ altGestureIndex = effectiveIdx - 1;
+ } else {
+ gestureIndex = effectiveIdx - 1;
+ }
+
+ }
+ if (vrcAnimLayer.type == VRCAvatarDescriptor.AnimLayerType.Additive)
+ {
+ if (i < dupeOffset) {
+ altAdditiveIndex = effectiveIdx - 1;
+ } else {
+ additiveIndex = effectiveIdx - 1;
+ }
+
+ }
+ if (vrcAnimLayer.type == VRCAvatarDescriptor.AnimLayerType.FX)
+ {
+ if (i < dupeOffset) {
+ altFXIndex = effectiveIdx - 1;
+ } else {
+ fxIndex = effectiveIdx - 1;
+ }
+ }
+ // AnimationControllerLayer acLayer = new AnimationControllerLayer()
+ if (mask != null)
+ {
+ playableMixer.SetLayerMaskFromAvatarMask((uint)effectiveIdx, mask);
+ }
+ if (additive)
+ {
+ playableMixer.SetLayerAdditive((uint)effectiveIdx, true);
+ }
+
+ if (i < dupeOffset) {//i == 0 && AnimatorToDebug != VRCAvatarDescriptor.AnimLayerType.Base) {
+ playableMixer.SetInputWeight(i, 0f);
+ }
+ }
+
+ //playableParamterIds
+ int whichcontroller = 0;
+ playableParamterIds.Clear();
+ foreach (AnimatorControllerPlayable playable in playables) {
+ Dictionary<string, int> parameterIndices = new Dictionary<string, int>();
+ playableParamterInts.Add(new Dictionary<int, int>());
+ playableParamterFloats.Add(new Dictionary<int, float>());
+ playableParamterBools.Add(new Dictionary<int, bool>());
+ // Debug.Log("SETUP index " + whichcontroller + " len " + playables.Count);
+ playableParamterIds.Add(parameterIndices);
+ int pcnt = playable.IsValid() ? playable.GetParameterCount() : 0;
+ for (i = 0; i < pcnt; i++) {
+ AnimatorControllerParameter aparam = playable.GetParameter(i);
+ string actualName;
+ if (!StageParamterToBuiltin.TryGetValue(aparam.name, out actualName)) {
+ actualName = aparam.name;
+ }
+ parameterIndices[actualName] = aparam.nameHash;
+ if (usedparams.Contains(actualName)) {
+ if (BoolToIndex.ContainsKey(aparam.name) && aparam.type == AnimatorControllerParameterType.Bool) {
+ Bools[BoolToIndex[aparam.name]].hasBool[whichcontroller] = true;
+ }
+ if (BoolToIndex.ContainsKey(aparam.name) && aparam.type == AnimatorControllerParameterType.Trigger) {
+ Bools[BoolToIndex[aparam.name]].hasTrigger[whichcontroller] = true;
+ }
+ continue;
+ }
+ if (aparam.type == AnimatorControllerParameterType.Int) {
+ IntParam param = new IntParam();
+ param.stageName = aparam.name + " (local)";
+ param.synced = false;
+ param.name = aparam.name;
+ param.value = aparam.defaultInt;
+ param.lastValue = param.value;
+ IntToIndex[param.name] = Ints.Count;
+ Ints.Add(param);
+ usedparams.Add(aparam.name);
+ } else if (aparam.type == AnimatorControllerParameterType.Float) {
+ FloatParam param = new FloatParam();
+ param.stageName = aparam.name + " (local)";
+ param.synced = false;
+ param.name = aparam.name;
+ param.value = aparam.defaultFloat;
+ param.lastValue = param.value;
+ FloatToIndex[param.name] = Floats.Count;
+ Floats.Add(param);
+ usedparams.Add(aparam.name);
+ } else if (aparam.type == AnimatorControllerParameterType.Trigger || aparam.type == AnimatorControllerParameterType.Bool) {
+ BoolParam param = new BoolParam();
+ param.stageName = aparam.name + " (local)";
+ param.synced = false;
+ param.name = aparam.name;
+ param.value = aparam.defaultBool;
+ param.lastValue = param.value;
+ param.hasBool = new bool[playables.Count];
+ param.hasTrigger = new bool[playables.Count];
+ param.hasBool[whichcontroller] = aparam.type == AnimatorControllerParameterType.Bool;
+ param.hasTrigger[whichcontroller] = aparam.type == AnimatorControllerParameterType.Trigger;
+ BoolToIndex[param.name] = Bools.Count;
+ Bools.Add(param);
+ usedparams.Add(aparam.name);
+ }
+ }
+ whichcontroller++;
+ }
+
+ // Plays the Graph.
+ playableGraph.SetTimeUpdateMode(DirectorUpdateMode.GameTime);
+ Debug.Log(this.gameObject.name + " : Awoken and ready to Play.");
+ playableGraph.Play();
+ Debug.Log(this.gameObject.name + " : Playing.");
+ }
+
+
+ private bool isResetting;
+ private bool isResettingHold;
+ private bool isResettingSel;
+ // Update is called once per frame
+ void Update()
+ {
+ if (isResettingSel) {
+ isResettingSel = false;
+ if (updateSelectionDelegate != null) {
+ updateSelectionDelegate(this.gameObject);
+ }
+ }
+ if (isResettingHold && (!ResetAvatar || !ResetAndHold)) {
+ ResetAndHold = ResetAvatar = false;
+ isResettingSel = true;
+ if (updateSelectionDelegate != null) {
+ updateSelectionDelegate(null);
+ }
+ }
+ if (ResetAvatar && ResetAndHold) {
+ return;
+ }
+ if (ResetAndHold && !ResetAvatar && !isResetting) {
+ ResetAvatar = true;
+ isResettingHold = true;
+ }
+ if (isResetting && !ResetAndHold) {
+ InitializeAnimator();
+ isResetting = false;
+ isResettingHold = false;
+ }
+ if (PrevAnimatorToDebug != (char)(int)AnimatorToDebug || ResetAvatar) {
+ actionIndex = fxIndex = gestureIndex = additiveIndex = sittingIndex = ikposeIndex = tposeIndex = -1;
+ altActionIndex = altFXIndex = altGestureIndex = altAdditiveIndex = -1;
+ // animator.runtimeAnimatorController = null;
+ if (playableGraph.IsValid()) {
+ playableGraph.Destroy();
+ }
+ if (animator.playableGraph.IsValid()) {
+ animator.playableGraph.Destroy();
+ }
+ animator.Update(0);
+ animator.Rebind();
+ animator.Update(0);
+ animator.StopPlayback();
+ GameObject.DestroyImmediate(animator);
+ // animator.runtimeAnimatorController = EmptyController;
+ if (updateSelectionDelegate != null) {
+ updateSelectionDelegate(null);
+ }
+ isResetting = true;
+ isResettingSel = true;
+ return;
+ }
+ if (CreateNonLocalClone) {
+ CreateNonLocalClone = false;
+ GameObject go = GameObject.Instantiate(AvatarSyncSource.gameObject);
+ AvatarSyncSource.CloneCount++;
+ go.name = go.name.Substring(0, go.name.Length - 7) + " (Non-Local " + AvatarSyncSource.CloneCount + ")";
+ go.transform.position = go.transform.position + AvatarSyncSource.CloneCount * new Vector3(0.4f, 0.0f, 0.4f);
+ }
+ if (AvatarSyncSource != this) {
+ for (int i = 0; i < Ints.Count; i++) {
+ if (StageParamterToBuiltin.ContainsKey(Ints[i].stageName)) {
+ Ints[i].value = ClampByte(AvatarSyncSource.Ints[i].value);
+ }
+ }
+ for (int i = 0; i < Floats.Count; i++) {
+ if (StageParamterToBuiltin.ContainsKey(Floats[i].stageName)) {
+ Floats[i].value = ClampAndQuantizeFloat(AvatarSyncSource.Floats[i].value);
+ }
+ }
+ for (int i = 0; i < Bools.Count; i++) {
+ if (StageParamterToBuiltin.ContainsKey(Bools[i].stageName)) {
+ Bools[i].value = AvatarSyncSource.Bools[i].value;
+ }
+ }
+ VisemeInt = VisemeIdx = AvatarSyncSource.VisemeInt;
+ Viseme = (VisemeIndex)VisemeInt;
+ GestureLeft = AvatarSyncSource.GestureLeft;
+ GestureLeftIdx = AvatarSyncSource.GestureLeftIdx;
+ GestureLeftIdxInt = AvatarSyncSource.GestureLeftIdxInt;
+ GestureLeftWeight = AvatarSyncSource.GestureLeftWeight;
+ GestureRight = AvatarSyncSource.GestureRight;
+ GestureRightIdx = AvatarSyncSource.GestureRightIdx;
+ GestureRightIdxInt = AvatarSyncSource.GestureRightIdxInt;
+ GestureRightWeight = AvatarSyncSource.GestureRightWeight;
+ Velocity = AvatarSyncSource.Velocity;
+ AngularY = AvatarSyncSource.AngularY;
+ Upright = AvatarSyncSource.Upright;
+ LocomotionMode = AvatarSyncSource.LocomotionMode;
+ GroundProximity = AvatarSyncSource.GroundProximity;
+ Grounded = AvatarSyncSource.Grounded;
+ Seated = AvatarSyncSource.Seated;
+ AFK = AvatarSyncSource.AFK;
+ Supine = AvatarSyncSource.Supine;
+ TrackingType = AvatarSyncSource.TrackingType;
+ TrackingTypeIdx = AvatarSyncSource.TrackingTypeIdx;
+ TrackingTypeIdxInt = AvatarSyncSource.TrackingTypeIdxInt;
+ VRMode = AvatarSyncSource.VRMode;
+ MuteSelf = AvatarSyncSource.MuteSelf;
+ InStation = AvatarSyncSource.InStation;
+ FootstepDisable = AvatarSyncSource.FootstepDisable;
+ }
+ for (int i = 0; i < Floats.Count; i++) {
+ if (StageParamterToBuiltin.ContainsKey(Floats[i].stageName)) {
+ if (locally8bitQuantizedFloats) {
+ Floats[i].value = ClampAndQuantizeFloat(Floats[i].value);
+ } else {
+ Floats[i].value = ClampFloatOnly(Floats[i].value);
+ }
+ }
+ }
+ for (int i = 0; i < Ints.Count; i++) {
+ if (StageParamterToBuiltin.ContainsKey(Ints[i].stageName)) {
+ Ints[i].value = ClampByte(Ints[i].value);
+ }
+ }
+ if (Seated != PrevSeated && sittingIndex >= 0)
+ {
+ playableBlendingStates[sittingIndex].StartBlend(playableMixer.GetInputWeight(sittingIndex + 1), Seated ? 1f : 0f, 0.25f);
+ PrevSeated = Seated;
+ }
+ if (TPoseCalibration != PrevTPoseCalibration) {
+ playableBlendingStates[tposeIndex].StartBlend(playableMixer.GetInputWeight(tposeIndex + 1), TPoseCalibration ? 1f : 0f, 0.0f);
+ PrevTPoseCalibration = TPoseCalibration;
+ }
+ if (IKPoseCalibration != PrevIKPoseCalibration) {
+ playableBlendingStates[ikposeIndex].StartBlend(playableMixer.GetInputWeight(ikposeIndex + 1), IKPoseCalibration ? 1f : 0f, 0.0f);
+ PrevIKPoseCalibration = IKPoseCalibration;
+ }
+ if (VisemeIdx != VisemeInt) {
+ VisemeInt = VisemeIdx;
+ Viseme = (VisemeIndex)VisemeInt;
+ }
+ if ((int)Viseme != VisemeInt) {
+ VisemeInt = (int)Viseme;
+ VisemeIdx = VisemeInt;
+ }
+ if (GestureLeftIdx != GestureLeftIdxInt) {
+ GestureLeft = (GestureIndex)GestureLeftIdx;
+ GestureLeftIdx = (int)GestureLeft;
+ GestureLeftIdxInt = (char)GestureLeftIdx;
+ }
+ if ((int)GestureLeft != (int)GestureLeftIdxInt) {
+ GestureLeftIdx = (int)GestureLeft;
+ GestureLeftIdxInt = (char)GestureLeftIdx;
+ }
+ if (GestureRightIdx != GestureRightIdxInt) {
+ GestureRight = (GestureIndex)GestureRightIdx;
+ GestureRightIdx = (int)GestureRight;
+ GestureRightIdxInt = (char)GestureRightIdx;
+ }
+ if ((int)GestureRight != (int)GestureRightIdxInt) {
+ GestureRightIdx = (int)GestureRight;
+ GestureRightIdxInt = (char)GestureRightIdx;
+ }
+ if (TrackingTypeIdx != TrackingTypeIdxInt) {
+ TrackingType = (TrackingTypeIndex)TrackingTypeIdx;
+ TrackingTypeIdx = (int)TrackingType;
+ TrackingTypeIdxInt = (char)TrackingTypeIdx;
+ }
+ if ((int)TrackingType != TrackingTypeIdxInt) {
+ TrackingTypeIdx = (int)TrackingType;
+ TrackingTypeIdxInt = (char)TrackingTypeIdx;
+ }
+ IsLocal = AvatarSyncSource == this;
+
+ int whichcontroller;
+ whichcontroller = 0;
+ foreach (AnimatorControllerPlayable playable in playables)
+ {
+ if (!playable.IsValid()) {
+ continue;
+ }
+ // Debug.Log("Index " + whichcontroller + " len " + playables.Count);
+ Dictionary<string, int> parameterIndices = playableParamterIds[whichcontroller];
+ int paramid;
+ foreach (FloatParam param in Floats)
+ {
+ if (parameterIndices.TryGetValue(param.name, out paramid))
+ {
+ if (param.value != param.lastValue) {
+ playable.SetFloat(paramid, param.value);
+ }
+ }
+ }
+ foreach (IntParam param in Ints)
+ {
+ if (parameterIndices.TryGetValue(param.name, out paramid))
+ {
+ if (param.value != param.lastValue) {
+ playable.SetInteger(paramid, param.value);
+ }
+ }
+ }
+ foreach (BoolParam param in Bools)
+ {
+ if (parameterIndices.TryGetValue(param.name, out paramid))
+ {
+ if (param.value != param.lastValue) {
+ playable.SetBool(paramid, param.value); // also sets triggers.
+ // if (param.value) {
+ // playable.SetTrigger(paramid);
+ // }
+ }
+ }
+ }
+ whichcontroller++;
+ }
+ foreach (FloatParam param in Floats) {
+ param.lastValue = param.value;
+ }
+ foreach (IntParam param in Ints) {
+ param.lastValue = param.value;
+ }
+ foreach (BoolParam param in Bools) {
+ param.lastValue = param.value;
+ }
+
+ whichcontroller = 0;
+ foreach (AnimatorControllerPlayable playable in playables)
+ {
+ if (!playable.IsValid()) {
+ continue;
+ }
+ // Debug.Log("Index " + whichcontroller + " len " + playables.Count);
+ Dictionary<string, int> parameterIndices = playableParamterIds[whichcontroller];
+ Dictionary<int, int> paramterInts = playableParamterInts[whichcontroller];
+ Dictionary<int, float> paramterFloats = playableParamterFloats[whichcontroller];
+ Dictionary<int, bool> paramterBools = playableParamterBools[whichcontroller];
+ int paramid;
+ float fparam;
+ int iparam;
+ bool bparam;
+ foreach (FloatParam param in Floats)
+ {
+ if (parameterIndices.TryGetValue(param.name, out paramid))
+ {
+ if (paramterFloats.TryGetValue(paramid, out fparam)) {
+ if (fparam != playable.GetFloat(paramid)) {
+ param.value = param.lastValue = playable.GetFloat(paramid);
+ }
+ }
+ paramterFloats[paramid] = param.value;
+ }
+ }
+ foreach (IntParam param in Ints)
+ {
+ if (parameterIndices.TryGetValue(param.name, out paramid))
+ {
+ if (paramterInts.TryGetValue(paramid, out iparam)) {
+ if (iparam != playable.GetInteger(paramid)) {
+ param.value = param.lastValue = playable.GetInteger(paramid);
+ }
+ }
+ paramterInts[paramid] = param.value;
+ }
+ }
+ foreach (BoolParam param in Bools)
+ {
+ if (param.hasBool[whichcontroller] && parameterIndices.TryGetValue(param.name, out paramid))
+ {
+ if (paramterBools.TryGetValue(paramid, out bparam)) {
+ if (bparam != (playable.GetBool(paramid))) {
+ param.value = param.lastValue = playable.GetBool(paramid);
+ }
+ }
+ paramterBools[paramid] = param.value;
+ }
+ }
+ if (parameterIndices.TryGetValue("Viseme", out paramid))
+ {
+ if (paramterInts.TryGetValue(paramid, out iparam) && iparam != playable.GetInteger(paramid)) {
+ VisemeInt = VisemeIdx = playable.GetInteger(paramid);
+ Viseme = (VisemeIndex)VisemeInt;
+ }
+ playable.SetInteger(paramid, VisemeInt);
+ paramterInts[paramid] = VisemeInt;
+ }
+ if (parameterIndices.TryGetValue("GestureLeft", out paramid))
+ {
+ if (paramterInts.TryGetValue(paramid, out iparam) && iparam != playable.GetInteger(paramid)) {
+ GestureLeftIdx = playable.GetInteger(paramid);
+ GestureLeftIdxInt = (char)GestureLeftIdx;
+ GestureLeft = (GestureIndex)GestureLeftIdx;
+ }
+ playable.SetInteger(paramid, (int)GestureLeft);
+ paramterInts[paramid] = (int)GestureLeft;
+ }
+ if (parameterIndices.TryGetValue("GestureLeftWeight", out paramid))
+ {
+ if (paramterFloats.TryGetValue(paramid, out fparam) && fparam != playable.GetFloat(paramid)) {
+ GestureLeftWeight = playable.GetFloat(paramid);
+ }
+ playable.SetFloat(paramid, GestureLeftWeight);
+ paramterFloats[paramid] = GestureLeftWeight;
+ }
+ if (parameterIndices.TryGetValue("GestureRight", out paramid))
+ {
+ if (paramterInts.TryGetValue(paramid, out iparam) && iparam != playable.GetInteger(paramid)) {
+ GestureRightIdx = playable.GetInteger(paramid);
+ GestureRightIdxInt = (char)GestureRightIdx;
+ GestureRight = (GestureIndex)GestureRightIdx;
+ }
+ playable.SetInteger(paramid, (int)GestureRight);
+ paramterInts[paramid] = (int)GestureRight;
+ }
+ if (parameterIndices.TryGetValue("GestureRightWeight", out paramid))
+ {
+ if (paramterFloats.TryGetValue(paramid, out fparam) && fparam != playable.GetFloat(paramid)) {
+ GestureRightWeight = playable.GetFloat(paramid);
+ }
+ playable.SetFloat(paramid, GestureRightWeight);
+ paramterFloats[paramid] = GestureRightWeight;
+ }
+ if (parameterIndices.TryGetValue("VelocityX", out paramid))
+ {
+ if (paramterFloats.TryGetValue(paramid, out fparam) && fparam != playable.GetFloat(paramid)) {
+ Velocity.x = playable.GetFloat(paramid);
+ }
+ playable.SetFloat(paramid, Velocity.x);
+ paramterFloats[paramid] = Velocity.x;
+ }
+ if (parameterIndices.TryGetValue("VelocityY", out paramid))
+ {
+ if (paramterFloats.TryGetValue(paramid, out fparam) && fparam != playable.GetFloat(paramid)) {
+ Velocity.y = playable.GetFloat(paramid);
+ }
+ playable.SetFloat(paramid, Velocity.y);
+ paramterFloats[paramid] = Velocity.y;
+ }
+ if (parameterIndices.TryGetValue("VelocityZ", out paramid))
+ {
+ if (paramterFloats.TryGetValue(paramid, out fparam) && fparam != playable.GetFloat(paramid)) {
+ Velocity.z = playable.GetFloat(paramid);
+ }
+ playable.SetFloat(paramid, Velocity.z);
+ paramterFloats[paramid] = Velocity.z;
+ }
+ if (parameterIndices.TryGetValue("AngularY", out paramid))
+ {
+ if (paramterFloats.TryGetValue(paramid, out fparam) && fparam != playable.GetFloat(paramid)) {
+ AngularY = playable.GetFloat(paramid);
+ }
+ playable.SetFloat(paramid, AngularY);
+ paramterFloats[paramid] = AngularY;
+ }
+ if (parameterIndices.TryGetValue("Upright", out paramid))
+ {
+ if (paramterFloats.TryGetValue(paramid, out fparam) && fparam != playable.GetFloat(paramid)) {
+ Upright = playable.GetFloat(paramid);
+ }
+ playable.SetFloat(paramid, Upright);
+ paramterFloats[paramid] = Upright;
+ }
+ if (parameterIndices.TryGetValue("GroundProximity", out paramid))
+ {
+ if (paramterFloats.TryGetValue(paramid, out fparam) && fparam != playable.GetFloat(paramid)) {
+ GroundProximity = playable.GetFloat(paramid);
+ }
+ playable.SetFloat(paramid, GroundProximity);
+ paramterFloats[paramid] = GroundProximity;
+ }
+ if (parameterIndices.TryGetValue("LocomotionMode", out paramid))
+ {
+ if (paramterInts.TryGetValue(paramid, out iparam) && iparam != playable.GetInteger(paramid)) {
+ LocomotionMode = playable.GetInteger(paramid);
+ }
+ playable.SetInteger(paramid, LocomotionMode);
+ paramterInts[paramid] = LocomotionMode;
+ }
+ if (parameterIndices.TryGetValue("IsLocal", out paramid))
+ {
+ playable.SetBool(paramid, IsLocal);
+ }
+ if (parameterIndices.TryGetValue("Grounded", out paramid))
+ {
+ if (paramterInts.TryGetValue(paramid, out iparam) && iparam != (playable.GetBool(paramid) ? 1 : 0)) {
+ Grounded = playable.GetBool(paramid);
+ }
+ playable.SetBool(paramid, Grounded);
+ paramterInts[paramid] = Grounded ? 1 : 0;
+ }
+ if (parameterIndices.TryGetValue("Seated", out paramid))
+ {
+ if (paramterInts.TryGetValue(paramid, out iparam) && iparam != (playable.GetBool(paramid) ? 1 : 0)) {
+ Seated = playable.GetBool(paramid);
+ }
+ playable.SetBool(paramid, Seated);
+ paramterInts[paramid] = Seated ? 1 : 0;
+ }
+ if (parameterIndices.TryGetValue("AFK", out paramid))
+ {
+ if (paramterInts.TryGetValue(paramid, out iparam) && iparam != (playable.GetBool(paramid) ? 1 : 0)) {
+ AFK = playable.GetBool(paramid);
+ }
+ playable.SetBool(paramid, AFK);
+ paramterInts[paramid] = AFK ? 1 : 0;
+ }
+ if (parameterIndices.TryGetValue("Supine", out paramid))
+ {
+ if (paramterInts.TryGetValue(paramid, out iparam) && iparam != (playable.GetBool(paramid) ? 1 : 0)) {
+ Supine = playable.GetBool(paramid);
+ }
+ playable.SetBool(paramid, Supine);
+ paramterInts[paramid] = Supine ? 1 : 0;
+ }
+ if (parameterIndices.TryGetValue("FootstepDisable", out paramid))
+ {
+ if (paramterInts.TryGetValue(paramid, out iparam) && iparam != (playable.GetBool(paramid) ? 1 : 0)) {
+ FootstepDisable = playable.GetBool(paramid);
+ }
+ playable.SetBool(paramid, FootstepDisable);
+ paramterInts[paramid] = FootstepDisable ? 1 : 0;
+ }
+ if (parameterIndices.TryGetValue("TrackingType", out paramid))
+ {
+ if (paramterInts.TryGetValue(paramid, out iparam) && iparam != playable.GetInteger(paramid)) {
+ TrackingTypeIdx = playable.GetInteger(paramid);
+ TrackingTypeIdxInt = (char)TrackingTypeIdx;
+ TrackingType = (TrackingTypeIndex)TrackingTypeIdx;
+ }
+ playable.SetInteger(paramid, (int)TrackingType);
+ paramterInts[paramid] = (int)TrackingType;
+ }
+ if (parameterIndices.TryGetValue("VRMode", out paramid))
+ {
+ if (paramterInts.TryGetValue(paramid, out iparam) && iparam != playable.GetInteger(paramid)) {
+ VRMode = playable.GetInteger(paramid) != 0;
+ }
+ playable.SetInteger(paramid, VRMode ? 1 : 0);
+ paramterInts[paramid] = VRMode ? 1 : 0;
+ }
+ if (parameterIndices.TryGetValue("MuteSelf", out paramid))
+ {
+ if (paramterInts.TryGetValue(paramid, out iparam) && iparam != (playable.GetBool(paramid) ? 1 : 0)) {
+ MuteSelf = playable.GetBool(paramid);
+ }
+ playable.SetBool(paramid, MuteSelf);
+ paramterInts[paramid] = MuteSelf ? 1 : 0;
+ }
+ if (parameterIndices.TryGetValue("InStation", out paramid))
+ {
+ if (paramterInts.TryGetValue(paramid, out iparam) && iparam != (playable.GetBool(paramid) ? 1 : 0))
+ {
+ InStation = playable.GetBool(paramid);
+ }
+ playable.SetBool(paramid, InStation);
+ paramterInts[paramid] = InStation ? 1 : 0;
+ }
+ if (parameterIndices.TryGetValue("AvatarVersion", out paramid)) {
+ playable.SetInteger(paramid, AvatarVersion);
+ }
+ whichcontroller++;
+ }
+ for (int i = 0; i < playableBlendingStates.Count; i++) {
+ var pbs = playableBlendingStates[i];
+ if (pbs.blending) {
+ float newWeight = pbs.UpdateBlending();
+ playableMixer.SetInputWeight(i + 1, newWeight);
+ // Debug.Log("Whole playable " + i + " is blending to " + newWeight);
+ }
+ for (int j = 0; j < pbs.layerBlends.Count; j++) {
+ if (pbs.layerBlends[j].blending) {
+ float newWeight = pbs.layerBlends[j].UpdateBlending();
+ playables[i].SetLayerWeight(j, newWeight);
+ // Debug.Log("Playable " + i + " layer " + j + " is blending to " + newWeight);
+ }
+ }
+ }
+ if (avadesc.lipSync == VRC.SDKBase.VRC_AvatarDescriptor.LipSyncStyle.JawFlapBone && avadesc.lipSyncJawBone != null) {
+ if (Viseme == VisemeIndex.sil) {
+ avadesc.lipSyncJawBone.transform.rotation = avadesc.lipSyncJawClosed;
+ } else {
+ avadesc.lipSyncJawBone.transform.rotation = avadesc.lipSyncJawOpen;
+ }
+ } else if (avadesc.lipSync == VRC.SDKBase.VRC_AvatarDescriptor.LipSyncStyle.JawFlapBlendShape && avadesc.VisemeSkinnedMesh != null && mouthOpenBlendShapeIdx != -1) {
+ if (Viseme == VisemeIndex.sil) {
+ avadesc.VisemeSkinnedMesh.SetBlendShapeWeight(mouthOpenBlendShapeIdx, 0.0f);
+ } else {
+ avadesc.VisemeSkinnedMesh.SetBlendShapeWeight(mouthOpenBlendShapeIdx, 100.0f);
+ }
+ } else if (avadesc.lipSync == VRC.SDKBase.VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape && avadesc.VisemeSkinnedMesh != null) {
+ for (int i = 0; i < visemeBlendShapeIdxs.Length; i++) {
+ if (visemeBlendShapeIdxs[i] != -1) {
+ avadesc.VisemeSkinnedMesh.SetBlendShapeWeight(visemeBlendShapeIdxs[i], (i == VisemeIdx ? 100.0f : 0.0f));
+ }
+ }
+ }
+ }
+}