mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-08-25 12:05:08 -05:00
TargetIsPrefab and DialogMessage
This commit is contained in:
82
Assets/UniGLTF/Editor/MeshUtility/MaterialKey.cs
Normal file
82
Assets/UniGLTF/Editor/MeshUtility/MaterialKey.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
// namespace UniGLTF.MeshUtility
|
||||
// {
|
||||
// [Serializable]
|
||||
// struct MaterialKey
|
||||
// {
|
||||
// public string Shader;
|
||||
// public KeyValuePair<string, object>[] Properties;
|
||||
|
||||
// public override bool Equals(object obj)
|
||||
// {
|
||||
// if (!(obj is MaterialKey))
|
||||
// {
|
||||
// return base.Equals(obj);
|
||||
// }
|
||||
|
||||
// var key = (MaterialKey)obj;
|
||||
|
||||
// return Shader == key.Shader
|
||||
// && Properties.SequenceEqual(key.Properties)
|
||||
// ;
|
||||
// }
|
||||
|
||||
// public override int GetHashCode()
|
||||
// {
|
||||
// return base.GetHashCode();
|
||||
// }
|
||||
// }
|
||||
|
||||
// [Serializable]
|
||||
// struct MaterialList
|
||||
// {
|
||||
// public Material[] Materials;
|
||||
|
||||
// public MaterialList(Material[] list)
|
||||
// {
|
||||
// Materials = list;
|
||||
// }
|
||||
// }
|
||||
|
||||
// static object GetPropertyValue(Shader shader, int i, Material m)
|
||||
// {
|
||||
// var propType = ShaderUtil.GetPropertyType(shader, i);
|
||||
// switch (propType)
|
||||
// {
|
||||
// case ShaderUtil.ShaderPropertyType.Color:
|
||||
// return m.GetColor(ShaderUtil.GetPropertyName(shader, i));
|
||||
|
||||
// case ShaderUtil.ShaderPropertyType.Range:
|
||||
// case ShaderUtil.ShaderPropertyType.Float:
|
||||
// return m.GetFloat(ShaderUtil.GetPropertyName(shader, i));
|
||||
|
||||
// case ShaderUtil.ShaderPropertyType.Vector:
|
||||
// return m.GetVector(ShaderUtil.GetPropertyName(shader, i));
|
||||
|
||||
// case ShaderUtil.ShaderPropertyType.TexEnv:
|
||||
// return m.GetTexture(ShaderUtil.GetPropertyName(shader, i));
|
||||
|
||||
// default:
|
||||
// throw new NotImplementedException(propType.ToString());
|
||||
// }
|
||||
// }
|
||||
|
||||
// static MaterialKey GetMaterialKey(Material m)
|
||||
// {
|
||||
// var key = new MaterialKey
|
||||
// {
|
||||
// Shader = m.shader.name,
|
||||
// };
|
||||
|
||||
// key.Properties = Enumerable.Range(0, ShaderUtil.GetPropertyCount(m.shader))
|
||||
// .Select(x => new KeyValuePair<string, object>(
|
||||
// ShaderUtil.GetPropertyName(m.shader, x),
|
||||
// GetPropertyValue(m.shader, x, m))
|
||||
// )
|
||||
// .OrderBy(x => x.Key)
|
||||
// .ToArray()
|
||||
// ;
|
||||
|
||||
// return key;
|
||||
// }
|
||||
|
||||
// }
|
||||
11
Assets/UniGLTF/Editor/MeshUtility/MaterialKey.cs.meta
Normal file
11
Assets/UniGLTF/Editor/MeshUtility/MaterialKey.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91c86a6f42c50c9438c124f0dbd048a7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -3,12 +3,15 @@ using UnityEditor;
|
||||
using UniGLTF.M17N;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
|
||||
|
||||
namespace UniGLTF.MeshUtility
|
||||
{
|
||||
public class MeshUtilityDialog : EditorWindow
|
||||
{
|
||||
public const string MENU_NAME = "glTF MeshUtility";
|
||||
protected const string ASSET_SUFFIX = ".mesh.asset";
|
||||
public static void OpenWindow()
|
||||
{
|
||||
var window =
|
||||
@@ -21,7 +24,6 @@ namespace UniGLTF.MeshUtility
|
||||
{
|
||||
Freeze,
|
||||
IntegrateSplit,
|
||||
BoneMeshEraser,
|
||||
}
|
||||
protected Tabs _tab;
|
||||
protected GameObject _exportTarget;
|
||||
@@ -62,10 +64,109 @@ namespace UniGLTF.MeshUtility
|
||||
}
|
||||
}
|
||||
bool IsValid => !_validations.Any(v => !v.CanExport);
|
||||
|
||||
MeshInfo[] integrationResults;
|
||||
|
||||
Vector2 _scrollPos;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
// Clear(HelpMessage.Ready, ValidationError.None);
|
||||
// OnValidate();
|
||||
}
|
||||
|
||||
// void Clear(HelpMessage help, ValidationError error)
|
||||
// {
|
||||
// helpString = help.Msg();
|
||||
// errorString = error != ValidationError.None ? error.Msg() : null;
|
||||
// m_uniqueMaterials = new Material[] { };
|
||||
// m_duplicateMaterials = new MaterialList[] { };
|
||||
// m_excludes.Clear();
|
||||
// isValid = false;
|
||||
// }
|
||||
|
||||
// void OnValidate()
|
||||
// {
|
||||
// isValid = false;
|
||||
// if (m_root == null)
|
||||
// {
|
||||
// Clear(HelpMessage.SetTarget, ValidationError.NoTarget);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if (m_root.GetGameObjectType() != GameObjectType.AssetPrefab)
|
||||
// {
|
||||
// Clear(HelpMessage.SetTarget, ValidationError.NotPrefab);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if (m_root.transform.parent != null)
|
||||
// {
|
||||
// Clear(HelpMessage.InvalidTarget, ValidationError.HasParent);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// var backup = m_excludes.ToArray();
|
||||
// Clear(HelpMessage.Ready, ValidationError.None);
|
||||
// isValid = true;
|
||||
// m_uniqueMaterials = MeshIntegratorUtility.EnumerateSkinnedMeshRenderer(m_root.transform, MeshEnumerateOption.OnlyWithoutBlendShape)
|
||||
// .SelectMany(x => x.sharedMaterials)
|
||||
// .Distinct()
|
||||
// .ToArray();
|
||||
|
||||
// m_duplicateMaterials = m_uniqueMaterials
|
||||
// .GroupBy(x => GetMaterialKey(x), x => x)
|
||||
// .Select(x => new MaterialList(x.ToArray()))
|
||||
// .Where(x => x.Materials.Length > 1)
|
||||
// .ToArray()
|
||||
// ;
|
||||
|
||||
// UpdateExcludes(backup);
|
||||
// }
|
||||
|
||||
// void UpdateExcludes(ExcludeItem[] backup)
|
||||
// {
|
||||
// var exclude_map = new Dictionary<Mesh, ExcludeItem>();
|
||||
// var excludes = new List<ExcludeItem>();
|
||||
// foreach (var x in m_root.GetComponentsInChildren<Renderer>())
|
||||
// {
|
||||
// var mesh = x.GetMesh();
|
||||
// if (mesh == null)
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// if (exclude_map.ContainsKey(mesh))
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// var item = new ExcludeItem
|
||||
// {
|
||||
// Mesh = mesh,
|
||||
// };
|
||||
// var found = backup.FirstOrDefault(y => y.Mesh == mesh);
|
||||
// if (found != null)
|
||||
// {
|
||||
// item.Exclude = found.Exclude;
|
||||
// }
|
||||
// excludes.Add(item);
|
||||
// exclude_map[mesh] = item;
|
||||
// }
|
||||
// m_excludes.AddRange(excludes);
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// Scene と Prefab で挙動をスイッチする。
|
||||
///
|
||||
/// - Scene: ヒエラルキーを操作する。Asset の 書き出しはしない。UNDO はする。TODO: 明示的な Asset の書き出し。
|
||||
/// - Prefab: 対象をコピーして処理する。Undo は実装しない。結果を Asset として書き出し、処理後にコピーは削除する。
|
||||
///
|
||||
/// </summary>
|
||||
bool TargetIsPrefab => _exportTarget != null && _exportTarget.scene.name == null;
|
||||
|
||||
protected virtual void DialogMessage()
|
||||
{
|
||||
EditorGUILayout.HelpBox(MeshUtilityMessages.MESH_UTILITY.Msg(), MessageType.Info);
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
@@ -74,6 +175,9 @@ namespace UniGLTF.MeshUtility
|
||||
_scrollPos = EditorGUILayout.BeginScrollView(_scrollPos);
|
||||
EditorGUIUtility.labelWidth = 200;
|
||||
LanguageGetter.OnGuiSelectLang();
|
||||
|
||||
DialogMessage();
|
||||
|
||||
var exportTarget = (GameObject)EditorGUILayout.ObjectField(
|
||||
MeshUtilityMessages.TARGET_OBJECT.Msg(),
|
||||
_exportTarget, typeof(GameObject), true);
|
||||
@@ -83,6 +187,14 @@ namespace UniGLTF.MeshUtility
|
||||
MeshIntegration.UpdateMeshIntegrationList(_exportTarget);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// GameObject or Prefab ?
|
||||
if (TargetIsPrefab)
|
||||
{
|
||||
EditorGUILayout.HelpBox(MeshUtilityMessages.PREFAB_TARGET.Msg(), MessageType.Warning);
|
||||
}
|
||||
|
||||
// tab bar
|
||||
_tab = TabBar.OnGUI(_tab, "LargeButton", GUI.ToolbarButtonSize.Fixed);
|
||||
|
||||
foreach (var validation in _validations)
|
||||
@@ -90,9 +202,6 @@ namespace UniGLTF.MeshUtility
|
||||
validation.DrawGUI();
|
||||
}
|
||||
|
||||
EditorGUILayout.HelpBox(MeshUtilityMessages.MESH_SEPARATOR.Msg(), MessageType.Info);
|
||||
EditorGUILayout.HelpBox(MeshUtilityMessages.MESH_INTEGRATOR.Msg(), MessageType.Info);
|
||||
|
||||
switch (_tab)
|
||||
{
|
||||
case Tabs.Freeze:
|
||||
@@ -113,24 +222,26 @@ namespace UniGLTF.MeshUtility
|
||||
break;
|
||||
}
|
||||
|
||||
case Tabs.BoneMeshEraser:
|
||||
{
|
||||
// TODO: FirstPerson 処理と統合する
|
||||
EditorGUILayout.HelpBox(MeshUtilityMessages.BONE_MESH_ERASER.Msg(), MessageType.Info);
|
||||
// if (_boneMeshEraserEditor)
|
||||
// {
|
||||
// _boneMeshEraserEditor.OnInspectorGUI();
|
||||
// }
|
||||
// if (TabBoneMeshRemover.TryExecutable(_exportTarget, _skinnedMeshRenderer, out string msg))
|
||||
// {
|
||||
// processed = TabBoneMeshRemover.OnGUI(_exportTarget, _skinnedMeshRenderer, _eraseBones);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// EditorGUILayout.HelpBox(msg, MessageType.Error);
|
||||
// }
|
||||
break;
|
||||
}
|
||||
// TODO:
|
||||
// Mesh統合のオプション
|
||||
// case Tabs.BoneMeshEraser:
|
||||
// {
|
||||
// // TODO: FirstPerson 処理と統合する
|
||||
// EditorGUILayout.HelpBox(MeshUtilityMessages.BONE_MESH_ERASER.Msg(), MessageType.Info);
|
||||
// // if (_boneMeshEraserEditor)
|
||||
// // {
|
||||
// // _boneMeshEraserEditor.OnInspectorGUI();
|
||||
// // }
|
||||
// // if (TabBoneMeshRemover.TryExecutable(_exportTarget, _skinnedMeshRenderer, out string msg))
|
||||
// // {
|
||||
// // processed = TabBoneMeshRemover.OnGUI(_exportTarget, _skinnedMeshRenderer, _eraseBones);
|
||||
// // }
|
||||
// // else
|
||||
// // {
|
||||
// // EditorGUILayout.HelpBox(msg, MessageType.Error);
|
||||
// // }
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
EditorGUILayout.EndScrollView();
|
||||
|
||||
@@ -144,18 +255,121 @@ namespace UniGLTF.MeshUtility
|
||||
GUI.enabled = true;
|
||||
if (pressed)
|
||||
{
|
||||
Undo.RegisterFullObjectHierarchyUndo(exportTarget, "MeshUtility");
|
||||
foreach (var go in MeshUtility.Process(exportTarget))
|
||||
if (TargetIsPrefab)
|
||||
{
|
||||
Undo.RegisterCreatedObjectUndo(go, "MeshUtility");
|
||||
/// [prefab]
|
||||
///
|
||||
/// * backup するのではなく 変更した copy を作成する。元は変えない
|
||||
/// * copy 先の統合前の renderer を disable で残さず destroy する
|
||||
/// * 実行すると mesh, blendshape, blendShape を新規に作成する
|
||||
/// * 新しいヒエラルキーを prefab に保存してから削除して終了する
|
||||
|
||||
// 出力フォルダを決める
|
||||
var folder = "Assets";
|
||||
var prefab = _exportTarget.GetPrefab();
|
||||
if (prefab != null)
|
||||
{
|
||||
folder = AssetDatabase.GetAssetPath(prefab);
|
||||
// Debug.Log(folder);
|
||||
}
|
||||
// 新規で作成されるアセットはすべてこのフォルダの中に作る。上書きチェックはしない
|
||||
var assetFolder = EditorUtility.SaveFolderPanel("select asset save folder", Path.GetDirectoryName(folder), "VrmIntegrated");
|
||||
var unityPath = UniGLTF.UnityPath.FromFullpath(assetFolder);
|
||||
if (!unityPath.IsUnderWritableFolder)
|
||||
{
|
||||
EditorUtility.DisplayDialog("asset folder", "Target folder must be in the Assets or writable Packages folder", "cancel");
|
||||
return;
|
||||
}
|
||||
assetFolder = unityPath.Value;
|
||||
|
||||
var copy = GameObject.Instantiate(_exportTarget);
|
||||
|
||||
var (results, created) = MeshUtility.Process(copy);
|
||||
|
||||
WriteAssets(copy, assetFolder, results);
|
||||
|
||||
// destroy scene
|
||||
UnityEngine.Object.DestroyImmediate(copy);
|
||||
}
|
||||
else
|
||||
{
|
||||
Undo.RegisterFullObjectHierarchyUndo(_exportTarget, "MeshUtility");
|
||||
var (results, created) = MeshUtility.Process(_exportTarget);
|
||||
foreach (var go in created)
|
||||
{
|
||||
Undo.RegisterCreatedObjectUndo(go, "MeshUtility");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Show Result ?
|
||||
_exportTarget = null;
|
||||
// Show Result ?
|
||||
// Close();
|
||||
// GUIUtility.ExitGUI();
|
||||
}
|
||||
}
|
||||
|
||||
void WriteAssets(GameObject copy, string assetFolder, List<MeshIntegrationResult> results)
|
||||
{
|
||||
//
|
||||
// write mesh asset
|
||||
foreach (var result in results)
|
||||
{
|
||||
var childAssetPath = $"{assetFolder}/{result.Integrated.IntegratedRenderer.gameObject.name}{ASSET_SUFFIX}";
|
||||
Debug.LogFormat("CreateAsset: {0}", childAssetPath);
|
||||
AssetDatabase.CreateAsset(result.Integrated.IntegratedRenderer.sharedMesh, childAssetPath);
|
||||
}
|
||||
|
||||
// 統合した結果をヒエラルキーに追加する
|
||||
foreach (var result in results)
|
||||
{
|
||||
if (result.Integrated.IntegratedRenderer != null)
|
||||
{
|
||||
result.Integrated.IntegratedRenderer.transform.SetParent(copy.transform, false);
|
||||
}
|
||||
}
|
||||
|
||||
// 統合した結果を反映した BlendShapeClip を作成して置き換える
|
||||
// var clips = VRMMeshIntegratorUtility.FollowBlendshapeRendererChange(results, copy, assetFolder);
|
||||
|
||||
// 用が済んだ 統合前 の renderer を削除する
|
||||
foreach (var result in results)
|
||||
{
|
||||
foreach (var renderer in result.SourceMeshRenderers)
|
||||
{
|
||||
GameObject.DestroyImmediate(renderer);
|
||||
}
|
||||
foreach (var renderer in result.SourceSkinnedMeshRenderers)
|
||||
{
|
||||
GameObject.DestroyImmediate(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
// reset firstperson
|
||||
// var firstperson = copy.GetComponent<VRMFirstPerson>();
|
||||
// if (firstperson != null)
|
||||
// {
|
||||
// firstperson.Reset();
|
||||
// }
|
||||
|
||||
// prefab
|
||||
var prefabPath = $"{assetFolder}/VrmIntegrated.prefab";
|
||||
Debug.Log(prefabPath);
|
||||
PrefabUtility.SaveAsPrefabAsset(copy, prefabPath, out bool success);
|
||||
if (!success)
|
||||
{
|
||||
throw new System.Exception($"PrefabUtility.SaveAsPrefabAsset: {prefabPath}");
|
||||
}
|
||||
|
||||
// var prefabReference = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
||||
// foreach (var clip in clips)
|
||||
// {
|
||||
// var so = new SerializedObject(clip);
|
||||
// so.Update();
|
||||
// // clip.Prefab = copy;
|
||||
// var prop = so.FindProperty("m_prefab");
|
||||
// prop.objectReferenceValue = prefabReference;
|
||||
// so.ApplyModifiedProperties();
|
||||
// }
|
||||
}
|
||||
|
||||
protected bool ToggleIsModified(string label, ref bool value)
|
||||
{
|
||||
var newValue = EditorGUILayout.Toggle(label, value);
|
||||
|
||||
@@ -8,61 +8,80 @@ namespace UniGLTF.MeshUtility
|
||||
[LangMsg(Languages.en, "TargetObject")]
|
||||
TARGET_OBJECT,
|
||||
|
||||
[LangMsg(Languages.ja, @"ターゲットオブジェクト下の SkinnedMeshRenderer にアタッチされたメッシュを、 BlendShape の有無で分割します。
|
||||
[LangMsg(Languages.ja, @"凍結 > 統合 > 分割 という一連の処理を実行します。
|
||||
|
||||
* Asset: 新しい Mesh Asset が元と同じフォルダに作成されます。例: Original -> Original_WithBlendShape.mesh & Original_WithoutBlendShape.mesh
|
||||
* Scene: コピーされたヒエラルキーでは、分割された Mesh は BlendShape のある Mesh で置き換えられて、BlendShape の無い Mesh を使った SkinnedMeshRenderer が追加されます。
|
||||
[凍結]
|
||||
- ヒエラルキーの 回転・拡縮を Mesh に焼き付けます。
|
||||
- BlendShape の現状を Mesh に焼き付けます。
|
||||
|
||||
[統合]
|
||||
- ヒエラルキーに含まれる MeshRenderer と SkinnedMeshRenderer をひとつの SkinnedMeshRenderer に統合します。
|
||||
|
||||
[分割]
|
||||
- 統合結果を BlendShape の有無を基準に分割します。
|
||||
|
||||
[Scene と Prefab]
|
||||
Scene と Prefab で挙動が異なります。
|
||||
|
||||
(Scene/Runtime)
|
||||
- 対象のヒエラルキーを変更します。UNDO可能。
|
||||
- Asset の書き出しはしません。Unityを再起動すると、書き出していない Mesh などの Asset が消滅します。
|
||||
|
||||
(Prefab/Editor)
|
||||
- 対象の prefab をシーンにコピーして処理を実行し、生成する Asset を指定されたフォルダに書き出します。
|
||||
- Asset 書き出し後にコピーを削除します。
|
||||
- Undo はありません。
|
||||
")]
|
||||
[LangMsg(Languages.en, @"Separate the mesh attached to the SkinnedMeshRenderer under the target object with or without BlendShape.
|
||||
[LangMsg(Languages.en, @"
|
||||
Separate the mesh attached to the SkinnedMeshRenderer under the target object with or without BlendShape.
|
||||
|
||||
* Asset: A new Mesh Asset will be created in the same folder as the original. Example: Original-> Original_WithBlendShape.mesh & Original_WithoutBlendShape.mesh
|
||||
* Scene: In the copied hierarchy, the split Mesh is replaced with a Mesh that holds the BlendShape, and a SkinnedMeshRenderer with a Mesh without BlendShape is added.
|
||||
")]
|
||||
MESH_SEPARATOR,
|
||||
|
||||
MESH_UTILITY,
|
||||
|
||||
[LangMsg(Languages.ja, "ブレンドシェイプの有無で分割する")]
|
||||
[LangMsg(Languages.en, "Divide by the presence or absence of `blendshape`")]
|
||||
MESH_SEPARATOR_BY_BLENDSHAPE,
|
||||
|
||||
[LangMsg(Languages.ja, @"ターゲットオブジェクト下の SkinnedMeshRenderer または MeshFilter にアタッチされたメッシュを統合します。
|
||||
// [LangMsg(Languages.ja, @"ターゲットオブジェクト下の SkinnedMeshRenderer または MeshFilter にアタッチされたメッシュを統合します。
|
||||
|
||||
* Asset: Assets/MeshIntegrated.mesh が作成されます(上書きされるので注意してください)。
|
||||
* Scene: コピーされたヒエラルキーでは、統合された Mesh は除去されます。新しく MeshIntegrator ノードが追加されます。
|
||||
* VRMではBlendShapeClipの統合など追加の処理が必要です。`VRM0-MeshIntegratorWizard` を使ってください。
|
||||
")]
|
||||
[LangMsg(Languages.en, @"Integrates the attached mesh into the SkinnedMeshRenderer or MeshFilter under the target object.
|
||||
// * Asset: Assets/MeshIntegrated.mesh が作成されます(上書きされるので注意してください)。
|
||||
// * Scene: コピーされたヒエラルキーでは、統合された Mesh は除去されます。新しく MeshIntegrator ノードが追加されます。
|
||||
// * VRMではBlendShapeClipの統合など追加の処理が必要です。`VRM0-MeshIntegratorWizard` を使ってください。
|
||||
// ")]
|
||||
// [LangMsg(Languages.en, @"Integrates the attached mesh into the SkinnedMeshRenderer or MeshFilter under the target object.
|
||||
|
||||
* Asset: Assets/MeshIntegrated.mesh is created (note that it will be overwritten).
|
||||
* Scene: In the copied hierarchy, the integrated mesh is removed. A new MeshIntegrator node is added.
|
||||
* VRM requires additional processing such as BlendShapeClip integration. Use the `VRM0-MeshIntegratorWizard` integration feature.
|
||||
")]
|
||||
MESH_INTEGRATOR,
|
||||
// * Asset: Assets/MeshIntegrated.mesh is created (note that it will be overwritten).
|
||||
// * Scene: In the copied hierarchy, the integrated mesh is removed. A new MeshIntegrator node is added.
|
||||
// * VRM requires additional processing such as BlendShapeClip integration. Use the `VRM0-MeshIntegratorWizard` integration feature.
|
||||
// ")]
|
||||
// MESH_INTEGRATOR,
|
||||
|
||||
// [LangMsg(Languages.ja, "静的メッシュを一つに統合します")]
|
||||
// [LangMsg(Languages.en, "Integrate static meshes into one")]
|
||||
// STATIC_MESH_INTEGRATOR,
|
||||
// // [LangMsg(Languages.ja, "静的メッシュを一つに統合します")]
|
||||
// // [LangMsg(Languages.en, "Integrate static meshes into one")]
|
||||
// // STATIC_MESH_INTEGRATOR,
|
||||
|
||||
[LangMsg(Languages.ja, @"指定された SkinnedMeshRenderer から、指定されたボーンに対する Weight を保持する三角形を除去します。
|
||||
// [LangMsg(Languages.ja, @"指定された SkinnedMeshRenderer から、指定されたボーンに対する Weight を保持する三角形を除去します。
|
||||
|
||||
* Asset: 元の Mesh と同じフォルダに、三角形を除去した Mesh を保存します。
|
||||
* Scene: コピーされたヒエラルキーでは、三角形が除去された Mesh に差し替えられます。
|
||||
")]
|
||||
[LangMsg(Languages.en, @"Removes the triangle that holds the weight for the specified bone from the specified SkinnedMeshRenderer.
|
||||
// * Asset: 元の Mesh と同じフォルダに、三角形を除去した Mesh を保存します。
|
||||
// * Scene: コピーされたヒエラルキーでは、三角形が除去された Mesh に差し替えられます。
|
||||
// ")]
|
||||
// [LangMsg(Languages.en, @"Removes the triangle that holds the weight for the specified bone from the specified SkinnedMeshRenderer.
|
||||
|
||||
* Assets: Save the mesh with the triangles removed in the same folder as the original mesh.
|
||||
* Scene: In the copied hierarchy, it will be replaced with a Mesh with the triangles removed.
|
||||
")]
|
||||
// * Assets: Save the mesh with the triangles removed in the same folder as the original mesh.
|
||||
// * Scene: In the copied hierarchy, it will be replaced with a Mesh with the triangles removed.
|
||||
// ")]
|
||||
|
||||
BONE_MESH_ERASER,
|
||||
// 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, "Erase Rootを選んでください")]
|
||||
// [LangMsg(Languages.en, "Select a erase root")]
|
||||
// SELECT_ERASE_ROOT,
|
||||
|
||||
[LangMsg(Languages.ja, "GameObjectを選んでください")]
|
||||
[LangMsg(Languages.en, "Select a GameObject first")]
|
||||
@@ -72,9 +91,9 @@ namespace UniGLTF.MeshUtility
|
||||
[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, "No static mesh is contained")]
|
||||
// NO_STATIC_MESH,
|
||||
|
||||
[LangMsg(Languages.ja, "GameObjectにスキンメッシュ・静的メッシュが含まれていません")]
|
||||
[LangMsg(Languages.en, "Skinned/Static mesh is not contained")]
|
||||
@@ -83,5 +102,9 @@ namespace UniGLTF.MeshUtility
|
||||
[LangMsg(Languages.ja, "BlendShapeClipが不整合を起こすので、`VRM0-> MeshIntegrator`を使ってください")]
|
||||
[LangMsg(Languages.en, "Because BlendShapeClip causes inconsistency , use `VRM0 -> MeshIntegrator` instead")]
|
||||
VRM_DETECTED,
|
||||
|
||||
[LangMsg(Languages.ja, "対象は, Prefab です。実行時に書き出しファイルの指定があります。")]
|
||||
[LangMsg(Languages.en, "The target is prefab. A temporary file is specified during execution.")]
|
||||
PREFAB_TARGET,
|
||||
}
|
||||
}
|
||||
@@ -155,7 +155,7 @@ namespace UniGLTF.MeshUtility
|
||||
return MeshIntegrationGroups.ToList();
|
||||
}
|
||||
|
||||
public virtual List<GameObject> Process(GameObject go)
|
||||
public virtual (List<MeshIntegrationResult>, List<GameObject>) Process(GameObject go)
|
||||
{
|
||||
// TODO unpack prefab
|
||||
|
||||
@@ -181,48 +181,45 @@ namespace UniGLTF.MeshUtility
|
||||
|
||||
var copy = CopyMeshIntegrationGroups();
|
||||
|
||||
var newGo = new List<GameObject>();
|
||||
var newList = new List<GameObject>();
|
||||
|
||||
var empty = GetOrCreateEmpty(go, "mesh");
|
||||
|
||||
var results = new List<MeshIntegrationResult>();
|
||||
foreach (var group in copy)
|
||||
{
|
||||
var empty = GetOrCreateEmpty(go, "mesh");
|
||||
var (result, newGo) = Integrate(empty, group);
|
||||
results.Add(result);
|
||||
newList.AddRange(newGo);
|
||||
}
|
||||
|
||||
var results = new List<MeshIntegrationResult>();
|
||||
foreach (var group in copy)
|
||||
foreach (var result in results)
|
||||
{
|
||||
foreach (var r in result.SourceMeshRenderers)
|
||||
{
|
||||
Integrate(newGo, empty, results, group);
|
||||
RemoveComponent(r);
|
||||
}
|
||||
|
||||
foreach (var result in results)
|
||||
foreach (var r in result.SourceSkinnedMeshRenderers)
|
||||
{
|
||||
foreach (var r in result.SourceMeshRenderers)
|
||||
{
|
||||
RemoveComponent(r);
|
||||
}
|
||||
foreach (var r in result.SourceSkinnedMeshRenderers)
|
||||
{
|
||||
RemoveComponent(r);
|
||||
}
|
||||
RemoveComponent(r);
|
||||
}
|
||||
}
|
||||
|
||||
MeshIntegrationGroups.Clear();
|
||||
|
||||
return newGo;
|
||||
return (results, newList);
|
||||
}
|
||||
|
||||
protected virtual MeshIntegrationResult Integrate(List<GameObject> newGo,
|
||||
GameObject empty, List<MeshIntegrationResult> results, MeshIntegrationGroup group)
|
||||
protected virtual (MeshIntegrationResult, GameObject[]) Integrate(GameObject empty,
|
||||
MeshIntegrationGroup group)
|
||||
{
|
||||
var result = MeshIntegrator.Integrate(group, SplitByBlendShape
|
||||
? MeshIntegrator.BlendShapeOperation.Split
|
||||
: MeshIntegrator.BlendShapeOperation.Use);
|
||||
results.Add(result);
|
||||
|
||||
foreach (var created in result.AddIntegratedRendererTo(empty))
|
||||
{
|
||||
newGo.Add(created);
|
||||
}
|
||||
var newGo = result.AddIntegratedRendererTo(empty).ToArray();
|
||||
|
||||
return result;
|
||||
return (result, newGo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,370 +1,88 @@
|
||||
#pragma warning disable 0414, 0649
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniGLTF.MeshUtility;
|
||||
using System.IO;
|
||||
using UniGLTF.M17N;
|
||||
|
||||
|
||||
namespace VRM
|
||||
{
|
||||
public class VrmMeshIntegratorWizard : ScriptableWizard
|
||||
public class VrmMeshIntegratorWizard : UniGLTF.MeshUtility.MeshUtilityDialog
|
||||
{
|
||||
public const string MENU_NAME = "VRM 0.x MeshUtility";
|
||||
const string ASSET_SUFFIX = ".mesh.asset";
|
||||
public new const string MENU_NAME = "VRM 0.x MeshUtility";
|
||||
|
||||
enum HelpMessage
|
||||
public new static void OpenWindow()
|
||||
{
|
||||
Ready,
|
||||
SetTarget,
|
||||
InvalidTarget,
|
||||
var window =
|
||||
(VrmMeshIntegratorWizard)EditorWindow.GetWindow(typeof(VrmMeshIntegratorWizard));
|
||||
window.titleContent = new GUIContent(MENU_NAME);
|
||||
window.Show();
|
||||
}
|
||||
|
||||
enum ValidationError
|
||||
// void Integrate()
|
||||
// {
|
||||
// // 統合
|
||||
// var excludes = m_excludes.Where(x => x.Exclude).Select(x => x.Mesh);
|
||||
// var results = Integrate(copy, excludes, m_separateByBlendShape);
|
||||
|
||||
// }
|
||||
|
||||
// static List<UniGLTF.MeshUtility.MeshIntegrationResult> Integrate(GameObject root, IEnumerable<Mesh> excludes, bool separateByBlendShape)
|
||||
// {
|
||||
// var results = new List<UniGLTF.MeshUtility.MeshIntegrationResult>();
|
||||
// if (separateByBlendShape)
|
||||
// {
|
||||
// results.Add(MeshIntegratorUtility.Integrate(root, onlyBlendShapeRenderers: MeshEnumerateOption.OnlyWithoutBlendShape, excludes: excludes));
|
||||
// results.Add(MeshIntegratorUtility.Integrate(root, onlyBlendShapeRenderers: MeshEnumerateOption.OnlyWithBlendShape, excludes: excludes));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// results.Add(MeshIntegratorUtility.Integrate(root, onlyBlendShapeRenderers: MeshEnumerateOption.All, excludes: excludes));
|
||||
// }
|
||||
// return results;
|
||||
// }
|
||||
|
||||
protected override void DialogMessage()
|
||||
{
|
||||
None,
|
||||
NoTarget,
|
||||
HasParent,
|
||||
NotPrefab,
|
||||
EditorGUILayout.HelpBox(Message.MESH_UTILITY.Msg(), MessageType.Info);
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
GameObject m_root;
|
||||
|
||||
[SerializeField]
|
||||
bool m_separateByBlendShape = true;
|
||||
|
||||
[Header("Validation")]
|
||||
[SerializeField]
|
||||
Material[] m_uniqueMaterials;
|
||||
|
||||
[Serializable]
|
||||
struct MaterialKey
|
||||
enum Message
|
||||
{
|
||||
public string Shader;
|
||||
public KeyValuePair<string, object>[] Properties;
|
||||
[LangMsg(Languages.ja, @"(VRM-0.x専用) 凍結 > 統合 > 分割 という一連の処理を実行します。
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is MaterialKey))
|
||||
{
|
||||
return base.Equals(obj);
|
||||
}
|
||||
[凍結]
|
||||
- ヒエラルキーの 回転・拡縮を Mesh に焼き付けます。
|
||||
- BlendShape の現状を Mesh に焼き付けます。
|
||||
|
||||
var key = (MaterialKey)obj;
|
||||
- VRM-0.x の正規化処理です。
|
||||
- HumanoidAvatar の再生成。
|
||||
- BlendShapeClip, SpringBone, Constraint なども影響を受けます。
|
||||
|
||||
return Shader == key.Shader
|
||||
&& Properties.SequenceEqual(key.Properties)
|
||||
;
|
||||
}
|
||||
[統合]
|
||||
- ヒエラルキーに含まれる MeshRenderer と SkinnedMeshRenderer をひとつの SkinnedMeshRenderer に統合します。
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return base.GetHashCode();
|
||||
}
|
||||
}
|
||||
- VRM の FirstPerson 設定に応じて3種類(BOTH, FirstPerson, ThirdPerson) にグループ化して統合します。
|
||||
- FirstPerson=AUTO を前処理できます。
|
||||
- 元の Mesh は ThirdPerson として処理されます。頭なしのモデルを追加生成して FirstPersonOnly とします。
|
||||
|
||||
[Serializable]
|
||||
struct MaterialList
|
||||
{
|
||||
public Material[] Materials;
|
||||
[分割]
|
||||
- 統合結果を BlendShape の有無を基準に分割します。
|
||||
- BOTH, FirstPerson, ThirdPerson x 2 で、最大で 6Mesh になります。空の部分ができることが多いので 3Mesh くらいが多くなります。
|
||||
|
||||
public MaterialList(Material[] list)
|
||||
{
|
||||
Materials = list;
|
||||
}
|
||||
}
|
||||
[SerializeField]
|
||||
MaterialList[] m_duplicateMaterials;
|
||||
[Scene と Prefab]
|
||||
Scene と Prefab で挙動が異なります。
|
||||
|
||||
[Serializable]
|
||||
public class ExcludeItem
|
||||
{
|
||||
public Mesh Mesh;
|
||||
public bool Exclude;
|
||||
}
|
||||
(Scene/Runtime)
|
||||
- 対象のヒエラルキーを変更します。UNDO可能。
|
||||
- Asset の書き出しはしません。Unityを再起動すると、書き出していない Mesh などの Asset が消滅します。
|
||||
|
||||
[Header("Options")]
|
||||
[SerializeField]
|
||||
List<ExcludeItem> m_excludes = new List<ExcludeItem>();
|
||||
|
||||
[Header("Result")]
|
||||
[SerializeField]
|
||||
MeshInfo[] integrationResults;
|
||||
|
||||
public static void CreateWizard()
|
||||
{
|
||||
ScriptableWizard.DisplayWizard<VrmMeshIntegratorWizard>(MENU_NAME, "Integrate and close window", "Integrate");
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
Clear(HelpMessage.Ready, ValidationError.None);
|
||||
OnValidate();
|
||||
}
|
||||
|
||||
protected override bool DrawWizardGUI()
|
||||
{
|
||||
var t = m_root.GetGameObjectType();
|
||||
EditorGUILayout.HelpBox($"{t}", MessageType.Info);
|
||||
return base.DrawWizardGUI();
|
||||
}
|
||||
|
||||
static object GetPropertyValue(Shader shader, int i, Material m)
|
||||
{
|
||||
var propType = ShaderUtil.GetPropertyType(shader, i);
|
||||
switch (propType)
|
||||
{
|
||||
case ShaderUtil.ShaderPropertyType.Color:
|
||||
return m.GetColor(ShaderUtil.GetPropertyName(shader, i));
|
||||
|
||||
case ShaderUtil.ShaderPropertyType.Range:
|
||||
case ShaderUtil.ShaderPropertyType.Float:
|
||||
return m.GetFloat(ShaderUtil.GetPropertyName(shader, i));
|
||||
|
||||
case ShaderUtil.ShaderPropertyType.Vector:
|
||||
return m.GetVector(ShaderUtil.GetPropertyName(shader, i));
|
||||
|
||||
case ShaderUtil.ShaderPropertyType.TexEnv:
|
||||
return m.GetTexture(ShaderUtil.GetPropertyName(shader, i));
|
||||
|
||||
default:
|
||||
throw new NotImplementedException(propType.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
static MaterialKey GetMaterialKey(Material m)
|
||||
{
|
||||
var key = new MaterialKey
|
||||
{
|
||||
Shader = m.shader.name,
|
||||
};
|
||||
|
||||
key.Properties = Enumerable.Range(0, ShaderUtil.GetPropertyCount(m.shader))
|
||||
.Select(x => new KeyValuePair<string, object>(
|
||||
ShaderUtil.GetPropertyName(m.shader, x),
|
||||
GetPropertyValue(m.shader, x, m))
|
||||
)
|
||||
.OrderBy(x => x.Key)
|
||||
.ToArray()
|
||||
;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
void Clear(HelpMessage help, ValidationError error)
|
||||
{
|
||||
helpString = help.Msg();
|
||||
errorString = error != ValidationError.None ? error.Msg() : null;
|
||||
m_uniqueMaterials = new Material[] { };
|
||||
m_duplicateMaterials = new MaterialList[] { };
|
||||
m_excludes.Clear();
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
isValid = false;
|
||||
if (m_root == null)
|
||||
{
|
||||
Clear(HelpMessage.SetTarget, ValidationError.NoTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_root.GetGameObjectType() != GameObjectType.AssetPrefab)
|
||||
{
|
||||
Clear(HelpMessage.SetTarget, ValidationError.NotPrefab);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_root.transform.parent != null)
|
||||
{
|
||||
Clear(HelpMessage.InvalidTarget, ValidationError.HasParent);
|
||||
return;
|
||||
}
|
||||
|
||||
var backup = m_excludes.ToArray();
|
||||
Clear(HelpMessage.Ready, ValidationError.None);
|
||||
isValid = true;
|
||||
m_uniqueMaterials = MeshIntegratorUtility.EnumerateSkinnedMeshRenderer(m_root.transform, MeshEnumerateOption.OnlyWithoutBlendShape)
|
||||
.SelectMany(x => x.sharedMaterials)
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
|
||||
m_duplicateMaterials = m_uniqueMaterials
|
||||
.GroupBy(x => GetMaterialKey(x), x => x)
|
||||
.Select(x => new MaterialList(x.ToArray()))
|
||||
.Where(x => x.Materials.Length > 1)
|
||||
.ToArray()
|
||||
;
|
||||
|
||||
UpdateExcludes(backup);
|
||||
}
|
||||
|
||||
void UpdateExcludes(ExcludeItem[] backup)
|
||||
{
|
||||
var exclude_map = new Dictionary<Mesh, ExcludeItem>();
|
||||
var excludes = new List<ExcludeItem>();
|
||||
foreach (var x in m_root.GetComponentsInChildren<Renderer>())
|
||||
{
|
||||
var mesh = x.GetMesh();
|
||||
if (mesh == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (exclude_map.ContainsKey(mesh))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var item = new ExcludeItem
|
||||
{
|
||||
Mesh = mesh,
|
||||
};
|
||||
var found = backup.FirstOrDefault(y => y.Mesh == mesh);
|
||||
if (found != null)
|
||||
{
|
||||
item.Exclude = found.Exclude;
|
||||
}
|
||||
excludes.Add(item);
|
||||
exclude_map[mesh] = item;
|
||||
}
|
||||
m_excludes.AddRange(excludes);
|
||||
}
|
||||
|
||||
void OnWizardUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
/// 2022.05 仕様変更
|
||||
///
|
||||
/// * prefab 専用
|
||||
/// * backup するのではなく 変更した copy を作成する。元は変えない
|
||||
/// * copy 先の統合前の renderer を disable で残さず destroy する
|
||||
/// * 実行すると mesh, blendshape, blendShape を新規に作成する
|
||||
/// * 新しいヒエラルキーを prefab に保存してから削除して終了する
|
||||
///
|
||||
void Integrate()
|
||||
{
|
||||
if (m_root.GetGameObjectType() != GameObjectType.AssetPrefab)
|
||||
{
|
||||
throw new Exception("for prefab only");
|
||||
}
|
||||
|
||||
String folder = "Assets";
|
||||
var prefab = m_root.GetPrefab();
|
||||
if (prefab != null)
|
||||
{
|
||||
folder = AssetDatabase.GetAssetPath(prefab);
|
||||
Debug.Log(folder);
|
||||
}
|
||||
|
||||
// 新規で作成されるアセットはすべてこのフォルダの中に作る。上書きチェックはしない
|
||||
var assetFolder = EditorUtility.SaveFolderPanel("select asset save folder", Path.GetDirectoryName(folder), "VrmIntegrated");
|
||||
var unityPath = UniGLTF.UnityPath.FromFullpath(assetFolder);
|
||||
if (!unityPath.IsUnderWritableFolder)
|
||||
{
|
||||
EditorUtility.DisplayDialog("asset folder", "Target folder must be in the Assets or writable Packages folder", "cancel");
|
||||
return;
|
||||
}
|
||||
assetFolder = unityPath.Value;
|
||||
|
||||
var copy = GameObject.Instantiate(m_root);
|
||||
|
||||
// 統合
|
||||
var excludes = m_excludes.Where(x => x.Exclude).Select(x => x.Mesh);
|
||||
var results = Integrate(copy, excludes, m_separateByBlendShape);
|
||||
|
||||
// write mesh asset
|
||||
foreach (var result in results)
|
||||
{
|
||||
var childAssetPath = $"{assetFolder}/{result.Integrated.IntegratedRenderer.gameObject.name}{ASSET_SUFFIX}";
|
||||
Debug.LogFormat("CreateAsset: {0}", childAssetPath);
|
||||
AssetDatabase.CreateAsset(result.Integrated.IntegratedRenderer.sharedMesh, childAssetPath);
|
||||
}
|
||||
|
||||
// 統合した結果をヒエラルキーに追加する
|
||||
foreach (var result in results)
|
||||
{
|
||||
if (result.Integrated.IntegratedRenderer != null)
|
||||
{
|
||||
result.Integrated.IntegratedRenderer.transform.SetParent(copy.transform, false);
|
||||
}
|
||||
}
|
||||
|
||||
// 統合した結果を反映した BlendShapeClip を作成して置き換える
|
||||
var clips = VRMMeshIntegratorUtility.FollowBlendshapeRendererChange(results, copy, assetFolder);
|
||||
|
||||
// 用が済んだ 統合前 の renderer を削除する
|
||||
foreach (var result in results)
|
||||
{
|
||||
foreach (var renderer in result.SourceMeshRenderers)
|
||||
{
|
||||
GameObject.DestroyImmediate(renderer);
|
||||
}
|
||||
foreach (var renderer in result.SourceSkinnedMeshRenderers)
|
||||
{
|
||||
GameObject.DestroyImmediate(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
// reset firstperson
|
||||
var firstperson = copy.GetComponent<VRMFirstPerson>();
|
||||
if (firstperson != null)
|
||||
{
|
||||
firstperson.Reset();
|
||||
}
|
||||
|
||||
// prefab
|
||||
var prefabPath = $"{assetFolder}/VrmIntegrated.prefab";
|
||||
Debug.Log(prefabPath);
|
||||
PrefabUtility.SaveAsPrefabAsset(copy, prefabPath, out bool success);
|
||||
if (!success)
|
||||
{
|
||||
throw new System.Exception($"PrefabUtility.SaveAsPrefabAsset: {prefabPath}");
|
||||
}
|
||||
|
||||
// destroy scene
|
||||
UnityEngine.Object.DestroyImmediate(copy);
|
||||
|
||||
var prefabReference = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
||||
foreach (var clip in clips)
|
||||
{
|
||||
var so = new SerializedObject(clip);
|
||||
so.Update();
|
||||
// clip.Prefab = copy;
|
||||
var prop = so.FindProperty("m_prefab");
|
||||
prop.objectReferenceValue = prefabReference;
|
||||
so.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
static List<UniGLTF.MeshUtility.MeshIntegrationResult> Integrate(GameObject root, IEnumerable<Mesh> excludes, bool separateByBlendShape)
|
||||
{
|
||||
var results = new List<UniGLTF.MeshUtility.MeshIntegrationResult>();
|
||||
if (separateByBlendShape)
|
||||
{
|
||||
results.Add(MeshIntegratorUtility.Integrate(root, onlyBlendShapeRenderers: MeshEnumerateOption.OnlyWithoutBlendShape, excludes: excludes));
|
||||
results.Add(MeshIntegratorUtility.Integrate(root, onlyBlendShapeRenderers: MeshEnumerateOption.OnlyWithBlendShape, excludes: excludes));
|
||||
}
|
||||
else
|
||||
{
|
||||
results.Add(MeshIntegratorUtility.Integrate(root, onlyBlendShapeRenderers: MeshEnumerateOption.All, excludes: excludes));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
void OnWizardCreate()
|
||||
{
|
||||
Integrate();
|
||||
// close
|
||||
}
|
||||
|
||||
void OnWizardOtherButton()
|
||||
{
|
||||
Integrate();
|
||||
(Prefab/Editor)
|
||||
- 対象の prefab をシーンにコピーして処理を実行し、生成する Asset を指定されたフォルダに書き出します。
|
||||
- Asset 書き出し後にコピーを削除します。
|
||||
- Undo はありません。
|
||||
")]
|
||||
[LangMsg(Languages.en, @"TODO
|
||||
")]
|
||||
MESH_UTILITY,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace VRM
|
||||
|
||||
|
||||
[MenuItem(UserMenuPrefix + "/" + VrmMeshIntegratorWizard.MENU_NAME, false, 3)]
|
||||
private static void OpenMeshIntegratorWizard() => VrmMeshIntegratorWizard.CreateWizard();
|
||||
private static void OpenMeshIntegratorWizard() => VrmMeshIntegratorWizard.OpenWindow();
|
||||
|
||||
|
||||
[MenuItem(UserMenuPrefix + "/" + VRMHumanoidNormalizerMenu.MENU_NAME, true, 51)]
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UniGLTF;
|
||||
using UniGLTF.M17N;
|
||||
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
@@ -57,5 +59,50 @@ namespace UniVRM10
|
||||
var mod = base.MeshIntegrateGui();
|
||||
return firstPerson || mod;
|
||||
}
|
||||
|
||||
protected override void DialogMessage()
|
||||
{
|
||||
EditorGUILayout.HelpBox(Message.MESH_UTILITY.Msg(), MessageType.Info);
|
||||
}
|
||||
enum Message
|
||||
{
|
||||
[LangMsg(Languages.ja, @"(VRM-1.0専用) 凍結 > 統合 > 分割 という一連の処理を実行します。
|
||||
|
||||
[凍結]
|
||||
- ヒエラルキーの 回転・拡縮を Mesh に焼き付けます。
|
||||
- BlendShape の現状を Mesh に焼き付けます。
|
||||
|
||||
- VRM-1.0 では正規化は必須でなくなりました。任意のオプションです。
|
||||
- VRM-1.0 でも拡縮の凍結は推奨しています。
|
||||
- HumanoidAvatar の再生成。
|
||||
- Expression, SpringBone, Constraint なども影響を受けます。
|
||||
|
||||
[統合]
|
||||
- ヒエラルキーに含まれる MeshRenderer と SkinnedMeshRenderer をひとつの SkinnedMeshRenderer に統合します。
|
||||
|
||||
- VRM の FirstPerson 設定に応じて3種類(BOTH, FirstPerson, ThirdPerson) にグループ化して統合します。
|
||||
- FirstPerson=AUTO を前処理できます。
|
||||
- 元の Mesh は ThirdPerson として処理されます。頭なしのモデルを追加生成して FirstPersonOnly とします。
|
||||
|
||||
[分割]
|
||||
- 統合結果を BlendShape の有無を基準に分割します。
|
||||
- BOTH, FirstPerson, ThirdPerson x 2 で、最大で 6Mesh になります。空の部分ができることが多いので 3Mesh くらいが多くなります。
|
||||
|
||||
[Scene と Prefab]
|
||||
Scene と Prefab で挙動が異なります。
|
||||
|
||||
(Scene/Runtime)
|
||||
- 対象のヒエラルキーを変更します。UNDO可能。
|
||||
- Asset の書き出しはしません。Unityを再起動すると、書き出していない Mesh などの Asset が消滅します。
|
||||
|
||||
(Prefab/Editor)
|
||||
- 対象の prefab をシーンにコピーして処理を実行し、生成する Asset を指定されたフォルダに書き出します。
|
||||
- Asset 書き出し後にコピーを削除します。
|
||||
- Undo はありません。
|
||||
")]
|
||||
[LangMsg(Languages.en, @"TODO
|
||||
")]
|
||||
MESH_UTILITY,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniGLTF.MeshUtility;
|
||||
using UniHumanoid;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
@@ -37,12 +39,12 @@ namespace UniVRM10
|
||||
return copy;
|
||||
}
|
||||
|
||||
protected override UniGLTF.MeshUtility.MeshIntegrationResult Integrate(List<GameObject> newGo,
|
||||
protected override
|
||||
(UniGLTF.MeshUtility.MeshIntegrationResult, GameObject[]) Integrate(
|
||||
GameObject empty,
|
||||
List<UniGLTF.MeshUtility.MeshIntegrationResult> results,
|
||||
UniGLTF.MeshUtility.MeshIntegrationGroup group)
|
||||
{
|
||||
var result = base.Integrate(newGo, empty, results, group);
|
||||
var (result, newList) = base.Integrate(empty, group);
|
||||
|
||||
if (_generateFirstPerson && group.Name == "auto")
|
||||
{
|
||||
@@ -57,14 +59,14 @@ namespace UniVRM10
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return (result, newList);
|
||||
}
|
||||
|
||||
Vrm10Instance _vrmInstance = null;
|
||||
/// <summary>
|
||||
/// glTF に比べて Humanoid や FirstPerson の処理が追加される
|
||||
/// </summary>
|
||||
public override List<GameObject> Process(GameObject go)
|
||||
public override (List<MeshIntegrationResult>, List<GameObject>) Process(GameObject go)
|
||||
{
|
||||
_vrmInstance = go.GetComponent<Vrm10Instance>();
|
||||
if (_vrmInstance == null)
|
||||
@@ -85,7 +87,7 @@ namespace UniVRM10
|
||||
// TODO: update: spring
|
||||
// TODO: update: constraint
|
||||
// TODO: update: firstPerson offset
|
||||
var list = base.Process(go);
|
||||
var (list, newList) = base.Process(go);
|
||||
|
||||
if (FreezeBlendShape || FreezeRotation || FreezeScaling)
|
||||
{
|
||||
@@ -94,7 +96,7 @@ namespace UniVRM10
|
||||
animator.avatar = newAvatar;
|
||||
}
|
||||
|
||||
return list;
|
||||
return (list, newList);
|
||||
}
|
||||
|
||||
void _ProcessFirstPerson(Vrm10Instance vrmInstance, UniGLTF.MeshUtility.MeshInfo info)
|
||||
|
||||
Reference in New Issue
Block a user