Merge pull request #1876 from ousttrue/feature10/springbone_picker

[1.0] SpringBone の 設定 UI
This commit is contained in:
ousttrue
2022-10-25 16:33:10 +09:00
committed by GitHub
11 changed files with 280 additions and 340 deletions

View File

@@ -16,30 +16,6 @@ namespace VRM
m_target = (VRMSpringBoneColliderGroup)target;
}
private void OnSceneGUI()
{
Undo.RecordObject(m_target, "VRMSpringBoneColliderGroupEditor");
Handles.matrix = m_target.transform.localToWorldMatrix;
Gizmos.color = Color.green;
bool changed = false;
foreach (var x in m_target.Colliders)
{
var offset = Handles.PositionHandle(x.Offset, Quaternion.identity);
if (offset != x.Offset)
{
changed = true;
x.Offset = offset;
}
}
if (changed)
{
EditorUtility.SetDirty(m_target);
}
}
override public void OnInspectorGUI()
{

View File

@@ -1,54 +0,0 @@
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
namespace UniVRM10
{
/// <summary>
/// TreeView でアクティブな SpringBone, ColliderGroup を管理して、
/// アクティブな SpringBone と ColliderGroup を SceneHandle で Edit する。
/// </summary>
public static class SpringBoneEditor
{
static SpringBoneTreeView s_treeView;
static SpringBoneTreeView GetTree(Vrm10Instance target, SerializedObject so)
{
if (s_treeView == null || s_treeView.Target != target)
{
var state = new TreeViewState();
s_treeView = new SpringBoneTreeView(state, target, so);
s_treeView.Reload();
}
return s_treeView;
}
public static void Disable()
{
s_treeView = null;
}
/// <summary>
/// 2D の GUI 描画
/// </summary>
public static void Draw2D(Vrm10Instance target, SerializedObject so)
{
var tree = GetTree(target, so);
if (GUILayout.Button("Reload"))
{
Disable();
return;
}
tree.Draw2D();
}
/// <summary>
/// 3D の Handle 描画
/// </summary>
public static void Draw3D(Vrm10Instance target, SerializedObject so)
{
var tree = GetTree(target, so);
tree.Draw3D();
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 33ca35799b1168949bb4466aae13dfd4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,8 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEditorInternal;
using UnityEngine;
namespace UniVRM10
@@ -37,24 +35,33 @@ namespace UniVRM10
// load
_map = new Dictionary<int, object>();
for (var i = 0; i < target.SpringBone.ColliderGroups.Count; ++i)
if (target != null)
{
var colliderGroup = target.SpringBone.ColliderGroups[i];
var name = colliderGroup.GUIName(i);
var id = _nextNodeID++;
var item = new TreeViewItem(id, 2, name);
_map.Add(id, colliderGroup);
_colliderGroups.AddChild(item);
}
if (target.SpringBone?.ColliderGroups != null)
{
for (var i = 0; i < target.SpringBone.ColliderGroups.Count; ++i)
{
var colliderGroup = target.SpringBone.ColliderGroups[i];
var name = colliderGroup.GUIName(i);
var id = _nextNodeID++;
var item = new TreeViewItem(id, 2, name);
_map.Add(id, colliderGroup);
_colliderGroups.AddChild(item);
}
}
for (var i = 0; i < target.SpringBone.Springs.Count; ++i)
{
var spring = target.SpringBone.Springs[i];
var name = spring.GUIName(i);
var id = _nextNodeID++;
var item = new TreeViewItem(id, 2, name);
_map.Add(id, spring);
_springs.AddChild(item);
if (target.SpringBone?.Springs != null)
{
for (var i = 0; i < target.SpringBone.Springs.Count; ++i)
{
var spring = target.SpringBone.Springs[i];
var name = spring.GUIName(i);
var id = _nextNodeID++;
var item = new TreeViewItem(id, 2, name);
_map.Add(id, spring);
_springs.AddChild(item);
}
}
}
}
@@ -107,16 +114,191 @@ namespace UniVRM10
}
}
public void Draw3D()
Color s_selectedColor = new Color(0.5f, 0, 0, 0.5f);
Color s_hoverColor = new Color(0.5f, 0.5f, 0, 0.5f);
Color s_Color = new Color(0.6f, 0.6f, 0.6f, 0.5f);
Color GetColor(MonoBehaviour target)
{
if (target == Active)
{
return s_selectedColor;
}
if (target == Hover)
{
return s_hoverColor;
}
return s_Color;
}
Vrm10InstanceSpringBone last = null;
MonoBehaviour Hover = null;
public MonoBehaviour Active = null;
public bool Draw3D(Vrm10InstanceSpringBone springBone)
{
var lastActive = Active;
var lastHover = Hover;
if (springBone != last)
{
Active = null;
Hover = null;
last = springBone;
}
if (_selected is SelectedColliderGroupGUI colliderGroup)
{
colliderGroup.Draw3D();
// colliderGroup.Draw3D();
}
else if (_selected is SelectedSpringGUI spring)
{
// TODO
}
MonoBehaviour newActive = null;
//
// collider
//
foreach (var group in springBone.ColliderGroups)
{
foreach (var collider in group.Colliders)
{
var color = GetColor(collider);
var (isActive, isHover) = DrawCollider(collider, color);
if (isActive)
{
newActive = collider;
}
if (isHover)
{
Hover = collider;
}
}
}
//
// joint
//
foreach (var spring in springBone.Springs)
{
VRM10SpringBoneJoint head = null;
for (int i = 0; i < spring.Joints.Count; ++i)
{
var joint = spring.Joints[i];
var color = GetColor(joint);
var (isActive, isHover) = DrawJoint(joint, color, head);
if (isActive)
{
newActive = joint;
}
if (isHover)
{
Hover = joint;
}
head = joint;
}
}
if (Active is VRM10SpringBoneCollider activeCollider)
{
// Active な collider の移動ハンドル(Offset, Tail)
EditorGUI.BeginChangeCheck();
Handles.matrix = activeCollider.transform.localToWorldMatrix;
var offset = Handles.PositionHandle(activeCollider.Offset, Quaternion.identity);
var tail = Vector3.zero;
if (activeCollider.ColliderType == VRM10SpringBoneColliderTypes.Capsule)
{
tail = Handles.PositionHandle(activeCollider.Tail, Quaternion.identity);
}
var offsetChanged = EditorGUI.EndChangeCheck();
if (offsetChanged)
{
// apply
Undo.RecordObject(activeCollider, "activeCollider");
activeCollider.Offset = offset;
if (activeCollider.ColliderType == VRM10SpringBoneColliderTypes.Capsule)
{
activeCollider.Tail = tail;
}
}
Handles.matrix = Matrix4x4.identity;
}
if (newActive != null)
{
Selection.activeObject = newActive.gameObject;
Active = newActive;
}
if (Hover != null)
{
Handles.Label(Hover.transform.position, Hover.name);
}
return lastActive != Active || lastHover != Hover;
}
static bool IsLeftDown()
{
return Event.current.type == EventType.MouseDown && Event.current.button == 0;
}
(bool IsActive, bool IsHover) DrawCollider(VRM10SpringBoneCollider collider, Color color)
{
if (Event.current.type == EventType.Repaint || Event.current.type == EventType.Layout)
{
Handles.matrix = collider.transform.localToWorldMatrix;
Handles.color = color;
switch (collider.ColliderType)
{
case VRM10SpringBoneColliderTypes.Sphere:
// Handles.color = Color.magenta;
Handles.SphereHandleCap(collider.GetInstanceID(), collider.Offset, Quaternion.identity, collider.Radius * 2, Event.current.type);
break;
case VRM10SpringBoneColliderTypes.Capsule:
// Handles.color = Color.cyan;
Handles.SphereHandleCap(collider.GetInstanceID(), collider.Offset, Quaternion.identity, collider.Radius * 2, Event.current.type);
Handles.SphereHandleCap(collider.GetInstanceID(), collider.Tail, Quaternion.identity, collider.Radius * 2, Event.current.type);
Handles.DrawLine(collider.Offset, collider.Tail);
break;
}
Handles.matrix = Matrix4x4.identity;
}
var isHover = HandleUtility.nearestControl == collider.GetInstanceID();
return (IsLeftDown() && isHover, isHover);
}
(bool IsActive, bool IsHover) DrawJoint(VRM10SpringBoneJoint joint, Color color, VRM10SpringBoneJoint head)
{
if (Event.current.type == EventType.Repaint || Event.current.type == EventType.Layout)
{
if (head == null)
{
// 先頭
Handles.color = color;
Handles.CubeHandleCap(joint.GetInstanceID(), joint.transform.position, joint.transform.rotation,
// head の radius
joint.m_jointRadius, Event.current.type);
}
else
{
// line
Handles.color = Color.green;
Handles.DrawLine(joint.transform.position, head.transform.position);
// joint
Handles.color = color;
Handles.SphereHandleCap(joint.GetInstanceID(), joint.transform.position, joint.transform.rotation,
// head の radius
head.m_jointRadius * 2, Event.current.type);
}
}
var isHover = HandleUtility.nearestControl == joint.GetInstanceID();
return (IsLeftDown() && isHover, isHover);
}
}
}

View File

@@ -6,17 +6,12 @@ namespace UniVRM10
[CustomEditor(typeof(VRM10SpringBoneCollider))]
class VRM10SpringBoneColliderEditor : Editor
{
VRM10SpringBoneCollider _target;
void OnEnable()
{
_target = target as VRM10SpringBoneCollider;
}
public override void OnInspectorGUI()
{
if (_target.GetInstanceID() == VRM10SpringBoneCollider.SelectedGuid)
if (VRM10Window.Active == target)
{
GUI.backgroundColor = Color.red;
GUI.backgroundColor = Color.cyan;
Repaint();
}
base.OnInspectorGUI();
}

View File

@@ -10,13 +10,15 @@ namespace UniVRM10
/// </summary>
public class VRM10Window : EditorWindow
{
const string WINDOW_TITLE = "VRM1 Window";
public static void Open()
public const string WINDOW_TITLE = "VRM1.0 Model Editor";
public static VRM10Window Open()
{
var window = (VRM10Window)GetWindow(typeof(VRM10Window));
window.titleContent = new GUIContent(WINDOW_TITLE);
window.Show();
window.Root = UnityEditor.Selection.activeTransform?.GetComponent<Vrm10Instance>();
return window;
}
void OnEnable()
@@ -30,7 +32,7 @@ namespace UniVRM10
void OnDisable()
{
SpringBoneEditor.Disable();
s_treeView = null;
SceneView.duringSceneGui -= OnSceneGUI;
// Debug.Log("OnDisable");
@@ -59,64 +61,28 @@ namespace UniVRM10
}
m_root = id;
m_so = value != null ? new SerializedObject(value) : null;
m_constraints = null;
if (Root != null)
{
var animator = Root.GetComponent<Animator>();
m_head = animator.GetBoneTransform(HumanBodyBones.Head);
}
}
}
Transform m_head;
public IVrm10Constraint[] m_constraints;
ScrollView m_scrollView = new ScrollView();
enum VRMSceneUI
{
Constraints,
SpringBone,
}
static VRMSceneUI s_ui = default;
static string[] s_selection;
static string[] Selection
{
get
{
if (s_selection == null)
{
s_selection = Enum.GetNames(typeof(VRMSceneUI));
}
return s_selection;
}
}
/// <summary>
/// public entry point
/// Scene 上の 3D 表示
///
/// * Joint/Collider の Picker
///
/// </summary>
/// <param name="target"></param>
void OnSceneGUI(SceneView sceneView)
{
switch (s_ui)
{
case VRMSceneUI.Constraints:
Tools.hidden = false;
break;
case VRMSceneUI.SpringBone:
Tools.hidden = true;
SpringBoneEditor.Draw3D(Root, m_so);
break;
default:
throw new NotImplementedException();
}
Tools.hidden = true;
Draw3D(Root, m_so);
}
//
/// <summary>
/// Window 上の GUI
///
/// * 対象 VRM の保持
/// * 選択 Joint/Collider の表示
///
/// </summary>
private void OnGUI()
{
if (Root == null)
@@ -131,65 +97,58 @@ namespace UniVRM10
}
}
Root = (Vrm10Instance)EditorGUILayout.ObjectField("vrm1", Root, typeof(Vrm10Instance), true);
// Root
Root = (Vrm10Instance)EditorGUILayout.ObjectField("Editing Model", Root, typeof(Vrm10Instance), true);
if (Root == null)
{
return;
}
var ui = (VRMSceneUI)GUILayout.SelectionGrid((int)s_ui, Selection, 3);
if (s_ui != ui)
{
s_ui = ui;
SceneView.RepaintAll();
}
// active
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.ObjectField("Selected Object", Active, typeof(MonoBehaviour), true);
EditorGUI.EndDisabledGroup();
if (m_so == null)
{
m_so = new SerializedObject(Root);
}
if (m_so == null)
{
return;
}
m_so.Update();
switch (s_ui)
{
case VRMSceneUI.Constraints:
m_scrollView.Draw(this.position.y, DrawConstraints, Repaint);
break;
case VRMSceneUI.SpringBone:
SpringBoneEditor.Draw2D(Root, m_so);
break;
default:
throw new NotImplementedException();
}
m_so.ApplyModifiedProperties();
// if (m_so == null)
// {
// m_so = new SerializedObject(Root);
// }
// if (m_so == null)
// {
// return;
// }
// m_so.Update();
// SpringBoneEditor.Draw2D(Root, m_so);
// m_so.ApplyModifiedProperties();
}
void DrawConstraints()
SpringBoneTreeView s_treeView;
SpringBoneTreeView GetTree(Vrm10Instance target, SerializedObject so)
{
if (Root != null)
if (s_treeView == null || s_treeView.Target != target)
{
if (m_constraints == null)
{
m_constraints = Root.GetComponentsInChildren<IVrm10Constraint>();
}
var state = new UnityEditor.IMGUI.Controls.TreeViewState();
s_treeView = new SpringBoneTreeView(state, target, so);
s_treeView.Reload();
}
return s_treeView;
}
using (new EditorGUI.DisabledScope(true))
public static MonoBehaviour Active;
/// <summary>
/// 3D の Handle 描画
/// </summary>
public void Draw3D(Vrm10Instance target, SerializedObject so)
{
var tree = GetTree(target, so);
if (tree != null && target != null)
{
if (m_constraints != null)
if (tree.Draw3D(target.SpringBone))
{
foreach (var c in m_constraints)
{
EditorGUILayout.ObjectField(c.ConstraintTarget, typeof(MonoBehaviour), true);
}
Repaint();
}
Active = tree.Active;
}
}
}

View File

@@ -1,86 +0,0 @@
using UnityEditor;
using UnityEditor.EditorTools;
using UnityEngine;
namespace UniVRM10
{
[EditorTool("vrm-1.0/SpringBoneCollider", typeof(UniVRM10.VRM10SpringBoneCollider))]
class VRM10SpringBoneColliderEditorTool : EditorTool
{
static GUIContent s_cachedIcon;
public override GUIContent toolbarIcon
{
get
{
if (s_cachedIcon == null)
{
s_cachedIcon = EditorGUIUtility.IconContent("d_editcollision_32", "|vrm-1.0 SpringBoneCollider");
}
return s_cachedIcon;
}
}
void OnEnable()
{
ToolManager.activeToolChanged += ActiveToolDidChange;
}
void OnDisable()
{
ToolManager.activeToolChanged -= ActiveToolDidChange;
}
void ActiveToolDidChange()
{
if (!ToolManager.IsActiveTool(this))
{
return;
}
}
public override void OnToolGUI(EditorWindow window)
{
if (Selection.activeTransform == null)
{
return;
}
var collider = Selection.activeTransform.GetComponent<VRM10SpringBoneCollider>();
if (collider == null)
{
return;
}
Handles.matrix = collider.transform.localToWorldMatrix;
{
Handles.Label(collider.Offset, "Head");
EditorGUI.BeginChangeCheck();
var offset = Handles.PositionHandle(collider.Offset, Quaternion.identity);
var offsetChanged = EditorGUI.EndChangeCheck();
if (offsetChanged)
{
Undo.RecordObject(collider, "SpringBoneCollider");
var delta = offset - collider.Offset;
collider.Offset = offset;
if (collider.ColliderType == VRM10SpringBoneColliderTypes.Capsule)
{
collider.Tail += delta;
}
}
if (collider.ColliderType == VRM10SpringBoneColliderTypes.Capsule)
{
Handles.Label(collider.Tail, "Tail");
EditorGUI.BeginChangeCheck();
var tail = Handles.PositionHandle(collider.Tail, Quaternion.identity);
var tailChanged = EditorGUI.EndChangeCheck();
if (tailChanged)
{
Undo.RecordObject(collider, "SpringBoneCollider");
collider.Tail = tail;
}
}
}
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: eb79df70485ab8548bdd417c7fcbe04a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -115,14 +115,8 @@ namespace UniVRM10
return loaded;
}
void Setup(Vrm10Instance instance)
void SetupVRM10Object(Vrm10Instance instance)
{
if (instance.Vrm != null)
{
// OK
return;
}
if (!CheckHumanoid(instance.gameObject))
{
// can not
@@ -162,9 +156,22 @@ namespace UniVRM10
public override void OnInspectorGUI()
{
if (target is Vrm10Instance instance)
{
Setup(instance);
if (instance.Vrm == null)
{
SetupVRM10Object(instance);
}
if (instance.Vrm != null)
{
EditorGUILayout.HelpBox("SpringBone gizmo etc...", MessageType.Info);
if (GUILayout.Button("Open " + VRM10Window.WINDOW_TITLE))
{
VRM10Window.Open();
}
}
}
base.OnInspectorGUI();

View File

@@ -38,7 +38,7 @@ namespace UniVRM10
break;
case VRM10SpringBoneColliderTypes.Capsule:
Gizmos.color = Color.cyan;
Gizmos.color = new Color(1.0f, 0.1f, 0.1f);
Gizmos.DrawWireSphere(Offset, Radius);
Gizmos.DrawWireSphere(Tail, Radius);
Gizmos.DrawLine(Offset, Tail);

View File

@@ -26,9 +26,6 @@ namespace UniVRM10
[SerializeField, Range(0, 0.5f), Header("Collision")]
public float m_jointRadius = 0.02f;
[SerializeField]
public Color m_gizmoColor = Color.green;
void AddJointRecursive(Transform t, VRM10SpringBoneJoint src)
{
var joint = t.gameObject.GetComponent<VRM10SpringBoneJoint>();
@@ -139,38 +136,24 @@ namespace UniVRM10
return;
}
Gizmos.color = m_gizmoColor;
// draw head
// Gizmos.DrawSphere(transform.position, m_jointRadius);
var (spring, joint_index) = FindTail(vrm, this);
if (spring == null)
{
return;
}
if (spring.Joints != null && joint_index + 1 < spring.Joints.Count)
{
var tail = spring.Joints[joint_index + 1];
if (tail != null)
{
// draw tail
Gizmos.DrawSphere(tail.transform.position, tail.m_jointRadius);
Gizmos.color = Color.yellow;
// tail の radius は head の m_jointRadius で決まる
Gizmos.DrawWireSphere(tail.transform.position, m_jointRadius);
Gizmos.DrawLine(transform.position, tail.transform.position);
}
}
foreach (var colliderGroup in spring.ColliderGroups)
{
foreach (var collider in colliderGroup.Colliders)
{
if (collider != null)
{
collider.OnDrawGizmosSelected();
}
}
}
}
}
}