VRM10RotationConstraintEditor

This commit is contained in:
ousttrue 2021-04-27 18:13:38 +09:00
parent 20431dad6d
commit d8836717cb
11 changed files with 297 additions and 103 deletions

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7b369ee7eb21d1047bb03e139faa7eda
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,90 @@
using System.Text;
using UniGLTF.Extensions.VRMC_node_constraint;
using UnityEditor;
using UnityEngine;
namespace UniVRM10
{
[CustomEditor(typeof(VRM10RotationConstraint))]
public class VRM10RotationConstraintEditor : Editor
{
VRM10RotationConstraint m_target;
void OnEnable()
{
m_target = (VRM10RotationConstraint)target;
}
void DrawSrcModel()
{
var model = m_target.ModelRoot;
var src = m_target.Source;
if (model == null)
{
Handles.Label(src.position, "ModelRoot required");
return;
}
const float size = 0.05f;
Handles.color = Color.red;
Handles.DrawLine(src.position, src.position + model.right * size);
Handles.color = Color.green;
Handles.DrawLine(src.position, src.position + model.up * size);
Handles.color = Color.black;
Handles.DrawLine(src.position, src.position + model.forward * size);
}
void DrawSrcLocal()
{
// init
// current
// delta
}
private GUIStyle _style;
public void OnSceneGUI()
{
if (m_target.Source == null)
{
return;
}
// this to target line
Handles.color = Color.yellow;
var rot = Quaternion.LookRotation(m_target.Source.position - m_target.transform.position, Vector3.up);
var len = (m_target.Source.position - m_target.transform.position).magnitude;
Handles.ArrowHandleCap(0, m_target.transform.position, rot, len, EventType.Repaint);
// show delta
if (_style == null)
{
_style = new GUIStyle("box");
}
var euler = m_target.Delta.eulerAngles;
var sb = new StringBuilder();
sb.AppendLine(m_target.SourceCoordinate.ToString());
sb.AppendLine(m_target.FreezeAxes.HasFlag(AxisMask.X) ? $"{euler.x:0.} => 0" : $"{euler.x:0.}");
sb.AppendLine(m_target.FreezeAxes.HasFlag(AxisMask.Y) ? $"{euler.y:0.} => 0" : $"{euler.y:0.}");
sb.AppendLine(m_target.FreezeAxes.HasFlag(AxisMask.Z) ? $"{euler.z:0.} => 0" : $"{euler.z:0.}");
Handles.Label(m_target.Source.position, sb.ToString(), _style);
switch (m_target.SourceCoordinate)
{
case ObjectSpace.model:
DrawSrcModel();
break;
case ObjectSpace.local:
DrawSrcLocal();
break;
default:
throw new System.NotImplementedException();
}
}
}
}

View File

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

View File

@ -0,0 +1,64 @@
using System;
using UnityEngine;
using UnityEditor;
namespace UniVRM10
{
/// <summary>
/// Flag設定したEnumのインスペクター表示を変えるクラス
/// </summary>
[CustomPropertyDrawer(typeof(EnumFlagsAttribute))]
public sealed class EnumFlagsAttributeDrawer : PropertyDrawer
{
public override void OnGUI(
Rect position,
SerializedProperty prop,
GUIContent label
)
{
var buttonsIntValue = 0;
var enumLength = prop.enumNames.Length;
var labelWidth = EditorGUIUtility.labelWidth;
var buttonPressed = new bool[enumLength];
var buttonWidth = (position.width - labelWidth) / enumLength;
var labelPos = new Rect(
position.x,
position.y,
labelWidth,
position.height
);
EditorGUI.LabelField(labelPos, label);
EditorGUI.BeginChangeCheck();
for (int i = 0; i < enumLength; i++)
{
buttonPressed[i] = (prop.intValue & (1 << i)) == 1 << i;
var buttonPos = new Rect(
position.x + labelWidth + buttonWidth * i,
position.y,
buttonWidth,
position.height
);
buttonPressed[i] = GUI.Toggle(
buttonPos,
buttonPressed[i],
prop.enumNames[i],
"Button"
);
if (buttonPressed[i])
{
buttonsIntValue += 1 << i;
}
}
if (EditorGUI.EndChangeCheck())
{
prop.intValue = buttonsIntValue;
}
}
}
}

View File

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

View File

@ -7,49 +7,39 @@ namespace UniVRM10
class ConstraintDestination
{
readonly Transform m_transform;
readonly ObjectSpace m_coords;
readonly TRS m_modelInitial;
readonly TRS m_localInitial;
public readonly Transform ModelRoot;
readonly TRS m_initial;
public ConstraintDestination(Transform t, ObjectSpace coords, Transform modelRoot = null)
public ConstraintDestination(Transform t, Transform modelRoot = null)
{
ModelRoot = modelRoot;
m_transform = t;
m_coords = coords;
switch (m_coords)
{
// case ObjectSpace.World:
// m_initial = TRS.GetWorld(t);
// break;
case ObjectSpace.local:
m_initial = TRS.GetLocal(t);
break;
case ObjectSpace.model:
m_initial = TRS.GetRelative(t, modelRoot.worldToLocalMatrix);
break;
default:
throw new NotImplementedException();
}
m_localInitial = TRS.GetLocal(t);
m_modelInitial = TRS.GetRelative(t, modelRoot.worldToLocalMatrix);
}
public void ApplyTranslation(Vector3 delta, float weight, Transform modelRoot = null)
public void ApplyTranslation(Vector3 delta, float weight, ObjectSpace coords, Transform modelRoot = null)
{
var value = m_initial.Translation + delta * weight;
switch (m_coords)
switch (coords)
{
// case DestinationCoordinates.World:
// m_transform.position = value;
// break;
case ObjectSpace.local:
m_transform.localPosition = value;
{
var value = m_localInitial.Translation + delta * weight;
m_transform.localPosition = value;
}
break;
case ObjectSpace.model:
m_transform.position = modelRoot.localToWorldMatrix.MultiplyPoint(value);
{
var value = m_modelInitial.Translation + delta * weight;
m_transform.position = modelRoot.localToWorldMatrix.MultiplyPoint(value);
}
break;
default:
@ -57,22 +47,27 @@ namespace UniVRM10
}
}
public void ApplyRotation(Quaternion delta, float weight, Transform modelRoot = null)
public void ApplyRotation(Quaternion delta, float weight, ObjectSpace coords, Transform modelRoot = null)
{
// 0~1 で clamp しない slerp
var value = Quaternion.LerpUnclamped(Quaternion.identity, delta, weight) * m_initial.Rotation;
switch (m_coords)
switch (coords)
{
// case DestinationCoordinates.World:
// m_transform.rotation = value;
// break;
case ObjectSpace.local:
m_transform.localRotation = value;
{
var value = Quaternion.LerpUnclamped(Quaternion.identity, delta, weight) * m_localInitial.Rotation;
m_transform.localRotation = value;
}
break;
case ObjectSpace.model:
m_transform.rotation = modelRoot.rotation * value;
{
var value = Quaternion.LerpUnclamped(Quaternion.identity, delta, weight) * m_modelInitial.Rotation;
m_transform.rotation = modelRoot.rotation * value;
}
break;
default:

View File

@ -6,72 +6,50 @@ namespace UniVRM10
{
class ConstraintSource
{
readonly Transform m_modelRoot;
public readonly Transform ModelRoot;
readonly Transform m_transform;
readonly TRS m_modelInitial;
readonly TRS m_localInitial;
readonly ObjectSpace m_coords;
readonly TRS m_initial;
public Vector3 TranslationDelta
public Vector3 TranslationDelta(ObjectSpace coords)
{
get
{
switch (m_coords)
{
// case ObjectSpace.World: return m_transform.position - m_initial.Translation;
case ObjectSpace.local: return m_transform.localPosition - m_initial.Translation;
case ObjectSpace.model: return m_modelRoot.worldToLocalMatrix.MultiplyPoint(m_transform.position) - m_initial.Translation;
default: throw new NotImplementedException();
}
}
}
public Quaternion RotationDelta
{
get
{
switch (m_coords)
{
// 右からかけるか、左からかけるか、それが問題なのだ
// case SourceCoordinates.World: return m_transform.rotation * Quaternion.Inverse(m_initial.Rotation);
case ObjectSpace.local: return m_transform.localRotation * Quaternion.Inverse(m_initial.Rotation);
case ObjectSpace.model: return m_transform.rotation * Quaternion.Inverse(m_modelRoot.rotation) * Quaternion.Inverse(m_initial.Rotation);
default: throw new NotImplementedException();
}
}
}
public ConstraintSource(Transform t, ObjectSpace coords, Transform modelRoot = null)
{
m_transform = t;
m_coords = coords;
switch (coords)
{
// case SourceCoordinates.World:
// m_initial = TRS.GetWorld(t);
// break;
// case ObjectSpace.World: return m_transform.position - m_initial.Translation;
case ObjectSpace.local: return m_transform.localPosition - m_localInitial.Translation;
case ObjectSpace.model: return ModelRoot.worldToLocalMatrix.MultiplyPoint(m_transform.position) - m_modelInitial.Translation;
default: throw new NotImplementedException();
}
}
case ObjectSpace.local:
m_initial = TRS.GetLocal(t);
break;
public Quaternion RotationDelta(ObjectSpace coords)
{
switch (coords)
{
// 右からかけるか、左からかけるか、それが問題なのだ
// case SourceCoordinates.World: return m_transform.rotation * Quaternion.Inverse(m_initial.Rotation);
case ObjectSpace.local: return m_transform.localRotation * Quaternion.Inverse(m_localInitial.Rotation);
case ObjectSpace.model: return m_transform.rotation * Quaternion.Inverse(ModelRoot.rotation) * Quaternion.Inverse(m_modelInitial.Rotation);
default: throw new NotImplementedException();
}
}
case ObjectSpace.model:
{
var world = TRS.GetWorld(t);
m_modelRoot = modelRoot;
m_initial = new TRS
{
Translation = modelRoot.worldToLocalMatrix.MultiplyPoint(world.Translation),
Rotation = world.Rotation * Quaternion.Inverse(m_modelRoot.rotation),
};
}
break;
public ConstraintSource(Transform t, Transform modelRoot = null)
{
m_transform = t;
default:
throw new NotImplementedException();
{
m_localInitial = TRS.GetLocal(t);
}
{
var world = TRS.GetWorld(t);
ModelRoot = modelRoot;
m_modelInitial = new TRS
{
Translation = modelRoot.worldToLocalMatrix.MultiplyPoint(world.Translation),
Rotation = world.Rotation * Quaternion.Inverse(ModelRoot.rotation),
};
}
}
}

View File

@ -38,8 +38,14 @@ namespace UniVRM10
void OnValidate()
{
// Debug.Log("Validate");
m_src = null;
m_dst = null;
if (m_src != null && m_src.ModelRoot != ModelRoot)
{
m_src = null;
}
if (m_dst != null && m_dst.ModelRoot != ModelRoot)
{
m_dst = null;
}
}
void Reset()
@ -62,15 +68,15 @@ namespace UniVRM10
if (m_src == null)
{
m_src = new ConstraintSource(Source, SourceCoordinate, ModelRoot);
m_src = new ConstraintSource(Source, ModelRoot);
}
if (m_dst == null)
{
m_dst = new ConstraintDestination(transform, DestinationCoordinate, ModelRoot);
m_dst = new ConstraintDestination(transform, ModelRoot);
}
var delta = FreezeAxes.Freeze(m_src.TranslationDelta);
m_dst.ApplyTranslation(delta, Weight, ModelRoot);
var delta = FreezeAxes.Freeze(m_src.TranslationDelta(SourceCoordinate));
m_dst.ApplyTranslation(delta, Weight, DestinationCoordinate, ModelRoot);
}
}
}

View File

@ -23,6 +23,7 @@ namespace UniVRM10
public ObjectSpace DestinationCoordinate = default;
[SerializeField]
[EnumFlags]
public AxisMask FreezeAxes = default;
[SerializeField]
@ -33,6 +34,11 @@ namespace UniVRM10
public Transform ModelRoot = default;
ConstraintSource m_src;
public Quaternion Delta
{
get;
private set;
}
ConstraintDestination m_dst;
@ -42,8 +48,14 @@ namespace UniVRM10
void OnValidate()
{
// Debug.Log("Validate");
m_src = null;
m_dst = null;
if (m_src != null && m_src.ModelRoot != ModelRoot)
{
m_src = null;
}
if (m_dst != null && m_dst.ModelRoot != ModelRoot)
{
m_dst = null;
}
}
void Reset()
@ -70,20 +82,20 @@ namespace UniVRM10
if (m_src == null)
{
m_src = new ConstraintSource(Source, SourceCoordinate, ModelRoot);
m_src = new ConstraintSource(Source, ModelRoot);
}
if (m_dst == null)
{
m_dst = new ConstraintDestination(transform, DestinationCoordinate, ModelRoot);
m_dst = new ConstraintDestination(transform, ModelRoot);
}
// 軸制限をしたオイラー角
var delta = m_src.RotationDelta;
var fleezed = FreezeAxes.Freeze(delta.eulerAngles);
Delta = m_src.RotationDelta(SourceCoordinate);
var fleezed = FreezeAxes.Freeze(Delta.eulerAngles);
var rotation = Quaternion.Euler(fleezed);
// Debug.Log($"{delta} => {rotation}");
// オイラー角を再度Quaternionへ。weight を加味してSlerpする
m_dst.ApplyRotation(rotation, Weight, ModelRoot);
m_dst.ApplyRotation(rotation, Weight, DestinationCoordinate, ModelRoot);
}
}
}

View File

@ -0,0 +1,8 @@
using System;
using UnityEngine;
namespace UniVRM10
{
[AttributeUsage(AttributeTargets.Enum | AttributeTargets.Field)]
public sealed class EnumFlagsAttribute : PropertyAttribute { }
}

View File

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