/// <summary>
        /// nesessaryShapeKeysで指定されたシェイプキーから法線・接線を削除し、それ以外のシェイプキーは削除します。
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="nesessaryShapeKeys"></param>
        /// <returns></returns>
        internal static void CleanUpShapeKeys(Mesh mesh, IEnumerable <string> nesessaryShapeKeys)
        {
            var shapeKeys = SkinnedMeshUtility.GetAllShapeKeys(mesh, useShapeKeyNormalsAndTangents: false);

            mesh.ClearBlendShapes();
            foreach (var name in nesessaryShapeKeys)
            {
                var shapeKey = shapeKeys.FirstOrDefault(key => key.Name == name);
                if (shapeKey == null)
                {
                    continue;
                }

                mesh.AddBlendShapeFrame(
                    shapeKey.Name,
                    BlendShapeReplacer.MaxBlendShapeFrameWeight,
                    shapeKey.Positions.ToArray(),
                    shapeKey.Normals.ToArray(),
                    shapeKey.Tangents.ToArray()
                    );
            }
        }
Пример #2
0
        protected override bool DrawWizardGUI()
        {
            this.isValid = true;

            if (VRChatUtility.SDKVersion == null)
            {
                EditorGUILayout.HelpBox(_("VRChat SDK2 or SDK3 has not been imported."), MessageType.Error);
                this.isValid = false;
                return(true);
            }

            if (!this.prefabOrInstance.GetComponent <Animator>().isHuman)
            {
                EditorGUILayout.HelpBox(_("This is not humanoid."), MessageType.Error);
                this.isValid = false;
                return(true);
            }

            if (Application.unityVersion != VRChatUtility.SDKSupportedUnityVersion)
            {
                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,
                                            VRChatUtility.SDKSupportedUnityVersion,
                                            VRChatUtility.DownloadURL
                                            ), MessageType.Warning);
            }

            if (this.expressions == null)
            {
                this.shapeKeyNames = this.prefabOrInstance.GetComponentsInChildren <SkinnedMeshRenderer>()
                                     .Select(renderer => renderer.sharedMesh)
                                     .Where(mesh => mesh != null)
                                     .SelectMany(mesh => SkinnedMeshUtility.GetAllShapeKeys(mesh, useShapeKeyNormalsAndTangents: false))
                                     .Select(shapeKey => shapeKey.Name)
                                     .Distinct();

                (this.animations, this.expressions)
                    = VRChatUtility.DetectVRChatExpressions(this.prefabOrInstance, this.shapeKeyNames);
                this.animationNames = new[] { "-" }.Concat(this.animations.Select(animation => animation.name)).ToArray();

                this.maybeBlinkShapeKeyNames = this.expressions
                                               .Where(expression => VRChatToVRMWizard.PresetFieldPairs.ContainsKey(expression.Key) &&
                                                      VRChatToVRMWizard.PresetFieldPairs[expression.Key] == nameof(VRChatExpressionBinding.ShapeKeyNames))
                                               .SelectMany(expression => expression.Value.ShapeKeyNames)
                                               .Concat(VRChatUtility.DetectBlinkShapeKeyNames(this.shapeKeyNames))
                                               .Distinct()
                                               .Take(VRChatToVRMWizard.UnityEditorMaxMultiSelectCount)
                                               .ToArray();

                this.expressionPresetFlagPairs = VRChatToVRMWizard.PresetFieldPairs
                                                 .ToDictionary(
                    presetFieldPair => presetFieldPair.Key,
                    presetFieldPair => this.expressions.ContainsKey(presetFieldPair.Key)
                            ? (VRChatToVRMWizard.PresetFieldPairs[presetFieldPair.Key] == nameof(VRChatExpressionBinding.AnimationClip)
                                ? 1 + this.animations.ToList().IndexOf(this.expressions[presetFieldPair.Key].AnimationClip)
                                : VRChatToVRMWizard.ToFlags(this.maybeBlinkShapeKeyNames, this.expressions[presetFieldPair.Key].ShapeKeyNames))
                            : 0
                    );

                this.metaEditor = Editor.CreateEditor(this.meta);
            }

            EditorGUILayout.LabelField("Expressions", EditorStyles.boldLabel);
            foreach (var(preset, field) in VRChatToVRMWizard.PresetFieldPairs)
            {
                this.expressionPresetFlagPairs[preset]
                    = VRChatToVRMWizard.PresetFieldPairs[preset] == nameof(VRChatExpressionBinding.AnimationClip)
                        ? EditorGUILayout.Popup(
                          preset.ToString(),
                          this.expressionPresetFlagPairs[preset],
                          this.animationNames
                          )
                        : EditorGUILayout.MaskField(
                          preset.ToString(),
                          this.expressionPresetFlagPairs[preset],
                          this.maybeBlinkShapeKeyNames
                          );
            }

            this.metaEditor.OnInspectorGUI();

            return(true);
        }
        /// <summary>
        /// VRChatアバターインスタンスからVRMインスタンスへ変換します。
        /// </summary>
        /// <param name="instance">ヒエラルキー上のGameObject。</param>
        /// <param name="presetVRChatBindingPairs">各表情への割り当て。</param>
        internal static void Convert(
            string outputPath,
            GameObject instance,
            VRMMetaObject meta,
            IDictionary <ExpressionPreset, VRChatExpressionBinding> presetVRChatBindingPairs
            )
        {
            GameObject clone = null, normalized = null;

            try
            {
                var rootObjectName = instance.name;
                clone = Object.Instantiate(instance);

                // 非表示のオブジェクト・コンポーネントを削除
                // TODO: アクティブ・非アクティブの切り替えをシェイプキーに変換する
                VRChatToVRMConverter.RemoveInactiveObjectsAndDisabledComponents(clone);


                // 表情とシェイプキー名の組み合わせを取得
                var presetShapeKeyNameWeightPairsPairs = presetVRChatBindingPairs.ToDictionary(
                    presetVRChatBindingPair => presetVRChatBindingPair.Key,
                    presetVRChatBindingPair => VRChatExpressionsReplacer.ExtractShapeKeyNames(presetVRChatBindingPair.Value)
                    );

                // VRM設定1
                var temporaryFolder = UnityPath.FromUnityPath(VRChatToVRMConverter.TemporaryFolderPath);
                temporaryFolder.EnsureFolder();
                var temporaryPrefabPath = temporaryFolder.Child(VRChatToVRMConverter.TemporaryPrefabFileName).Value;
                VRMInitializer.Initialize(temporaryPrefabPath, clone);
                VRChatToVRMConverter.SetFirstPersonOffset(clone);
                VRChatToVRMConverter.SetLookAtBoneApplyer(clone);
                var sourceAndDestination = clone.GetComponent <Animator>();
                if (DynamicBones.IsImported())
                {
                    DynamicBonesToVRMSpringBonesConverter.Convert(
                        source: sourceAndDestination,
                        destination: sourceAndDestination
                        );
                    VRChatToVRMConverter.RemoveUnusedColliderGroups(clone);
                }

                // 正規化
                normalized = VRMBoneNormalizer.Execute(clone, forceTPose: true);

                // 全メッシュ結合
                var combinedRenderer = CombineMeshesAndSubMeshes.Combine(
                    normalized,
                    notCombineRendererObjectNames: new List <string>(),
                    destinationObjectName: "vrm-mesh",
                    savingAsAsset: false
                    );

                // 使用していないシェイプキーの削除
                SkinnedMeshUtility.CleanUpShapeKeys(combinedRenderer.sharedMesh, presetShapeKeyNameWeightPairsPairs
                                                    .SelectMany(presetShapeKeyNameWeightPairsPair => presetShapeKeyNameWeightPairsPair.Value.Keys)
                                                    .Distinct());

                // シェイプキーの分離
                Utilities.MeshUtility.SeparationProcessing(normalized);

                // マテリアルの設定・アセットとして保存
                VRChatToVRMConverter.ReplaceShaders(normalized, temporaryPrefabPath);

                // GameObject・メッシュなどをアセットとして保存 (アセットとして存在しないと正常にエクスポートできない)
                normalized.name = rootObjectName;
                var animator = normalized.GetComponent <Animator>();
                animator.avatar = Duplicator.CreateObjectToFolder(animator.avatar, temporaryPrefabPath);
                meta.name       = "Meta";
                normalized.GetComponent <VRMMeta>().Meta = Duplicator.CreateObjectToFolder(meta, temporaryPrefabPath);
                foreach (var renderer in normalized.GetComponentsInChildren <SkinnedMeshRenderer>())
                {
                    renderer.sharedMesh.name = renderer.name;
                    renderer.sharedMesh      = Duplicator.CreateObjectToFolder(renderer.sharedMesh, temporaryPrefabPath);
                }

                // VRM設定2
                VRChatToVRMConverter.SetFirstPersonRenderers(normalized);

                // 表情の設定
                VRChatExpressionsReplacer.SetExpressions(normalized, presetShapeKeyNameWeightPairsPairs);

                var prefab = PrefabUtility
                             .SaveAsPrefabAssetAndConnect(normalized, temporaryPrefabPath, InteractionMode.AutomatedAction);

                // エクスポート
                AssetDatabase.SaveAssets();
                File.WriteAllBytes(
                    outputPath,
                    VRMEditorExporter.Export(prefab, meta: null, ScriptableObject.CreateInstance <VRMExportSettings>())
                    );
            }
            catch (Exception exception)
            {
                ErrorDialog.Open(exception);
                throw;
            }
            finally
            {
                if (clone != null)
                {
                    Object.DestroyImmediate(clone);
                }
                if (normalized != null)
                {
                    Object.DestroyImmediate(normalized);
                }
                AssetDatabase.DeleteAsset("Assets/VRMConverterTemporary");
            }
        }
Пример #4
0
        /// <summary>
        /// リップシンクの設定を行います。
        /// </summary>
        /// <remarks>
        /// <see cref="BlendShapePreset.A"/>、<see cref="BlendShapePreset.I"/>、<see cref="BlendShapePreset.O"/>が
        /// 単一のフレームを持つシェイプキーが存在しない場合、設定を行いません。
        /// 生成するシェイプキー名と同じシェイプキーが存在する場合、それを利用します。
        /// </remarks>
        /// <param name="avatar"></param>
        /// <param name="clips"></param>
        /// <param name="useShapeKeyNormalsAndTangents"></param>
        private static void SetLipSync(
            GameObject avatar,
            IEnumerable <VRMBlendShapeClip> clips,
            bool useShapeKeyNormalsAndTangents
            )
        {
            Transform transform = avatar.transform.Find(VRChatUtility.AutoBlinkMeshPath);
            var       renderer  = transform.GetComponent <SkinnedMeshRenderer>();
            Mesh      mesh      = renderer.sharedMesh;

            foreach (var preset in new[] { BlendShapePreset.A, BlendShapePreset.I, BlendShapePreset.O })
            {
                if (!clips.FirstOrDefault(c => c.Preset == preset))
                {
                    return;
                }
            }

            IEnumerable <BlendShape> shapeKeys = SkinnedMeshUtility.GetAllShapeKeys(mesh, useShapeKeyNormalsAndTangents);

            foreach (var(newName, values) in BlendShapeReplacer.VisemeShapeKeyNamesAndValues)
            {
                if (mesh.GetBlendShapeIndex(newName) != -1)
                {
                    continue;
                }

                Vector3[] deltaVertices = null;
                foreach (Vector3[] vertices in values.SelectMany(presetAndWeight =>
                                                                 BlendShapeReplacer.SubtractNeutralShapeKeyValues(clips.First(clip => clip.Preset == presetAndWeight.Key).ShapeKeyValues, clips)
                                                                 .Select(shapeKeyNameAndWeight => shapeKeys
                                                                         .First(shapeKey => shapeKey.Name == shapeKeyNameAndWeight.Key)
                                                                         .Positions
                                                                         .Select(vertix => vertix * (shapeKeyNameAndWeight.Value / VRMUtility.MaxBlendShapeBindingWeight * presetAndWeight.Value))
                                                                         .ToArray()
                                                                         )
                                                                 ))
                {
                    if (deltaVertices == null)
                    {
                        deltaVertices = vertices;
                        continue;
                    }

                    for (var i = 0; i < deltaVertices.Length; i++)
                    {
                        deltaVertices[i] += vertices[i];
                    }
                }

                mesh.AddBlendShapeFrame(
                    newName,
                    BlendShapeReplacer.MaxBlendShapeFrameWeight,
                    deltaVertices,
                    null,
                    null
                    );
            }
            EditorUtility.SetDirty(mesh);

            var avatarDescriptor
#if VRC_SDK_VRCSDK3
                = avatar.GetComponent <VRC_AvatarDescriptor>();

            avatarDescriptor.lipSync = VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape;