chore: use ndmf Avatar Root api where applicable (#482)

* expose ndmf AvatarRoot APIs through ma.RuntimeUtil

* refactor: prefer RuntimeUtil helpers for checking avatar root

* refactor: ndmf.BuildContext represents Avatar (log)

* refactor: ndmf.BuildContext represents Avatar (WorldFixed)

* refactor: ndmf.BuildContext represents Avatar (FirstPersonVisible)

* refactor: ndmf.BuildContext represents Avatar (BlendShapeSync)

* refactor: prefer FindAvatarTransformInParents() (ErrorReportUI)

* refactor: prefer FindAvatarTransformInParents() (Runtime)

* refactor: prefer FindAvatarTransformInParents() (Editor)

* delegate more ndmf AvatarRoot APIs through ma.RuntimeUtil
This commit is contained in:
kaikoga 2023-10-15 18:44:53 +09:00 committed by GitHub
parent 939e63c297
commit ae7103cf82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 104 additions and 113 deletions

View File

@ -57,7 +57,7 @@ namespace nadena.dev.modular_avatar.core.editor
_boneDatabase = boneDatabase; _boneDatabase = boneDatabase;
_pathMappings = context.PluginBuildContext.Extension<AnimationServicesContext>().PathMappings; _pathMappings = context.PluginBuildContext.Extension<AnimationServicesContext>().PathMappings;
while (root != null && root.GetComponent<VRCAvatarDescriptor>() == null) while (root != null && !RuntimeUtil.IsAvatarRoot(root))
{ {
var originalPath = RuntimeUtil.AvatarRootPath(root.gameObject); var originalPath = RuntimeUtil.AvatarRootPath(root.gameObject);
System.Diagnostics.Debug.Assert(originalPath != null); System.Diagnostics.Debug.Assert(originalPath != null);

View File

@ -42,16 +42,17 @@ namespace nadena.dev.modular_avatar.core.editor
} }
} }
public void OnPreprocessAvatar(GameObject avatar, BuildContext context) public void OnPreprocessAvatar(BuildContext context)
{ {
_context = context; _context = context;
var avatarGameObject = context.AvatarRootObject;
var animDb = _context.AnimationDatabase; var animDb = _context.AnimationDatabase;
var avatarDescriptor = avatar.GetComponent<VRCAvatarDescriptor>(); var avatarDescriptor = context.AvatarDescriptor;
_bindingMappings = new Dictionary<SummaryBinding, List<SummaryBinding>>(); _bindingMappings = new Dictionary<SummaryBinding, List<SummaryBinding>>();
_motionCache = new Dictionary<Motion, Motion>(); _motionCache = new Dictionary<Motion, Motion>();
var components = avatarDescriptor.GetComponentsInChildren<ModularAvatarBlendshapeSync>(true); var components = avatarGameObject.GetComponentsInChildren<ModularAvatarBlendshapeSync>(true);
if (components.Length == 0) return; if (components.Length == 0) return;
var layers = avatarDescriptor.baseAnimationLayers; var layers = avatarDescriptor.baseAnimationLayers;
@ -77,7 +78,7 @@ namespace nadena.dev.modular_avatar.core.editor
foreach (var component in components) foreach (var component in components)
{ {
BuildReport.ReportingObject(component, () => ProcessComponent(avatarDescriptor, component)); BuildReport.ReportingObject(component, () => ProcessComponent(avatarGameObject, component));
} }
// Walk and transform all clips // Walk and transform all clips
@ -91,9 +92,9 @@ namespace nadena.dev.modular_avatar.core.editor
}); });
} }
private void ProcessComponent(VRCAvatarDescriptor avatarDescriptor, ModularAvatarBlendshapeSync component) private void ProcessComponent(GameObject avatarGameObject, ModularAvatarBlendshapeSync component)
{ {
var targetObj = RuntimeUtil.RelativePath(avatarDescriptor.gameObject, component.gameObject); var targetObj = RuntimeUtil.RelativePath(avatarGameObject, component.gameObject);
foreach (var binding in component.Bindings) foreach (var binding in component.Bindings)
{ {
@ -102,7 +103,7 @@ namespace nadena.dev.modular_avatar.core.editor
var refSmr = refObj.GetComponent<SkinnedMeshRenderer>(); var refSmr = refObj.GetComponent<SkinnedMeshRenderer>();
if (refSmr == null) continue; if (refSmr == null) continue;
var refPath = RuntimeUtil.RelativePath(avatarDescriptor.gameObject, refObj); var refPath = RuntimeUtil.RelativePath(avatarGameObject, refObj);
var srcBinding = new SummaryBinding(refPath, binding.Blendshape); var srcBinding = new SummaryBinding(refPath, binding.Blendshape);

View File

@ -15,6 +15,8 @@ namespace nadena.dev.modular_avatar.core.editor
internal readonly nadena.dev.ndmf.BuildContext PluginBuildContext; internal readonly nadena.dev.ndmf.BuildContext PluginBuildContext;
internal VRCAvatarDescriptor AvatarDescriptor => PluginBuildContext.AvatarDescriptor; internal VRCAvatarDescriptor AvatarDescriptor => PluginBuildContext.AvatarDescriptor;
internal GameObject AvatarRootObject => PluginBuildContext.AvatarRootObject;
internal Transform AvatarRootTransform => PluginBuildContext.AvatarRootTransform;
internal AnimationDatabase AnimationDatabase => internal AnimationDatabase AnimationDatabase =>
PluginBuildContext.Extension<AnimationServicesContext>().AnimationDatabase; PluginBuildContext.Extension<AnimationServicesContext>().AnimationDatabase;
@ -50,6 +52,11 @@ namespace nadena.dev.modular_avatar.core.editor
{ {
} }
public BuildContext(GameObject avatarGameObject)
: this(new ndmf.BuildContext(avatarGameObject, null))
{
}
public void SaveAsset(Object obj) public void SaveAsset(Object obj)
{ {
if (!SaveImmediate || AssetDatabase.IsMainAsset(obj) || AssetDatabase.IsSubAsset(obj)) return; if (!SaveImmediate || AssetDatabase.IsMainAsset(obj) || AssetDatabase.IsSubAsset(obj)) return;

View File

@ -280,16 +280,16 @@ namespace nadena.dev.modular_avatar.core.editor
// descriptors when transplanting outfits. Block this (and require that there be only one avdesc) by // 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 // refusing to run if we detect multiple avatar descriptors above the current object (or if we're run on
// the avdesc object itself) // the avdesc object itself)
var nearestAvatar = RuntimeUtil.FindAvatarInParents(xform); var nearestAvatarTransform = RuntimeUtil.FindAvatarTransformInParents(xform);
if (nearestAvatar == null || nearestAvatar.transform == xform) if (nearestAvatarTransform == null || nearestAvatarTransform == xform)
{ {
errorMessageGroups = new string[] errorMessageGroups = new string[]
{S_f("setup_outfit.err.multiple_avatar_descriptors", xform.gameObject.name)}; {S_f("setup_outfit.err.multiple_avatar_descriptors", xform.gameObject.name)};
return false; return false;
} }
var parent = nearestAvatar.transform.parent; var parent = nearestAvatarTransform.parent;
if (parent != null && RuntimeUtil.FindAvatarInParents(parent) != null) if (parent != null && RuntimeUtil.FindAvatarTransformInParents(parent) != null)
{ {
errorMessageGroups = new string[] errorMessageGroups = new string[]
{ {
@ -308,7 +308,7 @@ namespace nadena.dev.modular_avatar.core.editor
avatarHips = outfitHips = null; avatarHips = outfitHips = null;
var outfitRoot = obj as GameObject; var outfitRoot = obj as GameObject;
avatarRoot = outfitRoot != null avatarRoot = outfitRoot != null
? RuntimeUtil.FindAvatarInParents(outfitRoot.transform)?.gameObject ? RuntimeUtil.FindAvatarTransformInParents(outfitRoot.transform)?.gameObject
: null; : null;
if (avatarRoot == null) if (avatarRoot == null)

View File

@ -5,7 +5,6 @@ using System.Linq;
using nadena.dev.modular_avatar.core; using nadena.dev.modular_avatar.core;
using Newtonsoft.Json; using Newtonsoft.Json;
using UnityEngine; using UnityEngine;
using VRC.SDK3.Avatars.Components;
using UnityEditor; using UnityEditor;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
@ -273,17 +272,17 @@ namespace nadena.dev.modular_avatar.editor.ErrorReporting
} }
} }
internal IDisposable ReportingOnAvatar(VRCAvatarDescriptor descriptor) internal IDisposable ReportingOnAvatar(GameObject avatarGameObject)
{ {
if (descriptor != null) if (avatarGameObject != null)
{ {
AvatarReport report = new AvatarReport(); AvatarReport report = new AvatarReport();
report.objectRef = new ObjectRef(descriptor.gameObject); report.objectRef = new ObjectRef(avatarGameObject);
Avatars.Add(report); Avatars.Add(report);
_currentAvatar = report; _currentAvatar = report;
_currentAvatar.successful = true; _currentAvatar.successful = true;
_currentAvatar.logs.AddRange(ComponentValidation.ValidateAll(descriptor.gameObject)); _currentAvatar.logs.AddRange(ComponentValidation.ValidateAll(avatarGameObject));
} }
return new AvatarReportScope(); return new AvatarReportScope();

View File

@ -110,7 +110,7 @@ namespace nadena.dev.modular_avatar.editor.ErrorReporting
GameObject activeAvatarObject = null; GameObject activeAvatarObject = null;
if (Selection.gameObjects.Length == 1) if (Selection.gameObjects.Length == 1)
{ {
activeAvatarObject = RuntimeUtil.FindAvatarInParents(Selection.activeGameObject.transform)?.gameObject; activeAvatarObject = RuntimeUtil.FindAvatarTransformInParents(Selection.activeGameObject.transform)?.gameObject;
activeAvatar = BuildReport.CurrentReport.Avatars.FirstOrDefault(av => activeAvatar = BuildReport.CurrentReport.Avatars.FirstOrDefault(av =>
av.objectRef.path == RuntimeUtil.RelativePath(null, activeAvatarObject) av.objectRef.path == RuntimeUtil.RelativePath(null, activeAvatarObject)
|| av.objectRef.path == RuntimeUtil.RelativePath(null, activeAvatarObject) + "(Clone)"); || av.objectRef.path == RuntimeUtil.RelativePath(null, activeAvatarObject) + "(Clone)");

View File

@ -356,7 +356,7 @@ namespace nadena.dev.modular_avatar.core.editor
internal static void RenameBonesByHeuristic(ModularAvatarMergeArmature config) internal static void RenameBonesByHeuristic(ModularAvatarMergeArmature config)
{ {
var target = config.mergeTarget.Get(RuntimeUtil.FindAvatarInParents(config.transform)); var target = config.mergeTarget.Get(RuntimeUtil.FindAvatarTransformInParents(config.transform));
if (target == null) return; if (target == null) return;
Traverse(config.transform, target.transform); Traverse(config.transform, target.transform);

View File

@ -1,6 +1,5 @@
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
using VRC.SDK3.Avatars.Components;
namespace nadena.dev.modular_avatar.core.editor namespace nadena.dev.modular_avatar.core.editor
{ {
@ -34,15 +33,15 @@ namespace nadena.dev.modular_avatar.core.editor
try try
{ {
var avatar = findContainingAvatar(property); var avatarTransform = findContainingAvatarTransform(property);
if (avatar == null) return false; if (avatarTransform == null) return false;
bool isRoot = property.stringValue == AvatarObjectReference.AVATAR_ROOT; bool isRoot = property.stringValue == AvatarObjectReference.AVATAR_ROOT;
bool isNull = string.IsNullOrEmpty(property.stringValue); bool isNull = string.IsNullOrEmpty(property.stringValue);
Transform target; Transform target;
if (isNull) target = null; if (isNull) target = null;
else if (isRoot) target = avatar.transform; else if (isRoot) target = avatarTransform;
else target = avatar.transform.Find(property.stringValue); else target = avatarTransform.Find(property.stringValue);
var labelRect = position; var labelRect = position;
position = EditorGUI.PrefixLabel(position, label); position = EditorGUI.PrefixLabel(position, label);
@ -62,14 +61,14 @@ namespace nadena.dev.modular_avatar.core.editor
{ {
property.stringValue = ""; property.stringValue = "";
} }
else if (newTarget == avatar.transform) else if (newTarget == avatarTransform)
{ {
property.stringValue = AvatarObjectReference.AVATAR_ROOT; property.stringValue = AvatarObjectReference.AVATAR_ROOT;
} }
else else
{ {
var relPath = var relPath =
RuntimeUtil.RelativePath(avatar.gameObject, ((Transform) newTarget).gameObject); RuntimeUtil.RelativePath(avatarTransform.gameObject, ((Transform) newTarget).gameObject);
if (relPath == null) return true; if (relPath == null) return true;
property.stringValue = relPath; property.stringValue = relPath;
@ -93,14 +92,14 @@ namespace nadena.dev.modular_avatar.core.editor
{ {
property.stringValue = ""; property.stringValue = "";
} }
else if (newTarget == avatar.transform) else if (newTarget == avatarTransform)
{ {
property.stringValue = AvatarObjectReference.AVATAR_ROOT; property.stringValue = AvatarObjectReference.AVATAR_ROOT;
} }
else else
{ {
var relPath = var relPath =
RuntimeUtil.RelativePath(avatar.gameObject, ((Transform) newTarget).gameObject); RuntimeUtil.RelativePath(avatarTransform.gameObject, ((Transform) newTarget).gameObject);
if (relPath == null) return true; if (relPath == null) return true;
property.stringValue = relPath; property.stringValue = relPath;
@ -122,12 +121,12 @@ namespace nadena.dev.modular_avatar.core.editor
} }
} }
private static VRCAvatarDescriptor findContainingAvatar(SerializedProperty property) private static Transform findContainingAvatarTransform(SerializedProperty property)
{ {
// Find containing object, and from that the avatar // Find containing object, and from that the avatar
if (property.serializedObject == null) return null; if (property.serializedObject == null) return null;
VRCAvatarDescriptor commonAvatar = null; Transform commonAvatar = null;
var targets = property.serializedObject.targetObjects; var targets = property.serializedObject.targetObjects;
for (int i = 0; i < targets.Length; i++) for (int i = 0; i < targets.Length; i++)
{ {
@ -135,7 +134,7 @@ namespace nadena.dev.modular_avatar.core.editor
if (obj == null) return null; if (obj == null) return null;
var transform = obj.transform; var transform = obj.transform;
var avatar = RuntimeUtil.FindAvatarInParents(transform); var avatar = RuntimeUtil.FindAvatarTransformInParents(transform);
if (i == 0) if (i == 0)
{ {

View File

@ -263,7 +263,7 @@ namespace nadena.dev.modular_avatar.core.editor
{ {
if (_window != null) DestroyImmediate(_window); if (_window != null) DestroyImmediate(_window);
_window = ScriptableObject.CreateInstance<BlendshapeSelectWindow>(); _window = ScriptableObject.CreateInstance<BlendshapeSelectWindow>();
_window.AvatarRoot = RuntimeUtil.FindAvatarInParents(((ModularAvatarBlendshapeSync) target).transform) _window.AvatarRoot = RuntimeUtil.FindAvatarTransformInParents(((ModularAvatarBlendshapeSync) target).transform)
.gameObject; .gameObject;
_window.OfferBinding += OfferBinding; _window.OfferBinding += OfferBinding;
_window.Show(); _window.Show();

View File

@ -50,10 +50,10 @@ namespace nadena.dev.modular_avatar.core.editor
for (int i = 0; i < targets.Length; i++) for (int i = 0; i < targets.Length; i++)
{ {
var t = (ModularAvatarBoneProxy) targets[i]; var t = (ModularAvatarBoneProxy) targets[i];
var av = RuntimeUtil.FindAvatarInParents(t.transform); var avTr = RuntimeUtil.FindAvatarTransformInParents(t.transform);
if (av != null && parentAvatar == null) parentAvatar = av.gameObject; if (avTr != null && parentAvatar == null) parentAvatar = avTr.gameObject;
if (av == null || parentAvatar != av.gameObject) if (avTr == null || parentAvatar != avTr.gameObject)
{ {
suppressTarget = true; suppressTarget = true;
break; break;
@ -91,7 +91,7 @@ namespace nadena.dev.modular_avatar.core.editor
var t = (ModularAvatarBoneProxy) targets[i]; var t = (ModularAvatarBoneProxy) targets[i];
Undo.RecordObjects(targets, "Set targets"); Undo.RecordObjects(targets, "Set targets");
var xform = ((TempObjRef) objRefs[i]).target; var xform = ((TempObjRef) objRefs[i]).target;
if (RuntimeUtil.FindAvatarInParents(xform)?.gameObject != parentAvatar) continue; if (RuntimeUtil.FindAvatarTransformInParents(xform)?.gameObject != parentAvatar) continue;
t.target = xform; t.target = xform;
} }
} }

View File

@ -10,9 +10,9 @@ namespace nadena.dev.modular_avatar.core.editor
private void OnEnable() private void OnEnable()
{ {
var target = (ModularAvatarVisibleHeadAccessory) this.target; var target = (ModularAvatarVisibleHeadAccessory) this.target;
var avatar = RuntimeUtil.FindAvatarInParents(target.transform); var avatar = RuntimeUtil.FindAvatarTransformInParents(target.transform);
if (avatar != null) _processor = new VisibleHeadAccessoryProcessor(avatar); if (avatar != null) _processor = new VisibleHeadAccessoryProcessor(new BuildContext(avatar.gameObject));
} }
protected override void OnInnerInspectorGUI() protected override void OnInnerInspectorGUI()

View File

@ -20,7 +20,7 @@ namespace nadena.dev.modular_avatar.core.editor
var target = targets[0] as Component; var target = targets[0] as Component;
if (target == null) return; if (target == null) return;
if (RuntimeUtil.FindAvatarInParents(target.transform) == null) if (RuntimeUtil.FindAvatarTransformInParents(target.transform) == null)
{ {
EditorGUILayout.HelpBox(Localization.S("hint.not_in_avatar"), MessageType.Warning); EditorGUILayout.HelpBox(Localization.S("hint.not_in_avatar"), MessageType.Warning);
} }

View File

@ -41,11 +41,11 @@ namespace nadena.dev.modular_avatar.core.editor
if (targets.Length == 1) if (targets.Length == 1)
{ {
settings = (ModularAvatarMeshSettings) target; settings = (ModularAvatarMeshSettings) target;
var avatar = RuntimeUtil.FindAvatarInParents(settings.transform); var avatarTransform = RuntimeUtil.FindAvatarTransformInParents(settings.transform);
if (avatar != null) if (avatarTransform != null)
{ {
Component mesh = (Component) target; Component mesh = (Component) target;
merged = MeshSettingsPass.MergeSettings(avatar.transform, mesh.transform); merged = MeshSettingsPass.MergeSettings(avatarTransform, mesh.transform);
haveMerged = true; haveMerged = true;
} }
} }

View File

@ -180,9 +180,8 @@ namespace nadena.dev.modular_avatar.core.editor
[CanBeNull] [CanBeNull]
public Mesh Retarget() public Mesh Retarget()
{ {
var avatar = RuntimeUtil.FindAvatarInParents(renderer.transform); var avatarTransform = RuntimeUtil.FindAvatarTransformInParents(renderer.transform);
if (avatar == null) throw new System.Exception("Could not find avatar in parents of " + renderer.name); if (avatarTransform == null) throw new System.Exception("Could not find avatar in parents of " + renderer.name);
var avatarTransform = avatar.transform;
var avPos = avatarTransform.position; var avPos = avatarTransform.position;
var avRot = avatarTransform.rotation; var avRot = avatarTransform.rotation;

View File

@ -16,7 +16,7 @@ namespace nadena.dev.modular_avatar.core.editor
BuildContext = new BuildContext(context); BuildContext = new BuildContext(context);
} }
toDispose = BuildReport.CurrentReport.ReportingOnAvatar(context.AvatarDescriptor); toDispose = BuildReport.CurrentReport.ReportingOnAvatar(context.AvatarRootObject);
} }
public void OnDeactivate(ndmf.BuildContext context) public void OnDeactivate(ndmf.BuildContext context)

View File

@ -42,7 +42,7 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
seq.Run(BoneProxyPluginPass.Instance); seq.Run(BoneProxyPluginPass.Instance);
seq.Run(VisibleHeadAccessoryPluginPass.Instance); seq.Run(VisibleHeadAccessoryPluginPass.Instance);
seq.Run("World Fixed Object", seq.Run("World Fixed Object",
ctx => new WorldFixedObjectProcessor(ctx.AvatarDescriptor).Process(ctx) ctx => new WorldFixedObjectProcessor().Process(ctx)
); );
seq.Run(ReplaceObjectPluginPass.Instance); seq.Run(ReplaceObjectPluginPass.Instance);
seq.Run(BlendshapeSyncAnimationPluginPass.Instance); seq.Run(BlendshapeSyncAnimationPluginPass.Instance);
@ -186,7 +186,7 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
{ {
protected override void Execute(ndmf.BuildContext context) protected override void Execute(ndmf.BuildContext context)
{ {
new VisibleHeadAccessoryProcessor(context.AvatarDescriptor).Process(MAContext(context)); new VisibleHeadAccessoryProcessor(MAContext(context)).Process();
} }
} }
@ -202,7 +202,7 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
{ {
protected override void Execute(ndmf.BuildContext context) protected override void Execute(ndmf.BuildContext context)
{ {
new BlendshapeSyncAnimationProcessor().OnPreprocessAvatar(context.AvatarRootObject, MAContext(context)); new BlendshapeSyncAnimationProcessor().OnPreprocessAvatar(MAContext(context));
} }
} }

View File

@ -29,7 +29,6 @@ using JetBrains.Annotations;
using UnityEditor; using UnityEditor;
using UnityEditor.Animations; using UnityEditor.Animations;
using UnityEngine; using UnityEngine;
using VRC.SDK3.Avatars.Components;
using VRC.SDKBase.Editor.BuildPipeline; using VRC.SDKBase.Editor.BuildPipeline;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
@ -238,7 +237,7 @@ namespace nadena.dev.modular_avatar.core.editor
{ {
var component = ptr.GetComponent<T>(); var component = ptr.GetComponent<T>();
if (component != null) yield return component; if (component != null) yield return component;
if (ptr.GetComponent<VRCAvatarDescriptor>() != null) break; if (RuntimeUtil.IsAvatarRoot(ptr)) break;
ptr = ptr.parent; ptr = ptr.parent;
} }
} }

View File

@ -2,7 +2,6 @@
using nadena.dev.modular_avatar.editor.ErrorReporting; using nadena.dev.modular_avatar.editor.ErrorReporting;
using UnityEngine; using UnityEngine;
using UnityEngine.Animations; using UnityEngine.Animations;
using VRC.SDK3.Avatars.Components;
using VRC.SDK3.Dynamics.PhysBone.Components; using VRC.SDK3.Dynamics.PhysBone.Components;
namespace nadena.dev.modular_avatar.core.editor namespace nadena.dev.modular_avatar.core.editor
@ -19,21 +18,23 @@ namespace nadena.dev.modular_avatar.core.editor
InPhysBoneChain InPhysBoneChain
} }
private VRCAvatarDescriptor _avatar; private BuildContext _context;
private Transform _avatarTransform;
private HashSet<Transform> _activeBones = new HashSet<Transform>(); private HashSet<Transform> _activeBones = new HashSet<Transform>();
private Transform _headBone; private Transform _headBone;
private HashSet<Transform> _visibleBones = new HashSet<Transform>(); private HashSet<Transform> _visibleBones = new HashSet<Transform>();
private Transform _proxyHead; private Transform _proxyHead;
public VisibleHeadAccessoryProcessor(VRCAvatarDescriptor avatar) public VisibleHeadAccessoryProcessor(BuildContext context)
{ {
_avatar = avatar; _context = context;
_avatarTransform = context.AvatarRootTransform;
var animator = avatar.GetComponent<Animator>(); var animator = _avatarTransform.GetComponent<Animator>();
_headBone = animator != null ? animator.GetBoneTransform(HumanBodyBones.Head) : null; _headBone = animator != null ? animator.GetBoneTransform(HumanBodyBones.Head) : null;
foreach (var physBone in avatar.GetComponentsInChildren<VRCPhysBone>(true)) foreach (var physBone in _avatarTransform.GetComponentsInChildren<VRCPhysBone>(true))
{ {
var boneRoot = physBone.rootTransform != null ? physBone.rootTransform : physBone.transform; var boneRoot = physBone.rootTransform != null ? physBone.rootTransform : physBone.transform;
var ignored = new HashSet<Transform>(physBone.ignoreTransforms); var ignored = new HashSet<Transform>(physBone.ignoreTransforms);
@ -56,11 +57,11 @@ namespace nadena.dev.modular_avatar.core.editor
} }
} }
public void Process(BuildContext context) public void Process()
{ {
bool didWork = false; bool didWork = false;
foreach (var target in _avatar.GetComponentsInChildren<ModularAvatarVisibleHeadAccessory>(true)) foreach (var target in _avatarTransform.GetComponentsInChildren<ModularAvatarVisibleHeadAccessory>(true))
{ {
var w = BuildReport.ReportingObject(target, () => Process(target)); var w = BuildReport.ReportingObject(target, () => Process(target));
didWork = didWork || w; didWork = didWork || w;
@ -69,10 +70,10 @@ namespace nadena.dev.modular_avatar.core.editor
if (didWork) if (didWork)
{ {
// Process meshes // Process meshes
foreach (var smr in _avatar.GetComponentsInChildren<SkinnedMeshRenderer>(true)) foreach (var smr in _avatarTransform.GetComponentsInChildren<SkinnedMeshRenderer>(true))
{ {
BuildReport.ReportingObject(smr, BuildReport.ReportingObject(smr,
() => new VisibleHeadAccessoryMeshProcessor(smr, _visibleBones, _proxyHead).Retarget(context)); () => new VisibleHeadAccessoryMeshProcessor(smr, _visibleBones, _proxyHead).Retarget(_context));
} }
} }
} }

View File

@ -3,25 +3,20 @@ using nadena.dev.modular_avatar.editor.ErrorReporting;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
using UnityEngine.Animations; using UnityEngine.Animations;
using VRC.SDK3.Avatars.Components;
namespace nadena.dev.modular_avatar.core.editor namespace nadena.dev.modular_avatar.core.editor
{ {
internal class WorldFixedObjectProcessor internal class WorldFixedObjectProcessor
{ {
private BuildContext _context; private BuildContext _context;
private VRCAvatarDescriptor _avatar; private Transform _avatarTransform;
private Transform _proxy; private Transform _proxy;
public WorldFixedObjectProcessor(VRCAvatarDescriptor avatar)
{
_avatar = avatar;
}
public void Process(BuildContext context) public void Process(BuildContext context)
{ {
_avatarTransform = context.AvatarRootTransform;
_context = context; _context = context;
foreach (var target in _avatar.GetComponentsInChildren<ModularAvatarWorldFixedObject>(true) foreach (var target in _avatarTransform.GetComponentsInChildren<ModularAvatarWorldFixedObject>(true)
.OrderByDescending(x => NestCount(x.transform))) .OrderByDescending(x => NestCount(x.transform)))
BuildReport.ReportingObject(target, () => Process(target)); BuildReport.ReportingObject(target, () => Process(target));
} }
@ -66,7 +61,7 @@ namespace nadena.dev.modular_avatar.core.editor
var fixedGameObject = AssetDatabase.LoadAssetAtPath<GameObject>( var fixedGameObject = AssetDatabase.LoadAssetAtPath<GameObject>(
AssetDatabase.GUIDToAssetPath("78828bfbcb4cb4ce3b00de044eb2d927")); AssetDatabase.GUIDToAssetPath("78828bfbcb4cb4ce3b00de044eb2d927"));
var avatarRoot = _avatar.transform; var avatarRoot = _avatarTransform;
GameObject obj = new GameObject("(MA WorldFixedRoot)"); GameObject obj = new GameObject("(MA WorldFixedRoot)");
obj.transform.SetParent(avatarRoot, false); obj.transform.SetParent(avatarRoot, false);

View File

@ -1,6 +1,5 @@
using System; using System;
using UnityEngine; using UnityEngine;
using VRC.SDK3.Avatars.Components;
namespace nadena.dev.modular_avatar.core namespace nadena.dev.modular_avatar.core
{ {
@ -34,16 +33,16 @@ namespace nadena.dev.modular_avatar.core
RuntimeUtil.OnHierarchyChanged -= InvalidateCache; RuntimeUtil.OnHierarchyChanged -= InvalidateCache;
RuntimeUtil.OnHierarchyChanged += InvalidateCache; RuntimeUtil.OnHierarchyChanged += InvalidateCache;
var avatar = RuntimeUtil.FindAvatarInParents(container.transform); var avatarTransform = RuntimeUtil.FindAvatarTransformInParents(container.transform);
if (avatar == null) return (_cachedReference = null); if (avatarTransform == null) return (_cachedReference = null);
if (referencePath == AVATAR_ROOT) if (referencePath == AVATAR_ROOT)
{ {
_cachedReference = avatar.gameObject; _cachedReference = avatarTransform.gameObject;
return _cachedReference; return _cachedReference;
} }
_cachedReference = avatar.transform.Find(referencePath)?.gameObject; _cachedReference = avatarTransform.Find(referencePath)?.gameObject;
if (_cachedReference == null) return null; if (_cachedReference == null) return null;
// https://github.com/bdunderscore/modular-avatar/issues/308 // https://github.com/bdunderscore/modular-avatar/issues/308
@ -72,7 +71,7 @@ namespace nadena.dev.modular_avatar.core
{ {
referencePath = ""; referencePath = "";
} }
else if (target.GetComponent<VRCAvatarDescriptor>() != null) else if (RuntimeUtil.IsAvatarRoot(target.transform))
{ {
referencePath = AVATAR_ROOT; referencePath = AVATAR_ROOT;
} }

View File

@ -161,20 +161,20 @@ namespace nadena.dev.modular_avatar.core
return null; return null;
} }
var avatar = RuntimeUtil.FindAvatarInParents(transform); var avatarTransform = RuntimeUtil.FindAvatarTransformInParents(transform);
if (avatar == null) return null; if (avatarTransform == null) return null;
if (subPath == "$$AVATAR") if (subPath == "$$AVATAR")
{ {
return avatar.transform; return avatarTransform;
} }
if (boneReference == HumanBodyBones.LastBone) if (boneReference == HumanBodyBones.LastBone)
{ {
return avatar.transform.Find(subPath); return avatarTransform.Find(subPath);
} }
var animator = avatar.GetComponent<Animator>(); var animator = avatarTransform.GetComponent<Animator>();
if (animator == null) return null; if (animator == null) return null;
var bone = animator.GetBoneTransform(boneReference); var bone = animator.GetBoneTransform(boneReference);
if (bone == null) return null; if (bone == null) return null;
@ -184,9 +184,9 @@ namespace nadena.dev.modular_avatar.core
private void UpdateStaticMapping(Transform newTarget) private void UpdateStaticMapping(Transform newTarget)
{ {
var avatar = RuntimeUtil.FindAvatarInParents(transform); var avatarTransform = RuntimeUtil.FindAvatarTransformInParents(transform);
var humanBones = new Dictionary<Transform, HumanBodyBones>(); var humanBones = new Dictionary<Transform, HumanBodyBones>();
var animator = avatar.GetComponent<Animator>(); var animator = avatarTransform.GetComponent<Animator>();
if (animator == null) if (animator == null)
{ {
return; return;
@ -201,7 +201,6 @@ namespace nadena.dev.modular_avatar.core
} }
Transform iter = newTarget; Transform iter = newTarget;
Transform avatarTransform = avatar.transform;
if (newTarget == avatarTransform) if (newTarget == avatarTransform)
{ {

View File

@ -163,7 +163,7 @@ namespace nadena.dev.modular_avatar.core
public void InferPrefixSuffix() public void InferPrefixSuffix()
{ {
// We only infer if targeting the armature (below the Hips bone) // We only infer if targeting the armature (below the Hips bone)
var rootAnimator = RuntimeUtil.FindAvatarInParents(transform)?.GetComponent<Animator>(); var rootAnimator = RuntimeUtil.FindAvatarTransformInParents(transform)?.GetComponent<Animator>();
if (rootAnimator == null) return; if (rootAnimator == null) return;
var hips = rootAnimator.GetBoneTransform(HumanBodyBones.Hips); var hips = rootAnimator.GetBoneTransform(HumanBodyBones.Hips);

View File

@ -63,28 +63,18 @@ namespace nadena.dev.modular_avatar.core
[CanBeNull] [CanBeNull]
public static string RelativePath(GameObject root, GameObject child) public static string RelativePath(GameObject root, GameObject child)
{ {
if (root == child) return ""; return ndmf.runtime.RuntimeUtil.RelativePath(root, child);
List<string> pathSegments = new List<string>();
while (child != root && child != null)
{
pathSegments.Add(child.name);
child = child.transform.parent?.gameObject;
}
if (child == null && root != null) return null;
pathSegments.Reverse();
return String.Join("/", pathSegments);
} }
[CanBeNull] [CanBeNull]
public static string AvatarRootPath(GameObject child) public static string AvatarRootPath(GameObject child)
{ {
if (child == null) return null; return ndmf.runtime.RuntimeUtil.AvatarRootPath(child);
var avatar = FindAvatarInParents(child.transform); }
if (avatar == null) return null;
return RelativePath(avatar.gameObject, child); public static bool IsAvatarRoot(Transform target)
{
return ndmf.runtime.RuntimeUtil.IsAvatarRoot(target);
} }
public static VRCAvatarDescriptor FindAvatarInParents(Transform target) public static VRCAvatarDescriptor FindAvatarInParents(Transform target)
@ -99,6 +89,11 @@ namespace nadena.dev.modular_avatar.core
return null; return null;
} }
public static Transform FindAvatarTransformInParents(Transform target)
{
return ndmf.runtime.RuntimeUtil.FindAvatarInParents(target);
}
public static void MarkDirty(UnityEngine.Object obj) public static void MarkDirty(UnityEngine.Object obj)
{ {
#if UNITY_EDITOR #if UNITY_EDITOR
@ -154,11 +149,8 @@ namespace nadena.dev.modular_avatar.core
#endif #endif
} }
#if UNITY_EDITOR public static bool isPlaying => ndmf.runtime.RuntimeUtil.IsPlaying;
public static bool isPlaying => UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode;
#else
public static bool isPlaying => true;
#endif
public static void InvokeHierarchyChanged() public static void InvokeHierarchyChanged()
{ {
OnHierarchyChanged?.Invoke(); OnHierarchyChanged?.Invoke();

View File

@ -1,7 +1,8 @@
{ {
"name": "nadena.dev.modular-avatar.core", "name": "nadena.dev.modular-avatar.core",
"references": [ "references": [
"GUID:2665a8d13d1b3f18800f46e256720795" "GUID:2665a8d13d1b3f18800f46e256720795",
"GUID:fe747755f7b44e048820525b07f9b956"
], ],
"includePlatforms": [], "includePlatforms": [],
"excludePlatforms": [], "excludePlatforms": [],

View File

@ -105,7 +105,7 @@ namespace modular_avatar_tests.ReplaceObject
BuildReport.Clear(); BuildReport.Clear();
Assert.Throws<Exception>(() => Assert.Throws<Exception>(() =>
{ {
using (BuildReport.CurrentReport.ReportingOnAvatar(root.GetComponent<VRCAvatarDescriptor>())) using (BuildReport.CurrentReport.ReportingOnAvatar(root))
{ {
Process(root); Process(root);
} }
@ -125,7 +125,7 @@ namespace modular_avatar_tests.ReplaceObject
BuildReport.Clear(); BuildReport.Clear();
Assert.Throws<Exception>(() => Assert.Throws<Exception>(() =>
{ {
using (BuildReport.CurrentReport.ReportingOnAvatar(root.GetComponent<VRCAvatarDescriptor>())) using (BuildReport.CurrentReport.ReportingOnAvatar(root))
{ {
Process(root); Process(root);
} }

View File

@ -19,7 +19,7 @@ public class WorldFixedObjectTest : TestBase
var buildContext = new BuildContext(descriptor); var buildContext = new BuildContext(descriptor);
buildContext.PluginBuildContext.ActivateExtensionContext<AnimationServicesContext>(); buildContext.PluginBuildContext.ActivateExtensionContext<AnimationServicesContext>();
new WorldFixedObjectProcessor(descriptor).Process(buildContext); new WorldFixedObjectProcessor().Process(buildContext);
var fixedRoot = avatar.transform.Find("(MA WorldFixedRoot)"); var fixedRoot = avatar.transform.Find("(MA WorldFixedRoot)");
var movedFixedObject = avatar.transform.Find("(MA WorldFixedRoot)/FixedObject"); var movedFixedObject = avatar.transform.Find("(MA WorldFixedRoot)/FixedObject");
@ -46,7 +46,7 @@ public class WorldFixedObjectTest : TestBase
var buildContext = new BuildContext(descriptor); var buildContext = new BuildContext(descriptor);
buildContext.PluginBuildContext.ActivateExtensionContext<AnimationServicesContext>(); buildContext.PluginBuildContext.ActivateExtensionContext<AnimationServicesContext>();
new WorldFixedObjectProcessor(descriptor).Process(buildContext); new WorldFixedObjectProcessor().Process(buildContext);
var fixedRoot = avatar.transform.Find("(MA WorldFixedRoot)"); var fixedRoot = avatar.transform.Find("(MA WorldFixedRoot)");
var movedFixedObject = avatar.transform.Find("(MA WorldFixedRoot)/FixedObject"); var movedFixedObject = avatar.transform.Find("(MA WorldFixedRoot)/FixedObject");