2022-12-01 04:49:21 +08:00
|
|
|
|
using System.Text;
|
|
|
|
|
using UnityEditor;
|
2022-10-05 10:43:46 +08:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
2022-11-11 12:39:58 +08:00
|
|
|
|
namespace nadena.dev.modular_avatar.core.editor
|
2022-10-05 10:43:46 +08:00
|
|
|
|
{
|
|
|
|
|
public class EasySetupOutfit
|
|
|
|
|
{
|
|
|
|
|
private const int PRIORITY = 49;
|
|
|
|
|
|
|
|
|
|
[MenuItem("GameObject/[ModularAvatar] Setup Outfit", false, PRIORITY)]
|
|
|
|
|
static void SetupOutfit(MenuCommand cmd)
|
|
|
|
|
{
|
|
|
|
|
if (!FindBones(cmd.context,
|
|
|
|
|
out var avatarRoot, out var avatarHips, out var outfitHips)
|
|
|
|
|
) return;
|
|
|
|
|
|
|
|
|
|
var avatarArmature = avatarHips.transform.parent;
|
|
|
|
|
var outfitArmature = outfitHips.transform.parent;
|
|
|
|
|
|
|
|
|
|
var merge = Undo.AddComponent<ModularAvatarMergeArmature>(outfitArmature.gameObject);
|
2022-11-04 09:09:20 +08:00
|
|
|
|
merge.mergeTarget = new AvatarObjectReference();
|
2022-10-05 10:43:46 +08:00
|
|
|
|
merge.mergeTarget.referencePath = RuntimeUtil.RelativePath(avatarRoot, avatarArmature.gameObject);
|
|
|
|
|
merge.InferPrefixSuffix();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[MenuItem("GameObject/[ModularAvatar] Setup Outfit", true, PRIORITY)]
|
|
|
|
|
static bool ValidateSetupOutfit()
|
|
|
|
|
{
|
|
|
|
|
foreach (var obj in Selection.objects)
|
|
|
|
|
{
|
2022-12-01 04:49:21 +08:00
|
|
|
|
if (!(obj is GameObject gameObj)) return false;
|
|
|
|
|
var xform = gameObj.transform;
|
|
|
|
|
|
2022-10-05 10:43:46 +08:00
|
|
|
|
if (!FindBones(obj, out var _, out var _, out var outfitHips)
|
|
|
|
|
|| outfitHips.transform.parent.GetComponent<ModularAvatarMergeArmature>() != null)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-12-01 04:49:21 +08:00
|
|
|
|
|
|
|
|
|
// Some users have been accidentally running Setup Outfit on the avatar itself, and/or nesting avatar
|
|
|
|
|
// descriptors when transplanting outfits. Block this (and require that there be only one avdesc) by
|
|
|
|
|
// refusing to run if we detect multiple avatar descriptors above the current object (or if we're run on
|
|
|
|
|
// the avdesc object itself)
|
|
|
|
|
var nearestAvatar = RuntimeUtil.FindAvatarInParents(xform);
|
|
|
|
|
if (nearestAvatar == null || nearestAvatar.transform == xform) return false;
|
|
|
|
|
|
|
|
|
|
var parent = nearestAvatar.transform.parent;
|
|
|
|
|
if (parent != null && RuntimeUtil.FindAvatarInParents(parent) != null) return false;
|
2022-10-05 10:43:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool FindBones(Object obj, out GameObject avatarRoot, out GameObject avatarHips,
|
|
|
|
|
out GameObject outfitHips)
|
|
|
|
|
{
|
|
|
|
|
avatarHips = outfitHips = null;
|
|
|
|
|
var outfitRoot = obj as GameObject;
|
|
|
|
|
avatarRoot = outfitRoot != null
|
|
|
|
|
? RuntimeUtil.FindAvatarInParents(outfitRoot.transform)?.gameObject
|
|
|
|
|
: null;
|
|
|
|
|
if (outfitRoot == null || avatarRoot == null) return false;
|
|
|
|
|
|
|
|
|
|
var avatarAnimator = avatarRoot.GetComponent<Animator>();
|
|
|
|
|
if (avatarAnimator == null) return false;
|
|
|
|
|
|
|
|
|
|
avatarHips = avatarAnimator.GetBoneTransform(HumanBodyBones.Hips)?.gameObject;
|
|
|
|
|
if (avatarHips == null) return false;
|
|
|
|
|
|
|
|
|
|
var outfitAnimator = outfitRoot.GetComponent<Animator>();
|
|
|
|
|
if (outfitAnimator != null)
|
|
|
|
|
{
|
|
|
|
|
outfitHips = outfitAnimator.GetBoneTransform(HumanBodyBones.Hips)?.gameObject;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (outfitHips == null)
|
|
|
|
|
{
|
|
|
|
|
// Heuristic search - usually there'll be root -> Armature -> (single child) Hips
|
|
|
|
|
foreach (Transform child in outfitRoot.transform)
|
|
|
|
|
{
|
2022-11-29 12:34:13 +08:00
|
|
|
|
foreach (Transform tempHip in child)
|
2022-10-05 10:43:46 +08:00
|
|
|
|
{
|
2022-11-29 12:34:13 +08:00
|
|
|
|
if (tempHip.name.Contains(avatarHips.name))
|
2022-10-05 10:43:46 +08:00
|
|
|
|
{
|
2022-11-29 12:34:13 +08:00
|
|
|
|
outfitHips = tempHip.gameObject;
|
2022-10-05 10:43:46 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return avatarHips != null && outfitHips != null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|