Example #1
0
 private void Awake()
 {
     //Debug.Log("Avatar Controller waking");
     current = this;
 }
        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);
        }
Example #3
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;
	}