mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2025-02-19 12:45:02 +08:00
feat: add UI for setting outfit position to match base avatar (#521)
This commit is contained in:
parent
892c7d6171
commit
a98b8fb457
@ -1,4 +1,8 @@
|
||||
using UnityEditor;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Collections;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static nadena.dev.modular_avatar.core.editor.Localization;
|
||||
|
||||
@ -80,6 +84,11 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
EditorGUI.LabelField(l2, desc, normalLabel);
|
||||
}
|
||||
|
||||
private bool posResetOptionFoldout = false;
|
||||
private bool posReset_adjustRotation = false;
|
||||
private bool posReset_adjustScale = false;
|
||||
private bool posReset_heuristicRootScale = true;
|
||||
|
||||
protected override void OnInnerInspectorGUI()
|
||||
{
|
||||
var target = (ModularAvatarMergeArmature) this.target;
|
||||
@ -110,7 +119,149 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Separator();
|
||||
|
||||
if (targets.Length == 1)
|
||||
{
|
||||
posResetOptionFoldout = EditorGUILayout.Foldout(posResetOptionFoldout, G("merge_armature.reset_pos"));
|
||||
if (posResetOptionFoldout)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
try
|
||||
{
|
||||
EditorGUILayout.HelpBox(
|
||||
S("merge_armature.reset_pos.info"),
|
||||
MessageType.Info
|
||||
);
|
||||
|
||||
posReset_adjustRotation = EditorGUILayout.ToggleLeft(
|
||||
G("merge_armature.reset_pos.adjust_rotation"),
|
||||
posReset_adjustRotation);
|
||||
posReset_adjustScale = EditorGUILayout.ToggleLeft(G("merge_armature.reset_pos.adjust_scale"),
|
||||
posReset_adjustScale);
|
||||
posReset_heuristicRootScale = EditorGUILayout.ToggleLeft(
|
||||
G("merge_armature.reset_pos.heuristic_scale"),
|
||||
posReset_heuristicRootScale);
|
||||
|
||||
if (GUILayout.Button(G("merge_armature.reset_pos.execute")))
|
||||
{
|
||||
ForcePositionToBaseAvatar();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Localization.ShowLanguageUI();
|
||||
}
|
||||
|
||||
private void ForcePositionToBaseAvatar()
|
||||
{
|
||||
var mama = (ModularAvatarMergeArmature) target;
|
||||
var mergeTarget = mama.mergeTarget.Get(mama);
|
||||
var xform_to_bone = new Dictionary<Transform, HumanBodyBones>();
|
||||
var bone_to_xform = new Dictionary<HumanBodyBones, Transform>();
|
||||
var rootAnimator = RuntimeUtil.FindAvatarTransformInParents(mergeTarget.transform)
|
||||
.GetComponent<Animator>();
|
||||
|
||||
foreach (var bone in Enum.GetValues(typeof(HumanBodyBones)).Cast<HumanBodyBones>())
|
||||
{
|
||||
if (bone != HumanBodyBones.LastBone)
|
||||
{
|
||||
var xform = rootAnimator.GetBoneTransform(bone);
|
||||
if (xform != null)
|
||||
{
|
||||
xform_to_bone[xform] = bone;
|
||||
bone_to_xform[bone] = xform;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (posReset_heuristicRootScale)
|
||||
{
|
||||
AdjustRootScale();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Walk(mama.transform, mergeTarget.transform);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mama.ResetArmatureLock();
|
||||
}
|
||||
|
||||
void AdjustRootScale()
|
||||
{
|
||||
// Adjust the overall scale of the avatar based on wingspan (arm length)
|
||||
if (!bone_to_xform.TryGetValue(HumanBodyBones.LeftHand, out var target_hand)) return;
|
||||
|
||||
// Find the merge hand as well
|
||||
var hand_path = RuntimeUtil.RelativePath(mergeTarget, target_hand.gameObject);
|
||||
hand_path = string.Join("/", hand_path.Split('/').Select(elem => mama.prefix + elem + mama.suffix));
|
||||
|
||||
var merge_hand = mama.transform.Find(hand_path);
|
||||
if (merge_hand == null) return;
|
||||
|
||||
var target_wingspan = Mathf.Abs(rootAnimator.transform.InverseTransformPoint(target_hand.position).x);
|
||||
var merge_wingspan = Mathf.Abs(rootAnimator.transform.InverseTransformPoint(merge_hand.position).x);
|
||||
|
||||
var scale = target_wingspan / merge_wingspan;
|
||||
mama.transform.localScale *= scale;
|
||||
}
|
||||
|
||||
void Walk(Transform t_merge, Transform t_target)
|
||||
{
|
||||
Undo.RecordObject(t_merge, "Merge Armature: Force outfit position");
|
||||
|
||||
Debug.Log("=== Processing: " + t_merge.gameObject.name);
|
||||
|
||||
if (!t_merge.IsChildOf(mama.transform))
|
||||
{
|
||||
throw new ArgumentException("t_merge not a child of mama.transform");
|
||||
}
|
||||
|
||||
t_merge.position = t_target.position;
|
||||
if (posReset_adjustScale)
|
||||
{
|
||||
if (!posReset_heuristicRootScale || t_merge != mama.transform)
|
||||
{
|
||||
t_merge.localScale = t_target.localScale;
|
||||
}
|
||||
}
|
||||
|
||||
if (posReset_adjustRotation)
|
||||
{
|
||||
t_merge.localRotation = t_target.localRotation;
|
||||
}
|
||||
|
||||
foreach (Transform t_child in t_merge)
|
||||
{
|
||||
if (TryMatchChildBone(t_target, t_child, out var t_target_child))
|
||||
{
|
||||
Walk(t_child, t_target_child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TryMatchChildBone(Transform t_target, Transform t_child, out Transform t_target_child)
|
||||
{
|
||||
var childName = t_child.gameObject.name;
|
||||
|
||||
t_target_child = null;
|
||||
if (childName.StartsWith(mama.prefix) && childName.EndsWith(mama.suffix))
|
||||
{
|
||||
var targetObjectName = childName.Substring(mama.prefix.Length,
|
||||
childName.Length - mama.prefix.Length - mama.suffix.Length);
|
||||
t_target_child = t_target.transform.Find(targetObjectName);
|
||||
}
|
||||
|
||||
return t_target_child != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -59,6 +59,13 @@
|
||||
"merge_armature.lockmode.base_to_merge.body": "Moving the base avatar will move the merge armature. If you move the merged armature, it will not affect the base avatar. This is useful when adding normal outfits, where you might want to adjust the position of bones in the outfit.",
|
||||
"merge_armature.lockmode.bidirectional.title": "Avatar <=====> Target (Bidirectional)",
|
||||
"merge_armature.lockmode.bidirectional.body": "The base armature and the merged armature will always have the same position. This is useful when creating animations that are meant to target the base armature. In order to activate this, your armatures must already be in the exact same position.",
|
||||
"merge_armature.reset_pos": "Reset position to base avatar",
|
||||
"merge_armature.reset_pos.info": "This command will force the position of all bones in the outfit to match that of the base avatar. This can be helpful as a starting point for installing outfits not set up for your current avatar.",
|
||||
"merge_armature.reset_pos.adjust_rotation": "Also set rotation to base avatar",
|
||||
"merge_armature.reset_pos.adjust_scale": "Also set local scale to base avatar",
|
||||
"merge_armature.reset_pos.execute": "Do it!",
|
||||
"merge_armature.reset_pos.heuristic_scale": "Adjust outfit overall scale to match base avatar",
|
||||
"merge_armature.reset_pos.heuristic_scale.tooltip": "Will set the overall scale of the outfit as a whole based on armspan measurements. Recommended for setting up outfits.",
|
||||
|
||||
"worldfixed.quest": "This component is not compatible with the standalone Oculus Quest and will have no effect.",
|
||||
"worldfixed.normal": "This object will be fixed to world unless you fixed to avatar with constraint.",
|
||||
|
@ -47,7 +47,15 @@
|
||||
"merge_armature.lockmode.base_to_merge.body": "アバターを動かすと統合されるアーマチュアも動きますが、統合されるアーマチュアが動かされてもアバターは動きません。ボーンの位置調整を保持するので、通常の衣装追加であればこちらがお勧めです。",
|
||||
"merge_armature.lockmode.bidirectional.title": "アバター <=====> オブジェクト (双方向)",
|
||||
"merge_armature.lockmode.bidirectional.body": "アバターと統合されるアーマチュアは常に同じ位置になります。元のアバターを操作するアニメーションを作る時に便利かもしれません。有効にするためには、統合されるアーマチュアの位置を統合先と同じにしておく必要があります。",
|
||||
|
||||
"merge_armature.reset_pos": "位置を元アバターに合わせてリセット",
|
||||
"merge_armature.reset_pos.info": "このコマンドは、衣装のボーンの位置をアバターのボーンの位置に合わせます。非対応衣装を導入するとき、アバウトに合わせるために便利です。",
|
||||
"merge_armature.reset_pos.adjust_rotation": "回転も合わせる",
|
||||
"merge_armature.reset_pos.adjust_scale": "スケールも合わせる",
|
||||
"merge_armature.reset_pos.execute": "実行",
|
||||
"merge_armature.reset_pos.heuristic_scale": "衣装の全体的なスケールをアバターに合わせる",
|
||||
"merge_armature.reset_pos.heuristic_scale.tooltip": "腕の長さを参考に、衣装全体のスケールをアバターに合わせます。非対応衣装を導入するときは推奨です。",
|
||||
|
||||
|
||||
"path_mode.Relative": "相対的(このオブジェクトからのパスを使用)",
|
||||
"path_mode.Absolute": "絶対的(アバタールートからのパスを使用)",
|
||||
"merge_animator.animator": "統合されるアニメーター",
|
||||
|
@ -76,6 +76,17 @@ namespace nadena.dev.modular_avatar.core
|
||||
RuntimeUtil.delayCall(SetLockMode);
|
||||
}
|
||||
|
||||
internal void ResetArmatureLock()
|
||||
{
|
||||
if (_lockController != null)
|
||||
{
|
||||
_lockController.Dispose();
|
||||
_lockController = null;
|
||||
}
|
||||
|
||||
SetLockMode();
|
||||
}
|
||||
|
||||
private void SetLockMode()
|
||||
{
|
||||
if (this == null) return;
|
||||
@ -133,7 +144,7 @@ namespace nadena.dev.modular_avatar.core
|
||||
private List<(Transform, Transform)> GetBonesForLock()
|
||||
{
|
||||
if (this == null) return null;
|
||||
|
||||
|
||||
var mergeRoot = this.transform;
|
||||
var baseRoot = mergeTarget.Get(this);
|
||||
|
||||
|
BIN
docs~/docs/reference/merge-armature-reset-position.png
Normal file
BIN
docs~/docs/reference/merge-armature-reset-position.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
@ -1,4 +1,4 @@
|
||||
# Merge Armature
|
||||
Res# Merge Armature
|
||||
|
||||
The Merge Armature component merges a tree of GameObjects onto the armature of the avatar.
|
||||
|
||||
@ -51,6 +51,27 @@ animates the base avatar's hair or animal ears.
|
||||
When you set up an outfit with "setup outfit", the position lock mode will be set to "Base =======> Target
|
||||
(Unidirectional)". You can change this in the inspector if desired.
|
||||
|
||||
## Reset position to base avatar
|
||||
|
||||

|
||||
|
||||
When installing outfits that weren't originally created for your avatar of choice, it can be helpful to roughly set the
|
||||
outfit to match the position of your avatar before making fine adjustments. The "reset position to base avatar" tool in
|
||||
the Merge Armature Inspector can do this for you.
|
||||
|
||||
When you click "Do It!", all bones in the outfit will have their position set to the corresponding bones in the avatar.
|
||||
There are three additional options you can use:
|
||||
|
||||
* "Also set rotation": Sets the local rotation of all bones in the outfit to that of the base avatar. This is intended
|
||||
for special cases; if the outfit was not made using the same 3D software as the avatar, you'll see strange effects.
|
||||
* "Also set local scale": Sets the local scale of all bones in the outfit to that of the base avatar. This is intended
|
||||
for cases where you've adjusted the scale of bones in the base avatar, and need the outfit to match.
|
||||
* "Adjust outfit overall scale to match base avatar": Changes the scale of the outfit as a whole to match that of the
|
||||
avatar as a whole. This uses arm length to determine the overall size of the avatar and outfit, and scales the whole
|
||||
outfit up (or down) before adjusting any positions. This is generally recommended for setting up outfits.
|
||||
|
||||
These options are not saved; clicking "do it!" will just reset the positions/etc once.
|
||||
|
||||
## Object references
|
||||
|
||||
Although the editor UI allows you to drag in a target object for the merge armature component, internally this is saved as a path reference.
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
@ -52,6 +52,22 @@ Modular Avatar 1.7.0以降、連鎖的に統合することができます。つ
|
||||
「Setup Outfit」で衣装を導入すると、位置追従モードは「アバター =======> ターゲット(一方向)」に自動設定されます。
|
||||
必要に応じてインスペクタで変更してください。
|
||||
|
||||
## 位置を元アバターに合わせてリセット
|
||||
|
||||

|
||||
|
||||
非対応衣装を導入する時は、まず大まかに位置を合わせてから微調整すると便利です。Merge
|
||||
Armatureの設定UIの中の、「位置を元アバターに合わせてリセット」ツールがそのための機能です。
|
||||
|
||||
「実行」を押すと、衣装のボーンの位置がベースアバターのボーンの位置に合わせられます。そのほか、以下の追加オプションもあります。
|
||||
|
||||
* 「回転も合わせる」 各ボーンのローカル回転もベースアバターのボーンの回転に合わせます。特殊な用途向けです。特に、衣装がベースアバターと同じ3Dソフトで作られていない場合は、不自然な挙動になります。
|
||||
* 「スケールも合わせる」 各ボーンのローカルスケールもベースアバターのボーンのスケールに合わせます。ベースアバターのボーンのスケールを調整している場合に使います。
|
||||
* 「衣装の全体的なスケールをアバターに合わせる」
|
||||
衣装全体のスケールをベースアバターに合わせます。アバターの腕の長さから全体の大きさを決め、位置合わせをする前に衣装全体を拡大・縮小します。非対応衣装セットアップ向けです。
|
||||
|
||||
オプションの設定は保存されません。「実行」を押す時一回だけ適用されます。
|
||||
|
||||
## オブジェクト引用
|
||||
|
||||
エディタ上では統合先をドラッグアンドドロップで指定しますが、内部ではパスで保存されます。プレハブ化してもちゃんと統合先を保存できるということです。
|
||||
|
Loading…
Reference in New Issue
Block a user