mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-09-13 22:39:39 -05:00
Vrm10MeshUtilityDialog
This commit is contained in:
@@ -5,7 +5,6 @@ using UnityEngine;
|
||||
|
||||
namespace UniGLTF.MeshUtility
|
||||
{
|
||||
|
||||
public static class TabBar
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
8
Assets/VRM10/Editor/MeshUtility.meta
Normal file
8
Assets/VRM10/Editor/MeshUtility.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6fb2795d5ca5ce9449de94ea5fb94dde
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
158
Assets/VRM10/Editor/MeshUtility/MeshIntegrationAndSplit.cs
Normal file
158
Assets/VRM10/Editor/MeshUtility/MeshIntegrationAndSplit.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UniGLTF;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
class MeshIntegrationAndSplit
|
||||
{
|
||||
class MeshIntegrationGroup
|
||||
{
|
||||
public string Name;
|
||||
public List<Renderer> Renderers = new List<Renderer>();
|
||||
}
|
||||
List<MeshIntegrationGroup> _meshIntegrationList = new List<MeshIntegrationGroup>();
|
||||
List<Renderer> _renderers = new List<Renderer>();
|
||||
|
||||
Splitter _splitter;
|
||||
ReorderableList _groupList;
|
||||
ReorderableList _rendererList;
|
||||
int _selected = -1;
|
||||
int Selected
|
||||
{
|
||||
set
|
||||
{
|
||||
if (_selected == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (value < 0 || value >= _meshIntegrationList.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_selected = value;
|
||||
_renderers.Clear();
|
||||
_renderers.AddRange(_meshIntegrationList[_selected].Renderers);
|
||||
}
|
||||
}
|
||||
|
||||
public MeshIntegrationAndSplit(EditorWindow editor)
|
||||
{
|
||||
_splitter = new VerticalSplitter(editor, 50, 50);
|
||||
|
||||
_groupList = new ReorderableList(_meshIntegrationList, typeof(MeshIntegrationGroup));
|
||||
_groupList.drawHeaderCallback = (Rect rect) =>
|
||||
{
|
||||
GUI.Label(rect, "Integration group");
|
||||
};
|
||||
_groupList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
|
||||
{
|
||||
var group = _meshIntegrationList[index];
|
||||
EditorGUI.TextField(rect, group.Name);
|
||||
};
|
||||
_groupList.onSelectCallback = rl =>
|
||||
{
|
||||
Selected = (rl.selectedIndices.Count > 0) ? rl.selectedIndices[0] : -1;
|
||||
};
|
||||
|
||||
_rendererList = new ReorderableList(_renderers, typeof(Renderer));
|
||||
_rendererList.drawHeaderCallback = (Rect rect) =>
|
||||
{
|
||||
GUI.Label(rect, "Renderer");
|
||||
};
|
||||
_rendererList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
|
||||
{
|
||||
var r = _renderers[index];
|
||||
EditorGUI.ObjectField(rect, r, typeof(Renderer), true);
|
||||
};
|
||||
}
|
||||
|
||||
public void UpdateMeshIntegrationList(GameObject root)
|
||||
{
|
||||
_selected = -1;
|
||||
_meshIntegrationList.Clear();
|
||||
|
||||
IntegrateFirstPerson(root);
|
||||
Selected = 0;
|
||||
}
|
||||
|
||||
void IntegrateAll(GameObject root)
|
||||
{
|
||||
if (root == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_meshIntegrationList.Add(new MeshIntegrationGroup
|
||||
{
|
||||
Name = "ALL",
|
||||
Renderers = root.GetComponentsInChildren<Renderer>().ToList(),
|
||||
});
|
||||
}
|
||||
|
||||
MeshIntegrationGroup GetOrCreateGroup(string name)
|
||||
{
|
||||
foreach (var g in _meshIntegrationList)
|
||||
{
|
||||
if (g.Name == name)
|
||||
{
|
||||
return g;
|
||||
}
|
||||
}
|
||||
_meshIntegrationList.Add(new MeshIntegrationGroup
|
||||
{
|
||||
Name = name,
|
||||
});
|
||||
return _meshIntegrationList.Last();
|
||||
}
|
||||
|
||||
void IntegrateFirstPerson(GameObject root)
|
||||
{
|
||||
if (root == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var vrm1 = root.GetComponent<Vrm10Instance>();
|
||||
if (vrm1 == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var vrmObject = vrm1.Vrm;
|
||||
if (vrmObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var fp = vrmObject.FirstPerson;
|
||||
if (fp == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var a in fp.Renderers)
|
||||
{
|
||||
var g = GetOrCreateGroup(a.FirstPersonFlag.ToString());
|
||||
g.Renderers.Add(a.GetRenderer(root.transform));
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowGroup(Rect rect)
|
||||
{
|
||||
_groupList.DoList(rect);
|
||||
}
|
||||
|
||||
private void ShowSelected(Rect rect)
|
||||
{
|
||||
_rendererList.DoList(rect);
|
||||
}
|
||||
|
||||
public void OnGui(Rect rect)
|
||||
{
|
||||
_splitter.OnGUI(
|
||||
rect,
|
||||
ShowGroup,
|
||||
ShowSelected);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0dd9385e7eb3d8b468ca851b18d9846f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
183
Assets/VRM10/Editor/MeshUtility/Splitter.cs
Normal file
183
Assets/VRM10/Editor/MeshUtility/Splitter.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
[Serializable]
|
||||
public abstract class Splitter
|
||||
{
|
||||
internal enum SplitMode
|
||||
{
|
||||
Horizontal,
|
||||
Vertical,
|
||||
}
|
||||
|
||||
protected float _value;
|
||||
|
||||
private EditorWindow _window;
|
||||
private SplitMode _lockMode;
|
||||
private float _lockValues;
|
||||
private MouseCursor _mouseCursor;
|
||||
protected float _barSize;
|
||||
|
||||
private bool _isResize;
|
||||
private Rect _resizeRect;
|
||||
private float _ratio = 0.5f;
|
||||
|
||||
private static readonly Color SPLITTER_COLOR = new Color(0, 0, 0, 1.0f);
|
||||
private static readonly Color SPLITTER_COLOR_FREE = new Color(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
|
||||
internal Splitter(EditorWindow window, float defaultValue, SplitMode lockMode, float minValue, float barSize = 16f)
|
||||
{
|
||||
_window = window;
|
||||
_value = defaultValue;
|
||||
_lockMode = lockMode;
|
||||
_lockValues = minValue;
|
||||
_mouseCursor = lockMode == SplitMode.Vertical ? MouseCursor.ResizeHorizontal : MouseCursor.ResizeVertical;
|
||||
_barSize = barSize;
|
||||
}
|
||||
|
||||
protected abstract RectOffset RectOffset();
|
||||
|
||||
protected abstract Rect MainRect(Rect rect);
|
||||
|
||||
protected abstract Rect SubRect(Rect rect);
|
||||
|
||||
protected abstract Rect BarRect(Rect rect);
|
||||
|
||||
public void OnGUI(Rect rect, Action<Rect> mainDelegate, Action<Rect> subDelegate)
|
||||
{
|
||||
var current = Event.current;
|
||||
|
||||
mainDelegate(MainRect(rect));
|
||||
subDelegate(SubRect(rect));
|
||||
|
||||
_resizeRect = BarRect(rect);
|
||||
EditorGUIUtility.AddCursorRect(_resizeRect, _mouseCursor);
|
||||
|
||||
var clampMax = _lockMode == SplitMode.Vertical ? rect.width - _lockValues : rect.height - _lockValues;
|
||||
var targetSplitterValue = _lockMode == SplitMode.Vertical ? rect.width : rect.height;
|
||||
|
||||
_ratio = _lockMode == SplitMode.Vertical ? _value / rect.width : _value / rect.height;
|
||||
|
||||
if (current.type == EventType.MouseDown && _resizeRect.Contains(current.mousePosition))
|
||||
_isResize = true;
|
||||
if (current.type == EventType.MouseUp)
|
||||
_isResize = false;
|
||||
|
||||
if (_isResize && current.type == EventType.MouseDrag)
|
||||
{
|
||||
var targetValue = _lockMode == SplitMode.Vertical
|
||||
? current.mousePosition.x
|
||||
: current.mousePosition.y;
|
||||
var diffValue = _lockMode == SplitMode.Vertical ? rect.width : rect.height;
|
||||
_ratio = targetValue / diffValue;
|
||||
}
|
||||
else if (current.type != EventType.Layout && Event.current.type != EventType.Used)
|
||||
{
|
||||
_ratio = targetSplitterValue * _ratio / targetSplitterValue;
|
||||
}
|
||||
|
||||
_value = Mathf.Clamp(targetSplitterValue * _ratio, _lockValues, clampMax);
|
||||
|
||||
EditorGUI.DrawRect(RectOffset().Remove(_resizeRect), SPLITTER_COLOR_FREE);
|
||||
|
||||
if (_isResize)
|
||||
_window.Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class HorizontalSplitter : Splitter
|
||||
{
|
||||
private static readonly RectOffset RECT_OFFSET = new RectOffset(0, 0, 7, 8);
|
||||
|
||||
public HorizontalSplitter(EditorWindow window, float defaultValue, float minValue, float barSize = 16)
|
||||
: base(window, defaultValue, SplitMode.Horizontal, minValue, barSize)
|
||||
{
|
||||
}
|
||||
|
||||
protected override RectOffset RectOffset()
|
||||
{
|
||||
return RECT_OFFSET;
|
||||
}
|
||||
|
||||
protected override Rect MainRect(Rect rect)
|
||||
{
|
||||
return new Rect(rect)
|
||||
{
|
||||
x = 0,
|
||||
y = 0,
|
||||
height = _value,
|
||||
};
|
||||
}
|
||||
|
||||
protected override Rect SubRect(Rect rect)
|
||||
{
|
||||
return new Rect(rect)
|
||||
{
|
||||
x = 0,
|
||||
y = _value,
|
||||
height = rect.height - _value,
|
||||
};
|
||||
}
|
||||
|
||||
protected override Rect BarRect(Rect rect)
|
||||
{
|
||||
return new Rect(rect)
|
||||
{
|
||||
x = 0,
|
||||
y = _value - _barSize / 2,
|
||||
height = _barSize,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class VerticalSplitter : Splitter
|
||||
{
|
||||
private static readonly RectOffset RECT_OFFSET = new RectOffset(0, 0, 7, 8);
|
||||
|
||||
public VerticalSplitter(EditorWindow window, float defaultValue, float minValue, float barSize = 4)
|
||||
: base(window, defaultValue, SplitMode.Vertical, minValue, barSize)
|
||||
{
|
||||
}
|
||||
|
||||
protected override RectOffset RectOffset()
|
||||
{
|
||||
return RECT_OFFSET;
|
||||
}
|
||||
|
||||
protected override Rect MainRect(Rect rect)
|
||||
{
|
||||
return new Rect(rect)
|
||||
{
|
||||
x = rect.x + 0,
|
||||
y = rect.y + 0,
|
||||
width = _value,
|
||||
};
|
||||
}
|
||||
|
||||
protected override Rect SubRect(Rect rect)
|
||||
{
|
||||
return new Rect(rect)
|
||||
{
|
||||
x = rect.x + _value,
|
||||
y = rect.y + 0,
|
||||
width = rect.width - _value,
|
||||
};
|
||||
}
|
||||
|
||||
protected override Rect BarRect(Rect rect)
|
||||
{
|
||||
return new Rect(rect)
|
||||
{
|
||||
x = rect.x + _value - _barSize / 2,
|
||||
y = rect.y + 0,
|
||||
width = _barSize,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/VRM10/Editor/MeshUtility/Splitter.cs.meta
Normal file
11
Assets/VRM10/Editor/MeshUtility/Splitter.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 385274edf555ed84a9f3706ca2a99023
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
144
Assets/VRM10/Editor/MeshUtility/Vrm10MeshUtilityDialog.cs
Normal file
144
Assets/VRM10/Editor/MeshUtility/Vrm10MeshUtilityDialog.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UniGLTF.M17N;
|
||||
using System.Collections.Generic;
|
||||
using UniGLTF.MeshUtility;
|
||||
using UniGLTF;
|
||||
using System.Linq;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
public class Vrm10MeshUtilityDialog : EditorWindow
|
||||
{
|
||||
const string TITLE = "Vrm10 Mesh Utility Window";
|
||||
|
||||
enum Tabs
|
||||
{
|
||||
MeshFreeze,
|
||||
MeshIntegration,
|
||||
}
|
||||
Tabs _tab;
|
||||
|
||||
public static void OpenWindow()
|
||||
{
|
||||
var window =
|
||||
(Vrm10MeshUtilityDialog)EditorWindow.GetWindow(typeof(Vrm10MeshUtilityDialog));
|
||||
window.titleContent = new GUIContent(TITLE);
|
||||
window.Show();
|
||||
}
|
||||
|
||||
private void Validate()
|
||||
{
|
||||
_validations.Clear();
|
||||
if (_exportTarget == null)
|
||||
{
|
||||
_validations.Add(Validation.Error("set vrm1"));
|
||||
return;
|
||||
}
|
||||
if (_exportTarget.GetComponent<Vrm10Instance>() == null)
|
||||
{
|
||||
_validations.Add(Validation.Error("target is not vrm1"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
bool IsValid => !_validations.Any(v => !v.CanExport);
|
||||
|
||||
Vector2 _scrollPos;
|
||||
List<Validation> _validations = new List<Validation>();
|
||||
GameObject _exportTarget;
|
||||
|
||||
MeshIntegrationAndSplit _meshIntegration;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
_meshIntegration = new MeshIntegrationAndSplit(this);
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
var modified = false;
|
||||
_scrollPos = EditorGUILayout.BeginScrollView(_scrollPos);
|
||||
EditorGUIUtility.labelWidth = 200;
|
||||
LanguageGetter.OnGuiSelectLang();
|
||||
var exportTarget = (GameObject)EditorGUILayout.ObjectField(
|
||||
MeshUtilityMessages.TARGET_OBJECT.Msg(),
|
||||
_exportTarget, typeof(GameObject), true);
|
||||
if (exportTarget != _exportTarget)
|
||||
{
|
||||
_exportTarget = exportTarget;
|
||||
_meshIntegration.UpdateMeshIntegrationList(_exportTarget);
|
||||
modified = true;
|
||||
}
|
||||
_tab = TabBar.OnGUI(_tab, "LargeButton", GUI.ToolbarButtonSize.Fixed);
|
||||
|
||||
foreach (var validation in _validations)
|
||||
{
|
||||
validation.DrawGUI();
|
||||
}
|
||||
|
||||
switch (_tab)
|
||||
{
|
||||
case Tabs.MeshFreeze:
|
||||
{
|
||||
if (MeshBakeGui())
|
||||
{
|
||||
modified = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Tabs.MeshIntegration:
|
||||
{
|
||||
if (MeshIntegrateGui())
|
||||
{
|
||||
modified = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndScrollView();
|
||||
|
||||
if (modified)
|
||||
{
|
||||
Validate();
|
||||
}
|
||||
|
||||
GUI.enabled = IsValid;
|
||||
var pressed = GUILayout.Button("Process", GUILayout.MinWidth(100));
|
||||
GUI.enabled = true;
|
||||
if (pressed)
|
||||
{
|
||||
// Show Result ?
|
||||
// Close();
|
||||
// GUIUtility.ExitGUI();
|
||||
Debug.Log("Process !");
|
||||
}
|
||||
}
|
||||
|
||||
bool MeshBakeGui()
|
||||
{
|
||||
EditorGUILayout.Toggle("BlendShape", false);
|
||||
EditorGUILayout.Toggle("Scale", false);
|
||||
EditorGUILayout.Toggle("Rotation", false);
|
||||
return default;
|
||||
}
|
||||
|
||||
bool MeshIntegrateGui()
|
||||
{
|
||||
EditorGUILayout.Toggle("FirstPerson == AUTO の生成", false);
|
||||
EditorGUILayout.Toggle("Separate by BlendShape", false);
|
||||
var p = position;
|
||||
var last = GUILayoutUtility.GetLastRect();
|
||||
var y = last.y + last.height;
|
||||
var rect = new Rect
|
||||
{
|
||||
x = last.x,
|
||||
y = y,
|
||||
width = p.width,
|
||||
height = p.height - y - 30
|
||||
};
|
||||
_meshIntegration.OnGui(rect);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f3af8a1b265d8a4da7ad8c1c29d2585
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,3 +1,4 @@
|
||||
using UniGLTF.MeshUtility;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UniVRM10
|
||||
@@ -11,6 +12,9 @@ namespace UniVRM10
|
||||
[MenuItem(UserMenuPrefix + "/Export VRM-1.0", priority = 1)]
|
||||
private static void OpenExportDialog() => VRM10ExportDialog.Open();
|
||||
|
||||
[MenuItem(UserMenuPrefix + "/MeshUtility", priority = 2)]
|
||||
private static void OpenMeshUtility() => Vrm10MeshUtilityDialog.OpenWindow();
|
||||
|
||||
[MenuItem(ExperimentalMenuPrefix + "/Convert BVH to VRM-Animation", priority = 100)]
|
||||
private static void ConvertVrmAnimation() => VrmAnimationMenu.BvhToVrmAnimationMenu();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user