public static void Open(List <TEA_ValidationIssues> issues) { TEA_Error_Window window = GetWindow <TEA_Error_Window>(true, $"Validation Issues"); window.minSize = new Vector2(400, 700); window.issues = issues; int count = 0; foreach (TEA_ValidationIssues issue in issues) { if (count == 0) { window.foldout.Add(true); } else { window.foldout.Add(false); } window.serializedObjects.Add(new SerializedObject(issue)); count++; } }
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; }