bdunderscore / modular-avatar

Other
497 stars 65 forks source link

Avatar にバインドされる Humanoid ボーンの Transform に対する操作が Rebind humanoid avatar パスで破棄される #1036

Closed kb10uy closed 1 month ago

kb10uy commented 1 month ago

https://misskey.niri.la/notes/9x9ptzcfqe

概要

431 で導入された Animator のボーンの再マッチの際、再設定される Avatar によって Transform への参照だけでなく local transform まで再設定されてしまう模様。

このため、 Modular Avatar よりも前に(素体側の) Humanoid ボーンの Transform を操作しても local transform レベルでビルド前の値に戻されてしまう。

再現手順

以下のコードを PluginDefinition/PluginDefinition.cs に挿入する。

seq.Run("Modify humanoid bone transform", (ctx) =>
{
    var leftEye = ctx.AvatarRootTransform.Find("Armature/Hips/Spine/Chest/Neck/Head/LeftEye");
    leftEye.localPosition = new Vector3(0.0f, 0.01f, 0.0f);
});

Rebind humanoid avatar より前に挿入すると左目の位置は変化しない(操作した内容が破棄されている)。 Rebind humanoid avatar より後に挿入すると左目の位置が変化する。

補足

簡易的には Rebind humanoid avatar の処理を以下のようなものに置き換えることで解消可能。

RebindAvatarWithTransform メソッド ```csharp private void RebindAvatarWithTransform(BuildContext ctx) { var animator = ctx.AvatarRootObject.GetComponent(); if (animator == null) return; // save old local transforms of previously bound humanoid bones. var oldTransforms = new Dictionary(); for (var bone = HumanBodyBones.Hips; bone < HumanBodyBones.LastBone; ++bone) { var oldBoneTramsform = animator.GetBoneTransform(bone); if (oldBoneTramsform == null) continue; oldTransforms.Add(bone, (oldBoneTramsform.localPosition, oldBoneTramsform.localRotation, oldBoneTramsform.localScale)); } // rebind avatar var avatar = animator.avatar; animator.avatar = null; animator.avatar = avatar; // restore local transforms if corresponding entries exist respectively. for (var bone = HumanBodyBones.Hips; bone < HumanBodyBones.LastBone; ++bone) { var newBoneTransform = animator.GetBoneTransform(bone); if (newBoneTransform == null) continue; if (oldTransforms.TryGetValue(bone, out var oldPair)) { newBoneTransform.SetLocalPositionAndRotation(oldPair.Item1, oldPair.Item2); newBoneTransform.localScale = oldPair.Item3; } } } ```

ただし、これだと #430 で言及されている「服のボーンに Humanoid 判定が吸われている」パターンには対応できない(oldBoneTramsform で素体側のボーンを取得できない)。 これに対応するためには rebind されるであろう素体側の Humanoid ボーンの Transform を特定して local transform を収集する必要があるが、実現性は不明。

Unity の Avatar のボーンのマッチングに関する問題と「NDMF Plugin で Humanoid ボーンの Transform を操作する場合は Modular Avatar より後に実行しなければならない」という旨をドキュメントに記述する方が適切な可能性がある。

bdunderscore commented 1 month ago

この場合はHumanoidかどうかにかかわらず、すべてのTransformのローカル位置を保存することで回避できるかな?

kb10uy commented 1 month ago

確かに全部保存しておけば確実に回避できそう