/// <summary>
        /// 指定されたブレンドシェイプに、<see cref="BlendShapePreset.Neutral"/>、および<see cref="BlendShapePreset.Blink"/>に含まれるシェイプキーと
        /// 同一のシェイプキーが含まれていれば、そのシェイプキーを複製します。
        /// </summary>
        /// <param name="avatar"></param>
        /// <param name="clip"></param>
        /// <param name="clips"></param>
        /// <returns>置換後のVRMのブレンドシェイプ一覧。</returns>
        private static IEnumerable <VRMBlendShapeClip> DuplicateShapeKeyToUnique(
            GameObject avatar,
            VRMBlendShapeClip clip,
            IEnumerable <VRMBlendShapeClip> clips
            )
        {
            string[] neutralAndBlinkShapeKeyNames = new[] { BlendShapePreset.Neutral, BlendShapePreset.Blink }
            .Select(selector: blendShapePreset => clips.FirstOrDefault(c => c.Preset == blendShapePreset))
            .SelectMany(selector: blendShapeClip =>
                        blendShapeClip ? blendShapeClip.ShapeKeyValues : new Dictionary <string, float>())
            .Select(selector: nameAndWeight => nameAndWeight.Key)
            .ToArray();

            Mesh mesh = avatar.transform.Find(VRChatUtility.AutoBlinkMeshPath).GetSharedMesh();

            foreach (var(oldName, weight) in clip.ShapeKeyValues)
            {
                if (!neutralAndBlinkShapeKeyNames.Contains(oldName))
                {
                    continue;
                }

                var newName    = BlendShapeReplacer.FeelingsShapeKeyPrefix + oldName;
                var index      = mesh.GetBlendShapeIndex(oldName);
                var frameCount = mesh.GetBlendShapeFrameCount(index);
                for (var i = 0; i < frameCount; i++)
                {
                    var deltaVertices = new Vector3[mesh.vertexCount];
                    var deltaNormals  = new Vector3[mesh.vertexCount];
                    var deltaTangents = new Vector3[mesh.vertexCount];

                    mesh.GetBlendShapeFrameVertices(index, i, deltaVertices, deltaNormals, deltaTangents);

                    mesh.AddBlendShapeFrame(
                        newName,
                        mesh.GetBlendShapeFrameWeight(shapeIndex: index, frameIndex: i),
                        deltaVertices,
                        deltaNormals,
                        deltaTangents
                        );
                }

                EditorUtility.SetDirty(mesh);

                clips = VRMUtility.ReplaceShapeKeyName(clips, oldName, newName);
            }

            return(clips);
        }
Пример #2
0
        /// <summary>
        /// 変換ダイアログを開きます。
        /// </summary>
        /// <param name="avatar"></param>
        internal static void Open(GameObject avatar)
        {
            var     wizard         = DisplayWizard <Wizard>(Converter.Name + " " + Converter.Version, _("Duplicate and Convert"));
            Vector2 defaultMinSize = Wizard.MinSize;

            defaultMinSize.y = Wizard.MinHeightWhenOpen;
            wizard.minSize   = defaultMinSize;
            wizard.minSize   = Wizard.MinSize;

            wizard.avatar = avatar.GetComponent <Animator>();

            wizard.LoadSettings();

            if (string.IsNullOrEmpty(wizard.blendShapeForFingerpoint))
            {
                var surprise = VRMUtility.GetBlendShapeClips(wizard.avatar)
                               .FirstOrDefault(clip => clip.Preset == BlendShapePreset.Unknown &&
                                               clip.BlendShapeName.StartsWith("Surprise", ignoreCase: true, culture: null));
                if (surprise != null)
                {
                    wizard.blendShapeForFingerpoint = surprise.BlendShapeName;
                }
            }
        }
        /// <summary>
        /// アニメーションクリップに、指定されたマテリアルを追加します。
        /// </summary>
        /// <param name="avatar"></param>
        /// <param name="animationClip"></param>
        /// <param name="name"></param>
        /// <param name="binding"></param>
        /// <param name="secondsList"></param>
        private static void SetBlendShapeCurve(
            AnimationClip animationClip,
            GameObject avatar,
            string vrmBlendShapeName,
            IEnumerable <MaterialValueBinding> bindings,
            IEnumerable <float> secondsList
            )
        {
            var materials = avatar.transform.Find(VRChatUtility.AutoBlinkMeshPath)
                            .GetComponent <SkinnedMeshRenderer>().sharedMaterials;

            var materialIndex = materials.ToList().FindIndex(m => m.name == bindings.First().MaterialName);

            var material = Duplicator.DuplicateAssetToFolder <Material>(
                source: materials[materialIndex],
                prefabInstance: avatar,
                fileName: $"{materials[materialIndex].name}-{vrmBlendShapeName}.mat"
                );

            VRMUtility.Bake(material, bindings);

            AnimationUtility.SetObjectReferenceCurve(
                animationClip,
                new EditorCurveBinding()
            {
                path         = VRChatUtility.AutoBlinkMeshPath,
                type         = typeof(SkinnedMeshRenderer),
                propertyName = $"m_Materials.Array.data[{materialIndex}]",
            },
                secondsList
                .Select(seconds => new ObjectReferenceKeyframe()
            {
                time = seconds, value = material
            }).ToArray()
                );
        }
Пример #4
0
        private void OnWizardCreate()
        {
            if (string.IsNullOrEmpty(this.destinationPath))
            {
                string sourcePath = this.GetAssetsPath(vrm: this.avatar.gameObject);
                this.destinationPath = UnityPath.FromUnityPath(sourcePath).Parent
                                       .Child(Path.GetFileNameWithoutExtension(sourcePath) + " (VRChat).prefab").Value;
            }
            else
            {
                UnityPath destinationFolderUnityPath = UnityPath.FromUnityPath(this.destinationPath).Parent;
                while (!destinationFolderUnityPath.IsDirectoryExists)
                {
                    destinationFolderUnityPath = destinationFolderUnityPath.Parent;
                }
                this.destinationPath = destinationFolderUnityPath.Child(Path.GetFileName(this.destinationPath)).Value;
            }

            string destinationPath = EditorUtility.SaveFilePanelInProject(
                "",
                Path.GetFileName(path: this.destinationPath),
                "prefab",
                "",
                Path.GetDirectoryName(path: this.destinationPath)
                );

            if (string.IsNullOrEmpty(destinationPath))
            {
                Wizard.Open(avatar: this.avatar.gameObject);
                return;
            }
            this.destinationPath = destinationPath;

            // プレハブ、およびシーン上のプレハブインスタンスのBlueprint IDを取得
            string prefabBlueprintId = "";
            var    blueprintIds      = new Dictionary <int, string>();
            var    previousPrefab    = AssetDatabase.LoadMainAssetAtPath(this.destinationPath) as GameObject;

            if (previousPrefab)
            {
                var pipelineManager = previousPrefab.GetComponent <PipelineManager>();
                prefabBlueprintId = pipelineManager ? pipelineManager.blueprintId : "";

                GameObject[] previousRootGameObjects = SceneManager.GetActiveScene().GetRootGameObjects();
                blueprintIds = previousRootGameObjects
                               .Where(root => PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(root) == this.destinationPath)
                               .Select(root =>
                {
                    var manager     = root.GetComponent <PipelineManager>();
                    var blueprintId = manager ? manager.blueprintId : "";
                    return(new
                    {
                        index = Array.IndexOf(previousRootGameObjects, root),
                        blueprintId = blueprintId != prefabBlueprintId ? blueprintId : "",
                    });
                }).ToDictionary(
                    keySelector: indexAndBlueprintId => indexAndBlueprintId.index,
                    elementSelector: indexAndBlueprintId => indexAndBlueprintId.blueprintId
                    );
            }

            GameObject prefabInstance = Duplicator.Duplicate(
                sourceAvatar: this.avatar.gameObject,
                destinationPath: this.destinationPath,
                notCombineRendererObjectNames: this.notCombineRendererObjectNames,
                combineMeshesAndSubMeshes: this.combineMeshes
                );

            var messages = new List <Converter.Message>();

            if (this.forQuest)
            {
                messages.AddRange(Wizard.GenerateQuestLimitationsErrorMessages(prefab: prefabInstance));
            }

            this.SaveSettings();

            foreach (VRMSpringBone springBone in this.GetSpringBonesWithComments(prefab: prefabInstance, comments: this.excludedSpringBoneComments)
                     .SelectMany(springBone => springBone))
            {
                UnityEngine.Object.DestroyImmediate(springBone);
            }

            var clips = VRMUtility.GetAllVRMBlendShapeClips(avatar: this.avatar.gameObject);

            messages.AddRange(Converter.Convert(
                                  prefabInstance: prefabInstance,
                                  clips: clips,
                                  swayingObjectsConverterSetting: this.swayingObjects,
                                  takingOverSwayingParameters: this.takeOverSwayingParameters,
                                  swayingParametersConverter: this.swayingParametersConverter,
                                  enableAutoEyeMovement: this.enableEyeMovement,
                                  addedShouldersPositionY: this.shoulderHeights,
                                  moveEyeBoneToFrontForEyeMovement: this.moveEyeBoneToFrontForEyeMovement,
                                  forQuest: this.forQuest,
                                  addedArmaturePositionY: this.armatureHeight,
                                  useAnimatorForBlinks: this.useAnimatorForBlinks,
                                  useShapeKeyNormalsAndTangents: this.useShapeKeyNormalsAndTangents,
                                  vrmBlendShapeForFINGERPOINT: !string.IsNullOrEmpty(this.blendShapeForFingerpoint)
                    ? VRMUtility.GetUserDefinedBlendShapeClip(clips, this.blendShapeForFingerpoint) as VRMBlendShapeClip
                    : null
                                  ));

            // 変換前のプレハブのPipeline ManagerのBlueprint IDを反映
            if (!string.IsNullOrEmpty(prefabBlueprintId))
            {
                prefabInstance.GetComponent <PipelineManager>().blueprintId = prefabBlueprintId;
            }

            if (this.postConverting != null)
            {
                this.postConverting(prefabInstance, prefabInstance.GetComponent <VRMMeta>());
            }

            PrefabUtility.ApplyPrefabInstance(prefabInstance, InteractionMode.AutomatedAction);

            // 変換前のプレハブインスタンスのPipeline ManagerのBlueprint IDを反映
            GameObject[] rootGameObjects = SceneManager.GetActiveScene().GetRootGameObjects();
            foreach (var(avatarIndex, blueprintId) in blueprintIds)
            {
                if (string.IsNullOrEmpty(blueprintId))
                {
                    continue;
                }
                rootGameObjects[avatarIndex].GetComponent <PipelineManager>().blueprintId = blueprintId;
            }

            if (blueprintIds.Count > 0)
            {
                // シーンのルートに、すでに他のプレハブインスタンスが存在していれば、変換用のインスタンスは削除
                UnityEngine.Object.DestroyImmediate(prefabInstance);
            }

            ResultDialog.Open(messages: messages);
        }
Пример #5
0
        protected override bool DrawWizardGUI()
        {
            base.DrawWizardGUI();
            isValid = true;

            if (this.callbackFunctions)
            {
                Type callBackFunctions = this.callbackFunctions.GetClass();

                this.swayingParametersConverter = Delegate.CreateDelegate(
                    type: typeof(Converter.SwayingParametersConverter),
                    target: callBackFunctions,
                    method: "SwayingParametersConverter",
                    ignoreCase: false,
                    throwOnBindFailure: false
                    ) as Converter.SwayingParametersConverter;

                this.postConverting = Delegate.CreateDelegate(
                    type: typeof(Wizard.PostConverting),
                    target: callBackFunctions,
                    method: "PostConverting",
                    ignoreCase: false,
                    throwOnBindFailure: false
                    ) as Wizard.PostConverting;
            }

            var indentStyle = new GUIStyle()
            {
                padding = new RectOffset()
                {
                    left = Wizard.Indent
                }
            };

            EditorGUILayout.LabelField(
                (this.swayingParametersConverter != null ? "☑" : "☐")
                + " public static DynamicBoneParameters SwayingParametersConverter(SpringBoneParameters, BoneInfo)",
                indentStyle
                );

            EditorGUILayout.LabelField(
                (this.postConverting != null ? "☑" : "☐")
                + " public static void PostConverting(GameObject, VRMMeta)",
                indentStyle
                );

#if VRC_SDK_VRCSDK2 || VRC_SDK_VRCSDK3
            foreach (var type in Converter.RequiredComponents)
            {
                if (!this.avatar.GetComponent(type))
                {
                    EditorGUILayout.HelpBox(string.Format(_("Not set “{0}” component."), type), MessageType.Error);
                    isValid = false;
                }
            }

            IEnumerable <string> excludedSpringBoneComments = this.excludedSpringBoneComments.Except(new[] { "" });
            if (excludedSpringBoneComments.Count() > 0)
            {
                IEnumerable <string> comments = excludedSpringBoneComments.Except(
                    this.GetSpringBonesWithComments(prefab: this.avatar.gameObject, comments: excludedSpringBoneComments)
                    .Select(commentAndSpringBones => commentAndSpringBones.Key)
                    );
                if (comments.Count() > 0)
                {
                    EditorGUILayout.HelpBox(string.Join(separator: "\n• ", value: new[] { _("VRMSpringBones with the below Comments do not exist.") }
                                                        .Concat(comments).ToArray()), MessageType.Warning);
                }
            }

            if (this.combineMeshes)
            {
                IEnumerable <string> notCombineRendererObjectNames
                    = this.notCombineRendererObjectNames.Except(new[] { "" });
                if (notCombineRendererObjectNames.Count() > 0)
                {
                    IEnumerable <string> names = notCombineRendererObjectNames.Except(
                        this.avatar.GetComponentsInChildren <SkinnedMeshRenderer>()
                        .Concat <Component>(this.avatar.GetComponentsInChildren <MeshRenderer>())
                        .Select(renderer => renderer.name)
                        );
                    if (names.Count() > 0)
                    {
                        EditorGUILayout.HelpBox(string.Join(separator: "\n• ", value: new[] { _("Renderers on the below name GameObject do not exist.") }
                                                            .Concat(names).ToArray()), MessageType.Warning);
                    }
                }
            }
            else
            {
                EditorGUILayout.HelpBox(_("If you do not “Combine Meshes”,"
                                          + " and any of VRMBlendShapes references meshes other than the mesh having most shape keys"
                                          + " or the mesh is not direct child of the avatar root,"
                                          + " the avatar will not be converted correctly."), MessageType.Warning);
            }

            if (!string.IsNullOrEmpty(this.blendShapeForFingerpoint) &&
                !VRMUtility.GetUserDefinedBlendShapeClip(this.avatar, this.blendShapeForFingerpoint))
            {
                EditorGUILayout.HelpBox(string.Format(
                                            _("There is no user-defined VRMBlensShape with the name “{0}”."),
                                            this.blendShapeForFingerpoint
                                            ), MessageType.Warning);
            }

            string version = VRChatUtility.GetSupportedUnityVersion();
            if (version != "" && Application.unityVersion != version)
            {
                EditorGUILayout.HelpBox(string.Format(
                                            _("Unity {0} is running. If you are using a different version than {1}, VRChat SDK might not work correctly. Recommended using Unity downloaded from {2} ."),
                                            Application.unityVersion,
                                            version,
                                            VRChatUtility.DownloadURL
                                            ), MessageType.Warning);
            }

            if (!isValid || !this.forQuest)
            {
                return(true);
            }

            AvatarPerformanceStats statistics = new AvatarPerformanceStats();
            AvatarPerformance.CalculatePerformanceStats(
                avatarName: avatar.GetComponent <VRMMeta>().Meta.Title,
                avatarObject: this.avatar.gameObject,
                perfStats: statistics
                );
            int currentPolycount = (int)statistics.polyCount;

            int maxPolycount = VRChatUtility.AvatarPerformanceStatsLevelSets["Quest"].medium.polyCount;

            if (currentPolycount > maxPolycount)
            {
                EditorGUILayout.HelpBox(string.Format(
                                            _("The number of polygons is {0}."),
                                            currentPolycount
                                            ) + string.Format(
                                            _("If this value exceeds {0}, the avatar will not shown under the default user setting."),
                                            maxPolycount
                                            ), MessageType.Error);
            }
#else
            EditorGUILayout.HelpBox(_("VRChat SDK2 or SDK3 has not been imported."), MessageType.Error);
            isValid = false;
#endif

            return(true);
        }