Skip to content

Commit

Permalink
fix: ScaleAdjuster not usable when gizmos are not enabled (#595)
Browse files Browse the repository at this point in the history
Fixes: #588, #589
  • Loading branch information
bdunderscore authored Dec 28, 2023
1 parent de71e1f commit 2ad3243
Show file tree
Hide file tree
Showing 3 changed files with 255 additions and 122 deletions.
125 changes: 3 additions & 122 deletions Editor/Inspector/ScaleAdjusterInspector.cs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -12,133 +9,17 @@ internal class ScaleAdjusterInspector : MAEditorBase
{
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();

Expand Down
249 changes: 249 additions & 0 deletions Editor/Inspector/ScaleAdjusterTool.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Loading

0 comments on commit 2ad3243

Please sign in to comment.