mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-04-24 06:57:49 -05:00
VRM10RotationConstraintEditor
This commit is contained in:
parent
20431dad6d
commit
d8836717cb
8
Assets/VRM10/Editor/Components/Constraint.meta
Normal file
8
Assets/VRM10/Editor/Components/Constraint.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7b369ee7eb21d1047bb03e139faa7eda
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cd4ca141215a42c45a0036791f2f8a4d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
64
Assets/VRM10/Editor/EnumFlagAttributeDrawer.cs
Normal file
64
Assets/VRM10/Editor/EnumFlagAttributeDrawer.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/VRM10/Editor/EnumFlagAttributeDrawer.cs.meta
Normal file
11
Assets/VRM10/Editor/EnumFlagAttributeDrawer.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 62a17679a0c7f7843beaa97ae4121d63
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
8
Assets/VRM10/Runtime/EnumFlagsAttribute.cs
Normal file
8
Assets/VRM10/Runtime/EnumFlagsAttribute.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniVRM10
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Enum | AttributeTargets.Field)]
|
||||
public sealed class EnumFlagsAttribute : PropertyAttribute { }
|
||||
}
|
||||
11
Assets/VRM10/Runtime/EnumFlagsAttribute.cs.meta
Normal file
11
Assets/VRM10/Runtime/EnumFlagsAttribute.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5f3c3f9559a399143816d5eab6f0b4ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Loading…
Reference in New Issue
Block a user