private static void ReplaceShaders(GameObject instance, string temporaryPrefabPath) { var alreadyDuplicatedMaterials = new Dictionary <Material, Material>(); foreach (var renderer in instance.GetComponentsInChildren <SkinnedMeshRenderer>()) { renderer.sharedMaterials = renderer.sharedMaterials.Select(material => { if (VRChatToVRMConverter.VRMSupportedShaderNames.Contains(material.shader.name)) { return(material); } if (alreadyDuplicatedMaterials.ContainsKey(material)) { return(alreadyDuplicatedMaterials[material]); } var newMaterial = Object.Instantiate(material); newMaterial.name = material.name; var shaderName = material.shader.name.ToLower(); if (shaderName.Contains("unlit")) { newMaterial.shader = Shader.Find("UniGLTF/UniUnlit"); } else if (shaderName.Contains("toon")) { newMaterial.shader = Shader.Find("VRM/MToon"); } newMaterial.renderQueue = material.renderQueue; return(alreadyDuplicatedMaterials[material] = Duplicator.CreateObjectToFolder(newMaterial, temporaryPrefabPath)); }).ToArray(); } }
/// <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"); } }