fix/reimplement: scale adjuster results in infinite loops sometimes (#677)

Closes: #676
This commit is contained in:
bd_ 2024-02-18 20:21:26 +09:00 committed by GitHub
parent e01b707916
commit 197d847514
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 585 additions and 275 deletions

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cf056e4454ad43ababbfff1dd06ec1d0
timeCreated: 1708235917

View File

@ -0,0 +1,59 @@
#region
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using HarmonyLib;
using JetBrains.Annotations;
using UnityEditor.SceneManagement;
using UnityEngine;
#endregion
namespace nadena.dev.modular_avatar.core.editor.HarmonyPatches
{
/// <summary>
/// Try to prevent various internal objects from showing up in the Prefab Overrides window...
/// </summary>
internal class HideScaleAdjusterFromPrefabOverrideView
{
internal static Type t_PrefabOverrides;
internal static PropertyInfo p_AddedGameObjects, p_ObjectOverrides;
internal static void Patch(Harmony harmony)
{
var t_PrefabOverridesTreeView = AccessTools.TypeByName("UnityEditor.PrefabOverridesTreeView");
var m_GetPrefabOverrides = AccessTools.Method(t_PrefabOverridesTreeView, "GetPrefabOverrides");
var m_postfix = AccessTools.Method(typeof(HideScaleAdjusterFromPrefabOverrideView), "Postfix");
t_PrefabOverrides = AccessTools.TypeByName("UnityEditor.PrefabOverridesTreeView+PrefabOverrides");
p_AddedGameObjects = AccessTools.Property(t_PrefabOverrides, "addedGameObjects");
p_ObjectOverrides = AccessTools.Property(t_PrefabOverrides, "objectOverrides");
harmony.Patch(original: m_GetPrefabOverrides, postfix: new HarmonyMethod(m_postfix));
}
[UsedImplicitly]
private static void Postfix(GameObject prefabInstance, object __result)
{
var ignoredObjects = prefabInstance.GetComponentsInChildren<ScaleAdjusterRenderer>()
.Select(sar => sar.gameObject)
.ToImmutableHashSet();
List<AddedGameObject> added = p_AddedGameObjects.GetValue(__result) as List<AddedGameObject>;
if (added == null) return;
added.RemoveAll(obj => ignoredObjects.Contains(obj.instanceGameObject));
List<ObjectOverride> objectOverrides = p_ObjectOverrides.GetValue(__result) as List<ObjectOverride>;
if (objectOverrides == null) return;
objectOverrides.RemoveAll(oo =>
{
var c = oo.instanceObject as Component;
return c != null && ignoredObjects.Contains(c.gameObject);
});
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0893522c012a46358e5ecf1df6628b2e
timeCreated: 1708237029

View File

@ -0,0 +1,21 @@
#region
using HarmonyLib;
using UnityEditor;
#endregion
namespace nadena.dev.modular_avatar.core.editor.HarmonyPatches
{
internal class PatchLoader
{
[InitializeOnLoadMethod]
static void ApplyPatches()
{
var harmony = new Harmony("nadena.dev.modular_avatar");
SnoopHeaderRendering.Patch(harmony);
HideScaleAdjusterFromPrefabOverrideView.Patch(harmony);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e92aedf0fa324b8fb1b425a7fc2b1771
timeCreated: 1708235934

View File

@ -0,0 +1,40 @@
#region
using HarmonyLib;
using JetBrains.Annotations;
using UnityEngine;
#endregion
namespace nadena.dev.modular_avatar.core.editor.HarmonyPatches
{
/// <summary>
/// ScaleAdjusterRenderer toggles off the enabled state of the original mesh just before rendering,
/// in order to allow us to effectively replace it at rendering time. We restore this in OnPostRender,
/// but GUI rendering can happen before this; as such, snoop GUI events and re-enable the original
/// at that time.
/// </summary>
internal class SnoopHeaderRendering
{
internal static void Patch(Harmony harmony)
{
var t_orig = AccessTools.TypeByName("UnityEditor.UIElements.EditorElement");
var m_orig = AccessTools.Method(t_orig, "HeaderOnGUI");
var m_prefix = AccessTools.Method(typeof(SnoopHeaderRendering), "Prefix");
harmony.Patch(original: m_orig, prefix: new HarmonyMethod(m_prefix));
var t_GUIUtility = typeof(GUIUtility);
var m_ProcessEvent = AccessTools.Method(t_GUIUtility, "ProcessEvent");
harmony.Patch(original: m_ProcessEvent, prefix: new HarmonyMethod(m_prefix));
}
[UsedImplicitly]
private static void Prefix()
{
ScaleAdjusterRenderer.ClearAllOverrides();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cafb5b7e681644cbbeafbeb12d833f6e
timeCreated: 1708235926

View File

@ -0,0 +1,28 @@
{
"name": "nadena.dev.modular-avatar.harmony-patches",
"rootNamespace": "",
"references": [
"nadena.dev.modular-avatar.core",
"nadena.dev.modular-avatar.core.editor",
"VRC.SDKBase.Editor"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [
"VRCSDK_HAS_HARMONY"
],
"versionDefines": [
{
"name": "com.vrchat.base",
"expression": "(3.4.999,)",
"define": "VRCSDK_HAS_HARMONY"
}
],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 0f9cfa335019051479cc80ce30c95a68
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -36,6 +36,7 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
{
seq.Run(ClearEditorOnlyTags.Instance);
seq.Run(MeshSettingsPluginPass.Instance);
seq.Run(ScaleAdjusterPass.Instance);
#if MA_VRCSDK3_AVATARS
seq.Run(RenameParametersPluginPass.Instance);
seq.Run(MergeBlendTreePass.Instance);

View File

@ -0,0 +1,51 @@
using System.Collections.Generic;
using System.Linq;
using nadena.dev.ndmf;
using UnityEditor.EditorTools;
using UnityEngine;
namespace nadena.dev.modular_avatar.core.editor
{
internal class ScaleAdjusterPass : Pass<ScaleAdjusterPass>
{
protected override void Execute(ndmf.BuildContext context)
{
ScaleAdjusterRenderer.ClearAllOverrides();
Dictionary<Transform, Transform> boneMappings = new Dictionary<Transform, Transform>();
foreach (var component in context.AvatarRootObject.GetComponentsInChildren<ScaleProxy>())
{
var proxyTransform = component.transform;
var parentAdjuster = component.transform.parent?.GetComponent<ModularAvatarScaleAdjuster>();
if (parentAdjuster != null)
{
UnityEngine.Object.DestroyImmediate(component);
proxyTransform.localScale = parentAdjuster.Scale;
UnityEngine.Object.DestroyImmediate(parentAdjuster);
boneMappings.Add(proxyTransform.parent, proxyTransform);
}
}
foreach (var sar in context.AvatarRootObject.GetComponentsInChildren<ScaleAdjusterRenderer>())
{
UnityEngine.Object.DestroyImmediate(sar.gameObject);
}
foreach (var smr in context.AvatarRootObject.GetComponentsInChildren<SkinnedMeshRenderer>())
{
var bones = smr.bones;
for (int i = 0; i < bones.Length; i++)
{
if (boneMappings.TryGetValue(bones[i], out var newBone))
{
bones[i] = newBone;
}
}
smr.bones = bones;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 62b78456069047e0949dd1d8c4b25edc
timeCreated: 1708253304

View File

@ -1,4 +1,9 @@
using System.Runtime.CompilerServices;
#region
using System.Runtime.CompilerServices;
#endregion
[assembly: InternalsVisibleTo("net.fushizen.xdress")]
[assembly: InternalsVisibleTo("net.fushizen.xdress.editor")]
[assembly: InternalsVisibleTo("net.fushizen.xdress.editor")]
[assembly: InternalsVisibleTo("nadena.dev.modular-avatar.harmony-patches")]

View File

@ -1,268 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
namespace nadena.dev.modular_avatar.core
{
[Serializable]
internal struct ScalePatch
{
public SkinnedMeshRenderer smr;
public int boneIndex;
public ScalePatch(SkinnedMeshRenderer smr, int boneIndex)
{
this.smr = smr;
this.boneIndex = boneIndex;
}
public bool Equals(ScalePatch other)
{
return smr.Equals(other.smr) && boneIndex == other.boneIndex;
}
public override bool Equals(object obj)
{
return obj is ScalePatch other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
return (smr.GetHashCode() * 397) ^ boneIndex;
}
}
}
[ExecuteInEditMode]
[DisallowMultipleComponent]
[AddComponentMenu("Modular Avatar/MA Scale Adjuster")]
[HelpURL("https://modular-avatar.nadena.dev/docs/reference/scale-adjuster?lang=auto")]
public sealed class ModularAvatarScaleAdjuster : AvatarTagComponent
{
[SerializeField] private Vector3 m_Scale = Vector3.one;
public Vector3 Scale
{
get => m_Scale;
set
{
m_Scale = value;
Update();
}
}
[SerializeField] internal Transform scaleProxy;
[SerializeField] private List<ScalePatch> patches = new List<ScalePatch>();
private bool initialized = false;
#if UNITY_EDITOR
private void Update()
{
if (this == null) return;
PatchRenderers();
scaleProxy.localScale = m_Scale;
}
void OnValidate()
{
initialized = false;
EditorApplication.delayCall += Update;
}
private void PatchRenderers()
{
if (initialized || this == null) return;
if (PrefabUtility.IsPartOfPrefabInstance(this))
{
// Ensure we're using the same ScaleProxy as the corresponding prefab asset.
var prefab = PrefabUtility.GetCorrespondingObjectFromSource(this);
if (this.scaleProxy == null || prefab.scaleProxy == null || prefab.scaleProxy !=
PrefabUtility.GetCorrespondingObjectFromSource(this.scaleProxy))
{
if (prefab.scaleProxy == null && scaleProxy != null)
{
// Push our ScaleProxy down into the prefab (this happens after applying the ScaleAdjuster
// component to a prefab)
var assetPath = AssetDatabase.GetAssetPath(prefab);
PrefabUtility.ApplyAddedGameObject(scaleProxy.gameObject, assetPath,
InteractionMode.AutomatedAction);
prefab.scaleProxy = PrefabUtility.GetCorrespondingObjectFromSource(this.scaleProxy);
}
else
{
// Clear any duplicate scaleProxy we have
if (scaleProxy != null) DestroyImmediate(scaleProxy.gameObject);
}
var so = new SerializedObject(this);
var sp = so.FindProperty(nameof(scaleProxy));
PrefabUtility.RevertPropertyOverride(sp, InteractionMode.AutomatedAction);
so.ApplyModifiedPropertiesWithoutUndo();
// Find the corresponding child
foreach (Transform t in transform)
{
if (PrefabUtility.GetCorrespondingObjectFromSource(t) == prefab.scaleProxy)
{
scaleProxy = t;
break;
}
}
}
}
if (scaleProxy == null && !PrefabUtility.IsPartOfPrefabAsset(this))
{
scaleProxy = new GameObject(gameObject.name + " (Scale Proxy)").transform;
scaleProxy.SetParent(transform, false);
scaleProxy.localPosition = Vector3.zero;
scaleProxy.localRotation = Quaternion.identity;
scaleProxy.localScale = m_Scale;
scaleProxy.gameObject.AddComponent<ScaleProxy>();
PrefabUtility.RecordPrefabInstancePropertyModifications(this);
}
if (scaleProxy != null)
{
scaleProxy.hideFlags = HideFlags.HideInHierarchy;
RewriteBoneReferences(transform, scaleProxy);
}
initialized = true;
}
private void RewriteBoneReferences(Transform oldBone, Transform newBone, Transform selfTransform = null)
{
if (selfTransform == null) selfTransform = transform;
var prefabNewBone = PrefabUtility.GetCorrespondingObjectFromSource(newBone);
var oldPatches = new HashSet<ScalePatch>(this.patches);
var newPatches = new HashSet<ScalePatch>();
var avatarRoot = RuntimeUtil.FindAvatarTransformInParents(selfTransform);
if (avatarRoot != null)
{
foreach (var smr in avatarRoot.GetComponentsInChildren<SkinnedMeshRenderer>(true))
{
var serializedObject = new SerializedObject(smr);
var bonesArray = serializedObject.FindProperty("m_Bones");
int boneCount = bonesArray.arraySize;
var parentSmr = PrefabUtility.GetCorrespondingObjectFromSource(smr);
var parentBones = parentSmr != null ? parentSmr.bones : null;
var propMods = PrefabUtility.GetPropertyModifications(smr);
bool changed = false;
for (int i = 0; i < boneCount; i++)
{
var boneProp = bonesArray.GetArrayElementAtIndex(i);
var bone = boneProp.objectReferenceValue as Transform;
if (bone == oldBone || bone == newBone ||
(bone == null && oldPatches.Contains(new ScalePatch(smr, i))))
{
if (parentBones != null && parentBones[i] == prefabNewBone)
{
// Remove any prefab overrides for this bone entry
changed = boneProp.objectReferenceValue != newBone;
boneProp.objectReferenceValue = newBone;
serializedObject.ApplyModifiedPropertiesWithoutUndo();
PrefabUtility.RevertPropertyOverride(boneProp, InteractionMode.AutomatedAction);
}
else
{
boneProp.objectReferenceValue = newBone;
changed = true;
}
newPatches.Add(new ScalePatch(smr, i));
}
}
if (changed)
{
serializedObject.ApplyModifiedPropertiesWithoutUndo();
ConfigurePrefab();
}
}
if (this != null && newPatches != oldPatches)
{
this.patches = newPatches.ToList();
PrefabUtility.RecordPrefabInstancePropertyModifications(this);
}
}
}
private void ConfigurePrefab()
{
if (this == null || !PrefabUtility.IsPartOfPrefabInstance(this)) return;
var source = PrefabUtility.GetCorrespondingObjectFromSource(this);
var path = AssetDatabase.GetAssetPath(source);
var root = PrefabUtility.LoadPrefabContents(path);
foreach (var obj in root.GetComponentsInChildren<ModularAvatarScaleAdjuster>())
{
obj.PatchRenderers();
}
PrefabUtility.SaveAsPrefabAsset(root, path);
PrefabUtility.UnloadPrefabContents(root);
initialized = false;
}
protected override void OnDestroy()
{
base.OnDestroy();
UnpatchRenderers();
}
private void UnpatchRenderers()
{
var scaleProxy2 = this.scaleProxy;
var transform2 = this.transform;
EditorApplication.delayCall += () =>
{
if (scaleProxy2 == null) return;
if (transform2 != null)
{
RewriteBoneReferences(scaleProxy2, transform2, transform2);
}
try
{
DestroyImmediate(scaleProxy2.gameObject);
}
catch (InvalidOperationException e)
{
// not supported in Unity 2019...
}
};
}
#else
private void Update()
{
// placeholder to make builds work
}
#endif
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 96f10f3195df42bba58b9efb61948b9c
timeCreated: 1708232523

View File

@ -0,0 +1,128 @@
#region
using UnityEngine;
using UnityEngine.Serialization;
#if UNITY_EDITOR
using UnityEditor;
#endif
#endregion
namespace nadena.dev.modular_avatar.core
{
[ExecuteInEditMode]
[DisallowMultipleComponent]
[AddComponentMenu("Modular Avatar/MA Scale Adjuster")]
[HelpURL("https://modular-avatar.nadena.dev/docs/reference/scale-adjuster?lang=auto")]
public sealed class ModularAvatarScaleAdjuster : AvatarTagComponent
{
private const string ADJUSTER_OBJECT = "MA Scale Adjuster Proxy Renderer";
[SerializeField] private Vector3 m_Scale = Vector3.one;
public Vector3 Scale
{
get => m_Scale;
set
{
m_Scale = value;
Update();
}
}
[SerializeField] [FormerlySerializedAs("scaleProxy")]
internal Transform legacyScaleProxy;
internal Transform scaleProxy;
private bool initialized = false;
#if UNITY_EDITOR
void OnValidate()
{
base.OnValidate();
initialized = false;
}
private void Update()
{
if (scaleProxy == null || initialized == false)
{
InitializeProxy();
}
if (legacyScaleProxy != null && !PrefabUtility.IsPartOfPrefabAsset(legacyScaleProxy))
{
DestroyImmediate(legacyScaleProxy.gameObject);
legacyScaleProxy = null;
}
scaleProxy.localScale = m_Scale;
}
private void InitializeProxy()
{
if (scaleProxy == null)
{
scaleProxy = new GameObject(gameObject.name + " (Scale Proxy)").transform;
scaleProxy.SetParent(transform, false);
scaleProxy.localPosition = Vector3.zero;
scaleProxy.localRotation = Quaternion.identity;
scaleProxy.localScale = m_Scale;
scaleProxy.gameObject.AddComponent<ScaleProxy>();
PrefabUtility.RecordPrefabInstancePropertyModifications(this);
}
ConfigureRenderers();
initialized = true;
}
private void OnDestroy()
{
if (scaleProxy != null)
{
DestroyImmediate(scaleProxy.gameObject);
}
ScaleAdjusterRenderer.InvalidateAll();
base.OnDestroy();
}
private void ConfigureRenderers()
{
var avatar = RuntimeUtil.FindAvatarInParents(transform);
if (avatar == null) return;
foreach (var smr in avatar.GetComponentsInChildren<SkinnedMeshRenderer>(true))
{
if (smr.GetComponent<ScaleAdjusterRenderer>() != null) continue;
var child = smr.transform.Find(ADJUSTER_OBJECT)?.GetComponent<ScaleAdjusterRenderer>();
if (child == null)
{
var childObj = new GameObject(ADJUSTER_OBJECT);
var childSmr = childObj.AddComponent<SkinnedMeshRenderer>();
EditorUtility.CopySerialized(smr, childSmr);
child = childObj.AddComponent<ScaleAdjusterRenderer>();
child.transform.SetParent(smr.transform, false);
child.transform.localPosition = Vector3.zero;
child.transform.localRotation = Quaternion.identity;
child.transform.localScale = Vector3.one;
}
child.BoneMappings[transform] = scaleProxy;
child.ClearBoneCache();
}
}
#endif
#if !UNITY_EDITOR
private void Update()
{
// placeholder to make builds work
}
#endif
}
}

View File

@ -0,0 +1,178 @@
#region
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using VRC.SDKBase;
#endregion
namespace nadena.dev.modular_avatar.core
{
[ExecuteInEditMode]
//[AddComponentMenu("")]
[RequireComponent(typeof(SkinnedMeshRenderer))]
internal class ScaleAdjusterRenderer : MonoBehaviour, IEditorOnly
{
private static event Action OnPreInspector;
private static int RecreateHierarchyIndexCount = 0;
#if UNITY_EDITOR
[UnityEditor.InitializeOnLoadMethod]
static void Setup()
{
UnityEditor.EditorApplication.hierarchyChanged += InvalidateAll;
}
#endif
internal static void InvalidateAll()
{
RecreateHierarchyIndexCount++;
}
private SkinnedMeshRenderer myRenderer;
private SkinnedMeshRenderer parentRenderer;
private bool wasActive = false;
private bool redoBoneMappings = true;
private int lastRecreateHierarchyIndex = -1;
internal Dictionary<Transform, Transform> BoneMappings = new Dictionary<Transform, Transform>();
#if UNITY_EDITOR
private void OnValidate()
{
if (UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this)) return;
redoBoneMappings = true;
UnityEditor.EditorApplication.delayCall += () =>
{
if (this == null) return;
#if MODULAR_AVATAR_DEBUG_HIDDEN
gameObject.hideFlags = HideFlags.None;
#else
gameObject.hideFlags = HideFlags.HideInHierarchy | HideFlags.DontSaveInBuild;
#endif
if (BoneMappings == null)
{
BoneMappings = new Dictionary<Transform, Transform>();
}
};
}
#endif
private Transform MapBone(Transform bone)
{
if (bone == null) return null;
if (BoneMappings.TryGetValue(bone, out var newBone) && newBone != null) return newBone;
return bone;
}
private void OnDestroy()
{
ClearOverrides();
}
private void Update()
{
if (myRenderer == null)
{
myRenderer = GetComponent<SkinnedMeshRenderer>();
}
if (parentRenderer == null)
{
parentRenderer = transform.parent.GetComponent<SkinnedMeshRenderer>();
}
myRenderer.sharedMaterials = parentRenderer.sharedMaterials;
myRenderer.sharedMesh = parentRenderer.sharedMesh;
myRenderer.localBounds = parentRenderer.localBounds;
if (redoBoneMappings || lastRecreateHierarchyIndex != RecreateHierarchyIndexCount)
{
var deadBones = BoneMappings.Keys.Where(k => BoneMappings[k] == null)
.ToList();
deadBones.ForEach(k => { BoneMappings.Remove(k); });
if (BoneMappings.Count == 0)
{
DestroyImmediate(gameObject);
return;
}
myRenderer.rootBone = MapBone(parentRenderer.rootBone);
myRenderer.bones = parentRenderer.bones.Select(MapBone).ToArray();
redoBoneMappings = false;
lastRecreateHierarchyIndex = RecreateHierarchyIndexCount;
}
myRenderer.quality = parentRenderer.quality;
myRenderer.shadowCastingMode = parentRenderer.shadowCastingMode;
myRenderer.receiveShadows = parentRenderer.receiveShadows;
myRenderer.lightProbeUsage = parentRenderer.lightProbeUsage;
myRenderer.reflectionProbeUsage = parentRenderer.reflectionProbeUsage;
myRenderer.probeAnchor = parentRenderer.probeAnchor;
myRenderer.motionVectorGenerationMode = parentRenderer.motionVectorGenerationMode;
myRenderer.allowOcclusionWhenDynamic = parentRenderer.allowOcclusionWhenDynamic;
var blendShapeCount = myRenderer.sharedMesh.blendShapeCount;
for (int i = 0; i < blendShapeCount; i++)
{
myRenderer.SetBlendShapeWeight(i, parentRenderer.GetBlendShapeWeight(i));
}
ClearOverrides();
myRenderer.enabled = parentRenderer.enabled;
}
public void OnWillRenderObject()
{
if (myRenderer == null || parentRenderer == null)
{
return;
}
ClearOverrides();
if (!parentRenderer.enabled || !parentRenderer.gameObject.activeInHierarchy)
{
return;
}
parentRenderer.enabled = false;
wasActive = true;
OnPreInspector += ClearOverrides;
}
private void OnPostRender()
{
ClearOverrides();
}
private void ClearOverrides()
{
if (this == null) return;
if (wasActive && parentRenderer != null)
{
parentRenderer.enabled = true;
wasActive = false;
}
}
public void ClearBoneCache()
{
redoBoneMappings = true;
}
internal static void ClearAllOverrides()
{
OnPreInspector?.Invoke();
OnPreInspector = null;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c8bc16baa6c345eea5edf47232ee4069
timeCreated: 1708232586

View File

@ -1,7 +1,12 @@
#if UNITY_EDITOR

#region
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
#endregion
namespace nadena.dev.modular_avatar.core
{
@ -11,9 +16,16 @@ namespace nadena.dev.modular_avatar.core
#if UNITY_EDITOR
void OnValidate()
{
base.OnDestroy();
EditorApplication.delayCall += DeferredValidate;
}
void OnDestroy()
{
ScaleAdjusterRenderer.InvalidateAll();
base.OnDestroy();
}
private void DeferredValidate()
{
if (this == null) return;
@ -23,8 +35,16 @@ namespace nadena.dev.modular_avatar.core
gameObject.AddComponent<ModularAvatarPBBlocker>();
}
var avatar = ndmf.runtime.RuntimeUtil.FindAvatarInParents(transform);
ClearOverrides(avatar);
gameObject.hideFlags = HideFlags.HideInHierarchy;
#if MODULAR_AVATAR_DEBUG_HIDDEN
gameObject.hideFlags = HideFlags.None;
#endif
hideFlags = HideFlags.None;
var parentObject = transform.parent;
var parentScaleAdjuster =
parentObject != null ? parentObject.GetComponent<ModularAvatarScaleAdjuster>() : null;
@ -60,8 +80,19 @@ namespace nadena.dev.modular_avatar.core
while (root.parent != null) root = root.parent;
}
ClearOverrides(root);
DestroyImmediate(gameObject);
}
private void ClearOverrides(Transform root)
{
// This clears bone overrides that date back to the 1.9.0-rc.2 implementation, to ease rc.2 -> rc.3
// migrations. It'll be removed in 1.10.
foreach (var smr in root.GetComponentsInChildren<SkinnedMeshRenderer>(true))
{
if (smr.GetComponent<ScaleAdjusterRenderer>()) continue;
var bones = smr.bones;
bool changed = false;
@ -79,8 +110,6 @@ namespace nadena.dev.modular_avatar.core
smr.bones = bones;
}
}
DestroyImmediate(gameObject);
}
#endif
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f2c964d1229a4ce698db85bec9043608
timeCreated: 1708232304

View File

@ -1,6 +1,11 @@
#region
using System.Runtime.CompilerServices;
#endregion
[assembly: InternalsVisibleTo("nadena.dev.modular-avatar.core.editor")]
[assembly: InternalsVisibleTo("net.fushizen.xdress")]
[assembly: InternalsVisibleTo("net.fushizen.xdress.editor")]
[assembly: InternalsVisibleTo("Tests")]
[assembly: InternalsVisibleTo("Tests")]
[assembly: InternalsVisibleTo("nadena.dev.modular-avatar.harmony-patches")]

View File

@ -40,6 +40,7 @@ namespace modular_avatar_tests
if (type == typeof(AvatarActivator)) return;
if (type == typeof(TestComponent)) return;
if (type == typeof(ScaleProxy)) return;
if (type == typeof(ScaleAdjusterRenderer)) return;
// get icon
var component = (MonoBehaviour) _gameObject.AddComponent(type);
@ -65,6 +66,7 @@ namespace modular_avatar_tests
if (type == typeof(AvatarActivator)) return;
if (type == typeof(TestComponent)) return;
if (type == typeof(ScaleProxy)) return;
if (type == typeof(ScaleAdjusterRenderer)) return;
// get icon
var helpUrl = type.GetCustomAttribute<HelpURLAttribute>();