private AnimatorControllerLayer CreateVolumeLayer(string rootName, string paramName, AnimationClip volumeSet) { AnimatorControllerLayer layer = new AnimatorControllerLayer { name = paramName, defaultWeight = 1, stateMachine = new AnimatorStateMachine() { name = paramName } }; AnimatorStateMachine stateMachine = layer.stateMachine; AnimatorState waitState = stateMachine.AddState("Wait"); AnimatorState toggleState = stateMachine.AddState(paramName); toggleState.timeParameter = paramName; toggleState.timeParameterActive = true; toggleState.motion = volumeSet; waitState.AddTransition(toggleState).AddCondition(AnimatorConditionMode.Greater, 0, paramName); Avatar8Track.CreateAsset(AudioClip, volumeSet, paramName + ".anim"); return(layer); }
void OnGUI() { EditorGUILayout.BeginHorizontal(); scrollPos = EditorGUILayout.BeginScrollView(scrollPos, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true)); if (Stage.Instruction == stage) { EditorGUILayout.HelpBox("Welcome to Avatar 8Track. This tool let's you automagically turn a folder of songs into a music player in your avatars radial menu.\n" + "Song Library Setup\n" + " - Create a folder somewhere under Assets\n" + " - Put all of your songs into that folder\n" + " The folder name does not matter.\n" + " All songs from the folder will be added\n" , MessageType.None, true); EditorGUILayout.HelpBox("Do not store anything but your songs in the Song Library, it may be deleted", MessageType.Warning, true); EditorGUILayout.HelpBox("One song library per avatar!", MessageType.Warning, true); EditorGUILayout.HelpBox("If you update the library with new songs just run the tool again", MessageType.Info, true); EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); // --- Avatar stuff --- EditorGUI.BeginChangeCheck(); avatarIndex = EditorGUILayout.Popup("Avatar", avatarIndex, avatarKeys, EditorStyles.popup); if (EditorGUI.EndChangeCheck()) { expressionParameters = avatars[avatarKeys[avatarIndex]].expressionParameters; expressionsMenu = avatars[avatarKeys[avatarIndex]].expressionsMenu; _8TrackEditor.SetFromAvatar(avatars[avatarKeys[avatarIndex]]); } //--- 8Track object EditorGUILayout.HelpBox("The 8Track object will hold all of your AudioClip Components." + "\nIt must be a first child of your avatar." + "\nThe name of the 8Track object will determine names for various assets." + "\nI highly recommend you use the default object.", MessageType.Info, true); EditorGUILayout.LabelField("The 8Track Object"); _8TrackEditor._8TrackObject = (GameObject)EditorGUILayout.ObjectField(_8TrackEditor._8TrackObject, typeof(GameObject), true); if (null == _8TrackEditor._8TrackObject) { _8trackValid = false; if (GUILayout.Button("Create _8Track Object", GUILayout.Height(30))) { _8TrackEditor._8TrackObject = new GameObject("8Track"); _8TrackEditor._8TrackObject.transform.parent = _8TrackEditor.Avatar.transform; Debug.Log($"Created new object for tracks [{_8TrackEditor._8TrackObject.name}]"); } else { _8TrackEditor.SetFromAvatar(avatars[avatarKeys[avatarIndex]]); } } else if (_8TrackEditor._8TrackObject.transform.parent != _8TrackEditor.Avatar.transform) { EditorGUILayout.HelpBox("The 8Track object must be a child of the avatar selected", MessageType.Error, true); _8trackValid = false; } else { _8trackValid = true; } //set var if (null != _8TrackEditor._8TrackObject) { _8TrackEditor.VolumeEPName = Avatar8Track.GetVarName(_8TrackEditor._8TrackObject, Avatar8Track.volumeEPNameSuffix); _8TrackEditor.TrackEPName = Avatar8Track.GetVarName(_8TrackEditor._8TrackObject, Avatar8Track.trackEPNameSuffix); } EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); //--- Parameters if (expressionValid) { EditorGUILayout.HelpBox("A Integer and Float parameter will be added", MessageType.Info, true); } else if (null != expressionParameters) { EditorGUILayout.HelpBox("The ExpressionParameters does not have space for a float and an int" + "\nYou can delete empty parameters to make room", MessageType.Error, true); if (GUILayout.Button("Delete Empty Parameters", GUILayout.Height(30))) { for (int i = 0; i < expressionParameters.parameters.Length; i++) { if (string.IsNullOrEmpty(expressionParameters.parameters[i].name)) { ArrayUtility.RemoveAt <VRCExpressionParameters.Parameter>(ref expressionParameters.parameters, i); i--; } } } } else { EditorGUILayout.HelpBox("An ExpressionParameters object is required", MessageType.Error, true); } EditorGUILayout.LabelField("VRC ExpressionParameters File"); expressionParameters = (VRCExpressionParameters)EditorGUILayout.ObjectField(expressionParameters, typeof(VRCExpressionParameters), true); if (null != expressionParameters && null != _8TrackEditor._8TrackObject) { int cost = VRCExpressionParameters.TypeCost(VRCExpressionParameters.ValueType.Int) + VRCExpressionParameters.TypeCost(VRCExpressionParameters.ValueType.Float); VRCExpressionParameters.Parameter[] parameters = expressionParameters.parameters; foreach (VRCExpressionParameters.Parameter parameter in parameters) { if (parameter.name == _8TrackEditor.VolumeEPName) { cost -= VRCExpressionParameters.TypeCost(VRCExpressionParameters.ValueType.Float); } else if (parameter.name == _8TrackEditor.TrackEPName) { cost -= VRCExpressionParameters.TypeCost(VRCExpressionParameters.ValueType.Int); } } expressionValid = expressionParameters.CalcTotalCost() + cost <= VRCExpressionParameters.MAX_PARAMETER_COST; } else { expressionValid = null != expressionParameters; } EditorGUILayout.LabelField("", GUI.skin.horizontalSlider); //--- Menu if (menuValid) { EditorGUILayout.HelpBox("Which VRCExpressionsMenu do you want the SubMenu added to", MessageType.Info, true); } else if (null != expressionsMenu) { EditorGUILayout.HelpBox("The VRCExpressionsMenu is full", MessageType.Error, true); } if (null == expressionsMenu) { EditorGUILayout.HelpBox("ExpressionsMenu is not set", MessageType.Warning, true); } EditorGUILayout.LabelField("VRC Expressions Menu File"); expressionsMenu = (VRCExpressionsMenu)EditorGUILayout.ObjectField(expressionsMenu, typeof(VRCExpressionsMenu), true); if (null != expressionsMenu && null != _8TrackEditor._8TrackObject) { menuValid = expressionsMenu.controls.Count < VRCExpressionsMenu.MAX_CONTROLS; foreach (VRCExpressionsMenu.Control control in expressionsMenu.controls) { if (control.name == _8TrackEditor._8TrackObject.name) { menuValid = true; } } } else { menuValid = true; } if (null != expressionParameters && menuValid && expressionValid && _8trackValid) { Button(Stage._8Track, "Setup"); } } else if (Stage._8Track == stage) { _8TrackEditor.OnGUI(); Button(Stage.Instruction, "Back <-"); if (_8TrackEditor.IsValid()) { Button(Stage.Burn, "Continue ->"); } } if (Stage.Burn == stage) { EditorGUILayout.HelpBox("Read Carefully!!!!!!", MessageType.Error); EditorGUILayout.HelpBox("This will break the 8Track functionality for any other avatar this song library has been added to previously." + "\nDuplicate the song library if you want to add it to a separate avatar.", MessageType.Warning); EditorGUILayout.HelpBox("Avatar 8Tracks lets you update your song library by re-running it on the same avatar." + "\nfor convenience it auto-detects and deletes assets it has created previously and replaces them." + "\nDouble check the following items below were not manually edited by you." , MessageType.Warning); EditorGUILayout.LabelField("FX Controller Layers, Expression Parameters, and Expression Menu Controls with the following names", EditorStyles.wordWrappedLabel); EditorGUILayout.LabelField($" [{_8TrackEditor.TrackEPName}]", EditorStyles.boldLabel); EditorGUILayout.LabelField($" [{_8TrackEditor.VolumeEPName}]", EditorStyles.boldLabel); EditorGUILayout.LabelField($" [{_8TrackEditor._8TrackObject.name}]", EditorStyles.boldLabel); EditorGUILayout.HelpBox($"All AnimationClips, and ExpressionsMenus will be deleted in [{GetAssetDirectory(_8TrackEditor.AudioClip, true)}]", MessageType.Warning); Button(Stage._8Track, "Back <-"); if (GUILayout.Button("Accept And Burn Tracks", GUILayout.Height(30))) { try { string songLibrary = GetAssetDirectory(_8TrackEditor.AudioClip, false); CleanSongLibrary(songLibrary); List <AudioClip> libAssets = GetSongList(songLibrary); _8TrackEditor.BurnTracks(libAssets); VRC_Actions(songLibrary, libAssets, _8TrackEditor); AssetDatabase.SaveAssets(); EditorUtility.DisplayDialog("Avatar 8Tracks", "You're ready to rock", "great"); this.Close(); } catch (System.Exception e) { EditorUtility.DisplayDialog("so... this happened", e.Message, "sorry"); Debug.LogException(e); this.Close(); } } DrawEnd(); } else { DrawEnd(); } }
public bool BurnTracks(List <AudioClip> libAssets) { if (null == fxAnimator) { throw new System.Exception("We need your FX Animator Controller"); } if (null == AudioClip) { throw new System.Exception("We need an AudioClip from your Song Library"); } if (null == _8TrackObject) { throw new System.Exception("8Track object was not set"); } _8TrackObject.SetActive(false); int childIndex = 0; while (true) { if (0 == _8TrackObject.transform.childCount || childIndex >= _8TrackObject.transform.childCount) { break; } else if (_8TrackObject.transform.GetChild(childIndex).gameObject.name.StartsWith("Track")) { Debug.Log("deleting child " + _8TrackObject.transform.GetChild(childIndex).gameObject.name); DestroyImmediate(_8TrackObject.transform.GetChild(childIndex).gameObject); } else { childIndex++; } } RemoveExistingAnimatorLayer(fxAnimator, _8TrackObject.name); HandleAnimatorParameters(fxAnimator, _8TrackObject.name); //--- --- Volume Animation Controller --- --- AnimationClip volumeSet = new AnimationClip { name = VolumeEPName }; //--- --- Track Animation Controller --- --- AnimatorControllerLayer trackLayer = new AnimatorControllerLayer { name = _8TrackObject.name, defaultWeight = 1, stateMachine = new AnimatorStateMachine() { name = _8TrackObject.name } }; fxAnimator.AddLayer(trackLayer); AnimatorStateMachine stateMachine = trackLayer.stateMachine; //--- --- No Track --- --- int trackCount = 0; AnimatorState defaultState = stateMachine.AddState("Stop"); defaultState.speed = 100; AnimatorStateTransition anyToWait = stateMachine.AddAnyStateTransition(defaultState); anyToWait.AddCondition(AnimatorConditionMode.Equals, trackCount, TrackEPName); anyToWait.hasExitTime = true; anyToWait.exitTime = 0; anyToWait.duration = 0; AnimatorState loopState = stateMachine.AddState("LoopOff"); loopState.speed = 100; if (!PlayerLoops) { PersistAnimator(fxAnimator); VRCAvatarParameterDriver driver = loopState.AddStateMachineBehaviour <VRCAvatarParameterDriver>(); driver.parameters.Add(new VRC.SDKBase.VRC_AvatarParameterDriver.Parameter() { name = TrackEPName, value = 0, type = VRC.SDKBase.VRC_AvatarParameterDriver.ChangeType.Set }); } AnimatorStateTransition loopExit = loopState.AddExitTransition(); loopExit.hasExitTime = true; loopExit.exitTime = 0; loopExit.duration = 0; //--- --- All Tracks --- --- foreach (Object asset in libAssets) { if (asset is AudioClip) { //Animation setup string trackName = "Track " + ++trackCount; AnimationClip tracktoggle = new AnimationClip { name = trackName }; AudioClip clip = asset as AudioClip; tracktoggle.SetCurve(_8TrackObject.name, typeof(GameObject), "m_IsActive", AnimationCurve.Linear(0.0f, 1f, clip.length, 1f)); tracktoggle.SetCurve(_8TrackObject.name + "/" + trackName, typeof(GameObject), "m_IsActive", AnimationCurve.Linear(0.0f, 1f, clip.length, 1f)); AnimatorState state = stateMachine.AddState(trackName); state.motion = tracktoggle; AnimatorStateTransition toTrack = defaultState.AddTransition(state); toTrack.AddCondition(AnimatorConditionMode.Equals, trackCount, TrackEPName); toTrack.hasExitTime = true; toTrack.exitTime = 0; toTrack.duration = 0; AnimatorStateTransition trackOut = state.AddTransition(loopState); trackOut.hasExitTime = true; trackOut.exitTime = 1; trackOut.duration = 0; AnimatorStateTransition trackSwitch = state.AddTransition(defaultState); trackSwitch.AddCondition(AnimatorConditionMode.NotEqual, trackCount, TrackEPName); trackSwitch.duration = 0; Avatar8Track.CreateAsset(AudioClip, tracktoggle, trackName + ".anim"); //volume volumeSet.SetCurve(_8TrackObject.name + "/" + trackName, typeof(AudioSource), "m_Volume", AnimationCurve.Linear(0.0f, 0f, 0.033333335f, 1f)); //Object setup GameObject trackObject = new GameObject(trackName, new System.Type[] { typeof(AudioSource) }); trackObject.transform.SetParent(_8TrackObject.transform); trackObject.SetActive(false); //trackObject.AddComponent<AudioSource>(); Avatar8Track.CopyComponent(_8TrackObject.GetComponent <AudioSource>(), trackObject.GetComponent <AudioSource>()); AudioSource source = trackObject.GetComponent <AudioSource>(); source.enabled = true; source.clip = clip; trackObject.name = trackName; } } fxAnimator.AddLayer(CreateVolumeLayer(_8TrackObject.name, VolumeEPName, volumeSet)); PersistAnimator(fxAnimator); return(true); }