fix: ScaleAdjuster not usable when gizmos are not enabled (#595)

Fixes: #588, #589
This commit is contained in:
bd_ 2023-12-28 16:25:23 +09:00 committed by GitHub
parent de71e1f544
commit 2ad324380e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 255 additions and 122 deletions

View File

@ -1,7 +1,4 @@
using System;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEditor;
using static nadena.dev.modular_avatar.core.editor.Localization;
namespace nadena.dev.modular_avatar.core.editor
@ -12,133 +9,17 @@ namespace nadena.dev.modular_avatar.core.editor
{
private SerializedProperty _scale;
private ModularAvatarScaleAdjuster[] _sortedTargets;
private Vector3[] _originalScales;
private Vector3 gizmoScale = Vector3.one;
private bool _adjustChildPositions;
protected void OnEnable()
{
_scale = serializedObject.FindProperty("m_Scale");
_sortedTargets = targets.Cast<ModularAvatarScaleAdjuster>().OrderBy(TransformDepth).ToArray();
_originalScales = _sortedTargets.Select(t => t.Scale).ToArray();
}
private int TransformDepth(ModularAvatarScaleAdjuster obj)
{
var t = obj.transform;
var depth = 0;
while (t != null)
{
depth++;
t = t.parent;
}
return depth;
}
protected void OnDisable()
{
}
public void OnSceneGUI()
{
Selection.selectionChanged -= UnhideTools;
Selection.selectionChanged += UnhideTools;
Tools.hidden = (Tools.current == Tool.Scale);
if (!Tools.hidden) return;
var handlePos = _sortedTargets[0].transform;
EditorGUI.BeginChangeCheck();
var handleSize = HandleUtility.GetHandleSize(handlePos.position);
gizmoScale = Handles.ScaleHandle(gizmoScale, handlePos.position, handlePos.rotation, handleSize);
if (EditorGUI.EndChangeCheck())
{
for (int i = 0; i < _sortedTargets.Length; i++)
{
UpdateScale(i, handlePos);
}
}
}
private void UpdateScale(int i, Transform refTransform)
{
var xform = _sortedTargets[i].transform;
var target = _sortedTargets[i];
Matrix4x4 initialTransform = xform.parent.localToWorldMatrix * Matrix4x4.TRS(
xform.localPosition,
xform.localRotation,
xform.localScale
);
Matrix4x4 initialScale = Matrix4x4.TRS(
Vector3.zero,
Quaternion.identity,
_originalScales[i]
);
Matrix4x4 newTransform = refTransform.localToWorldMatrix * Matrix4x4.TRS(
Vector3.zero,
Quaternion.identity,
gizmoScale
);
float scaleX = TransformVec(Vector3.right);
float scaleY = TransformVec(Vector3.up);
float scaleZ = TransformVec(Vector3.forward);
Undo.RecordObject(target, "Adjust scale");
var targetL2W = target.transform.localToWorldMatrix;
var baseToScaleCoord = (targetL2W * Matrix4x4.Scale(target.Scale)).inverse * targetL2W;
target.Scale = new Vector3(scaleX, scaleY, scaleZ);
var scaleToBaseCoord = Matrix4x4.Scale(target.Scale);
PrefabUtility.RecordPrefabInstancePropertyModifications(target);
// Update child positions
if (_adjustChildPositions)
{
var updateTransform = scaleToBaseCoord * baseToScaleCoord;
foreach (Transform child in target.transform)
{
Undo.RecordObject(child, "Adjust scale");
child.localPosition = updateTransform.MultiplyPoint(child.localPosition);
PrefabUtility.RecordPrefabInstancePropertyModifications(child);
}
}
float TransformVec(Vector3 vec)
{
// first, place our measurement vector into world spoce
vec = (initialTransform * initialScale).MultiplyVector(vec);
// now put it into reference space
vec = refTransform.worldToLocalMatrix.MultiplyVector(vec);
// and return using the adjusted scale
vec = newTransform.MultiplyVector(vec);
// now come back into local space
vec = (initialTransform.inverse).MultiplyVector(vec);
return vec.magnitude;
}
}
private static void UnhideTools()
{
Tools.hidden = false;
}
protected override void OnInnerInspectorGUI()
{
EditorGUILayout.PropertyField(_scale, G("scale_adjuster.scale"));
_adjustChildPositions = EditorGUILayout.Toggle(G("scale_adjuster.adjust_children"), _adjustChildPositions);
ScaleAdjusterTool.AdjustChildPositions = EditorGUILayout.Toggle(G("scale_adjuster.adjust_children"),
ScaleAdjusterTool.AdjustChildPositions);
serializedObject.ApplyModifiedProperties();

View File

@ -0,0 +1,249 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace nadena.dev.modular_avatar.core.editor
{
internal static class ScaleAdjusterTool
{
private const string UNDO_STRING = "Adjust scale";
public static bool AdjustChildPositions = true;
[InitializeOnLoadMethod]
private static void Init()
{
SceneView.duringSceneGui += OnSceneGUI;
Selection.selectionChanged += OnSelectionChanged;
}
abstract class ScaleHolder
{
public abstract Object Obj { get; }
public Vector3 InitialScale { get; protected set; }
public Vector3 LastScale { get; protected set; }
public abstract bool IsValid { get; }
public abstract Vector3 Scale { get; set; }
public abstract Matrix4x4 ParentLocalToWorld { get; }
public abstract Matrix4x4 LocalBaseTransform { get; }
}
class GameObjectScaler : ScaleHolder
{
private readonly GameObject _obj;
public override Object Obj => _obj;
public GameObjectScaler(GameObject obj)
{
_obj = obj;
LastScale = InitialScale = obj.transform.localScale;
}
public override bool IsValid => _obj != null;
public override Vector3 Scale
{
get => _obj.transform.localScale;
set
{
Undo.RecordObject(_obj.transform, UNDO_STRING);
_obj.transform.localScale = value;
LastScale = value;
}
}
public override Matrix4x4 ParentLocalToWorld => _obj.transform.parent.localToWorldMatrix;
public override Matrix4x4 LocalBaseTransform => Matrix4x4.TRS(
_obj.transform.localPosition,
_obj.transform.localRotation,
Vector3.one
);
}
class AdjusterScaler : ScaleHolder
{
private readonly ModularAvatarScaleAdjuster _obj;
private string UNDO_STRING;
public override Object Obj => _obj;
public AdjusterScaler(ModularAvatarScaleAdjuster obj)
{
_obj = obj;
LastScale = InitialScale = obj.Scale;
}
public override bool IsValid => _obj != null;
public override Vector3 Scale
{
get => _obj.Scale;
set
{
Undo.RecordObject(_obj, UNDO_STRING);
var targetL2W = _obj.transform.localToWorldMatrix;
var baseToScaleCoord = (targetL2W * Matrix4x4.Scale(_obj.Scale)).inverse * targetL2W;
_obj.Scale = value;
LastScale = value;
var scaleToBaseCoord = Matrix4x4.Scale(_obj.Scale);
PrefabUtility.RecordPrefabInstancePropertyModifications(_obj);
// Update child positions
if (AdjustChildPositions)
{
var updateTransform = scaleToBaseCoord * baseToScaleCoord;
foreach (Transform child in _obj.transform)
{
Undo.RecordObject(child, UNDO_STRING);
child.localPosition = updateTransform.MultiplyPoint(child.localPosition);
PrefabUtility.RecordPrefabInstancePropertyModifications(child);
}
}
}
}
public override Matrix4x4 ParentLocalToWorld => _obj.transform.parent.localToWorldMatrix;
public override Matrix4x4 LocalBaseTransform => Matrix4x4.TRS(
_obj.transform.localPosition,
_obj.transform.localRotation,
_obj.transform.localScale
);
}
private static List<ScaleHolder> _selection = new List<ScaleHolder>();
private static bool? _active = null;
private static Vector3 _gizmoScale;
private static Quaternion _handleRotation;
private static void OnSelectionChanged()
{
_selection.Clear();
Tools.hidden = false;
_active = null;
}
private static void OnSceneGUI(SceneView obj)
{
if (Tools.current != Tool.Scale)
{
if (_active == true)
{
Tools.hidden = false;
_active = null;
}
return;
}
if (ShouldEnable())
{
Tools.hidden = true;
}
else
{
return;
}
var handleSize = HandleUtility.GetHandleSize(Tools.handlePosition);
EditorGUI.BeginChangeCheck();
_gizmoScale = Handles.ScaleHandle(_gizmoScale, Tools.handlePosition, _handleRotation, handleSize);
if (EditorGUI.EndChangeCheck())
{
foreach (var target in _selection)
{
UpdateScale(target, _gizmoScale);
}
}
}
private static void UpdateScale(ScaleHolder target, Vector3 gizmoScale)
{
var refTransformInv = Matrix4x4.TRS(
Tools.handlePosition,
_handleRotation,
Vector3.one
).inverse;
var gizmoTransform = Matrix4x4.TRS(
Tools.handlePosition,
_handleRotation,
gizmoScale
);
Matrix4x4 initialTransform = target.ParentLocalToWorld * target.LocalBaseTransform;
Matrix4x4 initialScale = Matrix4x4.Scale(target.InitialScale);
float scaleX = TransformVec(Vector3.right);
float scaleY = TransformVec(Vector3.up);
float scaleZ = TransformVec(Vector3.forward);
target.Scale = new Vector3(scaleX, scaleY, scaleZ);
float TransformVec(Vector3 vec)
{
// first, place our measurement vector into world spoce
vec = (initialTransform * initialScale).MultiplyVector(vec);
// now put it into reference space
vec = refTransformInv.MultiplyVector(vec);
// and return using the adjusted scale
vec = gizmoTransform.MultiplyVector(vec);
// now come back into local space
vec = (initialTransform.inverse).MultiplyVector(vec);
return vec.magnitude;
}
}
private static bool ShouldEnable()
{
if (_selection.Any(s => !s.IsValid))
{
_active = null;
}
if (_selection.Any(s => (s.Scale - s.LastScale).sqrMagnitude > 0.00001f))
{
_active = null;
}
if (_active.HasValue)
{
return _active.Value;
}
_selection.Clear();
_gizmoScale = Vector3.one;
_handleRotation = Tools.handleRotation;
bool anyAdjuster = false;
foreach (var obj in Selection.gameObjects)
{
var adjuster = obj.GetComponent<ModularAvatarScaleAdjuster>();
if (adjuster != null)
{
_selection.Add(new AdjusterScaler(adjuster));
anyAdjuster = true;
}
else
{
_selection.Add(new GameObjectScaler(obj));
}
}
_active = anyAdjuster;
return anyAdjuster;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 73643bebace8434baf2fcace7a8b7863
timeCreated: 1703745833