// ----- ------ Avatar ----- ----- public static Dictionary <string, VRCAvatarDescriptor> GetAvatars(out bool crossScene) { crossScene = false; Dictionary <string, VRCAvatarDescriptor> avatars = new Dictionary <string, VRCAvatarDescriptor>(); for (int i = 0; i < SceneManager.sceneCount; i++) { Scene scene = SceneManager.GetSceneAt(i); if (!scene.isLoaded) { continue; } GameObject[] rootObjects = scene.GetRootGameObjects(); foreach (GameObject root in rootObjects) { VRCAvatarDescriptor avatar = root.GetComponent <VRCAvatarDescriptor>(); if (null != avatar) { avatars.Add(TEA_Manager.GetSceneAvatarKey(scene, avatar), avatar); if (scene != SceneManager.GetActiveScene()) { crossScene = true; } } } } return(avatars); }
private void Awake() { current = this; System.Random random = new System.Random(); id = random.NextDouble(); Debug.Log($"TEA_Manager awake [{id}]"); }
public bool ManagerSetup(bool play, bool _play, bool _compile) { List <TEA_Manager> managers = new List <TEA_Manager>(); manager = null; int activeCount = 0; bool destroy = (!play || !_play || !_compile) && !settings.keepInScene; for (int i = 0; i < SceneManager.sceneCount; i++) { Scene scene = SceneManager.GetSceneAt(i); if (!scene.isLoaded) { continue; } int count = 0; foreach (GameObject obj in scene.GetRootGameObjects()) { Component comp = obj.GetComponentInChildren(typeof(TEA_Manager), true); if (null != comp) { count++; TEA_Manager manager = (TEA_Manager)comp; if (!play && (count > 1 || destroy || scene != SceneManager.GetActiveScene())) { DestroyImmediate(manager.gameObject); } else if (scene == SceneManager.GetActiveScene()) { this.manager = manager; activeCount++; } } //exists } //for obj } // for scene // add managers if (null == manager && _avatars && (_compile || _play || play || settings.keepInScene)) { TEA_Manager newManager = Instantiate(prefabObject).GetComponent <TEA_Manager>(); this.manager = newManager; } return(activeCount > 1); }
private bool Initialized() { if (_initialized) { return(true); } Avatar = TEA_Manager.current.Avatar; if (null == Avatar) { return(false); } newPosition = Avatar.transform.position; horizontalRotation = Avatar.transform.rotation; //--- paramters --- mainMenu = Avatar.expressionsMenu; parameters = Avatar.expressionParameters; avatarAnim = Avatar.gameObject.GetComponent <Animator>(); avatarAnim.runtimeAnimatorController = TEA_Manager.current.Controllers[TEA_Manager.AvatarIndex()]; Grounded = true; avatarAnim.SetBool("Grounded", Grounded); TEA_PlayableLayerData layerData = TEA_Manager.current.LayerInfo[TEA_Manager.AvatarIndex()]; TEA_PlayableLayerData.PlayableLayerData data = layerData.FindPlayableLayerData(VRCAvatarDescriptor.AnimLayerType.Base); for (int i = data.start; i < data.end; i++) { avatarAnim.SetLayerWeight(i, 1); } //--- events --- controlEvents = new Dictionary <VRC.SDKBase.VRC_PlayableLayerControl.BlendableLayer, TEA_PlayableLayerControl>(); if (null == TEA_PlayableLayerControl.ApplySettings) { TEA_PlayableLayerControl.ApplySettings += TEA_PlayableLayerEvent; } _initialized = true; return(true); }
public void OnTEAManagerUpdate(TEA_Manager tea_manager) { _initialized = false; Start(); }
private void Update() { if (!Initialized()) { return; } //--- controls --- List <VRCPlayableLayerControl.BlendableLayer> remove = new List <VRC.SDKBase.VRC_PlayableLayerControl.BlendableLayer>(); foreach (KeyValuePair <VRCPlayableLayerControl.BlendableLayer, TEA_PlayableLayerControl> item in controlEvents) { TEA_PlayableLayerData layerData = TEA_Manager.current.LayerInfo[TEA_Manager.AvatarIndex()]; TEA_PlayableLayerData.PlayableLayerData data = layerData.FindPlayableLayerData(TEA_PlayableLayerControl.AnimLayerType(item.Value.layer)); float prevDur = item.Value.duration; item.Value.duration += Time.deltaTime; float normalized = item.Value.duration / item.Value.blendDuration; for (int i = data.start; i < data.end; i++) { if (normalized >= 1) { item.Value.animator.SetLayerWeight(i, item.Value.goalWeight); remove.Add(item.Value.layer); } else { float newWeight = Mathf.Lerp(item.Value.animator.GetLayerWeight(i), item.Value.goalWeight, (item.Value.duration - prevDur) / (item.Value.blendDuration - prevDur)); item.Value.animator.SetLayerWeight(i, newWeight); } } } foreach (VRCPlayableLayerControl.BlendableLayer rm in remove) { controlEvents.Remove(rm); } if (CameraController.mouseIn && Input.GetMouseButton(1) && !CameraController.FreeCamera) { newPosition = Vector3.zero; Avatar.transform.position = newPosition; VelocityX = 0; VelocityZ = 0; } else if (null != Locomotion && CameraController.mouseIn && !CameraController.FreeCamera) { //Rotate float turnAmount = Mathf.Ceil(Locomotion.RotationAmount * Time.deltaTime); if (Input.GetKey(KeyCode.E)) { horizontalRotation *= Quaternion.Euler(Vector3.up * turnAmount); } if (Input.GetKey(KeyCode.Q)) { horizontalRotation *= Quaternion.Euler(Vector3.up * -turnAmount); } Avatar.transform.rotation = horizontalRotation; // --- input --- if (Input.GetKeyUp(KeyCode.LeftShift)) { speed++; } if (3 <= speed) { speed = 0; } if (0 == speed) { Locomotion.MoveType = TEA_Settings.MoveTypes.Walk; velocity = Locomotion.WalkVelocity; } else if (1 == speed) { Locomotion.MoveType = TEA_Settings.MoveTypes.Run; velocity = Locomotion.RunVelocity; } else if (2 == speed) { Locomotion.MoveType = TEA_Settings.MoveTypes.Sprint; velocity = Locomotion.SprintVelocity; } // Walk int forward = Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow) ? 1 : 0; int backward = Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow) ? -1 : 0; int right = Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow) ? 1 : 0; int left = Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow) ? -1 : 0; int z = forward + backward; int x = right + left; if (x != 0 || z != 0) { float distance = Time.deltaTime * velocity; float theta = Mathf.Atan2(z, x); float distanceX = Mathf.Cos(theta) * (distance); float distanceZ = Mathf.Sin(theta) * (distance); Vector3 cameraDirection = CameraController.RigCamera.transform.forward; // Animator Velocities if (TEA_Manager.current.ViewPort.gameObject.activeSelf) { newPosition = TEA_Utility.TransformPoint(Avatar.transform, new Vector3(distanceX, 0, distanceZ)); VelocityZ = distanceZ / Time.deltaTime; } else { newPosition += TEA_Manager.current.CameraRigPointer.transform.right * distanceX; newPosition += TEA_Manager.current.CameraRigPointer.transform.forward * distanceZ; Vector3 movePoint = TEA_Utility.InverseTransformPoint(Avatar.transform, newPosition); float aTheta = Mathf.Deg2Rad * Avatar.transform.rotation.eulerAngles.y; Vector3 movePointR = new Vector3(); movePointR.x = movePoint.x * Mathf.Cos(aTheta) - movePoint.z * Mathf.Sin(aTheta); movePointR.z = movePoint.z * Mathf.Cos(aTheta) + movePoint.x * Mathf.Sin(aTheta); VelocityX = 0 + movePointR.x / Time.deltaTime; VelocityZ = 0 + movePointR.z / Time.deltaTime; } //Debug.Log($"[{x},{z}] distance[{distance}] float[{distanceX}, {distanceZ}] velocity[{VelocityX}, {VelocityZ}]"); // world position Avatar.transform.position = newPosition; } else { newPosition = Avatar.transform.position; VelocityX = 0; VelocityZ = 0; } } else { newPosition = Avatar.transform.position; VelocityX = 0; VelocityZ = 0; } }
public bool CompileAnimators(TEA_Manager manager, TEA_Settings settings) { WorkingDirPath = ASSETS_CONTENT + settings.WorkingDirectory; WorkingDirContent = WorkingDirPath + "/"; try { // working folder if(!AssetDatabase.IsValidFolder(WorkingDirPath)) { if(string.IsNullOrEmpty(AssetDatabase.CreateFolder("Assets", settings.WorkingDirectory))) { EditorUtility.DisplayDialog(ERROR_HEADER, $"Could not create working folder [{WorkingDirPath}]", "ok"); return true; } } List<TEA_ValidationIssues> avatarIssues = new List<TEA_ValidationIssues>(); validationIssue = false; AnimatorController teaAnimContr = GenerateTEA_Animator(manager); foreach(string path in AssetDatabase.GetSubFolders(WorkingDirPath)) { AssetDatabase.DeleteAsset(path); } int aCount = 0; // --- --- --- for all avatars foreach(VRCAvatarDescriptor avatar in TEA_Manager.AvatarDescriptor) { //Scene Folder string sceneFolder = GetPath(false, WorkingDirPath, TEA_Manager.AvatarDescriptor[aCount].gameObject.scene.name); if(!AssetDatabase.IsValidFolder(sceneFolder)) { if(string.IsNullOrEmpty(AssetDatabase.CreateFolder(WorkingDirPath, TEA_Manager.AvatarDescriptor[aCount].gameObject.scene.name))) { EditorUtility.DisplayDialog(ERROR_HEADER, $"Could not create working folder [{sceneFolder}]", "ok"); return true; } } drivers = new List<DriverIssue>(); VRCAvatarDescriptor avatarComp = TEA_Manager.AvatarDescriptor[aCount]; currentAvatar = avatarComp; issues = TEA_ValidationIssues.CreateInstance<TEA_ValidationIssues>(); issues.AvatarName = avatarComp.name; string avatarKey = avatarComp.gameObject.name; //Debug.Log($"----- Creating animator controllers for [{avatarKey}]"); // avatar folder string folderPath = GetPath(false, sceneFolder, avatarKey); if(string.IsNullOrEmpty(AssetDatabase.CreateFolder(sceneFolder, avatarKey))) { EditorUtility.DisplayDialog(ERROR_HEADER, $"Could not create working folder [{folderPath}]", "ok"); return true; } //--- Animator --- superAnimator = new AnimatorController() { name = CONTROLLER_PREFIX + avatarKey }; TEA_PlayableLayerData layerInfo = TEA_PlayableLayerData.CreateInstance<TEA_PlayableLayerData>(); layerInfo.AvatarName = avatarKey; layerInfo.name = avatarKey + "-layerData"; RuntimeAnimatorController baseRunContr = manager.Base; if(!avatarComp.baseAnimationLayers[0].isDefault && null != avatarComp.baseAnimationLayers[0].animatorController) { baseRunContr = avatarComp.baseAnimationLayers[0].animatorController; EditorUtility.SetDirty(baseRunContr); AssetDatabase.SaveAssets(); } string baseControllerPath = GetPath(false, folderPath, "Base.controller"); AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(baseRunContr), baseControllerPath); AnimatorController baseAnimContr = AssetDatabase.LoadAssetAtPath<AnimatorController>(baseControllerPath); GetBehaviours(baseRunContr, baseAnimContr, layerInfo, VRCAvatarDescriptor.AnimLayerType.Base); CombineAnimator(superAnimator, baseAnimContr, null); layerInfo.data[0].start = 1; layerInfo.data[0].end = layerInfo.data[0].start + baseAnimContr.layers.Length; // Additive AnimatorController additiveAnimContr = null; if(!avatarComp.baseAnimationLayers[1].isDefault && null != avatarComp.baseAnimationLayers[1].animatorController) { RuntimeAnimatorController additiveRunContr = avatarComp.baseAnimationLayers[1].animatorController; EditorUtility.SetDirty(additiveRunContr); AssetDatabase.SaveAssets(); string additiveControllerPath = GetPath(false, folderPath, "Additive.controller"); AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(additiveRunContr), additiveControllerPath); additiveAnimContr = AssetDatabase.LoadAssetAtPath<AnimatorController>(additiveControllerPath); GetBehaviours(additiveRunContr, additiveAnimContr, layerInfo, VRCAvatarDescriptor.AnimLayerType.Additive); CombineAnimator(superAnimator, additiveAnimContr, null); layerInfo.data[1].start = layerInfo.data[0].end; layerInfo.data[1].end = layerInfo.data[0].end + (additiveAnimContr.layers.Length); } else { layerInfo.data[1].start = layerInfo.data[0].end; layerInfo.data[1].end = layerInfo.data[0].end; } // TEA Animations CombineAnimator(superAnimator, teaAnimContr, null); // Gesture RuntimeAnimatorController gestureRunContr = manager.Gesture_Male; if(!avatarComp.baseAnimationLayers[2].isDefault && null != avatarComp.baseAnimationLayers[2].animatorController) { gestureRunContr = avatarComp.baseAnimationLayers[2].animatorController; EditorUtility.SetDirty(gestureRunContr); AssetDatabase.SaveAssets(); } string gestureControllerPath = GetPath(false, folderPath, "Gesture.controller"); AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(gestureRunContr), gestureControllerPath); AnimatorController gestureAnimContr = AssetDatabase.LoadAssetAtPath<AnimatorController>(gestureControllerPath); GetBehaviours(gestureRunContr, gestureAnimContr, layerInfo, VRCAvatarDescriptor.AnimLayerType.Gesture); CombineAnimator(superAnimator, gestureAnimContr, null); layerInfo.data[2].start = layerInfo.data[1].end + teaAnimContr.layers.Length; layerInfo.data[2].end = layerInfo.data[1].end + teaAnimContr.layers.Length + (gestureAnimContr.layers.Length); //Actions RuntimeAnimatorController actionRunContr = manager.Action; if(!avatarComp.baseAnimationLayers[3].isDefault && null != avatarComp.baseAnimationLayers[3].animatorController) { actionRunContr = avatarComp.baseAnimationLayers[3].animatorController; EditorUtility.SetDirty(actionRunContr); AssetDatabase.SaveAssets(); } string actionControllerPath = GetPath(false, folderPath, "Action.controller"); AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(actionRunContr), actionControllerPath); AnimatorController actionAnimContr = AssetDatabase.LoadAssetAtPath<AnimatorController>(actionControllerPath); GetBehaviours(actionRunContr, actionAnimContr, layerInfo, VRCAvatarDescriptor.AnimLayerType.Action); CombineAnimator(superAnimator, actionAnimContr, null); layerInfo.data[3].start = layerInfo.data[2].end; layerInfo.data[3].end = layerInfo.data[2].end + (actionAnimContr.layers.Length); //FX AnimatorController fxAnimContr = null; if(!avatarComp.baseAnimationLayers[4].isDefault && null != avatarComp.baseAnimationLayers[4].animatorController) { RuntimeAnimatorController fxRunContr = avatarComp.baseAnimationLayers[4].animatorController; EditorUtility.SetDirty(fxRunContr); AssetDatabase.SaveAssets(); string fxControllerPath = GetPath(false, folderPath, "FX.controller"); AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(fxRunContr), fxControllerPath); fxAnimContr = AssetDatabase.LoadAssetAtPath<AnimatorController>(fxControllerPath); GetBehaviours(fxRunContr, fxAnimContr, layerInfo, VRCAvatarDescriptor.AnimLayerType.FX); CombineAnimator(superAnimator, fxAnimContr, manager.AvatarMaskNone); //SetFXDefault(action, fxAnimContr, avatarComp.gameObject, manager.AvatarMaskNone, folderPath); //CombineAnimator(action, fxAnimContr, manager.AvatarMaskNone); layerInfo.data[4].start = layerInfo.data[3].end + 1; layerInfo.data[4].end = layerInfo.data[3].end + 1 + (fxAnimContr.layers.Length); } else { layerInfo.data[4].start = layerInfo.data[3].end; layerInfo.data[4].end = layerInfo.data[3].end; } SetAnimationDefault(superAnimator, avatarComp.gameObject, manager.AvatarMaskNone, folderPath); string superAnimatorPath = GetPath(false, folderPath, superAnimator.name + ".controller"); AssetDatabase.CreateAsset(superAnimator, superAnimatorPath); manager.Controllers.Add(AssetDatabase.LoadAssetAtPath<RuntimeAnimatorController>(superAnimatorPath)); string layerInfoPath = GetPath(false, folderPath, layerInfo.name + ".asset"); AssetDatabase.CreateAsset(layerInfo, layerInfoPath); manager.LayerInfo.Add(AssetDatabase.LoadAssetAtPath<TEA_PlayableLayerData>(layerInfoPath)); /*Debug.Log($"HEAD[{AvatarController.GetBone(avatarComp, HumanBodyBones.Head).position.ToString("F4")}] " + $"ViewPort:[{avatarComp.ViewPosition.ToString("F4")}] " + $"Avatar:[{avatarComp.gameObject.transform.position.ToString("F4")}] " + $"World from Avatar:[{TEA_EditorUtility.TransformPoint(avatarComp.gameObject.transform, avatarComp.ViewPosition).ToString("F4")}]" + $"calc[{TEA_EditorUtility.InverseTransformPoint(AvatarController.GetBone(avatarComp, HumanBodyBones.Head), TEA_EditorUtility.TransformPoint(avatarComp.gameObject.transform, avatarComp.ViewPosition)).ToString("F4")}]");*/ manager.ViewPorts.Add(TEA_EditorUtility.InverseTransformPoint(AvatarController.GetBone(avatarComp, HumanBodyBones.Head), TEA_EditorUtility.TransformPoint(avatarComp.gameObject.transform, avatarComp.ViewPosition))); //Debug.Log($"----- Created animator controllers for [{avatarKey}]"); // Validation if(validate) { //--- check layers string nullLayer = "Playable Layer is not default, it should be set in Descriptor"; foreach(VRCAvatarDescriptor.CustomAnimLayer layer in avatarComp.baseAnimationLayers) { if(!layer.isDefault && null == layer.animatorController) { Issue issue = new TEA_ValidationIssues.Issue(nullLayer, currentAvatar); issues.GetLayer(layer.type).Add(issue); } if(settings.LayerRestrictions) ValidateOnlyTransforms(layer.type, AssetDatabase.LoadAssetAtPath<AnimatorController>(AssetDatabase.GetAssetPath(layer.animatorController))); } // missing Expression Parameters if(settings.AllParametersUsed) ValidateCustomExpressions(avatarComp, superAnimator); // drivers if(null == avatar.expressionParameters) { if(drivers.Count > 0) issues.ParameterDrivers.Add(new Issue("You have Parameter Drivers but no ExpressionParameters")); } else { foreach(DriverIssue driver in drivers) { if(driver.driver.parameters.Count == 0) { Issue issue = new TEA_ValidationIssues.Issue($"Layer[{ driver.layerName }]: no parameter set"); issue.Reference.Add(driver.state); issue.Reference.Add(driver.driver); issues.GetLayer(driver.layerType).Add(issue); } foreach(VRCAvatarParameterDriver.Parameter param in driver.driver.parameters) { if(null == currentAvatar.expressionParameters.FindParameter(param.name)) { Issue issue = new TEA_ValidationIssues.Issue($"Layer [{driver.layerName}]: [{param.name}] is not in ExpressionParameters"); issue.Reference.Add(driver.state); issue.Reference.Add(driver.driver); issues.GetLayer(driver.layerType).Add(issue); } if(!TEA_EditorUtility.HasAnimatorParameter(param.name, superAnimator.parameters)) { Issue issue = new TEA_ValidationIssues.Issue($"Layer [{driver.layerName}]: [{param.name}] is not a parameter in any Playable Layer"); issue.Reference.Add(driver.state); issue.Reference.Add(driver.driver); issues.GetLayer(driver.layerType).Add(issue); } } }//for } if(issues.ValidationIssues()) { avatarIssues.Add(issues); validationIssue = true; } }//validate AssetDatabase.SaveAssets(); aCount++; }// for avatar if(validationIssue) { TEA_Error_Window.Open(avatarIssues); } return !validationIssue; } catch(TEA_Exception e) { throw e; } catch(Exception e) { EditorUtility.DisplayDialog(ERROR_HEADER, $"TEA Manager ran into an unexpected issue while compiling [{currentAvatar.name}].\n" + "If you cannot resolve the issue please raise a ticket on the GitHub and include the error log in the console.", "ok"); Debug.LogError(new TEA_Exception("Unexpected Exception", e)); } return false; }
private static AnimatorController GenerateTEA_Animator(TEA_Manager manager) { // --- TEA --- AnimatorController teaAnimContr = AssetDatabase.LoadAssetAtPath<AnimatorController>(AssetDatabase.GetAssetPath(manager.TEA_Animations)); while(teaAnimContr.layers.Length > 0) { teaAnimContr.RemoveLayer(0); } AnimatorStateMachine stateD = new AnimatorStateMachine(); teaAnimContr.AddLayer(new AnimatorControllerLayer() { name = AvatarController.TEA_HAND_LAYER, defaultWeight = 1, avatarMask = manager.AvatarMaskArms, stateMachine = stateD }); //default stateD.defaultState = stateD.AddState("Default"); manager.TEA_AnimationClips.ClearOptions(); List<Dropdown.OptionData> options = new List<Dropdown.OptionData>(); int count = 0; List<AnimationClip> armClips = GetAnimationClips("Assets/TEA Manager/Resources/Animation/TEA Animations/TEA Hand Animations"); foreach(AnimationClip clip in armClips) { AnimatorState state = stateD.AddState(clip.name); state.motion = clip; stateD.defaultState.AddTransition(state).AddCondition(AnimatorConditionMode.Equals, count, AvatarController.TEA_ANIM_PARAM); state.AddExitTransition().AddCondition(AnimatorConditionMode.NotEqual, count, AvatarController.TEA_ANIM_PARAM); Dropdown.OptionData option = new Dropdown.OptionData(clip.name); options.Add(option); count++; }//for manager.GetComponent<AvatarController>().TEA_HAND_LAYER_COUNT = count; // --- Full Body Animations AnimatorStateMachine stateM = new AnimatorStateMachine(); teaAnimContr.AddLayer(new AnimatorControllerLayer() { name = AvatarController.TEA_LAYER, defaultWeight = 0, avatarMask = manager.AvatarMaskAll, stateMachine = stateM }); stateM.defaultState = stateM.AddState("Default"); // dynamic foreach(string folder in AssetDatabase.GetSubFolders("Assets/TEA Manager/Resources/Animation/TEA Animations")) { string name = folder.Substring(folder.LastIndexOf('/') + 1); if("TEA Hand Animations" == name) continue; Dropdown.OptionData option = new Dropdown.OptionData(name); options.Add(option); AnimationClip start = null; AnimationClip loop = null; foreach(AnimationClip clip in GetAnimationClips(folder)) { if(clip.name.Contains("intro") || clip.name.Contains("Intro")) { start = clip; } else loop = clip; }//for AnimatorState state = stateM.AddState(name); state.motion = loop; if(null != start) { AnimatorState startState = stateM.AddState(name + "-intro"); startState.motion = start; stateM.defaultState.AddTransition(startState).AddCondition(AnimatorConditionMode.Equals, count, AvatarController.TEA_ANIM_PARAM); startState.AddTransition(state).hasExitTime = true; } else { stateM.defaultState.AddTransition(state).AddCondition(AnimatorConditionMode.Equals, count, AvatarController.TEA_ANIM_PARAM); } state.AddExitTransition().AddCondition(AnimatorConditionMode.NotEqual, count, AvatarController.TEA_ANIM_PARAM); count++; }//for manager.TEA_AnimationClips.AddOptions(options); AssetDatabase.SaveAssets(); return teaAnimContr; }