diff options
Diffstat (limited to 'VRCSDK3AvatarsQuest/Assets/Resources/Lyuma/Av3Emulator/Scripts/LyumaAv3Runtime.cs')
-rw-r--r-- | VRCSDK3AvatarsQuest/Assets/Resources/Lyuma/Av3Emulator/Scripts/LyumaAv3Runtime.cs | 2529 |
1 files changed, 2529 insertions, 0 deletions
diff --git a/VRCSDK3AvatarsQuest/Assets/Resources/Lyuma/Av3Emulator/Scripts/LyumaAv3Runtime.cs b/VRCSDK3AvatarsQuest/Assets/Resources/Lyuma/Av3Emulator/Scripts/LyumaAv3Runtime.cs new file mode 100644 index 00000000..04d73cc2 --- /dev/null +++ b/VRCSDK3AvatarsQuest/Assets/Resources/Lyuma/Av3Emulator/Scripts/LyumaAv3Runtime.cs @@ -0,0 +1,2529 @@ +/* Copyright (c) 2020-2022 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; +using VRC.SDK3.Dynamics.Contact.Components; +using VRC.SDK3.Dynamics.PhysBone.Components; + +// [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(UnityEngine.Object obj); + public static UpdateSelectionFunc updateSelectionDelegate; + public delegate void AddRuntime(Component runtime); + public static AddRuntime addRuntimeDelegate; + public delegate void UpdateSceneLayersFunc(int layers); + public static UpdateSceneLayersFunc updateSceneLayersDelegate; + public delegate void ApplyOnEnableWorkaroundDelegateType(); + public static ApplyOnEnableWorkaroundDelegateType ApplyOnEnableWorkaroundDelegate; + + public LyumaAv3Runtime OriginalSourceClone = null; + + [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("Click if you modified your menu or parameter list")] + public bool RefreshExpressionParams; + [Tooltip("Simulates saving and reloading the avatar")] + public bool KeepSavedParametersOnReset = true; + [HideInInspector] public bool legacyMenuGUI = true; + private bool lastLegacyMenuGUI = true; + [Header("Animator to Debug. Unity is glitchy when not 'Base'.")] + [Tooltip("Selects the playable layer to be visible with parameters in the Animator. If you view any other playable in the Animator window, parameters will say 0 and will not update.")] + public VRCAvatarDescriptor.AnimLayerType DebugDuplicateAnimator; + private char PrevAnimatorToDebug; + [Tooltip("Selects the playable layer to be visible in Unity's Animator window. Does not reset avatar. Unless this is set to Base, will cause 'Invalid Layer Index' logspam; layers will show wrong weight and parameters will all be 0.")] + public VRCAvatarDescriptor.AnimLayerType ViewAnimatorOnlyNoParams; + private char PrevAnimatorToViewLiteParamsShow0; + [HideInInspector] public string SourceObjectPath; + [HideInInspector] public LyumaAv3Runtime AvatarSyncSource; + private float nextUpdateTime = 0.0f; + [Header("OSC (double click OSC Controller for debug and port settings)")] + public bool EnableAvatarOSC = false; + public LyumaAv3Osc OSCController = null; + public A3EOSCConfiguration OSCConfigurationFile = new A3EOSCConfiguration(); + + [Header("Network Clones and Sync")] + public bool CreateNonLocalClone; + [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; + private int CloneCount; + [Range(0.0f, 2.0f)] public float NonLocalSyncInterval = 0.2f; + [Tooltip("Parameters visible in the radial menu will IK sync")] public bool IKSyncRadialMenu = true; + [Header("PlayerLocal and MirrorReflection")] + public bool EnableHeadScaling; + public bool DisableMirrorAndShadowClones; + [HideInInspector] public LyumaAv3Runtime MirrorClone; + [HideInInspector] public LyumaAv3Runtime ShadowClone; + [Tooltip("To view both copies at once")] public bool DebugOffsetMirrorClone = false; + public bool ViewMirrorReflection; + private bool LastViewMirrorReflection; + public bool ViewBothRealAndMirror; + private bool LastViewBothRealAndMirror; + [HideInInspector] public VRCAvatarDescriptor avadesc; + Avatar animatorAvatar; + Animator animator; + private RuntimeAnimatorController origAnimatorController; + public Dictionary<VRCAvatarDescriptor.AnimLayerType, RuntimeAnimatorController> allControllers = new Dictionary<VRCAvatarDescriptor.AnimLayerType, RuntimeAnimatorController>(); + + private Transform[] allTransforms; + private Transform[] allMirrorTransforms; + private Transform[] allShadowTransforms; + 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; + + [NonSerialized] public VRCPhysBone[] AvDynamicsPhysBones = new VRCPhysBone[]{}; + [NonSerialized] public VRCContactReceiver[] AvDynamicsContactReceivers = new VRCContactReceiver[]{}; + + public class Av3EmuParameterAccess : VRC.SDKBase.IAnimParameterAccess { + public LyumaAv3Runtime runtime; + public string paramName; + public bool boolVal { + get { + // Debug.Log(paramName + " GETb"); + int idx; + if (runtime.IntToIndex.TryGetValue(paramName, out idx)) return runtime.Ints[idx].value != 0; + if (runtime.FloatToIndex.TryGetValue(paramName, out idx))return runtime.Floats[idx].exportedValue != 0.0f; + if (runtime.BoolToIndex.TryGetValue(paramName, out idx)) return runtime.Bools[idx].value; + return false; + } + set { + // Debug.Log(paramName + " SETb " + value); + int idx; + if (runtime.IntToIndex.TryGetValue(paramName, out idx)) runtime.Ints[idx].value = value ? 1 : 0; + if (runtime.FloatToIndex.TryGetValue(paramName, out idx)) { + runtime.Floats[idx].value = value ? 1.0f : 0.0f; + runtime.Floats[idx].exportedValue = runtime.Floats[idx].value; + } + if (runtime.BoolToIndex.TryGetValue(paramName, out idx)) runtime.Bools[idx].value = value; + } + } + public int intVal { + get { + int idx; + // Debug.Log(paramName + " GETi"); + if (runtime.IntToIndex.TryGetValue(paramName, out idx)) return runtime.Ints[idx].value; + if (runtime.FloatToIndex.TryGetValue(paramName, out idx)) return (int)runtime.Floats[idx].exportedValue; + if (runtime.BoolToIndex.TryGetValue(paramName, out idx)) return runtime.Bools[idx].value ? 1 : 0; + return 0; + } + set { + // Debug.Log(paramName + " SETi " + value); + int idx; + if (runtime.IntToIndex.TryGetValue(paramName, out idx)) runtime.Ints[idx].value = value; + if (runtime.FloatToIndex.TryGetValue(paramName, out idx)) { + runtime.Floats[idx].value = (float)value; + runtime.Floats[idx].exportedValue = runtime.Floats[idx].value; + } + if (runtime.BoolToIndex.TryGetValue(paramName, out idx)) runtime.Bools[idx].value = value != 0; + } + } + public float floatVal { + get { + // Debug.Log(paramName + " GETf"); + int idx; + if (runtime.IntToIndex.TryGetValue(paramName, out idx)) return (float)runtime.Ints[idx].value; + if (runtime.FloatToIndex.TryGetValue(paramName, out idx)) return runtime.Floats[idx].exportedValue; + if (runtime.BoolToIndex.TryGetValue(paramName, out idx)) return runtime.Bools[idx].value ? 1.0f : 0.0f; + return 0.0f; + } + set { + // Debug.Log(paramName + " SETf " + value); + int idx; + if (runtime.IntToIndex.TryGetValue(paramName, out idx)) runtime.Ints[idx].value = (int)value; + if (runtime.FloatToIndex.TryGetValue(paramName, out idx)) { + runtime.Floats[idx].value = value; + runtime.Floats[idx].exportedValue = value; + } + if (runtime.BoolToIndex.TryGetValue(paramName, out idx)) runtime.Bools[idx].value = value != 0.0f; + } + } + } + + public void assignContactParameters(VRCContactReceiver[] behaviours) { + AvDynamicsContactReceivers = behaviours; + foreach (var mb in AvDynamicsContactReceivers) { + var old_value = mb.paramAccess; + if (old_value == null || old_value.GetType() != typeof(Av3EmuParameterAccess)) { + string parameter = mb.parameter; + Av3EmuParameterAccess accessInst = new Av3EmuParameterAccess(); + accessInst.runtime = this; + accessInst.paramName = parameter; + mb.paramAccess = accessInst; + accessInst.floatVal = mb.paramValue; + // Debug.Log("Assigned access " + contactReceiverState.paramAccess.GetValue(mb) + " to param " + parameter + ": was " + old_value); + } + } + } + public void assignPhysBoneParameters(VRCPhysBone[] behaviours) { + AvDynamicsPhysBones = behaviours; + foreach (var mb in AvDynamicsPhysBones) { + var old_value = mb.param_Stretch; + if (old_value == null || old_value.GetType() != typeof(Av3EmuParameterAccess)) { + string parameter = mb.parameter; + Av3EmuParameterAccess accessInst = new Av3EmuParameterAccess(); + accessInst.runtime = this; + accessInst.paramName = parameter + VRCPhysBone.PARAM_ANGLE; + mb.param_Angle = accessInst; + accessInst.floatVal = mb.param_AngleValue; + accessInst = new Av3EmuParameterAccess(); + accessInst.runtime = this; + accessInst.paramName = parameter + VRCPhysBone.PARAM_ISGRABBED; + mb.param_IsGrabbed = accessInst; + accessInst.boolVal = mb.param_IsGrabbedValue; + accessInst = new Av3EmuParameterAccess(); + accessInst.runtime = this; + accessInst.paramName = parameter + VRCPhysBone.PARAM_STRETCH; + mb.param_Stretch = accessInst; + accessInst.floatVal = mb.param_StretchValue; + // Debug.Log("Assigned strech access " + physBoneState.param_Stretch.GetValue(mb) + " to param " + parameter + ": was " + old_value); + } + } + } + + 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 + } + public static HashSet<string> BUILTIN_PARAMETERS = new HashSet<string> { + "Viseme", "GestureLeft", "GestureLeftWeight", "GestureRight", "GestureRightWeight", "VelocityX", "VelocityY", "VelocityZ", "Upright", "AngularY", "Grounded", "Seated", "AFK", "TrackingType", "VRMode", "MuteSelf", "InStation" + }; + public static readonly HashSet<Type> MirrorCloneComponentBlacklist = new HashSet<Type> { + typeof(Camera), typeof(FlareLayer), typeof(AudioSource), typeof(Rigidbody), typeof(Joint) + }; + public static readonly HashSet<Type> ShadowCloneComponentBlacklist = new HashSet<Type> { + typeof(Camera), typeof(FlareLayer), typeof(AudioSource), typeof(Light), typeof(ParticleSystemRenderer), typeof(Rigidbody), typeof(Joint) + }; + [Header("Built-in inputs / Viseme")] + public VisemeIndex Viseme; + [Range(0, 15)] public int VisemeIdx; + private int VisemeInt; + [Tooltip("Voice amount from 0.0f to 1.0f for the current viseme")] + [Range(0,1)] public float Voice; + [Header("Built-in inputs / Hand Gestures")] + public GestureIndex GestureLeft; + [Range(0, 9)] public int GestureLeftIdx; + private char GestureLeftIdxInt; + [Range(0, 1)] public float GestureLeftWeight; + private float OldGestureLeftWeight; + public GestureIndex GestureRight; + [Range(0, 9)] public int GestureRightIdx; + private char GestureRightIdxInt; + [Range(0, 1)] public float GestureRightWeight; + private float OldGestureRightWeight; + [Header("Built-in inputs / Locomotion")] + public Vector3 Velocity; + [Range(-400, 400)] public float AngularY; + [Range(0, 1)] public float Upright; + public bool Grounded; + public bool Jump; + public float JumpPower = 5; + public float RunSpeed = 0.0f; + private bool WasJump; + private Vector3 JumpingHeight; + private Vector3 JumpingVelocity; + private bool PrevSeated, PrevTPoseCalibration, PrevIKPoseCalibration; + public bool Seated; + public bool AFK; + public bool TPoseCalibration; + public bool IKPoseCalibration; + [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; + private bool MuteTogglerOn; + public bool InStation; + [HideInInspector] public int AvatarVersion = 3; + + [Header("Output State (Read-only)")] + public bool IsLocal; + [HideInInspector] public bool IsMirrorClone; + [HideInInspector] public bool IsShadowClone; + public bool LocomotionIsDisabled; + + [Serializable] + public struct IKTrackingOutput { + public Vector3 HeadRelativeViewPosition; + public Vector3 ViewPosition; + public float AvatarScaleFactorGuess; + 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; + } + public IKTrackingOutput IKTrackingOutputData; + + [Serializable] + public class FloatParam + { + [HideInInspector] public string stageName; + public string name; + [HideInInspector] public bool synced; + [Range(-1, 1)] public float expressionValue; + [HideInInspector] public float lastExpressionValue_; + [Range(-1, 1)] public float value; + [HideInInspector] private float exportedValue_; + public float exportedValue { + get { + return exportedValue_; + } set { + this.exportedValue_ = value; + this.value = value; + this.lastExpressionValue_ = value; + this.expressionValue = 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>(); + + [HideInInspector] 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) { + IKTrackingOutputData.ViewPosition = animator.transform.InverseTransformPoint(head.TransformPoint(IKTrackingOutputData.HeadRelativeViewPosition)); + } + } else { + IKTrackingOutputData.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 HashSet<Animator> issuedWarningAnimators = new HashSet<Animator>(); + 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); + runtime.Awake(); + } + return true; + } + + if (!issuedWarningAnimators.Contains(innerAnimator)) + { + issuedWarningAnimators.Add(innerAnimator); + Debug.LogWarning("[" + component + "]: outermost Animator is not known: " + innerAnimator + ". If you changed something, consider resetting avatar", innerAnimator); + } + + return false; + } + + float getAdjustedParameterAsFloat(string paramName, bool convertRange=false, float srcMin=0.0f, float srcMax=0.0f, float dstMin=0.0f, float dstMax=0.0f) { + float newValue = 0; + int idx; + if (FloatToIndex.TryGetValue(paramName, out idx)) { + newValue = Floats[idx].exportedValue; + } else if (IntToIndex.TryGetValue(paramName, out idx)) { + newValue = (float)Ints[idx].value; + } else if (BoolToIndex.TryGetValue(paramName, out idx)) { + newValue = Bools[idx].value ? 1.0f : 0.0f; + } + if (convertRange) { + if (dstMax != dstMin) { + newValue = Mathf.Lerp(dstMin, dstMax, Mathf.Clamp01(Mathf.InverseLerp(srcMin, srcMax, newValue))); + } else { + newValue = dstMin; + } + } + return newValue; + } + + static LyumaAv3Runtime() { + VRCAvatarParameterDriver.OnApplySettings += (behaviour, animator) => + { + LyumaAv3Runtime runtime; + if (!getTopLevelRuntime("VRCAvatarParameterDriver", animator, out runtime)) { + return; + } + if (runtime.IsMirrorClone || runtime.IsShadowClone) { + 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 (runtime.DebugDuplicateAnimator != VRCAvatarDescriptor.AnimLayerType.Base && !runtime.IsMirrorClone && !runtime.IsShadowClone && (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 + 1); + break; + case VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Copy: + runtime.Ints[idx].value = (int)runtime.getAdjustedParameterAsFloat(parameter.source, parameter.convertRange, parameter.sourceMin, parameter.sourceMax, parameter.destMin, parameter.destMax); + break; + } + } + if (runtime.FloatToIndex.TryGetValue(actualName, out idx)) { + switch (parameter.type) { + case VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Set: + runtime.Floats[idx].exportedValue = parameter.value; + break; + case VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Add: + runtime.Floats[idx].exportedValue += parameter.value; + break; + case VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Random: + runtime.Floats[idx].exportedValue = UnityEngine.Random.Range(parameter.valueMin, parameter.valueMax); + break; + case VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Copy: + runtime.Floats[idx].exportedValue = runtime.getAdjustedParameterAsFloat(parameter.source, parameter.convertRange, parameter.sourceMin, parameter.sourceMax, parameter.destMin, parameter.destMax); + break; + } + runtime.Floats[idx].value = runtime.Floats[idx].exportedValue; + } + 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; + 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); + break; + case VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Random: + // random is *not* idempotent. + newValue = UnityEngine.Random.Range(0.0f, 1.0f) < parameter.chance; + break; + case VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Copy: + newValue = runtime.getAdjustedParameterAsFloat(parameter.source, parameter.convertRange, parameter.sourceMin, parameter.sourceMax, parameter.destMin, parameter.destMax) != 0.0f; + break; + default: + continue; + } + 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; + } + } + 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 (runtime.IsMirrorClone && runtime.IsShadowClone) { + 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 (runtime.IsMirrorClone) { + 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 (runtime.IsMirrorClone && runtime.IsShadowClone) { + 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 (runtime.IsMirrorClone && runtime.IsShadowClone) { + 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 (runtime.IsMirrorClone && runtime.IsShadowClone) { + 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.IKTrackingOutputData.trackingMouthAndJaw = behaviour.trackingMouth; + } + if (behaviour.trackingHead != VRCAnimatorTrackingControl.TrackingType.NoChange) + { + runtime.IKTrackingOutputData.trackingHead = behaviour.trackingHead; + } + if (behaviour.trackingRightFingers != VRCAnimatorTrackingControl.TrackingType.NoChange) + { + runtime.IKTrackingOutputData.trackingRightFingers = behaviour.trackingRightFingers; + } + if (behaviour.trackingEyes != VRCAnimatorTrackingControl.TrackingType.NoChange) + { + runtime.IKTrackingOutputData.trackingEyesAndEyelids = behaviour.trackingEyes; + } + if (behaviour.trackingLeftFingers != VRCAnimatorTrackingControl.TrackingType.NoChange) + { + runtime.IKTrackingOutputData.trackingLeftFingers = behaviour.trackingLeftFingers; + } + if (behaviour.trackingLeftFoot != VRCAnimatorTrackingControl.TrackingType.NoChange) + { + runtime.IKTrackingOutputData.trackingLeftFoot = behaviour.trackingLeftFoot; + } + if (behaviour.trackingHip != VRCAnimatorTrackingControl.TrackingType.NoChange) + { + runtime.IKTrackingOutputData.trackingHip = behaviour.trackingHip; + } + if (behaviour.trackingRightHand != VRCAnimatorTrackingControl.TrackingType.NoChange) + { + runtime.IKTrackingOutputData.trackingRightHand = behaviour.trackingRightHand; + } + if (behaviour.trackingLeftHand != VRCAnimatorTrackingControl.TrackingType.NoChange) + { + runtime.IKTrackingOutputData.trackingLeftHand = behaviour.trackingLeftHand; + } + if (behaviour.trackingRightFoot != VRCAnimatorTrackingControl.TrackingType.NoChange) + { + runtime.IKTrackingOutputData.trackingRightFoot = behaviour.trackingRightFoot; + } + }; + }; + } + + void OnDestroy () { + if (this.playableGraph.IsValid()) { + this.playableGraph.Destroy(); + } + if (attachedAnimators != null) { + foreach (var anim in attachedAnimators) { + LyumaAv3Runtime runtime; + if (animatorToTopLevelRuntime.TryGetValue(anim, out runtime) && runtime == this) + { + animatorToTopLevelRuntime.Remove(anim); + } + } + } + if (animator != null) { + if (animator.playableGraph.IsValid()) + { + animator.playableGraph.Destroy(); + } + animator.runtimeAnimatorController = origAnimatorController; + } + } + + void Awake() + { + if (AvatarSyncSource != null && OriginalSourceClone == null) { + Debug.Log("Awake returning early for " + gameObject.name, this); + return; + } + if (attachedAnimators != null) { + Debug.Log("Deduplicating Awake() call if we already got awoken by our children.", this); + return; + } + // Debug.Log("AWOKEN " + gameObject.name, this); + attachedAnimators = new HashSet<Animator>(); + if (AvatarSyncSource == null) { + var oml = GetComponent<UnityEngine.AI.OffMeshLink>(); + if (oml != null && oml.startTransform != null) { + this.emulator = oml.startTransform.GetComponent<LyumaAv3Emulator>(); + GameObject.DestroyImmediate(oml); + } + Transform transform = this.transform; + SourceObjectPath = ""; + while (transform != null) { + SourceObjectPath = "/" + transform.name + SourceObjectPath; + transform = transform.parent; + } + AvatarSyncSource = this; + } else { + AvatarSyncSource = GameObject.Find(SourceObjectPath).GetComponent<LyumaAv3Runtime>(); + } + + if (this.emulator != null) { + DebugDuplicateAnimator = this.emulator.DefaultAnimatorToDebug; + ViewAnimatorOnlyNoParams = this.emulator.DefaultAnimatorToDebug; + } + + animator = this.gameObject.GetOrAddComponent<Animator>(); + if (animatorAvatar != null && animator.avatar == null) { + animator.avatar = animatorAvatar; + } else { + 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]); + } + } + } + bool shouldClone = false; + if (OriginalSourceClone == null) { + OriginalSourceClone = this; + shouldClone = true; + } + if (shouldClone && GetComponent<PipelineSaver>() == null) { + GameObject cloned = GameObject.Instantiate(gameObject); + cloned.hideFlags = HideFlags.HideAndDontSave; + cloned.SetActive(false); + OriginalSourceClone = cloned.GetComponent<LyumaAv3Runtime>(); + Debug.Log("Spawned a hidden source clone " + OriginalSourceClone, OriginalSourceClone); + OriginalSourceClone.OriginalSourceClone = OriginalSourceClone; + } + foreach (var smr in gameObject.GetComponentsInChildren<SkinnedMeshRenderer>(true)) { + smr.updateWhenOffscreen = (AvatarSyncSource == this || IsMirrorClone || IsShadowClone); + } + int desiredLayer = 9; + if (AvatarSyncSource == this) { + desiredLayer = 10; + } + if (IsMirrorClone) { + desiredLayer = 18; + } + if (IsShadowClone) { + desiredLayer = 9; // the Shadowclone is always on playerLocal and never on UI Menu + } + if (gameObject.layer != 12 || desiredLayer == 18) { + gameObject.layer = desiredLayer; + } + allTransforms = gameObject.GetComponentsInChildren<Transform>(true); + foreach (Transform t in allTransforms) { + if (t.gameObject.layer != 12 || desiredLayer == 18) { + t.gameObject.layer = desiredLayer; + } + } + + InitializeAnimator(); + if (addRuntimeDelegate != null) { + addRuntimeDelegate(this); + } + if (AvatarSyncSource == this) { + CreateAv3MenuComponent(); + } + if (this.AvatarSyncSource != this || IsMirrorClone || IsShadowClone) { + PrevAnimatorToViewLiteParamsShow0 = (char)(int)ViewAnimatorOnlyNoParams; + } + if (!IsMirrorClone && !IsShadowClone && AvatarSyncSource == this) { + var pipelineManager = avadesc.GetComponent<VRC.Core.PipelineManager>(); + string avatarid = pipelineManager != null ? pipelineManager.blueprintId : null; + OSCConfigurationFile.EnsureOSCJSONConfig(avadesc.expressionParameters, avatarid, this.gameObject.name); + } + } + + public void CreateMirrorClone() { + if (AvatarSyncSource == this && GetComponent<PipelineSaver>() == null) { + OriginalSourceClone.IsMirrorClone = true; + MirrorClone = GameObject.Instantiate(OriginalSourceClone.gameObject).GetComponent<LyumaAv3Runtime>(); + MirrorClone.GetComponent<Animator>().avatar = null; + OriginalSourceClone.IsMirrorClone = false; + GameObject o = MirrorClone.gameObject; + o.name = gameObject.name + " (MirrorReflection)"; + o.SetActive(true); + allMirrorTransforms = MirrorClone.gameObject.GetComponentsInChildren<Transform>(true); + foreach (Component component in MirrorClone.gameObject.GetComponentsInChildren<Component>(true)) { + if (MirrorCloneComponentBlacklist.Contains(component.GetType()) || component.GetType().ToString().Contains("DynamicBone") + || component.GetType().ToString().Contains("VRCContact") || component.GetType().ToString().Contains("VRCPhysBone")) { + UnityEngine.Object.Destroy(component); + } + } + } + } + + public void CreateShadowClone() { + if (AvatarSyncSource == this && GetComponent<PipelineSaver>() == null) { + OriginalSourceClone.IsShadowClone = true; + ShadowClone = GameObject.Instantiate(OriginalSourceClone.gameObject).GetComponent<LyumaAv3Runtime>(); + ShadowClone.GetComponent<Animator>().avatar = null; + OriginalSourceClone.IsShadowClone = false; + GameObject o = ShadowClone.gameObject; + o.name = gameObject.name + " (ShadowClone)"; + o.SetActive(true); + allShadowTransforms = ShadowClone.gameObject.GetComponentsInChildren<Transform>(true); + foreach (Component component in ShadowClone.gameObject.GetComponentsInChildren<Component>(true)) { + if (ShadowCloneComponentBlacklist.Contains(component.GetType()) || component.GetType().ToString().Contains("DynamicBone") + || component.GetType().ToString().Contains("VRCContact") || component.GetType().ToString().Contains("VRCPhysBone")) { + UnityEngine.Object.Destroy(component); + continue; + } + if (component.GetType() == typeof(SkinnedMeshRenderer) || component.GetType() == typeof(MeshRenderer)) { + Renderer renderer = component as Renderer; + renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.ShadowsOnly; // ShadowCastingMode.TwoSided isn't accounted for and does not work locally + } + } + foreach (Renderer renderer in gameObject.GetComponentsInChildren<Renderer>(true)) { + renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; // ShadowCastingMode.TwoSided isn't accounted for and does not work locally + } + } + } + + private void InitializeAnimator() + { + ResetAvatar = false; + PrevAnimatorToDebug = (char)(int)DebugDuplicateAnimator; + ViewAnimatorOnlyNoParams = DebugDuplicateAnimator; + + animator = this.gameObject.GetOrAddComponent<Animator>(); + animator.avatar = animatorAvatar; + animator.applyRootMotion = false; + animator.updateMode = AnimatorUpdateMode.Normal; + animator.cullingMode = (this == AvatarSyncSource || IsMirrorClone || IsShadowClone) ? AnimatorCullingMode.AlwaysAnimate : AnimatorCullingMode.CullCompletely; + animator.runtimeAnimatorController = null; + + avadesc = this.gameObject.GetComponent<VRCAvatarDescriptor>(); + IKTrackingOutputData.ViewPosition = avadesc.ViewPosition; + IKTrackingOutputData.AvatarScaleFactorGuess = IKTrackingOutputData.ViewPosition.magnitude / BASE_HEIGHT; // mostly guessing... + IKTrackingOutputData.HeadRelativeViewPosition = IKTrackingOutputData.ViewPosition; + if (animator.avatar != null) + { + Transform head = animator.GetBoneTransform(HumanBodyBones.Head); + if (head != null) { + IKTrackingOutputData.HeadRelativeViewPosition = head.InverseTransformPoint(animator.transform.TransformPoint(IKTrackingOutputData.ViewPosition)); + } + } + expressionsMenu = avadesc.expressionsMenu; + 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 (DebugDuplicateAnimator != VRCAvatarDescriptor.AnimLayerType.Base && !IsMirrorClone && !IsShadowClone) { + foreach (VRCAvatarDescriptor.CustomAnimLayer cal in baselayers) { + if (DebugDuplicateAnimator == cal.type) { + i++; + allLayers.Add(cal); + break; + } + } + foreach (VRCAvatarDescriptor.CustomAnimLayer cal in speciallayers) { + if (DebugDuplicateAnimator == 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 (DebugDuplicateAnimator != cal.type) { + i++; + allLayers.Add(cal); + } + } + foreach (VRCAvatarDescriptor.CustomAnimLayer cal in speciallayers) { + if (DebugDuplicateAnimator != cal.type) { + i++; + allLayers.Add(cal); + } + } + } + int dupeOffset = i; + if (!IsMirrorClone && !IsShadowClone) { + 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 (IsMirrorClone || IsShadowClone) { + if (cal.type == VRCAvatarDescriptor.AnimLayerType.FX) { + i++; + allLayers.Add(cal); + } + } else 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); + foreach (Animator anim in animators) + { + attachedAnimators.Add(anim); + animatorToTopLevelRuntime.Add(anim, this); + } + + Dictionary<string, float> stageNameToValue = EarlyRefreshExpressionParameters(); + 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) { + Debug.Log(vrcAnimLayer.type + " controller is null: continue."); + // i was incremented, but one of the playableMixer inputs is left unconnected. + continue; + } + allControllers[vrcAnimLayer.type] = ac; + AnimatorControllerPlayable humanAnimatorPlayable = AnimatorControllerPlayable.Create(playableGraph, ac); + PlayableBlendingState pbs = new PlayableBlendingState(); + for (int j = 0; j < humanAnimatorPlayable.GetLayerCount(); j++) + { + 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); + } + + // Keep weight 1.0 if (i < dupeOffset). + // Layers have incorrect AAP values if playable weight is 0.0... + // and the duplicate layers will be overridden later anyway by Base. + } + + LateRefreshExpressionParameters(stageNameToValue); + + // Plays the Graph. + playableGraph.SetTimeUpdateMode(DirectorUpdateMode.GameTime); + Debug.Log(this.name + " : " + GetType() + " awoken and ready to Play.", this); + playableGraph.Play(); + } + + Dictionary<string, float> EarlyRefreshExpressionParameters() { + 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.exportedValue; + } + 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(); + return stageNameToValue; + } + void LateRefreshExpressionParameters(Dictionary<string, float> stageNameToValue) { + HashSet<string> usedparams = new HashSet<string>(BUILTIN_PARAMETERS); + int 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 = 0.0f; + if (AvatarSyncSource == this) { + 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.exportedValue = 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++; + } + } else { + IntParam param = new IntParam(); + param.stageName = "VRCEmote"; + param.synced = true; + param.name = "VRCEmote"; + Ints.Add(param); + usedparams.Add("VRCEmote"); + FloatParam fparam = new FloatParam(); + fparam.stageName = "VRCFaceBlendH"; + fparam.synced = true; + fparam.name = "VRCFaceBlendH"; + Floats.Add(fparam); + usedparams.Add("VRCFaceBlendH"); + fparam = new FloatParam(); + fparam.stageName = "VRCFaceBlendV"; + fparam.synced = true; + fparam.name = "VRCFaceBlendV"; + Floats.Add(fparam); + usedparams.Add("VRCFaceBlendV"); + } + + //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.exportedValue = 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++; + } + } + + void CreateAv3MenuComponent() { + System.Type gestureManagerMenu = System.Type.GetType("GestureManagerAv3Menu"); + if (gestureManagerMenu != null) { + foreach (var comp in avadesc.gameObject.GetComponents(gestureManagerMenu)) { + UnityEngine.Object.Destroy(comp); + } + } + foreach (var comp in avadesc.gameObject.GetComponents<LyumaAv3Menu>()) { + UnityEngine.Object.Destroy(comp); + } + LyumaAv3Menu mainMenu; + if (gestureManagerMenu != null) { + mainMenu = (LyumaAv3Menu)avadesc.gameObject.AddComponent(gestureManagerMenu); + mainMenu.useLegacyMenu = legacyMenuGUI; + } else { + mainMenu = avadesc.gameObject.AddComponent<LyumaAv3Menu>(); + } + mainMenu.Runtime = this; + mainMenu.RootMenu = avadesc.expressionsMenu; + } + + + private bool isResetting; + private bool isResettingHold; + private bool isResettingSel; + void LateUpdate() { + if (ResetAndHold || (emulator != null && (!emulator.enabled || !emulator.gameObject.activeInHierarchy))) { + return; + } + if (IsMirrorClone || IsShadowClone) { + // Experimental. Attempt to reproduce the 1-frame desync in some cases between normal and mirror copy. + NormalUpdate(); + } + if(animator != null && this == AvatarSyncSource && !IsMirrorClone && !IsShadowClone) { + if (MirrorClone != null) { + MirrorClone.gameObject.SetActive(true); + MirrorClone.transform.localRotation = transform.localRotation; + MirrorClone.transform.localScale = transform.localScale; + MirrorClone.transform.position = transform.position + (DebugOffsetMirrorClone ? new Vector3(0.0f, 1.3f * avadesc.ViewPosition.y, 0.0f) : Vector3.zero); + } + if (ShadowClone != null) { + ShadowClone.gameObject.SetActive(true); + ShadowClone.transform.localRotation = transform.localRotation; + ShadowClone.transform.localScale = transform.localScale; + ShadowClone.transform.position = transform.position; + } + foreach (Transform[] allXTransforms in new Transform[][]{allMirrorTransforms, allShadowTransforms}) { + if (allXTransforms != null) { + Transform head = animator.GetBoneTransform(HumanBodyBones.Head); + for(int i = 0; i < allTransforms.Length && i < allXTransforms.Length; i++) { + if (allXTransforms[i] == null || allTransforms[i] == this.transform) { + continue; + } + MeshRenderer mr = allTransforms[i].GetComponent<MeshRenderer>(); + MeshRenderer xmr = allXTransforms[i].GetComponent<MeshRenderer>(); + if (mr != null && xmr != null) { + for (int mri = 0; mri < mr.sharedMaterials.Length && mri < xmr.sharedMaterials.Length; mri++) { + xmr.sharedMaterials[mri] = mr.sharedMaterials[mri]; + } + } + allXTransforms[i].localPosition = allTransforms[i].localPosition; + allXTransforms[i].localRotation = allTransforms[i].localRotation; + if(allTransforms[i] == head && EnableHeadScaling) { + allXTransforms[i].localScale = new Vector3(1.0f, 1.0f, 1.0f); + } else { + allXTransforms[i].localScale = allTransforms[i].localScale; + } + bool theirs = allTransforms[i].gameObject.activeSelf; + if (allXTransforms[i].gameObject.activeSelf != theirs) { + allXTransforms[i].gameObject.SetActive(theirs); + } + } + } + } + } + } + + void FixedUpdate() { + if (Jump && !WasJump && Grounded) { + JumpingVelocity = new Vector3(0.0f, JumpPower, 0.0f); + JumpingHeight += JumpingVelocity; + Grounded = false; + } + WasJump = Jump; + if (JumpingHeight != Vector3.zero) { + JumpingHeight += JumpingVelocity; + JumpingVelocity += Physics.gravity * Time.fixedDeltaTime; + if (JumpingHeight.y <= 0.0f) { + JumpingHeight = Vector3.zero; + JumpingVelocity = Vector3.zero; + Grounded = true; + Jump = false; + WasJump = false; + } + Velocity.y = JumpingVelocity.y; + + } + } + + private bool broadcastStartNextFrame; + void OnEnable() { + if (emulator != null && emulator.WorkaroundPlayModeScriptCompile) { + ApplyOnEnableWorkaroundDelegate(); + } + if (attachedAnimators == null && AvatarSyncSource != null) { + broadcastStartNextFrame = true; + } + } + + void Update() { + if (broadcastStartNextFrame) { + Debug.Log("BROADCASTING START!"); + broadcastStartNextFrame = false; + BroadcastMessage("Start"); + } + if (emulator != null && (!emulator.enabled || !emulator.gameObject.activeInHierarchy)) { + return; + } + if (!IsMirrorClone && !IsShadowClone) { + NormalUpdate(); + } + } + + // Update is called once per frame + void NormalUpdate() + { + if (OSCConfigurationFile.OSCAvatarID == null) { + OSCConfigurationFile.OSCAvatarID = A3EOSCConfiguration.AVTR_EMULATOR_PREFIX + "Default"; + } + if ((OSCConfigurationFile.UseRealPipelineIdJSONFile && OSCConfigurationFile.OSCAvatarID.StartsWith(A3EOSCConfiguration.AVTR_EMULATOR_PREFIX)) || + (!OSCConfigurationFile.UseRealPipelineIdJSONFile && !OSCConfigurationFile.OSCAvatarID.StartsWith(A3EOSCConfiguration.AVTR_EMULATOR_PREFIX))) { + var pipelineManager = avadesc.GetComponent<VRC.Core.PipelineManager>(); + string avatarid = pipelineManager != null ? pipelineManager.blueprintId : null; + OSCConfigurationFile.EnsureOSCJSONConfig(avadesc.expressionParameters, avatarid, this.gameObject.name); + } + if (OSCConfigurationFile.SaveOSCConfig) { + OSCConfigurationFile.SaveOSCConfig = false; + A3EOSCConfiguration.WriteJSON(OSCConfigurationFile.OSCFilePath, OSCConfigurationFile.OSCJsonConfig); + } + if (OSCConfigurationFile.LoadOSCConfig) { + OSCConfigurationFile.LoadOSCConfig = false; + OSCConfigurationFile.OSCJsonConfig = A3EOSCConfiguration.ReadJSON(OSCConfigurationFile.OSCFilePath); + } + if (OSCConfigurationFile.GenerateOSCConfig) { + OSCConfigurationFile.GenerateOSCConfig = false; + OSCConfigurationFile.OSCJsonConfig = A3EOSCConfiguration.GenerateOuterJSON(avadesc.expressionParameters, OSCConfigurationFile.OSCAvatarID, this.gameObject.name); + } + if (lastLegacyMenuGUI != legacyMenuGUI && AvatarSyncSource == this) { + lastLegacyMenuGUI = legacyMenuGUI; + foreach (var av3MenuComponent in GetComponents<LyumaAv3Menu>()) { + av3MenuComponent.useLegacyMenu = legacyMenuGUI; + } + } + if (isResettingSel) { + isResettingSel = false; + if (updateSelectionDelegate != null && AvatarSyncSource == this) { + updateSelectionDelegate(this.gameObject); + PrevAnimatorToViewLiteParamsShow0 = (char)126; + } + } + if (isResettingHold && (!ResetAvatar || !ResetAndHold)) { + ResetAndHold = ResetAvatar = false; + isResettingSel = true; + if (updateSelectionDelegate != null && AvatarSyncSource == this) { + updateSelectionDelegate(this.emulator != null ? this.emulator.gameObject : null); + PrevAnimatorToViewLiteParamsShow0 = (char)126; + } + } + if (ResetAvatar && ResetAndHold) { + return; + } + if (ResetAndHold && !ResetAvatar && !isResetting) { + ResetAvatar = true; + isResettingHold = true; + } + if (isResetting && !ResetAndHold) { + if (attachedAnimators == null) { + if (AvatarSyncSource == this) { + AvatarSyncSource = null; + } + Awake(); + isResetting = false; + isResettingHold = false; + return; + } else { + InitializeAnimator(); + } + isResetting = false; + isResettingHold = false; + } + if (PrevAnimatorToDebug != (char)(int)DebugDuplicateAnimator || ResetAvatar || attachedAnimators == null) { + 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 && AvatarSyncSource == this) { + updateSelectionDelegate(this.emulator != null ? this.emulator.gameObject : null); + } + isResetting = true; + isResettingSel = true; + return; + } + if (PrevAnimatorToViewLiteParamsShow0 == (char)127) { + updateSelectionDelegate(this); + ViewAnimatorOnlyNoParams = (VRCAvatarDescriptor.AnimLayerType)(int)126; + PrevAnimatorToViewLiteParamsShow0 = (char)(int)ViewAnimatorOnlyNoParams; + } + if ((char)(int)ViewAnimatorOnlyNoParams != PrevAnimatorToViewLiteParamsShow0) { + PrevAnimatorToViewLiteParamsShow0 = (char)127; + RuntimeAnimatorController rac = null; + allControllers.TryGetValue(ViewAnimatorOnlyNoParams, out rac); + updateSelectionDelegate(rac == null ? (UnityEngine.Object)this.emulator : (UnityEngine.Object)rac); + } + if (RefreshExpressionParams) { + RefreshExpressionParams = false; + Dictionary<string, float> stageNameToValue = EarlyRefreshExpressionParameters(); + LateRefreshExpressionParameters(stageNameToValue); + } + if(this == AvatarSyncSource && !IsMirrorClone && !IsShadowClone) { + Transform head = animator.GetBoneTransform(HumanBodyBones.Head); + if (head != null) { + head.localScale = EnableHeadScaling ? new Vector3(0.0001f, 0.0001f, 0.0001f) : new Vector3(1.0f, 1.0f, 1.0f); // head bone is set to 0.0001 locally (not multiplied + } + } + if (DisableMirrorAndShadowClones && (MirrorClone != null || ShadowClone != null)) { + allMirrorTransforms = null; + allShadowTransforms = null; + GameObject.Destroy(MirrorClone.gameObject); + MirrorClone = null; + GameObject.Destroy(ShadowClone.gameObject); + ShadowClone = null; + } + if (!DisableMirrorAndShadowClones && MirrorClone == null && ShadowClone == null) { + CreateMirrorClone(); + CreateShadowClone(); + } + if (emulator != null) { + if (LastViewMirrorReflection != ViewMirrorReflection) { + emulator.ViewMirrorReflection = ViewMirrorReflection; + } else { + ViewMirrorReflection = emulator.ViewMirrorReflection; + } + LastViewMirrorReflection = ViewMirrorReflection; + if (LastViewBothRealAndMirror != ViewBothRealAndMirror) { + emulator.ViewBothRealAndMirror = ViewBothRealAndMirror; + } else { + ViewBothRealAndMirror = emulator.ViewBothRealAndMirror; + } + LastViewBothRealAndMirror = ViewBothRealAndMirror; + var osc = emulator.GetComponent<LyumaAv3Osc>(); + if (OSCController != null && EnableAvatarOSC && (!osc.openSocket || osc.avatarDescriptor != avadesc)) { + EnableAvatarOSC = false; + OSCController = null; + } + if (OSCController != null && !EnableAvatarOSC) { + osc.openSocket = false; + OSCController = null; + } + if (OSCController == null && EnableAvatarOSC) { + osc = emulator.gameObject.GetOrAddComponent<LyumaAv3Osc>(); + osc.openSocket = true; + osc.avatarDescriptor = avadesc; + osc.enabled = true; + OSCController = osc; + // updateSelectionDelegate(osc.gameObject); + } + } + + if (CreateNonLocalClone) { + CreateNonLocalClone = false; + GameObject go = GameObject.Instantiate(OriginalSourceClone.gameObject); + go.hideFlags = 0; + 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); + go.SetActive(true); + } + if (IsMirrorClone || IsShadowClone) { + NonLocalSyncInterval = 0.0f; + } else { + NonLocalSyncInterval = AvatarSyncSource.NonLocalSyncInterval; + } + if (nextUpdateTime == 0.0f) { + nextUpdateTime = Time.time + NonLocalSyncInterval; + } + bool ShouldSyncThisFrame = (AvatarSyncSource != this && (Time.time >= nextUpdateTime || NonLocalSyncInterval <= 0.0f)); + if (AvatarSyncSource != this) { + IKSyncRadialMenu = AvatarSyncSource.IKSyncRadialMenu; + LyumaAv3Menu[] menus = AvatarSyncSource.GetComponents<LyumaAv3Menu>(); + for (int i = 0; i < Ints.Count; i++) { + if (StageParamterToBuiltin.ContainsKey(Ints[i].stageName)) { + // Simulate IK sync of open gesture parameter. + if (ShouldSyncThisFrame || (IKSyncRadialMenu && menus.Length >= 1 && menus[0].IsControlIKSynced(Ints[i].name)) + || (IKSyncRadialMenu && menus.Length >= 2 && menus[1].IsControlIKSynced(Ints[i].name))) { + Ints[i].value = ClampByte(AvatarSyncSource.Ints[i].value); + } + } + } + for (int i = 0; i < Floats.Count; i++) { + if (StageParamterToBuiltin.ContainsKey(Floats[i].stageName)) { + // Simulate IK sync of open gesture parameter. + if (ShouldSyncThisFrame || (IKSyncRadialMenu && menus.Length >= 1 && menus[0].IsControlIKSynced(Floats[i].name)) + || (IKSyncRadialMenu && menus.Length >= 2 && menus[1].IsControlIKSynced(Floats[i].name))) { + Floats[i].exportedValue = ClampAndQuantizeFloat(AvatarSyncSource.Floats[i].exportedValue); + Floats[i].value = Floats[i].exportedValue; + } + } + } + for (int i = 0; i < Bools.Count; i++) { + if (StageParamterToBuiltin.ContainsKey(Bools[i].stageName)) { + if (ShouldSyncThisFrame) { + Bools[i].value = AvatarSyncSource.Bools[i].value; + } + } + } + if (ShouldSyncThisFrame) { + nextUpdateTime = Time.time + NonLocalSyncInterval; + } + } + if (AvatarSyncSource != this) { + // Simulate more continuous "IK sync" of these parameters. + 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; + Grounded = AvatarSyncSource.Grounded; + Seated = AvatarSyncSource.Seated; + AFK = AvatarSyncSource.AFK; + TrackingType = AvatarSyncSource.TrackingType; + TrackingTypeIdx = AvatarSyncSource.TrackingTypeIdx; + TrackingTypeIdxInt = AvatarSyncSource.TrackingTypeIdxInt; + VRMode = AvatarSyncSource.VRMode; + MuteSelf = AvatarSyncSource.MuteSelf; + InStation = AvatarSyncSource.InStation; + } + for (int i = 0; i < Floats.Count; i++) { + if (Floats[i].expressionValue != Floats[i].lastExpressionValue_) { + Floats[i].exportedValue = Floats[i].expressionValue; + Floats[i].lastExpressionValue_ = Floats[i].expressionValue; + } + if (StageParamterToBuiltin.ContainsKey(Floats[i].stageName)) { + if (locally8bitQuantizedFloats) { + Floats[i].exportedValue = ClampAndQuantizeFloat(Floats[i].exportedValue); + } else { + Floats[i].exportedValue = ClampFloatOnly(Floats[i].exportedValue); + } + Floats[i].value = Floats[i].exportedValue; + } + } + 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] != null) + { + playableBlendingStates[sittingIndex].StartBlend(playableMixer.GetInputWeight(sittingIndex + 1), Seated ? 1f : 0f, 0.25f); + PrevSeated = Seated; + } + if (TPoseCalibration != PrevTPoseCalibration && tposeIndex >= 0 && playableBlendingStates[tposeIndex] != null) { + playableBlendingStates[tposeIndex].StartBlend(playableMixer.GetInputWeight(tposeIndex + 1), TPoseCalibration ? 1f : 0f, 0.0f); + PrevTPoseCalibration = TPoseCalibration; + } + if (IKPoseCalibration != PrevIKPoseCalibration && ikposeIndex >= 0 && playableBlendingStates[ikposeIndex] != null) { + 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 (GestureLeftWeight != OldGestureLeftWeight) { + OldGestureLeftWeight = GestureLeftWeight; + if (GestureLeftWeight < 0.01f) { + GestureLeftIdx = 0; + } + if (GestureLeftWeight > 0.01f && (GestureLeftIdx == 0 || GestureLeftWeight < 0.99f)) { + GestureLeftIdx = 1; + } + } + if (GestureRightWeight != OldGestureRightWeight) { + OldGestureRightWeight = GestureRightWeight; + if (GestureRightWeight < 0.01f) { + GestureRightIdx = 0; + } + if (GestureRightWeight > 0.01f && (GestureRightIdx == 0 || GestureRightWeight < 0.99f)) { + GestureRightIdx = 1; + } + } + 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 (GestureLeft == GestureIndex.Neutral) { + GestureLeftWeight = 0; + } else if (GestureLeft != GestureIndex.Fist) { + GestureLeftWeight = 1; + } + if (GestureRight == GestureIndex.Neutral) { + GestureRightWeight = 0; + } else if (GestureRight != GestureIndex.Fist) { + GestureRightWeight = 1; + } + 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()) { + whichcontroller++; + 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()) { + whichcontroller++; + 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); + if (!playable.IsParameterControlledByCurve(paramid)) { + param.exportedValue = param.value; + } + } + } + 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("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("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++; + } + + if (((emulator != null && !emulator.DisableAvatarDynamicsIntegration) + || (AvatarSyncSource?.emulator != null && !AvatarSyncSource.emulator.DisableAvatarDynamicsIntegration)) && + !IsMirrorClone && !IsShadowClone) + { + assignContactParameters(avadesc.gameObject.GetComponentsInChildren<VRCContactReceiver>()); + assignPhysBoneParameters(avadesc.gameObject.GetComponentsInChildren<VRCPhysBone>()); + } + + for (int i = 0; i < playableBlendingStates.Count; i++) { + var pbs = playableBlendingStates[i]; + if (pbs == null) { + continue; + } + 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)); + } + } + } + } + + float getObjectFloat(object o) { + switch (o) { + // case bool b: + // return b ? 1.0f : 0.0f; + // case int i: + // return (float)i; + // case long l: + // return (float)l; + case float f: + return f; + // case double d: + // return (float)d; + } + return 0.0f; + } + int getObjectInt(object o) { + switch (o) { + // case bool b: + // return b ? 1 : 0; + case int i: + return i; + // case long l: + // return (int)l; + // case float f: + // return (int)f; + // case double d: + // return (int)d; + } + return 0; + } + bool isObjectTrue(object o) { + switch (o) { + case bool b: + return b; + case int i: + return i == 1; + // case long l: + // return l == 1; + // case float f: + // return f == 1.0f; + // case double d: + // return d == 1.0; + } + return false; + } + + public void GetOSCDataInto(List<A3ESimpleOSC.OSCMessage> messages) { + messages.Add(new A3ESimpleOSC.OSCMessage { + arguments = new object[1] {(object)OSCConfigurationFile.OSCAvatarID}, + path="/avatar/change", + time = new Vector2Int(-1,-1), + }); + if (OSCConfigurationFile.SendRecvAllParamsNotInJSON) { + foreach (var b in Bools) { + if (b.synced) { + messages.Add(new A3ESimpleOSC.OSCMessage { + arguments = new object[1] {(object)(int)((bool)b.value ? 1 : 0)}, + path = "/avatar/parameters/" + b.name, + time = new Vector2Int(-1,-1), + }); + } + } + foreach (var i in Ints) { + if (i.synced) { + messages.Add(new A3ESimpleOSC.OSCMessage { + arguments = new object[1] {(object)(int)i.value}, + path = "/avatar/parameters/" + i.name, + time = new Vector2Int(-1,-1), + }); + } + } + foreach (var f in Floats) { + if (f.synced) { + messages.Add(new A3ESimpleOSC.OSCMessage { + arguments = new object[1] {(object)(float)f.value}, + path = "/avatar/parameters/" + f.name, + time = new Vector2Int(-1,-1), + }); + } + } + } else { + foreach (var prop in OSCConfigurationFile.OSCJsonConfig.parameters) { + if (prop.name != null && prop.name.Length > 0 && prop.output.address != null && prop.output.address.Length > 0) { + string addr = prop.output.address; + float outputf = 0.0f; + string typ = "?"; + if (BoolToIndex.TryGetValue(prop.name, out var bidx)) { + if (!Bools[bidx].synced) { + continue; + } + outputf = Bools[bidx].value ? 1.0f : 0.0f; + typ = "bool"; + } else if (IntToIndex.TryGetValue(prop.name, out var iidx)) { + if (!Ints[iidx].synced) { + continue; + } + outputf = (float)Ints[iidx].value; + typ = "int"; + } else if (FloatToIndex.TryGetValue(prop.name, out var fidx)) { + if (!Floats[fidx].synced) { + continue; + } + outputf = Floats[fidx].value; + typ = "float"; + } else { + switch (prop.name) { + case "VelocityZ": + outputf = Velocity.z; + break; + case "VelocityY": + outputf = Velocity.y; + break; + case "VelocityX": + outputf = Velocity.x; + break; + case "InStation": + outputf = InStation ? 1.0f : 0.0f; + break; + case "Seated": + outputf = Seated ? 1.0f : 0.0f; + break; + case "AFK": + outputf = AFK ? 1.0f : 0.0f; + break; + case "Upright": + outputf = Upright; + break; + case "AngularY": + outputf = AngularY; + break; + case "Grounded": + outputf = Grounded ? 1.0f : 0.0f; + break; + case "MuteSelf": + outputf = MuteSelf ? 1.0f : 0.0f; + break; + case "VRMode": + outputf = VRMode ? 1.0f : 0.0f; + break; + case "TrackingType": + outputf = TrackingTypeIdxInt; + break; + case "GestureRightWeight": + outputf = GestureRightWeight; + break; + case "GestureRight": + outputf = GestureRightIdxInt; + break; + case "GestureLeftWeight": + outputf = GestureLeftWeight; + break; + case "GestureLeft": + outputf = GestureLeftIdxInt; + break; + case "Voice": + outputf = Voice; + break; + case "Viseme": + outputf = VisemeInt; + break; + default: + Debug.LogWarning("Unrecognized built in param"); + break; + } + } + object output; + switch (prop.output.type) { + case "Float": + output = (object)(float)outputf; + break; + case "Int": + output = (object)(int)outputf; + break; + case "Bool": + output = (object)(outputf != 0.0f); + break; + default: + Debug.LogError("Unrecognized JSON type " + prop.input.type + " for address " + addr + " for output " + typ + " parameter " + + prop.name + ". Should be \"Float\", \"Int\" or \"Bool\"."); + continue; + } + messages.Add(new A3ESimpleOSC.OSCMessage { + arguments = new object[1] {(object)output}, + path = addr, + time = new Vector2Int(-1,-1), + }); + } + } + } + } + public void processOSCInputMessage(string ParamName, object arg0) { + float argFloat = getObjectFloat(arg0); + int argInt = getObjectInt(arg0); + bool argBool = isObjectTrue(arg0); + switch (ParamName) { + case "Vertical": + Velocity.z = (3.0f + RunSpeed) * argFloat; + break; + case "Horizontal": + Velocity.x = (3.0f + RunSpeed) * (float)arg0; + break; + case "LookHorizontal": + AngularY = argFloat; + break; + case "UseAxisRight": + case "GrabAxisRight": + case "MoveHoldFB": + case "SpinHoldCwCcw": + case "SpinHoldUD": + case "SpinHoldLR": + break; + case "MoveForward": + Velocity.z = argBool ? 5.0f : 0.0f; + break; + case "MoveBackward": + Velocity.z = argBool ? -5.0f : 0.0f; + break; + case "MoveLeft": + Velocity.x = argBool ? -5.0f : 0.0f; + break; + case "MoveRight": + Velocity.x = argBool ? 5.0f : 0.0f; + break; + case "LookLeft": + AngularY = argBool ? -1.0f : 0.0f; + break; + case "LookRight": + AngularY = argBool ? 1.0f : 0.0f; + break; + case "Jump": + Jump = argBool; + break; + case "Run": + RunSpeed = argBool ? 3.0f : 0.0f; + break; + case "ComfortLeft": + case "ComfortRight": + case "DropRight": + case "UseRight": + case "GrabRight": + case "DropLeft": + case "UseLeft": + case "GrabLeft": + case "PanicButton": + case "QuickMenuToggleLeft": + case "QuickMenuToggleRight": + break; + case "Voice": + if (argBool && !MuteTogglerOn) { + MuteSelf = !MuteSelf; + } + MuteTogglerOn = argBool; + break; + default: + Debug.LogWarning("Unrecognized OSC input command " + ParamName); + break; + } + } + public void HandleOSCMessages(List<A3ESimpleOSC.OSCMessage> messages) { + var innerProperties = new Dictionary<string, A3EOSCConfiguration.InnerJson>(); + foreach (var ij in OSCConfigurationFile.OSCJsonConfig.parameters) { + if (ij.input.address != null && ij.input.address.Length > 0) { + innerProperties[ij.input.address] = ij; + } + } + foreach (var msg in messages) { + string msgPath = msg.path; + object[] arguments = msg.arguments; + if (AvatarSyncSource != this || !IsLocal || IsMirrorClone || IsShadowClone) { + return; + } + float argFloat = getObjectFloat(arguments[0]); + int argInt = getObjectInt(arguments[0]); + bool argBool = isObjectTrue(arguments[0]); + if (msgPath.StartsWith("/input/")) { + string ParamName = msgPath.Split(new char[]{'/'}, 3)[2]; + processOSCInputMessage(ParamName, arguments[0]); + } else { + string ParamName; + if (OSCConfigurationFile.SendRecvAllParamsNotInJSON) { + ParamName = msgPath.Split(new char[]{'/'}, 4)[3]; + } else if (innerProperties.ContainsKey(msgPath)) { + ParamName = innerProperties[msgPath].name; + if (innerProperties[msgPath].input.type == "Float") { + if (arguments[0].GetType() != typeof(float)) { + Debug.LogWarning("Address " + msgPath + " for parameter " + ParamName + " expected float in JSON but received " + arguments[0].GetType()); + continue; + } + } else if (innerProperties[msgPath].input.type == "Int" || innerProperties[msgPath].input.type == "Bool") { + if (arguments[0].GetType() != typeof(int) && arguments[0].GetType() != typeof(bool)) { + Debug.LogWarning("Address " + msgPath + " for parameter " + ParamName + " expected int/bool in JSON but received " + arguments[0].GetType()); + continue; + } + } else { + Debug.LogError("Unrecognized JSON type " + innerProperties[msgPath].input.type + " for address " + msgPath + " for inupt parameter " + + ParamName + " but received " + arguments[0].GetType() + ". Should be \"Float\", \"Int\" or \"Bool\"."); + continue; + } + } else { + Debug.LogWarning("Address " + msgPath + " not found for input in JSON."); + continue; + } + if (OSCController != null && OSCController.debugPrintReceivedMessages) { + Debug.Log("Recvd "+ParamName + ": " + msg); + } + if (arguments.Length > 0 && arguments[0].GetType() == typeof(bool)) { + int idx; + if (BoolToIndex.TryGetValue(ParamName, out idx)) { + Bools[idx].value = (bool)(arguments[0]); + } + if (IntToIndex.TryGetValue(ParamName, out idx)) { + Ints[idx].value = (int)(arguments[0]); + } + } + if (arguments.Length > 0 && arguments[0].GetType() == typeof(int)) { + int idx; + if (BoolToIndex.TryGetValue(ParamName, out idx)) { + Bools[idx].value = ((int)(arguments[0])) != 0; + } + if (IntToIndex.TryGetValue(ParamName, out idx)) { + Ints[idx].value = (int)(arguments[0]); + } + } + if (arguments.Length > 0 && arguments[0].GetType() == typeof(float)) { + int idx; + if (FloatToIndex.TryGetValue(ParamName, out idx)) { + Floats[idx].value = (float)(arguments[0]); + Floats[idx].exportedValue = Floats[idx].value; + } + } + } + } + } +} |