Esempio n. 1
0
        private void OnGUI()
        {
            srcController = EditorGUILayout.ObjectField("Src AnimatorController", srcController, typeof(AnimatorController), true) as AnimatorController;

            using (new EditorGUI.IndentLevelScope())
            {
                EditorGUILayout.LabelField("Copy ↓");
            }
            dstController = EditorGUILayout.ObjectField("Dst AnimatorController", dstController, typeof(AnimatorController), true) as AnimatorController;

            using (new EditorGUI.DisabledGroupScope(!srcController || !dstController))
            {
                if (GUILayout.Button("Combine"))
                {
                    AnimatorControllerUtility.CombineAnimatorController(srcController, dstController);
                }
            }
        }
Esempio n. 2
0
        private GameObject ConvertAvatarTo3(GameObject avatarPrefab2, VRCAvatarDescripterDeserializedObject avatar2Info)
        {
            var avatarObj3 = PrefabUtility.InstantiatePrefab(avatarPrefab2) as GameObject;
            avatarObj3.name = GameObjectUtility.GetUniqueNameForSibling(avatarObj3.transform.parent, $"{ avatarObj3.name}_3.0");
            var avatar = avatarObj3.AddComponent<VRCAvatarDescriptor>();
            {
                avatar.Name = avatar2Info.Name;
                avatar.ViewPosition = avatar2Info.ViewPosition;
                avatar.ScaleIPD = avatar2Info.ScaleIPD;
                avatar.lipSync = avatar2Info.lipSync;
                avatar.VisemeSkinnedMesh = avatarObj3.transform.Find(avatar2Info.faceMeshRendererPath)?
                                                .GetComponent<SkinnedMeshRenderer>() ?? null;
                avatar.VisemeBlendShapes = avatar2Info.VisemeBlendShapes;
            }

            // TODO: アバターによってはRotationStatesを設定しないと白目になってしまうのでenableEyeLook=falseにしておく
            avatar.customEyeLookSettings = new VRCAvatarDescriptor.CustomEyeLookSettings
            {
                leftEye = avatarObj3.transform.Find(LEFT_EYE_PATH),
                rightEye = avatarObj3.transform.Find(RIGHT_EYE_PATH),
                // TODO: 設定が未完了なのでアバターが鏡に写らなくなってしまう
                //eyelidType = VRCAvatarDescriptor.EyelidType.Blendshapes,
                eyelidsSkinnedMesh = avatarObj3.transform.Find(EYELIDS_MESH_PATH)?
                                        .GetComponent<SkinnedMeshRenderer>() ?? null
            };

            if (avatar.customEyeLookSettings.eyelidsSkinnedMesh is null)
            {
                avatar.customEyeLookSettings.eyelidType = VRCAvatarDescriptor.EyelidType.None;
            }

            // TODO: 足りない場合falseにする(現状必ずfalseなので一旦コメントアウトする)
            //if (avatar.customEyeLookSettings.leftEye is null && avatar.customEyeLookSettings.rightEye is null &&
            //    avatar.customEyeLookSettings.eyelidType == VRCAvatarDescriptor.EyelidType.None)
            //{
            //    avatar.enableEyeLook = false;
            //}

            InitializeCusomizeAnimationLayers(avatar);

            // CustomStandingAnimsが未設定ならPlayableLayerを設定しない
            if (avatar2Info.OverrideAnimationClips is null) return avatarObj3;

            // FaceEmotion
            var faceBlendShapes = new List<EditorCurveBinding>();
            string searchTargetHandsLayer;
            if (avatar2Info.DefaultAnimationSet == VRCAvatarDescripterDeserializedObject.AnimationSet.Male)
            {
                searchTargetHandsLayer = "vrc_AvatarV3HandsLayer t:AnimatorController";
            }
            else
            {
                searchTargetHandsLayer = "vrc_AvatarV3HandsLayer2 t:AnimatorController";
            }
            var originalHandLayerControllerPath = VRCAssetUtility.GetVRCAssetPathForSearch(searchTargetHandsLayer);
            var fxController = AnimatorControllerUtility.DuplicateAnimationLayerController(
                                    originalHandLayerControllerPath,
                                    Path.GetDirectoryName(avatar2Info.standingOverrideControllerPath),
                                    avatarPrefab2.name);

            SetControllerToAnimationLayer(avatar, AnimationLayerType.FX, fxController);

            var emptyAnimation = new AnimationClip();
            AssetDatabase.CreateAsset(
                emptyAnimation,
                AssetDatabase.GenerateUniqueAssetPath(
                    Path.Combine(
                        Path.GetDirectoryName(avatar2Info.standingOverrideControllerPath),
                        $"EmptyClip_{avatarPrefab2.name}.anim")));
            AssetDatabase.SaveAssets();

            foreach (var layer in fxController.layers)
            {
                if (layer.name != "Left Hand" && layer.name != "Right Hand") continue;

                foreach (var childState in layer.stateMachine.states)
                {
                    if (childState.state is null) continue;

                    var animationInfo = avatar2Info.OverrideAnimationClips
                                            .Where(c => c != null && childState.state.name.Contains(c.Type))
                                            .SingleOrDefault();

                    if (animationInfo != null)
                    {
                        var animClip = AssetDatabase.LoadAssetAtPath<AnimationClip>(animationInfo.Path);
                        if (childState.state != null && animClip != null)
                            childState.state.motion = animClip;

                        var bindings = AnimationUtility.GetCurveBindings(animClip);
                        faceBlendShapes.AddRange(bindings.Where(x => x.type == typeof(SkinnedMeshRenderer)));
                    }

                    // まばたき干渉防止
                    var control = childState.state.AddStateMachineBehaviour<VRCAnimatorTrackingControl>();
                    if (childState.state.name.StartsWith("Idle"))
                    {
                        control.trackingEyes = VRC.SDKBase.VRC_AnimatorTrackingControl.TrackingType.Tracking;

                        // WriteDefaultsオフ対策でResetFaceレイヤーで初期化しているので
                        // BlendShapeを何も適用しないように空のClipを設定する
                        childState.state.motion = emptyAnimation;
                    }
                    else
                    {
                        control.trackingEyes = VRC.SDKBase.VRC_AnimatorTrackingControl.TrackingType.Animation;
                    }
                }
            }

            // 表情をResetするためのLayerを追加(WriteDefaultオフだとIdleで表情が元に戻らない)
            var defaultFaceAnimation = CreateDefaultFaceAnimation(
                                            avatarObj3,
                                            avatarPrefab2.name, 
                                            faceBlendShapes.Distinct(),
                                            Path.GetDirectoryName(avatar2Info.standingOverrideControllerPath));
            var resetFaceStateMachine = new AnimatorStateMachine();
            var resetState = resetFaceStateMachine.AddState("Reset");
            {
                resetState.writeDefaultValues = false;
                resetState.motion = defaultFaceAnimation;
            }
            var fxControllerPath = AssetDatabase.GetAssetPath(fxController);
            AssetDatabase.AddObjectToAsset(resetState, fxControllerPath);
            AssetDatabase.AddObjectToAsset(resetFaceStateMachine, fxControllerPath);
            var resetFaceLayer = new AnimatorControllerLayer
            {
                defaultWeight = 1,
                name = "ResetFace",
                stateMachine = resetFaceStateMachine
            };
            InsertLayer(fxController, resetFaceLayer, 1);

            if (HasEmoteAnimation(avatar2Info.OverrideAnimationClips))
            {
                // Emote
                var originalActionLayerController = Resources.Load<AnimatorController>("Controllers/vrc_AvatarV3ActionLayer_VRCAV3T");
                var originalActionLayerControllerPath = AssetDatabase.GetAssetPath(originalActionLayerController);
                var actionController = AnimatorControllerUtility.DuplicateAnimationLayerController(
                                            originalActionLayerControllerPath,
                                            Path.GetDirectoryName(avatar2Info.standingOverrideControllerPath),
                                            avatarPrefab2.name);

                SetControllerToAnimationLayer(avatar, AnimationLayerType.Action, actionController);

                var actionLayer = actionController.layers.First();
                for (int i = 0; i < avatar2Info.OverrideAnimationClips.Length; i++)
                {
                    var animationInfo = avatar2Info.OverrideAnimationClips[i];
                    if (animationInfo is null || string.IsNullOrEmpty(animationInfo.Path) || !animationInfo.Type.StartsWith("Emote")) continue;

                    var animClip = AssetDatabase.LoadAssetAtPath(animationInfo.Path, typeof(AnimationClip)) as AnimationClip;
                    var state = GetAnimatorStateFromStateName(actionLayer.stateMachine, animationInfo.Type);
                    if (state is null) continue;
                    state.motion = animClip;
                }

                avatar.customExpressions = true;
                var exMenu = CreateInstance<VRCExpressionsMenu>();
                AssetDatabase.CreateAsset(
                                exMenu,
                                AssetDatabase.GenerateUniqueAssetPath(
                                    Path.Combine(
                                        Path.GetDirectoryName(avatar2Info.standingOverrideControllerPath),
                                        $"ExMenu_{avatarPrefab2.name}.asset")));
                var subMenuEmotes = CreateInstance<VRCExpressionsMenu>();
                AssetDatabase.CreateAsset(
                                subMenuEmotes,
                                AssetDatabase.GenerateUniqueAssetPath(
                                    Path.Combine(
                                        Path.GetDirectoryName(avatar2Info.standingOverrideControllerPath),
                                        $"ExMenu_Emotes_{avatarPrefab2.name}.asset")));
                var exParameters = CreateInstance<VRCExpressionParameters>();
                AssetDatabase.CreateAsset(
                                exParameters,
                                AssetDatabase.GenerateUniqueAssetPath(
                                    Path.Combine(
                                        Path.GetDirectoryName(avatar2Info.standingOverrideControllerPath),
                                        $"ExParams_{avatarPrefab2.name}.asset")));
                avatar.expressionsMenu = exMenu;
                avatar.expressionParameters = exParameters;

                var emoteIconPath = VRCAssetUtility.GetVRCAssetPathForSearch("person_dance t:texture");
                var emoteIcon = AssetDatabase.LoadAssetAtPath<Texture2D>(emoteIconPath);

                exMenu.controls.Add(new VRCExpressionsMenu.Control
                {
                    name = "Emotes",
                    icon = emoteIcon,
                    type = VRCExpressionsMenu.Control.ControlType.SubMenu,
                    subMenu = subMenuEmotes
                });

                for (int i = 0; i < avatar2Info.OverrideAnimationClips.Length; i++)
                {
                    var animationInfo = avatar2Info.OverrideAnimationClips[i];
                    if (animationInfo is null || string.IsNullOrEmpty(animationInfo.Path) || !animationInfo.Type.StartsWith("Emote")) continue;

                    subMenuEmotes.controls.Add(new VRCExpressionsMenu.Control
                    {
                        name = Path.GetFileNameWithoutExtension(animationInfo.Path),
                        icon = emoteIcon,
                        type = VRCExpressionsMenu.Control.ControlType.Button,
                        parameter = new VRCExpressionsMenu.Control.Parameter
                        {
                            name = "VRCEmote"
                        },
                        value = int.Parse(animationInfo.Type.Replace("Emote", string.Empty))
                    });
                }

                Selection.activeObject = exParameters;
            }

            // Sitting Animation
            string searchTargetSittingLayer;
            if (avatar2Info.DefaultAnimationSet == VRCAvatarDescripterDeserializedObject.AnimationSet.Male)
            {
                searchTargetSittingLayer = "vrc_AvatarV3SittingLayer t:AnimatorController";
            }
            else
            {
                searchTargetSittingLayer = "vrc_AvatarV3SittingLayer2 t:AnimatorController";
            }
            var originalSittingLayerControllerPath = VRCAssetUtility.GetVRCAssetPathForSearch(searchTargetSittingLayer);
            var sittingController = AnimatorControllerUtility.DuplicateAnimationLayerController(
                                        originalSittingLayerControllerPath,
                                        Path.GetDirectoryName(avatar2Info.standingOverrideControllerPath),
                                        avatarPrefab2.name);

            SetControllerToAnimationLayer(avatar, SpecialAnimationLayerType.Sitting, sittingController);

            // 元Avatars2.0のVRCAvatarDescriptorを削除
            // TODO: 現状だとMissingなスクリプトすべて消してしまうのでVRCAvatarDescriptorだけ消したい
            var components = avatar.GetComponents<Component>();
            int count = 0;
            for (int i = 0; i < components.Length; i++)
            {
                if (components[i] is null)
                {
                    var serializedObject = new SerializedObject(avatar.gameObject);
                    var property = serializedObject.FindProperty("m_Component");
                    property.DeleteArrayElementAtIndex(i - count);
                    count++;
                    serializedObject.ApplyModifiedProperties();
                }
            }
            PrefabUtility.UnpackPrefabInstance(avatar.gameObject, PrefabUnpackMode.OutermostRoot, InteractionMode.AutomatedAction);

            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();

            return avatarObj3;
        }
        private void OnGUI()
        {
            using (var check = new EditorGUI.ChangeCheckScope())
            {
                srcController = EditorGUILayout.ObjectField("Src AnimatorController", srcController, typeof(AnimatorController), true) as AnimatorController;
                if (check.changed)
                {
                    if (srcController != null)
                    {
                        isCopyLayers = Enumerable.Range(1, srcController.layers.Length)
                                       .Select(i => true)
                                       .ToArray();
                        isCopyParameters = Enumerable.Range(1, srcController.parameters.Length)
                                           .Select(i => true)
                                           .ToArray();
                    }
                }
            }
            if (srcController != null)
            {
                using (new EditorGUI.IndentLevelScope())
                    using (var scroll = new EditorGUILayout.ScrollViewScope(srcControllerScrollPos, new GUIStyle(), new GUIStyle("verticalScrollbar")))
                    {
                        srcControllerScrollPos = scroll.scrollPosition;
                        using (new EditorGUILayout.HorizontalScope())
                        {
                            using (new EditorGUILayout.VerticalScope())
                            {
                                EditorGUILayout.LabelField("Layers", EditorStyles.boldLabel);
                                for (int i = 0; i < srcController.layers.Length; i++)
                                {
                                    var layer = srcController.layers[i];
                                    using (new EditorGUILayout.HorizontalScope())
                                    {
                                        isCopyLayers[i] = EditorGUILayout.ToggleLeft(layer.name, isCopyLayers[i]);
                                    }
                                }
                            }
                            GUILayout.FlexibleSpace();
                            using (new EditorGUILayout.VerticalScope())
                            {
                                EditorGUILayout.LabelField("Parameters", EditorStyles.boldLabel);
                                for (int i = 0; i < srcController.parameters.Length; i++)
                                {
                                    var parameter = srcController.parameters[i];
                                    using (new EditorGUILayout.HorizontalScope())
                                    {
                                        isCopyParameters[i] = EditorGUILayout.ToggleLeft($"[{parameter.type}]{parameter.name}", isCopyParameters[i]);
                                    }
                                }
                            }
                        }
                        EditorGUILayout.Space();
                    }
            }

            EditorGUILayout.Space();
            using (new EditorGUI.IndentLevelScope())
                using (new EditorGUILayout.HorizontalScope())
                {
                    GUILayout.FlexibleSpace();
                    EditorGUILayout.LabelField("Copy ↓", GUILayout.Width(60f));
                    GUILayout.FlexibleSpace();
                }
            EditorGUILayout.Space();

            dstController = EditorGUILayout.ObjectField("Dst AnimatorController", dstController, typeof(AnimatorController), true) as AnimatorController;
            if (dstController != null)
            {
                using (new EditorGUI.IndentLevelScope())
                    using (var scroll = new EditorGUILayout.ScrollViewScope(dstControllerScrollPos, new GUIStyle(), new GUIStyle("verticalScrollbar")))
                    {
                        dstControllerScrollPos = scroll.scrollPosition;
                        using (new EditorGUILayout.HorizontalScope())
                        {
                            using (new EditorGUILayout.VerticalScope())
                            {
                                EditorGUILayout.LabelField("Layers", EditorStyles.boldLabel);
                                foreach (var layer in dstController.layers)
                                {
                                    using (new EditorGUILayout.HorizontalScope())
                                    {
                                        EditorGUILayout.LabelField(layer.name);
                                    }
                                }
                            }
                            GUILayout.FlexibleSpace();
                            using (new EditorGUILayout.VerticalScope())
                            {
                                EditorGUILayout.LabelField("Parameters", EditorStyles.boldLabel);
                                foreach (var parameter in dstController.parameters)
                                {
                                    using (new EditorGUILayout.HorizontalScope())
                                    {
                                        EditorGUILayout.LabelField($"[{parameter.type}]{parameter.name}");
                                    }
                                }
                            }
                        }
                        EditorGUILayout.Space();
                    }
            }

            EditorGUILayout.Space();

            using (new EditorGUI.DisabledGroupScope(!srcController || !dstController))
            {
                if (GUILayout.Button("Combine"))
                {
                    for (int i = 0; i < srcController.layers.Length; i++)
                    {
                        if (!isCopyLayers[i])
                        {
                            continue;
                        }
                        AnimatorControllerUtility.AddLayer(dstController, srcController.layers[i], i == 0);
                    }

                    for (int i = 0; i < srcController.parameters.Length; i++)
                    {
                        if (!isCopyParameters[i])
                        {
                            continue;
                        }
                        AnimatorControllerUtility.AddParameter(dstController, srcController.parameters[i]);
                    }
                }
            }

            EditorGUILayout.Space();
        }