Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

updated MeshUtility editor #693

Merged
merged 2 commits into from
Jan 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified Assets/UniGLTF/MeshUtility/Documentation/images/interface_2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,22 @@ 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):

<img src="../images/interface_1.jpg" width="200">

From menu go to `UniGLTF` -> `Mesh Utility` -> `MeshProcessing Wizard`, select `MeshSeparator` and click `process`:

<img src="../images/interface_2.jpg" width="300">

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.

<img src="../images/interface_3.jpg" width="200">

In the Hierarchy Window, click the generated GameObject and export.

<img src="../images/interface_4.jpg" width="250">

In this example, the model's mesh are split into two parts: face and body:

Face: with BlendShape | Body: without BlendShape
Expand Down
68 changes: 54 additions & 14 deletions Assets/UniGLTF/MeshUtility/Editor/MeshProcessDialog.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand Down Expand Up @@ -34,25 +32,67 @@ enum Tabs
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())
{
Expand Down Expand Up @@ -111,7 +151,7 @@ private bool InvokeWizardUpdate(string processFuntion)

private bool GameObjectNull()
{
EditorUtility.DisplayDialog("Failed", "No GameObject Selected", "ok");
EditorUtility.DisplayDialog("Failed", MeshProcessingMessages.NO_GAMEOBJECT_SELECTED.Msg(), "ok");
return false;
}

Expand All @@ -127,7 +167,7 @@ private bool MeshSeparator()
}
else
{
EditorUtility.DisplayDialog("Failed", "No skinned mesh contained", "ok");
EditorUtility.DisplayDialog("Failed", MeshProcessingMessages.NO_SKINNED_MESH.Msg(), "ok");
return false;
}
}
Expand All @@ -146,7 +186,7 @@ private bool MeshIntegrator()
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;
}
}
Expand All @@ -158,7 +198,7 @@ private bool MeshIntegrator()
}
else
{
EditorUtility.DisplayDialog("Failed", "Neither skinned mesh nor static mesh contained", "ok");
EditorUtility.DisplayDialog("Failed", MeshProcessingMessages.NO_MESH.Msg(), "ok");
return false;
}
}
Expand All @@ -174,7 +214,7 @@ private bool StaticMeshIntegrator()
}
else
{
EditorUtility.DisplayDialog("Failed", "No static mesh contained", "ok");
EditorUtility.DisplayDialog("Failed", MeshProcessingMessages.NO_STATIC_MESH.Msg(), "ok");
return false;
}
}
Expand Down
70 changes: 38 additions & 32 deletions Assets/UniGLTF/MeshUtility/Editor/MeshUtility.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
Expand All @@ -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
Expand All @@ -32,15 +27,6 @@ private enum BlendShapeLogic
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()
{
Expand Down Expand Up @@ -344,32 +330,35 @@ public static void MeshIntegrator(GameObject go)
// destroy integrated meshes in the source
foreach (var skinnedMesh in go.GetComponentsInChildren<SkinnedMeshRenderer>())
{
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<MeshRenderer>())
{
Expand All @@ -379,5 +368,22 @@ public static void MeshIntegrator(GameObject go)
}
}
}

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);
}
}
}
8 changes: 7 additions & 1 deletion Assets/UniGLTF/MeshUtility/Runtime/MeshIntegratorUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
/// <summary>
/// go を root としたヒエラルキーから Renderer を集めて、統合された Mesh 作成する
/// </summary>
Expand Down Expand Up @@ -54,7 +56,6 @@ public static MeshIntegrationResult Integrate(GameObject go, bool onlyBlendShape
}

var mesh = new Mesh();
mesh.name = "MeshesIntegrated";

if (integrator.Positions.Count > ushort.MaxValue)
{
Expand All @@ -77,6 +78,11 @@ public static MeshIntegrationResult Integrate(GameObject go, bool onlyBlendShape
if (onlyBlendShapeRenderers)
{
integrator.AddBlendShapesToMesh(mesh);
mesh.name = INTEGRATED_MESH_BLENDSHAPE_NAME;
}
else
{
mesh.name = INTEGRATED_MESH_NAME;
}

var integrated = meshNode.AddComponent<SkinnedMeshRenderer>();
Expand Down
2 changes: 1 addition & 1 deletion Assets/UniGLTF/MeshUtility/Runtime/StaticMeshIntegrator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public void Push(Matrix4x4 localToRoot, Mesh mesh, Material[] materials)
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)
Expand Down