mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-06 13:15:45 -05:00
moved BoneMeshEraser to Meshutility editor
This commit is contained in:
parent
0058e261f8
commit
45b5b618d9
|
|
@ -37,6 +37,7 @@ namespace MeshUtility
|
|||
|
||||
public class BoneMeshEraserWizard : ScriptableWizard
|
||||
{
|
||||
public const string BONE_MESH_ERASER_NAME = "BoneMeshEraser";
|
||||
const string ASSET_SUFFIX = ".asset";
|
||||
|
||||
[SerializeField]
|
||||
|
|
@ -132,7 +133,7 @@ namespace MeshUtility
|
|||
.Select(x => Array.IndexOf(bones, x.Bone))
|
||||
.ToArray();
|
||||
|
||||
var meshNode = new GameObject("BoneMeshEraser");
|
||||
var meshNode = new GameObject(BONE_MESH_ERASER_NAME);
|
||||
meshNode.transform.SetParent(go.transform, false);
|
||||
|
||||
var erased = meshNode.AddComponent<SkinnedMeshRenderer>();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
|
@ -6,6 +8,24 @@ using MeshUtility.M17N;
|
|||
|
||||
namespace MeshUtility
|
||||
{
|
||||
[CustomEditor(typeof(MeshProcessDialog), true)]
|
||||
public class BoneMeshEraserGUI : Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
var skinnedMesh = serializedObject.FindProperty("_cSkinnedMesh");
|
||||
EditorGUILayout.PropertyField(skinnedMesh, new GUIContent("Skinned Mesh"), true);
|
||||
var animator = serializedObject.FindProperty("_cAnimator");
|
||||
EditorGUILayout.PropertyField(animator, new GUIContent("Animator"), false);
|
||||
var eraseRoot = serializedObject.FindProperty("_cEraseRoot");
|
||||
EditorGUILayout.PropertyField(eraseRoot, new GUIContent("Erase Root"), false);
|
||||
var list = serializedObject.FindProperty("_eraseBones");
|
||||
EditorGUILayout.PropertyField(list, new GUIContent("Erase Bones"), true);
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
public class MeshProcessDialog : EditorWindow
|
||||
{
|
||||
const string MESH_UTILITY_DICT = "UniGLTF/Mesh Utility/";
|
||||
|
|
@ -13,8 +33,9 @@ namespace MeshUtility
|
|||
[MenuItem(MESH_UTILITY_DICT + "MeshProcessing Wizard", priority = 30)]
|
||||
static void MeshProcessFromMenu()
|
||||
{
|
||||
var window = (MeshProcessDialog)EditorWindow.GetWindowWithRect(typeof(MeshProcessDialog), new Rect(0, 0, 500, 250));
|
||||
window.titleContent = new GUIContent ("Mesh Processing Window");
|
||||
var window = (MeshProcessDialog)EditorWindow.GetWindowWithRect(typeof(MeshProcessDialog), new Rect(0, 0, 650, 500));
|
||||
window.titleContent = new GUIContent("Mesh Processing Window");
|
||||
window.Show();
|
||||
}
|
||||
|
||||
enum Tabs
|
||||
|
|
@ -22,10 +43,26 @@ namespace MeshUtility
|
|||
MeshSeparator,
|
||||
MeshIntegrator,
|
||||
StaticMeshIntegrator,
|
||||
BoneMeshEraser,
|
||||
}
|
||||
private Tabs _tab;
|
||||
|
||||
private GameObject _exportTarget;
|
||||
private Editor _boneMeshEraserEditor;
|
||||
private SkinnedMeshRenderer _pSkinnedMesh;
|
||||
private Animator _pAnimator;
|
||||
private Transform _pEraseRoot;
|
||||
private Vector2 _scrollPos = new Vector2(0, 0);
|
||||
|
||||
[SerializeField]
|
||||
private SkinnedMeshRenderer _cSkinnedMesh = null;
|
||||
[SerializeField]
|
||||
private Animator _cAnimator;
|
||||
[SerializeField]
|
||||
private Transform _cEraseRoot;
|
||||
[SerializeField]
|
||||
private BoneMeshEraser.EraseBone[] _eraseBones;
|
||||
|
||||
private MethodInfo _processFunction;
|
||||
private bool _isInvokeSuccess = false;
|
||||
|
||||
|
|
@ -42,14 +79,26 @@ namespace MeshUtility
|
|||
[LangMsg(Languages.en, "Meshes containing BlendShape will be split")]
|
||||
MESH_SEPARATOR,
|
||||
|
||||
[LangMsg(Languages.ja, "メッシュを統合する。BlendShapeを含むメッシュは独立して統合されます")]
|
||||
[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.ja, "静的メッシュを一つに統合します")]
|
||||
[LangMsg(Languages.en, "Integrate static meshes into one")]
|
||||
STATIC_MESH_INTEGRATOR,
|
||||
|
||||
[LangMsg(Languages.ja, "ボーン(Erase Rootのヒエラルキー)に関連するメッシュを削除します")]
|
||||
[LangMsg(Languages.en, "Eliminate meshes associated with the bones in EraseRoot hierarchy")]
|
||||
BONE_MESH_ERASER,
|
||||
|
||||
[LangMsg(Languages.ja, "Skinned Meshを選んでください")]
|
||||
[LangMsg(Languages.en, "Select a skinned mesh")]
|
||||
SELECT_SKINNED_MESH,
|
||||
|
||||
[LangMsg(Languages.ja, "Erase Rootを選んでください")]
|
||||
[LangMsg(Languages.en, "Select a erase root")]
|
||||
SELECT_ERASE_ROOT,
|
||||
|
||||
[LangMsg(Languages.ja, "GameObjectを選んでください")]
|
||||
[LangMsg(Languages.en, "Select a GameObject first")]
|
||||
NO_GAMEOBJECT_SELECTED,
|
||||
|
|
@ -71,8 +120,17 @@ namespace MeshUtility
|
|||
VRM_DETECTED,
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (!_boneMeshEraserEditor)
|
||||
{
|
||||
_boneMeshEraserEditor = Editor.CreateEditor(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
_scrollPos = EditorGUILayout.BeginScrollView(_scrollPos);
|
||||
EditorGUIUtility.labelWidth = 150;
|
||||
// lang
|
||||
Getter.OnGuiSelectLang();
|
||||
|
|
@ -82,23 +140,44 @@ namespace MeshUtility
|
|||
switch (_tab)
|
||||
{
|
||||
case Tabs.MeshSeparator:
|
||||
EditorGUILayout.TextField(MeshProcessingMessages.MESH_SEPARATOR.Msg());
|
||||
EditorGUILayout.LabelField(MeshProcessingMessages.MESH_SEPARATOR.Msg());
|
||||
break;
|
||||
case Tabs.MeshIntegrator:
|
||||
EditorGUILayout.TextField(MeshProcessingMessages.MESH_INTEGRATOR.Msg());
|
||||
EditorGUILayout.LabelField(MeshProcessingMessages.MESH_INTEGRATOR.Msg());
|
||||
break;
|
||||
case Tabs.StaticMeshIntegrator:
|
||||
EditorGUILayout.TextField(MeshProcessingMessages.STATIC_MESH_INTEGRATOR.Msg());
|
||||
EditorGUILayout.LabelField(MeshProcessingMessages.STATIC_MESH_INTEGRATOR.Msg());
|
||||
break;
|
||||
case Tabs.BoneMeshEraser:
|
||||
EditorGUILayout.LabelField(MeshProcessingMessages.BONE_MESH_ERASER.Msg());
|
||||
break;
|
||||
}
|
||||
|
||||
EditorGUILayout.LabelField(MeshProcessingMessages.TARGET_OBJECT.Msg());
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField(MeshProcessingMessages.TARGET_OBJECT.Msg(), GUILayout.MaxWidth(146.0f));
|
||||
_exportTarget = (GameObject)EditorGUILayout.ObjectField(_exportTarget, typeof(GameObject), true);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
if (_exportTarget == null && MeshUtility.IsGameObjectSelected())
|
||||
{
|
||||
_exportTarget = Selection.activeObject as GameObject;
|
||||
}
|
||||
|
||||
if (_tab == Tabs.BoneMeshEraser)
|
||||
{
|
||||
if (_boneMeshEraserEditor)
|
||||
{
|
||||
_boneMeshEraserEditor.OnInspectorGUI();
|
||||
}
|
||||
// any better way we can detect component change?
|
||||
if (_cSkinnedMesh != _pSkinnedMesh || _cAnimator != _pAnimator || _cEraseRoot != _pEraseRoot)
|
||||
{
|
||||
BoneMeshEraserValidate();
|
||||
}
|
||||
_pSkinnedMesh = _cSkinnedMesh;
|
||||
_pAnimator = _cAnimator;
|
||||
_pEraseRoot = _cEraseRoot;
|
||||
}
|
||||
|
||||
// Create Other Buttons
|
||||
{
|
||||
GUILayout.BeginVertical();
|
||||
|
|
@ -119,6 +198,9 @@ namespace MeshUtility
|
|||
case Tabs.StaticMeshIntegrator:
|
||||
_isInvokeSuccess = InvokeWizardUpdate("StaticMeshIntegrator");
|
||||
break;
|
||||
case Tabs.BoneMeshEraser:
|
||||
_isInvokeSuccess = InvokeWizardUpdate("BoneMeshRemover");
|
||||
break;
|
||||
}
|
||||
if (_isInvokeSuccess)
|
||||
{
|
||||
|
|
@ -132,6 +214,7 @@ namespace MeshUtility
|
|||
}
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
private bool InvokeWizardUpdate(string processFuntion)
|
||||
|
|
@ -218,5 +301,127 @@ namespace MeshUtility
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool BoneMeshRemover()
|
||||
{
|
||||
if (_exportTarget == null) return GameObjectNull();
|
||||
var go = _exportTarget;
|
||||
|
||||
if (_cSkinnedMesh == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Failed", MeshProcessingMessages.SELECT_SKINNED_MESH.Msg(), "ok");
|
||||
return false;
|
||||
}
|
||||
else if (_cEraseRoot == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Failed", MeshProcessingMessages.SELECT_ERASE_ROOT.Msg(), "ok");
|
||||
return false;
|
||||
}
|
||||
BoneMeshRemove(go);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void BoneMeshEraserValidate()
|
||||
{
|
||||
if (_cSkinnedMesh == null)
|
||||
{
|
||||
_eraseBones = new BoneMeshEraser.EraseBone[] { };
|
||||
return;
|
||||
}
|
||||
|
||||
if (_cEraseRoot == null)
|
||||
{
|
||||
if (_cAnimator != null)
|
||||
{
|
||||
_cEraseRoot = _cAnimator.GetBoneTransform(HumanBodyBones.Head);
|
||||
//Debug.LogFormat("head: {0}", EraseRoot);
|
||||
}
|
||||
}
|
||||
|
||||
_eraseBones = _cSkinnedMesh.bones.Select(x =>
|
||||
{
|
||||
var eb = new BoneMeshEraser.EraseBone
|
||||
{
|
||||
Bone = x,
|
||||
};
|
||||
|
||||
if (_cEraseRoot != null)
|
||||
{
|
||||
// 首の子孫を消去
|
||||
if (eb.Bone.Ancestor().Any(y => y == _cEraseRoot))
|
||||
{
|
||||
//Debug.LogFormat("erase {0}", x);
|
||||
eb.Erase = true;
|
||||
}
|
||||
}
|
||||
|
||||
return eb;
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private void BoneMeshRemove(GameObject go)
|
||||
{
|
||||
var renderer = Remove(go);
|
||||
var outputObject = GameObject.Instantiate(go);
|
||||
outputObject.name = outputObject.name + "_bone_mesh_erase";
|
||||
if (renderer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// save mesh to Assets
|
||||
var assetPath = string.Format("{0}{1}", go.name, MeshUtility.ASSET_SUFFIX);
|
||||
var prefab = MeshUtility.GetPrefab(go);
|
||||
if (prefab != null)
|
||||
{
|
||||
var prefabPath = AssetDatabase.GetAssetPath(prefab);
|
||||
assetPath = string.Format("{0}/{1}{2}",
|
||||
Path.GetDirectoryName(prefabPath),
|
||||
Path.GetFileNameWithoutExtension(prefabPath),
|
||||
MeshUtility.ASSET_SUFFIX
|
||||
);
|
||||
}
|
||||
|
||||
Debug.LogFormat("CreateAsset: {0}", assetPath);
|
||||
AssetDatabase.CreateAsset(renderer.sharedMesh, assetPath);
|
||||
|
||||
// destroy BoneMeshEraser in the source
|
||||
foreach (var skinnedMesh in go.GetComponentsInChildren<SkinnedMeshRenderer>())
|
||||
{
|
||||
if (skinnedMesh.gameObject.name == BoneMeshEraserWizard.BONE_MESH_ERASER_NAME)
|
||||
{
|
||||
GameObject.DestroyImmediate(skinnedMesh.gameObject);
|
||||
}
|
||||
}
|
||||
// destroy the original mesh in the copied GameObject
|
||||
foreach (var skinnedMesh in outputObject.GetComponentsInChildren<SkinnedMeshRenderer>())
|
||||
{
|
||||
if (skinnedMesh.sharedMesh == _cSkinnedMesh.sharedMesh)
|
||||
{
|
||||
GameObject.DestroyImmediate(skinnedMesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SkinnedMeshRenderer Remove(GameObject go)
|
||||
{
|
||||
var bones = _cSkinnedMesh.bones;
|
||||
var eraseBones = _eraseBones
|
||||
.Where(x => x.Erase)
|
||||
.Select(x => Array.IndexOf(bones, x.Bone))
|
||||
.ToArray();
|
||||
|
||||
var meshNode = new GameObject(BoneMeshEraserWizard.BONE_MESH_ERASER_NAME);
|
||||
meshNode.transform.SetParent(go.transform, false);
|
||||
|
||||
var erased = meshNode.AddComponent<SkinnedMeshRenderer>();
|
||||
erased.sharedMesh = BoneMeshEraser.CreateErasedMesh(_cSkinnedMesh.sharedMesh, eraseBones);
|
||||
erased.sharedMaterials = _cSkinnedMesh.sharedMaterials;
|
||||
erased.bones = _cSkinnedMesh.bones;
|
||||
|
||||
return erased;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ namespace MeshUtility
|
|||
public class MeshUtility
|
||||
{
|
||||
public const string MENU_PARENT = "UniGLTF/Mesh Utility/";
|
||||
private const string ASSET_SUFFIX = ".mesh.asset";
|
||||
public const string ASSET_SUFFIX = ".mesh.asset";
|
||||
private static readonly Vector3 ZERO_MOVEMENT = Vector3.zero;
|
||||
|
||||
public static Object GetPrefab(GameObject instance)
|
||||
|
|
@ -302,7 +302,7 @@ namespace MeshUtility
|
|||
AssetDatabase.CreateAsset(meshWithMaterials.Mesh, assetPath);
|
||||
|
||||
// add component
|
||||
var meshObject = new GameObject(go.name + ".integrated");
|
||||
var meshObject = new GameObject(go.name + ".static_meshes_integrated");
|
||||
if (go.transform.parent != null)
|
||||
{
|
||||
meshObject.transform.SetParent(go.transform.parent, false);
|
||||
|
|
@ -353,7 +353,7 @@ namespace MeshUtility
|
|||
else if (skinnedMesh.sharedMesh.name == MeshIntegratorUtility.INTEGRATED_MESH_NAME ||
|
||||
skinnedMesh.sharedMesh.name == MeshIntegratorUtility.INTEGRATED_MESH_BLENDSHAPE_NAME)
|
||||
{
|
||||
// SaveMeshData(skinnedMesh.sharedMesh);
|
||||
SaveMeshData(skinnedMesh.sharedMesh);
|
||||
}
|
||||
}
|
||||
foreach (var normalMesh in normalMeshes)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user