mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2024-12-29 18:55:06 +08:00
Remove the need for an av3emu patch
This commit is contained in:
parent
cf82c93e55
commit
ab51e9dd82
@ -34,13 +34,34 @@ namespace net.fushizen.modular_avatar.core.editor
|
||||
public static class ApplyOnPlay
|
||||
{
|
||||
private const string MENU_NAME = "Tools/Modular Avatar/Apply on Play";
|
||||
|
||||
|
||||
/**
|
||||
* We need to process avatars before lyuma's av3 emulator wakes up and processes avatars; it does this in Awake,
|
||||
* so we have to do our processing in Awake as well. This seems to work fine when first entering play mode, but
|
||||
* if you subsequently enable an initially-disabled avatar, processing from within Awake causes an editor crash.
|
||||
*
|
||||
* To workaround this, we initially process in awake; then, after OnPlayModeStateChanged is invoked (ie, after
|
||||
* all initially-enabled components have Awake called), we switch to processing from Start instead.
|
||||
*/
|
||||
private static RuntimeUtil.OnDemandSource armedSource = RuntimeUtil.OnDemandSource.Awake;
|
||||
|
||||
static ApplyOnPlay()
|
||||
{
|
||||
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||
RuntimeUtil.OnDemandProcessAvatar = MaybeProcessAvatar;
|
||||
Menu.SetChecked(MENU_NAME, ModularAvatarSettings.applyOnPlay);
|
||||
}
|
||||
|
||||
private static void MaybeProcessAvatar(RuntimeUtil.OnDemandSource source, AvatarTagComponent component)
|
||||
{
|
||||
if (ModularAvatarSettings.applyOnPlay && source == armedSource && component != null)
|
||||
{
|
||||
var avatar = RuntimeUtil.FindAvatarInParents(component.transform);
|
||||
if (avatar == null) return;
|
||||
AvatarProcessor.ProcessAvatar(avatar.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem(MENU_NAME)]
|
||||
private static void ToggleApplyOnPlay()
|
||||
{
|
||||
@ -50,35 +71,9 @@ namespace net.fushizen.modular_avatar.core.editor
|
||||
|
||||
private static void OnPlayModeStateChanged(PlayModeStateChange obj)
|
||||
{
|
||||
if (obj == PlayModeStateChange.EnteredPlayMode && ModularAvatarSettings.applyOnPlay)
|
||||
if (obj == PlayModeStateChange.EnteredPlayMode)
|
||||
{
|
||||
// TODO - only apply modular avatar changes?
|
||||
foreach (var root in SceneManager.GetActiveScene().GetRootGameObjects())
|
||||
{
|
||||
foreach (var avatar in root.GetComponentsInChildren<VRCAvatarDescriptor>(true))
|
||||
{
|
||||
if (avatar.GetComponentsInChildren<AvatarTagComponent>(true).Length > 0)
|
||||
{
|
||||
UnpackPrefabsCompletely(avatar.gameObject);
|
||||
VRCBuildPipelineCallbacks.OnPreprocessAvatar(avatar.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void UnpackPrefabsCompletely(GameObject obj)
|
||||
{
|
||||
if (PrefabUtility.IsAnyPrefabInstanceRoot(obj))
|
||||
{
|
||||
PrefabUtility.UnpackPrefabInstance(obj, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Transform child in obj.transform)
|
||||
{
|
||||
UnpackPrefabsCompletely(child.gameObject);
|
||||
}
|
||||
armedSource = RuntimeUtil.OnDemandSource.Start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,35 +25,17 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using VRC.SDK3.Avatars.Components;
|
||||
using VRC.SDKBase.Editor.BuildPipeline;
|
||||
|
||||
namespace net.fushizen.modular_avatar.core.editor
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
internal class Av3EmuHook
|
||||
internal class AvatarProcessor : IVRCSDKPreprocessAvatarCallback, IVRCSDKPostprocessAvatarCallback
|
||||
{
|
||||
static Av3EmuHook()
|
||||
static AvatarProcessor()
|
||||
{
|
||||
if (EditorApplication.isPlayingOrWillChangePlaymode)
|
||||
{
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
var runtime = assembly.GetType("LyumaAv3Runtime");
|
||||
if (runtime == null) continue;
|
||||
|
||||
var addHook = runtime.GetMethod("AddInitAvatarHook", BindingFlags.Static | BindingFlags.Public);
|
||||
if (addHook == null) continue;
|
||||
|
||||
addHook.Invoke(null, new object[]
|
||||
{
|
||||
-999999,
|
||||
(Action<VRCAvatarDescriptor>)(av => VRCBuildPipelineCallbacks.OnPreprocessAvatar(av.gameObject))
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||
}
|
||||
|
||||
@ -64,5 +46,42 @@ namespace net.fushizen.modular_avatar.core.editor
|
||||
Util.DeleteTemporaryAssets();
|
||||
}
|
||||
}
|
||||
|
||||
public int callbackOrder => -9000;
|
||||
|
||||
public void OnPostprocessAvatar()
|
||||
{
|
||||
Util.DeleteTemporaryAssets();
|
||||
}
|
||||
|
||||
public bool OnPreprocessAvatar(GameObject avatarGameObject)
|
||||
{
|
||||
try
|
||||
{
|
||||
ProcessAvatar(avatarGameObject);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ProcessAvatar(GameObject avatarGameObject)
|
||||
{
|
||||
BoneDatabase.ResetBones();
|
||||
PathMappings.Clear();
|
||||
|
||||
new MergeArmatureHook().OnPreprocessAvatar(avatarGameObject);
|
||||
new RetargetMeshes().OnPreprocessAvatar(avatarGameObject);
|
||||
new BoneProxyProcessor().OnPreprocessAvatar(avatarGameObject);
|
||||
new MergeAnimatorProcessor().OnPreprocessAvatar(avatarGameObject);
|
||||
|
||||
foreach (var component in avatarGameObject.GetComponentsInChildren<AvatarTagComponent>(true))
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -28,11 +28,9 @@ using VRC.SDKBase.Editor.BuildPipeline;
|
||||
|
||||
namespace net.fushizen.modular_avatar.core.editor
|
||||
{
|
||||
public class BoneProxyHook : HookBase
|
||||
internal class BoneProxyProcessor
|
||||
{
|
||||
public override int callbackOrder => HookSequence.SEQ_BONE_PROXY;
|
||||
|
||||
protected override bool OnPreprocessAvatarWrapped(GameObject avatarGameObject)
|
||||
internal void OnPreprocessAvatar(GameObject avatarGameObject)
|
||||
{
|
||||
var boneProxies = avatarGameObject.GetComponentsInChildren<ModularAvatarBoneProxy>(true);
|
||||
|
||||
@ -56,8 +54,6 @@ namespace net.fushizen.modular_avatar.core.editor
|
||||
}
|
||||
Object.DestroyImmediate(proxy);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using VRC.SDKBase.Editor.BuildPipeline;
|
||||
|
||||
namespace net.fushizen.modular_avatar.core.editor
|
||||
{
|
||||
public abstract class HookBase : IVRCSDKPreprocessAvatarCallback
|
||||
{
|
||||
public bool OnPreprocessAvatar(GameObject avatarGameObject)
|
||||
{
|
||||
try
|
||||
{
|
||||
return OnPreprocessAvatarWrapped(avatarGameObject);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract bool OnPreprocessAvatarWrapped(GameObject avatarGameObject);
|
||||
public abstract int callbackOrder { get; }
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d65a87d13d234177b18f1b0fbbf12360
|
||||
timeCreated: 1662780435
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 bd_
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace net.fushizen.modular_avatar.core.editor
|
||||
{
|
||||
internal static class HookSequence
|
||||
{
|
||||
public const int SEQ_RESETTERS = -90000;
|
||||
public const int SEQ_MERGE_ARMATURE = SEQ_RESETTERS + 1;
|
||||
public const int SEQ_RETARGET_MESH = SEQ_MERGE_ARMATURE + 1;
|
||||
public const int SEQ_BONE_PROXY = SEQ_RETARGET_MESH + 1;
|
||||
public const int SEQ_MERGE_ANIMATORS = SEQ_BONE_PROXY + 1;
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e6b3680d07242d38d5b2c6b00951ca0
|
||||
timeCreated: 1661632859
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 bd_
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using VRC.SDKBase.Editor.BuildPipeline;
|
||||
|
||||
namespace net.fushizen.modular_avatar.core.editor
|
||||
{
|
||||
/**
|
||||
* Ensure that any AvatarTagComponents are purged just before upload.
|
||||
*/
|
||||
public class LastResortTagComponentCleaner : HookBase
|
||||
{
|
||||
public override int callbackOrder => 0;
|
||||
|
||||
protected override bool OnPreprocessAvatarWrapped(GameObject avatarGameObject)
|
||||
{
|
||||
foreach (var component in avatarGameObject.GetComponentsInChildren<AvatarTagComponent>(true))
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(component);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 921628a5f4434452bcfa3a926d4ebdac
|
||||
timeCreated: 1661564171
|
@ -31,20 +31,18 @@ using VRC.SDKBase.Editor.BuildPipeline;
|
||||
|
||||
namespace net.fushizen.modular_avatar.core.editor
|
||||
{
|
||||
public class MergeAnimatorHook : HookBase
|
||||
internal class MergeAnimatorProcessor
|
||||
{
|
||||
private const string SAMPLE_PATH_PACKAGE = "Packages/com.vrchat.avatars/Samples/AV3 Demo Assets/Animation/Controllers";
|
||||
private const string SAMPLE_PATH_LEGACY = "Assets/VRCSDK/Examples3/Animation/Controllers";
|
||||
|
||||
public override int callbackOrder => HookSequence.SEQ_MERGE_ANIMATORS;
|
||||
|
||||
private Dictionary<VRCAvatarDescriptor.AnimLayerType, AnimatorController> defaultControllers_ =
|
||||
new Dictionary<VRCAvatarDescriptor.AnimLayerType, AnimatorController>();
|
||||
|
||||
Dictionary<VRCAvatarDescriptor.AnimLayerType, AnimatorCombiner> mergeSessions =
|
||||
new Dictionary<VRCAvatarDescriptor.AnimLayerType, AnimatorCombiner>();
|
||||
|
||||
protected override bool OnPreprocessAvatarWrapped(GameObject avatarGameObject)
|
||||
internal void OnPreprocessAvatar(GameObject avatarGameObject)
|
||||
{
|
||||
defaultControllers_.Clear();
|
||||
mergeSessions.Clear();
|
||||
@ -84,8 +82,6 @@ namespace net.fushizen.modular_avatar.core.editor
|
||||
|
||||
descriptor.baseAnimationLayers = FinishSessions(descriptor.baseAnimationLayers);
|
||||
descriptor.specialAnimationLayers = FinishSessions(descriptor.specialAnimationLayers);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private VRCAvatarDescriptor.CustomAnimLayer[] FinishSessions(
|
@ -34,14 +34,12 @@ using Vector3 = UnityEngine.Vector3;
|
||||
|
||||
namespace net.fushizen.modular_avatar.core.editor
|
||||
{
|
||||
public class MergeArmatureHook : HookBase
|
||||
public class MergeArmatureHook
|
||||
{
|
||||
public override int callbackOrder => HookSequence.SEQ_MERGE_ARMATURE;
|
||||
|
||||
private Dictionary<Transform, Transform> BoneRemappings = new Dictionary<Transform, Transform>();
|
||||
private List<GameObject> ToDelete = new List<GameObject>();
|
||||
|
||||
protected override bool OnPreprocessAvatarWrapped(GameObject avatarGameObject)
|
||||
internal bool OnPreprocessAvatar(GameObject avatarGameObject)
|
||||
{
|
||||
BoneRemappings.Clear();
|
||||
ToDelete.Clear();
|
||||
|
@ -30,16 +30,6 @@ using VRC.SDKBase.Editor.BuildPipeline;
|
||||
|
||||
namespace net.fushizen.modular_avatar.core.editor
|
||||
{
|
||||
internal class MeshRetargeterResetHook : HookBase
|
||||
{
|
||||
public override int callbackOrder => HookSequence.SEQ_RESETTERS;
|
||||
protected override bool OnPreprocessAvatarWrapped(GameObject avatarGameObject)
|
||||
{
|
||||
BoneDatabase.ResetBones();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class BoneDatabase
|
||||
{
|
||||
private static Dictionary<Transform, bool> IsRetargetable = new Dictionary<Transform, bool>();
|
||||
@ -84,10 +74,9 @@ namespace net.fushizen.modular_avatar.core.editor
|
||||
}
|
||||
}
|
||||
|
||||
internal class RetargetMeshes : HookBase
|
||||
internal class RetargetMeshes
|
||||
{
|
||||
public override int callbackOrder => HookSequence.SEQ_RETARGET_MESH;
|
||||
protected override bool OnPreprocessAvatarWrapped(GameObject avatarGameObject)
|
||||
internal void OnPreprocessAvatar(GameObject avatarGameObject)
|
||||
{
|
||||
foreach (var renderer in avatarGameObject.GetComponentsInChildren<SkinnedMeshRenderer>(true))
|
||||
{
|
||||
@ -131,8 +120,6 @@ namespace net.fushizen.modular_avatar.core.editor
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,14 +73,4 @@ namespace net.fushizen.modular_avatar.core.editor
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ClearPathMappings : HookBase
|
||||
{
|
||||
public override int callbackOrder => HookSequence.SEQ_RESETTERS;
|
||||
protected override bool OnPreprocessAvatarWrapped(GameObject avatarGameObject)
|
||||
{
|
||||
PathMappings.Clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace net.fushizen.modular_avatar.core
|
||||
@ -29,7 +30,19 @@ namespace net.fushizen.modular_avatar.core
|
||||
/**
|
||||
* This abstract base class is injected into the VRCSDK avatar component allowlist to avoid
|
||||
*/
|
||||
[DefaultExecutionOrder(-9999)] // run before av3emu
|
||||
public abstract class AvatarTagComponent : MonoBehaviour
|
||||
{
|
||||
private void Awake()
|
||||
{
|
||||
if (!RuntimeUtil.isPlaying || this == null) return;
|
||||
RuntimeUtil.OnDemandProcessAvatar(RuntimeUtil.OnDemandSource.Awake, this);
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (!RuntimeUtil.isPlaying || this == null) return;
|
||||
RuntimeUtil.OnDemandProcessAvatar(RuntimeUtil.OnDemandSource.Start, this);
|
||||
}
|
||||
}
|
||||
}
|
@ -37,7 +37,17 @@ namespace net.fushizen.modular_avatar.core
|
||||
|
||||
// Initialized in Util
|
||||
public static Action<NullCallback> delayCall = (_) => { };
|
||||
|
||||
|
||||
public enum OnDemandSource
|
||||
{
|
||||
Awake,
|
||||
Start
|
||||
}
|
||||
|
||||
public delegate void OnDemandProcessAvatarDelegate(OnDemandSource source, AvatarTagComponent component);
|
||||
|
||||
public static OnDemandProcessAvatarDelegate OnDemandProcessAvatar = (_m, _c) => { };
|
||||
|
||||
[CanBeNull]
|
||||
public static string RelativePath(GameObject root, GameObject child)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user