public static void OpenWindow() { TEA_Settings settings = TEA_EditorUtility.GetTEA_Settings(); TEA_Settings_EditorWindow window = EditorWindow.GetWindow(typeof(TEA_Settings_EditorWindow), false, "TEA Settings", true) as TEA_Settings_EditorWindow; window.minSize = new Vector2(300, 300); window.editor = Editor.CreateEditorWithContext(new Object[] { settings }, settings); }
public static void MakeAvatar() { GameObject newAvatar = Selection.activeGameObject; TEA_Settings settings = GetTEA_Settings(); // Folders string scenePath = GetParentPath(newAvatar.gameObject.scene.path, false); string parentFolder = GetPath(false, scenePath, newAvatar.gameObject.name); string controller_folder = GetPath(false, parentFolder, settings.PlayableLayersFolder); string animation_folder = GetPath(false, parentFolder, settings.AnimationsFolder); string expression_folder = GetPath(false, parentFolder, settings.ExpressionsFolder); if (!EditorUtility.DisplayDialog(MAKE_AVATAR, $"Avatar assets will be saved in root folder [{parentFolder}]" + "\n" + "\nThis operation may overridden files!" + $"\nExpression assets at [{expression_folder}]" + $"\nPlayable Layer assets at [{controller_folder}]" + $"\nAnimation assets at [{animation_folder}]" , "Continue", "Cancel")) { return; } VRCAvatarDescriptor vrcd = newAvatar.AddComponent <VRCAvatarDescriptor>(); // Folders if (!AssetDatabase.IsValidFolder(parentFolder)) { AssetDatabase.CreateFolder(scenePath, newAvatar.gameObject.name); } if (!AssetDatabase.IsValidFolder(controller_folder)) { AssetDatabase.CreateFolder(parentFolder, settings.PlayableLayersFolder); } if (!AssetDatabase.IsValidFolder(animation_folder)) { AssetDatabase.CreateFolder(parentFolder, settings.AnimationsFolder); } if (!AssetDatabase.IsValidFolder(expression_folder)) { AssetDatabase.CreateFolder(parentFolder, settings.ExpressionsFolder); } // ViewPort Transform leftEye = AvatarController.GetBone(vrcd, HumanBodyBones.LeftEye); Transform rightEye = AvatarController.GetBone(vrcd, HumanBodyBones.RightEye); Transform head = AvatarController.GetBone(vrcd, HumanBodyBones.Head); if (null != leftEye && null != rightEye) { vrcd.ViewPosition = TEA_EditorUtility.InverseTransformPoint(newAvatar.transform, Vector3.Lerp(rightEye.position, leftEye.position, 0.5f)); //Debug.Log($"{leftEye.position.x} - {rightEye.position.x}"); } else if (null != leftEye) { vrcd.ViewPosition = TEA_EditorUtility.InverseTransformPoint(newAvatar.transform, leftEye.position); } else if (null != rightEye) { vrcd.ViewPosition = TEA_EditorUtility.InverseTransformPoint(newAvatar.transform, rightEye.position); } else if (null != head) { vrcd.ViewPosition = TEA_EditorUtility.InverseTransformPoint(newAvatar.transform, head.position); } // Eye Look if (leftEye && rightEye) { vrcd.enableEyeLook = true; vrcd.customEyeLookSettings.leftEye = leftEye; vrcd.customEyeLookSettings.rightEye = rightEye; vrcd.customEyeLookSettings.eyesLookingDown = new VRCAvatarDescriptor.CustomEyeLookSettings.EyeRotations(); vrcd.customEyeLookSettings.eyesLookingDown.left = settings.EyeLookDownLeft; vrcd.customEyeLookSettings.eyesLookingDown.right = settings.EyeLookDownRight; vrcd.customEyeLookSettings.eyesLookingDown.linked = false; vrcd.customEyeLookSettings.eyesLookingRight = new VRCAvatarDescriptor.CustomEyeLookSettings.EyeRotations(); vrcd.customEyeLookSettings.eyesLookingRight.left = settings.EyeLookRightLeft; vrcd.customEyeLookSettings.eyesLookingRight.right = settings.EyeLookRightRight; vrcd.customEyeLookSettings.eyesLookingRight.linked = false; vrcd.customEyeLookSettings.eyesLookingLeft = new VRCAvatarDescriptor.CustomEyeLookSettings.EyeRotations(); vrcd.customEyeLookSettings.eyesLookingLeft.left = settings.EyeLookLeftLeft; vrcd.customEyeLookSettings.eyesLookingLeft.right = settings.EyeLookLeftRight; vrcd.customEyeLookSettings.eyesLookingLeft.linked = false; vrcd.customEyeLookSettings.eyesLookingUp = new VRCAvatarDescriptor.CustomEyeLookSettings.EyeRotations(); vrcd.customEyeLookSettings.eyesLookingUp.left = settings.EyeLookUpLeft; vrcd.customEyeLookSettings.eyesLookingUp.right = settings.EyeLookUpRight; vrcd.customEyeLookSettings.eyesLookingUp.linked = false; } // Lip Sync AutoDetectLipSync(vrcd); //portraitCameraPositionOffset if (settings.SetCameraPosition) { if (settings.PortraitCameraPositionOffset == Vector3.zero) { vrcd.portraitCameraPositionOffset = vrcd.ViewPosition + new Vector3(0, 0, 0.492f); } else { vrcd.portraitCameraPositionOffset = settings.PortraitCameraPositionOffset; } } if (settings.SetCameraRotation) { vrcd.portraitCameraRotationOffset = Quaternion.Euler(settings.PortraitCameraRotationOffset); } // Locomotion vrcd.autoLocomotion = false; // Playable Layers //Debug.Log($"[{vrcd.baseAnimationLayers[0]}]"); CopyPlayableLayer(vrcd, settings, controller_folder, animation_folder); vrcd.customizeAnimationLayers = true; // Custome Layers if (null != settings.Sitting) { vrcd.specialAnimationLayers[0].isDefault = false; vrcd.specialAnimationLayers[0].isEnabled = true; vrcd.specialAnimationLayers[0].animatorController = settings.Sitting; } if (null != settings.TPose) { vrcd.specialAnimationLayers[1].isDefault = false; vrcd.specialAnimationLayers[1].isEnabled = true; vrcd.specialAnimationLayers[1].animatorController = settings.TPose; } if (null != settings.IKPose) { vrcd.specialAnimationLayers[2].isDefault = false; vrcd.specialAnimationLayers[2].isEnabled = true; vrcd.specialAnimationLayers[2].animatorController = settings.IKPose; } // Expressions vrcd.customExpressions = true; string em = GetPath(expression_folder, "ExpressionsMenu.asset"); string ep = GetPath(expression_folder, "ExpressionParameters.asset"); AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(settings.ExpressionsMenu), em); vrcd.expressionsMenu = AssetDatabase.LoadAssetAtPath <VRCExpressionsMenu>(em); AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(settings.ExpressionParameters), ep); vrcd.expressionParameters = AssetDatabase.LoadAssetAtPath <VRCExpressionParameters>(ep); }
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; }