Merge pull request #693 from PoChang007/update_meshutility_editor

updated MeshUtility editor
This commit is contained in:
ousttrue 2021-01-25 19:10:49 +09:00 committed by GitHub
commit 4e4cc1150c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 194 additions and 50 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -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:

View File

@ -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):
<img src="../images/interface_1.jpg" width="200">
@ -12,10 +12,14 @@ From menu go to `UniGLTF` -> `Mesh Utility` -> `MeshProcessing Wizard`, select `
<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

View File

@ -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;
}
}

View File

@ -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<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>())
{
@ -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);
}
}
}

View File

@ -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>
@ -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<SkinnedMeshRenderer>();

View File

@ -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)