-
-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: ScaleAdjuster not usable when gizmos are not enabled (#595)
- Loading branch information
1 parent
de71e1f
commit 2ad3243
Showing
3 changed files
with
255 additions
and
122 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
Oops, something went wrong.