Skip to content

Commit

Permalink
feat: add a component to perform nonuniform scale adjustments (#583)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdunderscore authored Dec 27, 2023
1 parent 4d3f493 commit 2650566
Show file tree
Hide file tree
Showing 19 changed files with 602 additions and 5 deletions.
148 changes: 148 additions & 0 deletions Editor/Inspector/ScaleAdjusterInspector.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
using System;
using System.Linq;
using UnityEditor;
using UnityEngine;
using static nadena.dev.modular_avatar.core.editor.Localization;

namespace nadena.dev.modular_avatar.core.editor
{
[CustomEditor(typeof(ModularAvatarScaleAdjuster))]
[CanEditMultipleObjects]
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);

serializedObject.ApplyModifiedProperties();

Localization.ShowLanguageUI();
}
}
}
3 changes: 3 additions & 0 deletions Editor/Inspector/ScaleAdjusterInspector.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Editor/Localization/en-us.json
Original file line number Diff line number Diff line change
Expand Up @@ -232,5 +232,7 @@
"setup_outfit.err.no_animator": "Your avatar does not have an Animator component.",
"setup_outfit.err.no_hips": "Your avatar does not have a Hips bone. Setup Outfit only works on humanoid avatars.",
"setup_outfit.err.no_outfit_hips": "Unable to identify the Hips object for the outfit. Searched for objects containing the following names:",
"move_independently.group-header": "Objects to move together"
"move_independently.group-header": "Objects to move together",
"scale_adjuster.scale": "Scale adjustment",
"scale_adjuster.adjust_children": "Adjust position of child objects"
}
4 changes: 3 additions & 1 deletion Editor/Localization/ja-jp.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,5 +193,7 @@
"setup_outfit.err.no_animator": "アバターにAnimatorコンポーネントがありません。",
"setup_outfit.err.no_hips": "アバターにHipsボーンがありません。なお、Setup Outfitはヒューマノイドアバター以外には対応していません。",
"setup_outfit.err.no_outfit_hips": "衣装のHipsボーンを発見できませんでした。以下の名前を含むボーンを探しました:",
"move_independently.group-header": "一緒に動かすオブジェクト"
"move_independently.group-header": "一緒に動かすオブジェクト",
"scale_adjuster.scale": "Scale調整値",
"scale_adjuster.adjust_children": "子オブジェクトの位置を調整"
}
5 changes: 5 additions & 0 deletions Editor/MergeArmatureHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ internal void OnPreprocessAvatar(ndmf.BuildContext context, GameObject avatarGam
TopoProcessMergeArmatures(mergeArmatures);

#if MA_VRCSDK3_AVATARS
foreach (var c in avatarGameObject.transform.GetComponentsInChildren<ScaleProxy>(true))
{
BoneDatabase.AddMergedBone(c.transform);
}

foreach (var c in avatarGameObject.transform.GetComponentsInChildren<VRCPhysBone>(true))
{
if (c.rootTransform == null) c.rootTransform = c.transform;
Expand Down
2 changes: 1 addition & 1 deletion Runtime/MAMoveIndependently.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ void Awake()

private void OnValidate()
{
Debug.Log("=== OnValidate");
hideFlags = HideFlags.DontSave;
_excluded = new HashSet<Transform>();
if (m_groupedBones == null)
Expand Down Expand Up @@ -110,6 +109,7 @@ private void CheckChildren(Transform parent)
foreach (Transform child in parent)
{
if (_excluded.Contains(child)) continue;
if (child.GetComponent<ScaleProxy>() != null) continue;

_observed.Add(child);

Expand Down
Loading

0 comments on commit 2650566

Please sign in to comment.