diff --git a/Assets/UniGLTF/MeshUtility/Documentation/images/interface_2.jpg b/Assets/UniGLTF/MeshUtility/Documentation/images/interface_2.jpg index ce16445a5..aa2f3d15c 100644 Binary files a/Assets/UniGLTF/MeshUtility/Documentation/images/interface_2.jpg and b/Assets/UniGLTF/MeshUtility/Documentation/images/interface_2.jpg differ diff --git a/Assets/UniGLTF/MeshUtility/Documentation/images/interface_4.jpg b/Assets/UniGLTF/MeshUtility/Documentation/images/interface_4.jpg new file mode 100644 index 000000000..712ee0531 Binary files /dev/null and b/Assets/UniGLTF/MeshUtility/Documentation/images/interface_4.jpg differ diff --git a/Assets/UniGLTF/MeshUtility/Documentation/images/interface_4.jpg.meta b/Assets/UniGLTF/MeshUtility/Documentation/images/interface_4.jpg.meta new file mode 100644 index 000000000..f51cf076a --- /dev/null +++ b/Assets/UniGLTF/MeshUtility/Documentation/images/interface_4.jpg.meta @@ -0,0 +1,88 @@ +fileFormatVersion: 2 +guid: 669e2a7e945cb8f468940a05224a8efb +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 9 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniGLTF/MeshUtility/Documentation/notes/MeshSeparator.md b/Assets/UniGLTF/MeshUtility/Documentation/notes/MeshSeparator.md index d1ad8907b..5b68ab9f5 100644 --- a/Assets/UniGLTF/MeshUtility/Documentation/notes/MeshSeparator.md +++ b/Assets/UniGLTF/MeshUtility/Documentation/notes/MeshSeparator.md @@ -4,7 +4,7 @@ MeshSeparator provides a functionality of separating meshes contained BlendShape ## How to use -Select a GameObject contained skinned mesh and BlendShape: +Select a GameObject contained skinned mesh and BlendShape (or drag and drop a GameObject to TargetObject field shown below): @@ -12,10 +12,14 @@ From menu go to `UniGLTF` -> `Mesh Utility` -> `MeshProcessing Wizard`, select ` -The separate meshes are saved in the Assets folder. GameObjects with separate meshes are also available in the Hierarchy Window: +The separate meshes are saved in the Assets folder. +In the Hierarchy Window, click the generated GameObject and export. + + + In this example, the model's mesh are split into two parts: face and body: Face: with BlendShape | Body: without BlendShape diff --git a/Assets/UniGLTF/MeshUtility/Editor/MeshProcessDialog.cs b/Assets/UniGLTF/MeshUtility/Editor/MeshProcessDialog.cs index 7ce167d11..6cecdc4e4 100644 --- a/Assets/UniGLTF/MeshUtility/Editor/MeshProcessDialog.cs +++ b/Assets/UniGLTF/MeshUtility/Editor/MeshProcessDialog.cs @@ -1,10 +1,8 @@ +using System; +using System.Reflection; using UnityEngine; using UnityEditor; -using System.Reflection; -using MeshUtility; -using System.IO; -using System; -using System.Linq; +using MeshUtility.M17N; namespace MeshUtility { @@ -34,25 +32,67 @@ namespace MeshUtility GUIStyle _tabButtonStyle => "LargeButton"; GUI.ToolbarButtonSize _tabButtonSize => GUI.ToolbarButtonSize.Fixed; + private enum MeshProcessingMessages + { + [LangMsg(Languages.ja, "ターゲットオブジェクト")] + [LangMsg(Languages.en, "TargetObject")] + TARGET_OBJECT, + + [LangMsg(Languages.ja, "BlendShapeを含むメッシュは分割されます")] + [LangMsg(Languages.en, "Meshes containing BlendShape will be split")] + MESH_SEPARATOR, + + [LangMsg(Languages.ja, "メッシュを統合する。BlendShapeを含むメッシュは独立して統合されます")] + [LangMsg(Languages.en, "Generate a single mesh. Meshes w/ BlendShape will be grouped into another one")] + MESH_INTEGRATOR, + + [LangMsg(Languages.ja, "静的メッシュを一つに統合する")] + [LangMsg(Languages.en, "Integrate static meshes into one")] + STATIC_MESH_INTEGRATOR, + + [LangMsg(Languages.ja, "GameObjectを選んでください")] + [LangMsg(Languages.en, "Select a GameObject first")] + NO_GAMEOBJECT_SELECTED, + + [LangMsg(Languages.ja, "GameObjectにスキンメッシュが含まれていません")] + [LangMsg(Languages.en, "No skinned mesh is contained")] + NO_SKINNED_MESH, + + [LangMsg(Languages.ja, "GameObjectに静的メッシュが含まれていません")] + [LangMsg(Languages.en, "No static mesh is contained")] + NO_STATIC_MESH, + + [LangMsg(Languages.ja, "GameObjectにスキンメッシュ・静的メッシュが含まれていません")] + [LangMsg(Languages.en, "Skinned/Static mesh is not contained")] + NO_MESH, + + [LangMsg(Languages.ja, "ターゲットオブジェクトはVRMモデルです。`VRM0-> MeshIntegrator`を使ってください")] + [LangMsg(Languages.en, "Target object is VRM model, use `VRM0 -> MeshIntegrator` instead")] + VRM_DETECTED, + } + private void OnGUI() { EditorGUIUtility.labelWidth = 150; + // lang + Getter.OnGuiSelectLang(); + _tab = TabBar.OnGUI(_tab, _tabButtonStyle, _tabButtonSize); switch (_tab) { case Tabs.MeshSeparator: - EditorGUILayout.TextField("Meshes containing BlendShape data will be split"); + EditorGUILayout.TextField(MeshProcessingMessages.MESH_SEPARATOR.Msg()); break; case Tabs.MeshIntegrator: - EditorGUILayout.TextField("Generate a single mesh. Meshes w/ BlendShape will be grouped into another one"); + EditorGUILayout.TextField(MeshProcessingMessages.MESH_INTEGRATOR.Msg()); break; case Tabs.StaticMeshIntegrator: - EditorGUILayout.TextField("Integrate static meshes into one"); + EditorGUILayout.TextField(MeshProcessingMessages.STATIC_MESH_INTEGRATOR.Msg()); break; } - EditorGUILayout.LabelField("ExportTarget"); + EditorGUILayout.LabelField(MeshProcessingMessages.TARGET_OBJECT.Msg()); _exportTarget = (GameObject)EditorGUILayout.ObjectField(_exportTarget, typeof(GameObject), true); if (_exportTarget == null && MeshUtility.IsGameObjectSelected()) { @@ -111,7 +151,7 @@ namespace MeshUtility private bool GameObjectNull() { - EditorUtility.DisplayDialog("Failed", "No GameObject Selected", "ok"); + EditorUtility.DisplayDialog("Failed", MeshProcessingMessages.NO_GAMEOBJECT_SELECTED.Msg(), "ok"); return false; } @@ -127,7 +167,7 @@ namespace MeshUtility } else { - EditorUtility.DisplayDialog("Failed", "No skinned mesh contained", "ok"); + EditorUtility.DisplayDialog("Failed", MeshProcessingMessages.NO_SKINNED_MESH.Msg(), "ok"); return false; } } @@ -146,7 +186,7 @@ namespace MeshUtility var sourceString = component.ToString(); if (sourceString.Contains(keyWord)) { - EditorUtility.DisplayDialog("Failed", "Target object is VRM file, use `VRM0 -> MeshIntegrator` instead", "ok"); + EditorUtility.DisplayDialog("Failed", MeshProcessingMessages.VRM_DETECTED.Msg(), "ok"); return false; } } @@ -158,7 +198,7 @@ namespace MeshUtility } else { - EditorUtility.DisplayDialog("Failed", "Neither skinned mesh nor static mesh contained", "ok"); + EditorUtility.DisplayDialog("Failed", MeshProcessingMessages.NO_MESH.Msg(), "ok"); return false; } } @@ -174,7 +214,7 @@ namespace MeshUtility } else { - EditorUtility.DisplayDialog("Failed", "No static mesh contained", "ok"); + EditorUtility.DisplayDialog("Failed", MeshProcessingMessages.NO_STATIC_MESH.Msg(), "ok"); return false; } } diff --git a/Assets/UniGLTF/MeshUtility/Editor/MeshUtility.cs b/Assets/UniGLTF/MeshUtility/Editor/MeshUtility.cs index 400d752a9..a7eb133de 100644 --- a/Assets/UniGLTF/MeshUtility/Editor/MeshUtility.cs +++ b/Assets/UniGLTF/MeshUtility/Editor/MeshUtility.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using UnityEngine; @@ -9,14 +9,9 @@ namespace MeshUtility public class MeshUtility { public const string MENU_PARENT = "UniGLTF/Mesh Utility/"; - private const string ASSET_SUFFIX = ".mesh.asset"; - private const string MENU_NAME = MENU_PARENT + "MeshSeparator"; private static readonly Vector3 ZERO_MOVEMENT = Vector3.zero; - private const string INTEGRATED_MESH_NAME = "MeshesIntegrated"; - private const string INTEGRATED_MESH_BLENDSHAPE_NAME = "MeshesBlendShapeIntegrated"; - public static Object GetPrefab(GameObject instance) { #if UNITY_2018_2_OR_NEWER @@ -32,15 +27,6 @@ namespace MeshUtility WithoutBlendShape, } - [MenuItem(MENU_NAME, validate = true)] - private static bool ShowLogValidation() - { - if (Selection.activeTransform == null) - return false; - else - return true; - } - [MenuItem(MENU_PARENT + "MeshUtility Docs", priority = 32)] public static void LinkToMeshSeparatorDocs() { @@ -344,32 +330,35 @@ namespace MeshUtility // destroy integrated meshes in the source foreach (var skinnedMesh in go.GetComponentsInChildren()) { - if (skinnedMesh.sharedMesh.name == INTEGRATED_MESH_NAME) + if (skinnedMesh.sharedMesh.name == MeshIntegratorUtility.INTEGRATED_MESH_NAME || + skinnedMesh.sharedMesh.name == MeshIntegratorUtility.INTEGRATED_MESH_BLENDSHAPE_NAME) { GameObject.DestroyImmediate(skinnedMesh.gameObject); } } - // destroy original meshes in the copied GameObject foreach (var skinnedMesh in skinnedMeshes) { - if (skinnedMesh.sharedMesh.name != INTEGRATED_MESH_NAME) - { - GameObject.DestroyImmediate(skinnedMesh); - } - // avoid repeated name - else if (skinnedMesh.sharedMesh.blendShapeCount > 0) - { - skinnedMesh.sharedMesh.name = INTEGRATED_MESH_BLENDSHAPE_NAME; - } - // check if the integrated mesh is empty - else if (skinnedMesh.sharedMesh.subMeshCount == 0) - { - GameObject.DestroyImmediate(skinnedMesh.gameObject); - } + // destroy original meshes in the copied GameObject + if (!(skinnedMesh.sharedMesh.name == MeshIntegratorUtility.INTEGRATED_MESH_NAME || + skinnedMesh.sharedMesh.name == MeshIntegratorUtility.INTEGRATED_MESH_BLENDSHAPE_NAME)) + { + GameObject.DestroyImmediate(skinnedMesh); + } + // check if the integrated mesh is empty + else if (skinnedMesh.sharedMesh.subMeshCount == 0) + { + GameObject.DestroyImmediate(skinnedMesh.gameObject); + } + // save mesh data + else if (skinnedMesh.sharedMesh.name == MeshIntegratorUtility.INTEGRATED_MESH_NAME || + skinnedMesh.sharedMesh.name == MeshIntegratorUtility.INTEGRATED_MESH_BLENDSHAPE_NAME) + { + // SaveMeshData(skinnedMesh.sharedMesh); + } } foreach (var normalMesh in normalMeshes) { - if (normalMesh.sharedMesh.name != INTEGRATED_MESH_NAME) + if (normalMesh.sharedMesh.name != MeshIntegratorUtility.INTEGRATED_MESH_NAME) { if (normalMesh.gameObject.GetComponent()) { @@ -379,5 +368,22 @@ namespace MeshUtility } } } + + private static void SaveMeshData(Mesh mesh) + { + var assetPath = string.Format("{0}{1}", Path.GetFileNameWithoutExtension(mesh.name), ASSET_SUFFIX); + Debug.Log(assetPath); + if (!string.IsNullOrEmpty((AssetDatabase.GetAssetPath(mesh)))) + { + var directory = Path.GetDirectoryName(AssetDatabase.GetAssetPath(mesh)).Replace("\\", "/"); + assetPath = string.Format("{0}/{1}{2}", directory, Path.GetFileNameWithoutExtension(mesh.name), ASSET_SUFFIX); + } + else + { + assetPath = string.Format("Assets/{0}{1}", Path.GetFileNameWithoutExtension(mesh.name), ASSET_SUFFIX); + } + Debug.LogFormat("CreateAsset: {0}", assetPath); + AssetDatabase.CreateAsset(mesh, assetPath); + } } } \ No newline at end of file diff --git a/Assets/UniGLTF/MeshUtility/Runtime/MeshIntegratorUtility.cs b/Assets/UniGLTF/MeshUtility/Runtime/MeshIntegratorUtility.cs index 2f5b343ef..8285af6e6 100644 --- a/Assets/UniGLTF/MeshUtility/Runtime/MeshIntegratorUtility.cs +++ b/Assets/UniGLTF/MeshUtility/Runtime/MeshIntegratorUtility.cs @@ -6,6 +6,8 @@ namespace MeshUtility { public static class MeshIntegratorUtility { + public const string INTEGRATED_MESH_NAME = "MeshesIntegrated"; + public const string INTEGRATED_MESH_BLENDSHAPE_NAME = "MeshesBlendShapeIntegrated"; /// /// go を root としたヒエラルキーから Renderer を集めて、統合された Mesh 作成する /// @@ -54,7 +56,6 @@ namespace MeshUtility } var mesh = new Mesh(); - mesh.name = "MeshesIntegrated"; if (integrator.Positions.Count > ushort.MaxValue) { @@ -77,6 +78,11 @@ namespace MeshUtility if (onlyBlendShapeRenderers) { integrator.AddBlendShapesToMesh(mesh); + mesh.name = INTEGRATED_MESH_BLENDSHAPE_NAME; + } + else + { + mesh.name = INTEGRATED_MESH_NAME; } var integrated = meshNode.AddComponent(); diff --git a/Assets/UniGLTF/MeshUtility/Runtime/StaticMeshIntegrator.cs b/Assets/UniGLTF/MeshUtility/Runtime/StaticMeshIntegrator.cs index 219b0d04f..e865b5a17 100644 --- a/Assets/UniGLTF/MeshUtility/Runtime/StaticMeshIntegrator.cs +++ b/Assets/UniGLTF/MeshUtility/Runtime/StaticMeshIntegrator.cs @@ -66,7 +66,7 @@ namespace MeshUtility public Mesh ToMesh() { var mesh = new Mesh(); - mesh.name = "MeshesIntegrated"; + mesh.name = MeshIntegratorUtility.INTEGRATED_MESH_NAME; mesh.vertices = m_positions.ToArray(); if (m_normals.Count > 0)