mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2024-12-28 10:15:06 +08:00
feat: add UI to show parameter usage (#773)
This commit is contained in:
parent
532e3bc250
commit
654aec1aab
4
.github/ProjectRoot/vpm-manifest-2019.json
vendored
4
.github/ProjectRoot/vpm-manifest-2019.json
vendored
@ -4,7 +4,7 @@
|
||||
"version": "3.4.2"
|
||||
},
|
||||
"nadena.dev.ndmf": {
|
||||
"version": "1.3.6"
|
||||
"version": "1.4.0-rc.0"
|
||||
}
|
||||
},
|
||||
"locked": {
|
||||
@ -19,7 +19,7 @@
|
||||
"dependencies": {}
|
||||
},
|
||||
"nadena.dev.ndmf": {
|
||||
"version": "1.3.6"
|
||||
"version": "1.4.0-rc.0"
|
||||
}
|
||||
}
|
||||
}
|
4
.github/ProjectRoot/vpm-manifest-2022.json
vendored
4
.github/ProjectRoot/vpm-manifest-2022.json
vendored
@ -4,7 +4,7 @@
|
||||
"version": "3.5.0"
|
||||
},
|
||||
"nadena.dev.ndmf": {
|
||||
"version": "1.3.6"
|
||||
"version": "1.4.0-rc.0"
|
||||
}
|
||||
},
|
||||
"locked": {
|
||||
@ -19,7 +19,7 @@
|
||||
"dependencies": {}
|
||||
},
|
||||
"nadena.dev.ndmf": {
|
||||
"version": "1.3.6"
|
||||
"version": "1.4.0-rc.0"
|
||||
}
|
||||
}
|
||||
}
|
102
Editor/HarmonyPatches/InjectParamsUsageUI.cs
Normal file
102
Editor/HarmonyPatches/InjectParamsUsageUI.cs
Normal file
@ -0,0 +1,102 @@
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using HarmonyLib;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor.HarmonyPatches
|
||||
{
|
||||
internal static class InjectParamsUsageUI
|
||||
{
|
||||
private static readonly Type type = AccessTools.TypeByName("UnityEditor.PropertyEditor");
|
||||
private static readonly PropertyInfo _editorsElement = AccessTools.Property(type, "editorsElement");
|
||||
|
||||
private static readonly Type editorElem = AccessTools.TypeByName("UnityEditor.UIElements.EditorElement");
|
||||
private static readonly PropertyInfo editorElem_editor = AccessTools.Property(editorElem, "editor");
|
||||
|
||||
public static void Patch(Harmony h)
|
||||
{
|
||||
var type = AccessTools.TypeByName("UnityEditor.PropertyEditor");
|
||||
var drawEditors = AccessTools.Method(type, "DrawEditors");
|
||||
|
||||
h.Patch(drawEditors, transpiler: new HarmonyMethod(typeof(InjectParamsUsageUI), nameof(Transpile)));
|
||||
|
||||
var objNames = AccessTools.TypeByName("UnityEditor.ObjectNames");
|
||||
var m_GetObjectTypeName = AccessTools.Method(objNames, "GetObjectTypeName");
|
||||
var postfix_GetObjectTypeName =
|
||||
AccessTools.Method(typeof(InjectParamsUsageUI), nameof(Postfix_GetObjectTypeName));
|
||||
|
||||
h.Patch(m_GetObjectTypeName, postfix: new HarmonyMethod(postfix_GetObjectTypeName));
|
||||
}
|
||||
|
||||
private static void Postfix_GetObjectTypeName(ref string __result, Object o)
|
||||
{
|
||||
if (o is ModularAvatarInformation)
|
||||
{
|
||||
__result = "Modular Avatar Information";
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<CodeInstruction> Transpile(IEnumerable<CodeInstruction> ci)
|
||||
{
|
||||
var target = AccessTools.Method(typeof(VisualElement), "Add");
|
||||
|
||||
foreach (var i in ci)
|
||||
{
|
||||
if (i.opcode != OpCodes.Callvirt)
|
||||
{
|
||||
yield return i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i.opcode == OpCodes.Callvirt
|
||||
&& i.operand is MethodInfo method
|
||||
&& method == target
|
||||
)
|
||||
{
|
||||
yield return new CodeInstruction(OpCodes.Ldarg_0);
|
||||
yield return new CodeInstruction(OpCodes.Call,
|
||||
AccessTools.Method(typeof(InjectParamsUsageUI), nameof(EditorAdd)));
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return i;
|
||||
}
|
||||
}
|
||||
|
||||
private static void EditorAdd(VisualElement container, VisualElement child, object caller)
|
||||
{
|
||||
container.Add(child);
|
||||
|
||||
var editorsElement = _editorsElement.GetValue(caller) as VisualElement;
|
||||
if (editorsElement != container)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!child.ClassListContains("game-object-inspector"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var editor = editorElem_editor.GetValue(child) as Editor;
|
||||
if (editor == null) return;
|
||||
|
||||
if (editor.targets.Length != 1) return;
|
||||
|
||||
if (editor.target is GameObject obj)
|
||||
{
|
||||
var elem = new ParamsUsageUI();
|
||||
container.Add(elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Editor/HarmonyPatches/InjectParamsUsageUI.cs.meta
Normal file
3
Editor/HarmonyPatches/InjectParamsUsageUI.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d62a8f41641443ea8bffdc0429e0ad1
|
||||
timeCreated: 1710223876
|
@ -17,6 +17,7 @@ namespace nadena.dev.modular_avatar.core.editor.HarmonyPatches
|
||||
#if UNITY_2022_3_OR_NEWER
|
||||
HandleUtilityPatches.Patch_FilterInstanceIDs,
|
||||
PickingObjectPatch.Patch,
|
||||
InjectParamsUsageUI.Patch,
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -36,6 +37,8 @@ namespace nadena.dev.modular_avatar.core.editor.HarmonyPatches
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
AssemblyReloadEvents.beforeAssemblyReload += () => { harmony.UnpatchAll(); };
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
{
|
||||
"name": "nadena.dev.modular-avatar.harmony-patches",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"nadena.dev.modular-avatar.core",
|
||||
"nadena.dev.modular-avatar.core.editor",
|
||||
"VRC.SDKBase.Editor"
|
||||
"VRC.SDKBase.Editor",
|
||||
"nadena.dev.modular-avatar.param-introspection"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
|
@ -1,18 +1,18 @@
|
||||
/*
|
||||
* 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
|
||||
@ -22,7 +22,12 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#region
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
@ -30,6 +35,11 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
[CanEditMultipleObjects]
|
||||
internal class PBBlockerEditor : MAEditorBase
|
||||
{
|
||||
public PBBlockerEditor()
|
||||
{
|
||||
Debug.Log("ctor");
|
||||
}
|
||||
|
||||
protected override void OnInnerInspectorGUI()
|
||||
{
|
||||
EditorGUILayout.HelpBox(Localization.S("pb_blocker.help"), MessageType.Info);
|
||||
|
@ -1,9 +1,13 @@
|
||||
using System;
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using nadena.dev.ndmf.localization;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
internal class UIElementLocalizer
|
||||
@ -21,6 +25,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
internal void Localize(VisualElement elem)
|
||||
{
|
||||
WalkTree(elem);
|
||||
LanguagePrefs.ApplyFontPreferences(elem);
|
||||
}
|
||||
|
||||
private void WalkTree(VisualElement elem)
|
||||
|
@ -240,5 +240,9 @@
|
||||
"scale_adjuster.scale": "Scale adjustment",
|
||||
"scale_adjuster.adjust_children": "Adjust position of child objects",
|
||||
"world_fixed_object.err.unsupported_platform": "World Fixed Object is not supported on this platform",
|
||||
"world_fixed_object.err.unsupported_platform:description": "World Fixed Object is not supported on Android builds and will be ignored."
|
||||
"world_fixed_object.err.unsupported_platform:description": "World Fixed Object is not supported on Android builds and will be ignored.",
|
||||
"ma_info.param_usage_ui.header": "Expressions Parameter Usage",
|
||||
"ma_info.param_usage_ui.other_objects": "Other objects on this avatar",
|
||||
"ma_info.param_usage_ui.free_space": "Unused parameter space ({0} bits)",
|
||||
"ma_info.param_usage_ui.bits_template": "{0} ({1} bits)"
|
||||
}
|
@ -236,5 +236,9 @@
|
||||
"scale_adjuster.scale": "Scale調整値",
|
||||
"scale_adjuster.adjust_children": "子オブジェクトの位置を調整",
|
||||
"world_fixed_object.err.unsupported_platform": "World Fixed Objectがこのプラットフォームに対応していません",
|
||||
"world_fixed_object.err.unsupported_platform:description": "World Fixed ObjectはAndroid向けビルドには対応していないため、動作しません。"
|
||||
"world_fixed_object.err.unsupported_platform:description": "World Fixed ObjectはAndroid向けビルドには対応していないため、動作しません。",
|
||||
"ma_info.param_usage_ui.header": "Expressions Parameter 使用状況",
|
||||
"ma_info.param_usage_ui.other_objects": "このアバター内の他のオブジェクト",
|
||||
"ma_info.param_usage_ui.free_space": "未使用領域 ({0} 個のビット)",
|
||||
"ma_info.param_usage_ui.bits_template": "{0} ({1} 個のビットを使用中)"
|
||||
}
|
3
Editor/ParamsUsage.meta
Normal file
3
Editor/ParamsUsage.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9914a6ac6399437dbcaa252282d02beb
|
||||
timeCreated: 1710222101
|
100
Editor/ParamsUsage/MAParametersIntrospection.cs
Normal file
100
Editor/ParamsUsage/MAParametersIntrospection.cs
Normal file
@ -0,0 +1,100 @@
|
||||
#region
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using nadena.dev.modular_avatar.core.editor.plugin;
|
||||
using nadena.dev.ndmf;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
[ParameterProviderFor(typeof(ModularAvatarParameters))]
|
||||
internal class MAParametersIntrospection : IParameterProvider
|
||||
{
|
||||
private readonly ModularAvatarParameters _component;
|
||||
|
||||
public MAParametersIntrospection(ModularAvatarParameters parameters)
|
||||
{
|
||||
_component = parameters;
|
||||
}
|
||||
|
||||
public IEnumerable<ProvidedParameter> GetSuppliedParameters(ndmf.BuildContext context = null)
|
||||
{
|
||||
return _component.parameters.Select(p =>
|
||||
{
|
||||
AnimatorControllerParameterType paramType;
|
||||
bool animatorOnly = false;
|
||||
|
||||
switch (p.syncType)
|
||||
{
|
||||
case ParameterSyncType.Bool:
|
||||
paramType = AnimatorControllerParameterType.Bool;
|
||||
break;
|
||||
case ParameterSyncType.Float:
|
||||
paramType = AnimatorControllerParameterType.Float;
|
||||
break;
|
||||
case ParameterSyncType.Int:
|
||||
paramType = AnimatorControllerParameterType.Int;
|
||||
break;
|
||||
default:
|
||||
paramType = AnimatorControllerParameterType.Float;
|
||||
animatorOnly = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return new ProvidedParameter(
|
||||
p.nameOrPrefix,
|
||||
p.isPrefix ? ParameterNamespace.PhysBonesPrefix : ParameterNamespace.Animator,
|
||||
_component, PluginDefinition.Instance, paramType)
|
||||
{
|
||||
IsAnimatorOnly = animatorOnly,
|
||||
WantSynced = !p.localOnly,
|
||||
IsHidden = p.internalParameter,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public void RemapParameters(ref ImmutableDictionary<(ParameterNamespace, string), ParameterMapping> nameMap,
|
||||
ndmf.BuildContext context = null)
|
||||
{
|
||||
var remappings = context != null ? ParameterRenameMappings.Get(context) : null;
|
||||
|
||||
// TODO - internal parameter handling
|
||||
foreach (var p in _component.parameters)
|
||||
{
|
||||
ParameterNamespace ns = p.isPrefix ? ParameterNamespace.PhysBonesPrefix : ParameterNamespace.Animator;
|
||||
string remapTo = null;
|
||||
if (p.internalParameter)
|
||||
{
|
||||
if (remappings != null)
|
||||
{
|
||||
remapTo = remappings.Remap(_component, ns, p.nameOrPrefix);
|
||||
}
|
||||
else
|
||||
{
|
||||
remapTo = p.nameOrPrefix + "$" + GUID.Generate();
|
||||
}
|
||||
}
|
||||
else if (string.IsNullOrEmpty(p.remapTo))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
remapTo = p.remapTo;
|
||||
}
|
||||
|
||||
if (nameMap.TryGetKey((ns, remapTo), out var existingMapping))
|
||||
{
|
||||
remapTo = existingMapping.Item2;
|
||||
}
|
||||
|
||||
nameMap = nameMap.SetItem((ns, p.nameOrPrefix), new ParameterMapping(remapTo, p.internalParameter));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Editor/ParamsUsage/MAParametersIntrospection.cs.meta
Normal file
3
Editor/ParamsUsage/MAParametersIntrospection.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eadfd1e62f714d06a8b9f693dec21940
|
||||
timeCreated: 1710229132
|
23
Editor/ParamsUsage/ModularAvatarInformation.cs
Normal file
23
Editor/ParamsUsage/ModularAvatarInformation.cs
Normal file
@ -0,0 +1,23 @@
|
||||
#region
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
[HelpURL("https://m-a.nadena.dev/docs/intro?lang=auto")]
|
||||
internal class ModularAvatarInformation : ScriptableObject
|
||||
{
|
||||
internal static ModularAvatarInformation _instance;
|
||||
|
||||
internal static ModularAvatarInformation instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null) _instance = CreateInstance<ModularAvatarInformation>();
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Editor/ParamsUsage/ModularAvatarInformation.cs.meta
Normal file
11
Editor/ParamsUsage/ModularAvatarInformation.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f902feee12ad4fcbb8a975bbea565ab1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: a8edd5bd1a0a64a40aa99cc09fb5f198, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
75
Editor/ParamsUsage/ParamsUsage.uss
Normal file
75
Editor/ParamsUsage/ParamsUsage.uss
Normal file
@ -0,0 +1,75 @@
|
||||
Label.header {
|
||||
-unity-font-style: bold;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#Outerbox {
|
||||
margin-top: 4px;
|
||||
border-top-width: 1px;
|
||||
border-bottom-width: 1px;
|
||||
border-left-width: 1px;
|
||||
border-right-width: 1px;
|
||||
border-color: black;
|
||||
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
#root-box {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
#UsageBox {
|
||||
height: 16px;
|
||||
flex-direction: row;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
#UsageBox VisualElement {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
#UsageBox VisualElement.Hovering {
|
||||
border-top-width: 4px;
|
||||
border-bottom-width: 4px;
|
||||
/*border-left-width: 4px;
|
||||
border-right-width: 4px;
|
||||
margin: -4px;
|
||||
*/
|
||||
margin-top: -4px;
|
||||
margin-bottom: -4px;
|
||||
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
.Entry {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.IconOuter {
|
||||
border-top-width: 3px;
|
||||
border-bottom-width: 3px;
|
||||
border-left-width: 3px;
|
||||
border-right-width: 3px;
|
||||
border-color: grey;
|
||||
|
||||
padding: 1px;
|
||||
margin-right: 4px;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.IconInner {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.Entry.Hovering {
|
||||
margin-left: -4px;
|
||||
margin-right: -4px;
|
||||
border-left-width: 4px;
|
||||
border-right-width: 4px;
|
||||
}
|
3
Editor/ParamsUsage/ParamsUsage.uss.meta
Normal file
3
Editor/ParamsUsage/ParamsUsage.uss.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e6813d571517475dbf36efb2d266003a
|
||||
timeCreated: 1710399136
|
35
Editor/ParamsUsage/ParamsUsage.uxml
Normal file
35
Editor/ParamsUsage/ParamsUsage.uxml
Normal file
@ -0,0 +1,35 @@
|
||||
<UXML xmlns:ui="UnityEngine.UIElements" xmlns:ma="nadena.dev.modular_avatar.core.editor">
|
||||
<ui:VisualElement name="root-box">
|
||||
<ui:Label text="ma_info.param_usage_ui.header" class="header ndmf-tr"/>
|
||||
|
||||
<ui:VisualElement name="Outerbox">
|
||||
<ui:VisualElement name="UsageBox">
|
||||
<ui:VisualElement name="OtherObjects" style="background-color: #888888; flex-grow: 1;"/>
|
||||
<ui:VisualElement name="UnusedSpace" style="width: auto; background-color: #eeeeee; flex-grow: 1;"/>
|
||||
</ui:VisualElement>
|
||||
|
||||
<ui:VisualElement name="Legend">
|
||||
<ui:VisualElement class="retained">
|
||||
<ui:VisualElement class="Entry" name="OtherObjects" style="display: none">
|
||||
<ui:VisualElement class="IconOuter">
|
||||
<ui:VisualElement class="IconInner" style="background-color: #888888"/>
|
||||
</ui:VisualElement>
|
||||
|
||||
<ui:Label class="description" text="Other objects on this avatar (xx)"/>
|
||||
</ui:VisualElement>
|
||||
|
||||
<ui:VisualElement class="Entry" name="UnusedSpace" style="display: none">
|
||||
<ui:VisualElement class="IconOuter">
|
||||
<ui:VisualElement class="IconInner" style="background-color: #eeeeee"/>
|
||||
</ui:VisualElement>
|
||||
|
||||
<ui:Label class="description" text="Free parameter space (xx)"/>
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
|
||||
<ma:LanguageSwitcherElement/>
|
||||
</ui:VisualElement>
|
||||
</UXML>
|
3
Editor/ParamsUsage/ParamsUsage.uxml.meta
Normal file
3
Editor/ParamsUsage/ParamsUsage.uxml.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d51876a8e634298aa6d0271bb820189
|
||||
timeCreated: 1710399118
|
203
Editor/ParamsUsage/ParamsUsageEditor.cs
Normal file
203
Editor/ParamsUsage/ParamsUsageEditor.cs
Normal file
@ -0,0 +1,203 @@
|
||||
#region
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using nadena.dev.ndmf;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using VRC.SDK3.Avatars.ScriptableObjects;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
internal class ParamsUsageEditor : MAEditorBase
|
||||
{
|
||||
[SerializeField] private StyleSheet uss;
|
||||
[SerializeField] private VisualTreeAsset uxml;
|
||||
|
||||
private VisualElement _root;
|
||||
private VisualElement _entryTemplate;
|
||||
private VisualElement _usageBoxContainer;
|
||||
private VisualElement _legendContainer;
|
||||
|
||||
private bool _visible = false;
|
||||
|
||||
public bool Visible
|
||||
{
|
||||
get => _visible;
|
||||
set
|
||||
{
|
||||
if (_visible == value) return;
|
||||
_visible = value;
|
||||
|
||||
if (_visible) Recalculate();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
ObjectChangeEvents.changesPublished += OnChangesPublished;
|
||||
#endif
|
||||
Recalculate();
|
||||
}
|
||||
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
private void OnChangesPublished(ref ObjectChangeEventStream stream)
|
||||
{
|
||||
Recalculate();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
ObjectChangeEvents.changesPublished -= OnChangesPublished;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected override VisualElement CreateInnerInspectorGUI()
|
||||
{
|
||||
_root = uxml.CloneTree();
|
||||
_root.styleSheets.Add(uss);
|
||||
Localization.L.LocalizeUIElements(_root);
|
||||
|
||||
_legendContainer = _root.Q<VisualElement>("Legend");
|
||||
_usageBoxContainer = _root.Q<VisualElement>("UsageBox");
|
||||
|
||||
Recalculate();
|
||||
|
||||
return _root;
|
||||
}
|
||||
|
||||
protected override void OnInnerInspectorGUI()
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
private static IEnumerable<Color> Colors()
|
||||
{
|
||||
// Spiral inwards on an HSV scale
|
||||
float h_step = 0.33f;
|
||||
float h_step_mult = 0.8f;
|
||||
float h_step_min = 0.05f;
|
||||
|
||||
float v_mult = 0.98f;
|
||||
|
||||
float h = 0;
|
||||
float s = 1;
|
||||
float v = 0.9f;
|
||||
|
||||
while (true)
|
||||
{
|
||||
yield return Color.HSVToRGB(h, s, v);
|
||||
|
||||
h = (h + h_step) % 1;
|
||||
h_step = h_step_min + ((h_step - h_step_min) * h_step_mult);
|
||||
v *= v_mult;
|
||||
}
|
||||
}
|
||||
|
||||
private void Recalculate()
|
||||
{
|
||||
if (_root == null || !_visible) return;
|
||||
|
||||
var ctx = serializedObject.context as GameObject;
|
||||
|
||||
var avatarRoot = RuntimeUtil.FindAvatarTransformInParents(ctx.transform)?.gameObject;
|
||||
if (ctx == null || avatarRoot == null) return;
|
||||
|
||||
var orderedPlugins = ParameterInfo.ForUI.GetParametersForObject(ctx)
|
||||
.GroupBy(p => p.Plugin)
|
||||
.Select(group => (group.Key, group.Sum(p => p.BitUsage)))
|
||||
.OrderBy(group => group.Key.DisplayName)
|
||||
.ToList();
|
||||
|
||||
var byPlugin = orderedPlugins
|
||||
.Zip(Colors(), (kv, color) => (kv.Key.DisplayName, kv.Item2, kv.Key.ThemeColor ?? color))
|
||||
.ToList();
|
||||
|
||||
int totalUsage = byPlugin.Sum(kv => kv.Item2);
|
||||
|
||||
int avatarTotalUsage =
|
||||
ParameterInfo.ForUI.GetParametersForObject(avatarRoot).Sum(p => p.BitUsage);
|
||||
|
||||
int freeSpace = VRCExpressionParameters.MAX_PARAMETER_COST - avatarTotalUsage;
|
||||
|
||||
float avatarTotalPerc = avatarTotalUsage / (float)VRCExpressionParameters.MAX_PARAMETER_COST;
|
||||
float freeSpacePerc = freeSpace / (float)VRCExpressionParameters.MAX_PARAMETER_COST;
|
||||
|
||||
if (avatarTotalUsage > totalUsage)
|
||||
{
|
||||
byPlugin.Add((Localization.S("ma_info.param_usage_ui.other_objects"), avatarTotalUsage - totalUsage,
|
||||
Color.gray));
|
||||
}
|
||||
|
||||
var bits_template = Localization.S("ma_info.param_usage_ui.bits_template");
|
||||
byPlugin = byPlugin.Select((tuple, _) =>
|
||||
(string.Format(bits_template, tuple.Item1, tuple.Item2), tuple.Item2, tuple.Item3)).ToList();
|
||||
|
||||
if (freeSpace > 0)
|
||||
{
|
||||
var free_space_label = Localization.S("ma_info.param_usage_ui.free_space");
|
||||
byPlugin.Add((string.Format(free_space_label, freeSpace), freeSpace, Color.white));
|
||||
}
|
||||
|
||||
foreach (var child in _legendContainer.Children().ToList())
|
||||
{
|
||||
child.RemoveFromHierarchy();
|
||||
}
|
||||
|
||||
foreach (var child in _usageBoxContainer.Children().ToList())
|
||||
{
|
||||
child.RemoveFromHierarchy();
|
||||
}
|
||||
|
||||
foreach (var (label, usage, color) in byPlugin)
|
||||
{
|
||||
var colorBar = new VisualElement();
|
||||
colorBar.style.backgroundColor = color;
|
||||
colorBar.style.width =
|
||||
new StyleLength(new Length(100.0f * usage / (float)VRCExpressionParameters.MAX_PARAMETER_COST,
|
||||
LengthUnit.Percent));
|
||||
_usageBoxContainer.Add(colorBar);
|
||||
|
||||
var entry = new VisualElement();
|
||||
_legendContainer.Add(entry);
|
||||
entry.AddToClassList("Entry");
|
||||
|
||||
var icon_outer = new VisualElement();
|
||||
icon_outer.AddToClassList("IconOuter");
|
||||
entry.Add(icon_outer);
|
||||
|
||||
var icon_inner = new VisualElement();
|
||||
icon_inner.AddToClassList("IconInner");
|
||||
icon_outer.Add(icon_inner);
|
||||
icon_inner.style.backgroundColor = color;
|
||||
|
||||
var pluginLabel = new Label(label);
|
||||
entry.Add(pluginLabel);
|
||||
|
||||
entry.style.borderBottomColor = color;
|
||||
entry.style.borderTopColor = color;
|
||||
entry.style.borderLeftColor = color;
|
||||
entry.style.borderRightColor = color;
|
||||
|
||||
colorBar.style.borderBottomColor = color;
|
||||
colorBar.style.borderTopColor = color;
|
||||
colorBar.style.borderLeftColor = color;
|
||||
colorBar.style.borderRightColor = color;
|
||||
|
||||
SetMouseHover(entry, colorBar);
|
||||
SetMouseHover(colorBar, entry);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetMouseHover(VisualElement src, VisualElement other)
|
||||
{
|
||||
src.RegisterCallback<MouseEnterEvent>(ev => { other.AddToClassList("Hovering"); });
|
||||
|
||||
src.RegisterCallback<MouseLeaveEvent>(ev => { other.RemoveFromClassList("Hovering"); });
|
||||
}
|
||||
}
|
||||
}
|
13
Editor/ParamsUsage/ParamsUsageEditor.cs.meta
Normal file
13
Editor/ParamsUsage/ParamsUsageEditor.cs.meta
Normal file
@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aedf0b915d844b2992b447f61bd56f54
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- uss: {fileID: 7433441132597879392, guid: e6813d571517475dbf36efb2d266003a, type: 3}
|
||||
- uxml: {fileID: 9197481963319205126, guid: 0d51876a8e634298aa6d0271bb820189, type: 3}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
202
Editor/ParamsUsage/ParamsUsageUI.cs
Normal file
202
Editor/ParamsUsage/ParamsUsageUI.cs
Normal file
@ -0,0 +1,202 @@
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using HarmonyLib;
|
||||
using nadena.dev.ndmf.localization;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
internal class ParamsUsageUI : VisualElement
|
||||
{
|
||||
private static readonly Type editorElem = AccessTools.TypeByName("UnityEditor.UIElements.EditorElement");
|
||||
private static readonly PropertyInfo editorElem_editor = AccessTools.Property(editorElem, "editor");
|
||||
|
||||
private class FoldoutState
|
||||
{
|
||||
public bool Visible;
|
||||
}
|
||||
|
||||
private static ConditionalWeakTable<VisualElement, FoldoutState> FoldoutStateHolder =
|
||||
new ConditionalWeakTable<VisualElement, FoldoutState>();
|
||||
|
||||
private VisualElement _gameObjectEditorElement;
|
||||
private Editor _parentEditor;
|
||||
private Object _rawTarget;
|
||||
private GameObject _target;
|
||||
private ParamsUsageEditor _editor;
|
||||
private FoldoutState _foldoutState;
|
||||
|
||||
private bool _recursing = false;
|
||||
|
||||
public ParamsUsageUI()
|
||||
{
|
||||
RegisterCallback<AttachToPanelEvent>(OnAttach);
|
||||
RegisterCallback<DetachFromPanelEvent>(OnDetach);
|
||||
|
||||
LanguagePrefs.RegisterLanguageChangeCallback(this,
|
||||
(self) => self.OnLanguageChangedCallback());
|
||||
}
|
||||
|
||||
private void OnLanguageChangedCallback()
|
||||
{
|
||||
if (_editor != null)
|
||||
{
|
||||
BuildContent();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDetach(DetachFromPanelEvent evt)
|
||||
{
|
||||
if (_recursing) return;
|
||||
|
||||
Clear();
|
||||
|
||||
if (_editor != null)
|
||||
{
|
||||
Object.DestroyImmediate(_editor);
|
||||
_editor = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAttach(AttachToPanelEvent evt)
|
||||
{
|
||||
if (_recursing) return;
|
||||
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
private void Rebuild()
|
||||
{
|
||||
if (parent == null) return;
|
||||
|
||||
SetRedrawSensor();
|
||||
|
||||
if (_gameObjectEditorElement?.parent != parent)
|
||||
{
|
||||
_gameObjectEditorElement = null;
|
||||
|
||||
var kv = FindEditorElement();
|
||||
if (kv != null)
|
||||
{
|
||||
var elem = kv.Value.Item1;
|
||||
var index = kv.Value.Item2;
|
||||
|
||||
if (index != parent.Children().ToList().IndexOf(this))
|
||||
{
|
||||
_recursing = true;
|
||||
var p = parent;
|
||||
RemoveFromHierarchy();
|
||||
p.Insert(index + 1, this);
|
||||
_recursing = false;
|
||||
}
|
||||
|
||||
_gameObjectEditorElement = elem;
|
||||
}
|
||||
}
|
||||
|
||||
if (_gameObjectEditorElement == null) return;
|
||||
|
||||
_parentEditor = editorElem_editor.GetValue(_gameObjectEditorElement) as Editor;
|
||||
if (_parentEditor == null) return;
|
||||
|
||||
_rawTarget = _parentEditor.target;
|
||||
_target = _rawTarget as GameObject;
|
||||
|
||||
if (_target == null) return;
|
||||
|
||||
Clear();
|
||||
_redrawSensorActive = false;
|
||||
BuildContent();
|
||||
}
|
||||
|
||||
private (VisualElement, int)? FindEditorElement()
|
||||
{
|
||||
foreach (var (elem, index) in parent.Children().Select((e, i) => (e, i)))
|
||||
{
|
||||
if (elem.ClassListContains("game-object-inspector"))
|
||||
{
|
||||
return (elem, index);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool _redrawSensorActive = false;
|
||||
|
||||
private void SetRedrawSensor()
|
||||
{
|
||||
if (_redrawSensorActive) return;
|
||||
|
||||
Clear();
|
||||
_redrawSensorActive = true;
|
||||
Add(new IMGUIContainer(() => EditorApplication.delayCall += Rebuild));
|
||||
}
|
||||
|
||||
private void BuildContent()
|
||||
{
|
||||
Clear();
|
||||
|
||||
if (!FoldoutStateHolder.TryGetValue(parent, out _foldoutState))
|
||||
{
|
||||
_foldoutState = new FoldoutState();
|
||||
FoldoutStateHolder.Add(parent, _foldoutState);
|
||||
}
|
||||
|
||||
if (RuntimeUtil.FindAvatarTransformInParents(_target.transform) == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_editor = Editor.CreateEditorWithContext(new Object[] { ModularAvatarInformation.instance }, _target,
|
||||
typeof(ParamsUsageEditor))
|
||||
as ParamsUsageEditor;
|
||||
|
||||
if (_editor == null) return;
|
||||
|
||||
var inspectorElement = new InspectorElement(_editor);
|
||||
|
||||
Add(new IMGUIContainer(() =>
|
||||
{
|
||||
if (_gameObjectEditorElement?.parent != parent || _parentEditor == null ||
|
||||
_parentEditor.target != _rawTarget)
|
||||
{
|
||||
EditorApplication.delayCall += Rebuild;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (Event.current.rawType)
|
||||
{
|
||||
case EventType.Repaint:
|
||||
case EventType.MouseMove:
|
||||
case EventType.Layout:
|
||||
break;
|
||||
case EventType.MouseDrag:
|
||||
case EventType.DragUpdated:
|
||||
case EventType.DragPerform:
|
||||
case EventType.DragExited:
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_foldoutState.Visible = EditorGUILayout.InspectorTitlebar(_foldoutState.Visible, _editor);
|
||||
inspectorElement.style.display = _foldoutState.Visible ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
_editor.Visible = _foldoutState.Visible;
|
||||
}));
|
||||
_editor.Visible = _foldoutState.Visible;
|
||||
Add(inspectorElement);
|
||||
}
|
||||
}
|
||||
}
|
3
Editor/ParamsUsage/ParamsUsageUI.cs.meta
Normal file
3
Editor/ParamsUsage/ParamsUsageUI.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18364f2754ed43c3baba0f1e18ac03cd
|
||||
timeCreated: 1710226452
|
7
Editor/ParamsUsage/assembly-info.cs
Normal file
7
Editor/ParamsUsage/assembly-info.cs
Normal file
@ -0,0 +1,7 @@
|
||||
#region
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
#endregion
|
||||
|
||||
[assembly: InternalsVisibleTo("nadena.dev.modular-avatar.harmony-patches")]
|
3
Editor/ParamsUsage/assembly-info.cs.meta
Normal file
3
Editor/ParamsUsage/assembly-info.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6899d9fe550c492d887ce9e02d2a758b
|
||||
timeCreated: 1710404112
|
@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "nadena.dev.modular-avatar.param-introspection",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:fc900867c0f47cd49b6e2ae4ef907300",
|
||||
"GUID:5ce33783346c3124990afbe7b0390a06",
|
||||
"GUID:62ced99b048af7f4d8dfe4bed8373d76",
|
||||
"GUID:5718fb738711cd34ea54e9553040911d",
|
||||
"GUID:b906909fcc54f634db50f2cad0f988d9",
|
||||
"GUID:901e56b065a857d4483a77f8cae73588"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [
|
||||
"MODULAR_AVATAR_VRCSDK_AVATAR"
|
||||
],
|
||||
"versionDefines": [
|
||||
{
|
||||
"name": "com.vrchat.avatars",
|
||||
"expression": "(0,999)",
|
||||
"define": "MODULAR_AVATAR_VRCSDK_AVATAR"
|
||||
}
|
||||
],
|
||||
"noEngineReferences": false
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f906ad1132cf10c48a65d14ae0809457
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,12 +1,19 @@
|
||||
using System;
|
||||
#region
|
||||
|
||||
using System;
|
||||
using nadena.dev.modular_avatar.animation;
|
||||
using nadena.dev.modular_avatar.core.ArmatureAwase;
|
||||
using nadena.dev.modular_avatar.core.editor.plugin;
|
||||
using nadena.dev.modular_avatar.editor.ErrorReporting;
|
||||
using nadena.dev.ndmf;
|
||||
using nadena.dev.ndmf.fluent;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
#endregion
|
||||
|
||||
[assembly: ExportsPlugin(
|
||||
typeof(nadena.dev.modular_avatar.core.editor.plugin.PluginDefinition)
|
||||
typeof(PluginDefinition)
|
||||
)]
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor.plugin
|
||||
@ -17,6 +24,9 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
|
||||
public override string DisplayName => "Modular Avatar";
|
||||
public override Texture2D LogoTexture => LogoDisplay.LOGO_ASSET;
|
||||
|
||||
// 00a0e9
|
||||
public override Color? ThemeColor => new Color(0x00 / 255f, 0xa0 / 255f, 0xe9 / 255f, 1);
|
||||
|
||||
protected override void OnUnhandledException(Exception e)
|
||||
{
|
||||
BuildReport.LogException(e);
|
||||
@ -82,11 +92,11 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
|
||||
{
|
||||
foreach (var component in ctx.AvatarRootTransform.GetComponentsInChildren<AvatarTagComponent>(true))
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(component);
|
||||
Object.DestroyImmediate(component);
|
||||
}
|
||||
foreach (var component in ctx.AvatarRootTransform.GetComponentsInChildren<ArmatureAwase.MAMoveIndependently>(true))
|
||||
foreach (var component in ctx.AvatarRootTransform.GetComponentsInChildren<MAMoveIndependently>(true))
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(component);
|
||||
Object.DestroyImmediate(component);
|
||||
}
|
||||
});
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
@ -140,7 +150,7 @@ namespace nadena.dev.modular_avatar.core.editor.plugin
|
||||
{
|
||||
foreach (var component in obj.GetComponentsInChildren<AvatarTagComponent>(true))
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(component);
|
||||
Object.DestroyImmediate(component);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1,5 +1,7 @@
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
@ -18,8 +20,33 @@ using Object = UnityEngine.Object;
|
||||
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
internal class ParameterRenameMappings
|
||||
{
|
||||
public static ParameterRenameMappings Get(ndmf.BuildContext ctx)
|
||||
{
|
||||
return ctx.GetState<ParameterRenameMappings>();
|
||||
}
|
||||
|
||||
public Dictionary<(ModularAvatarParameters, ParameterNamespace, string), string> Remappings =
|
||||
new Dictionary<(ModularAvatarParameters, ParameterNamespace, string), string>();
|
||||
|
||||
private int internalParamIndex;
|
||||
|
||||
public string Remap(ModularAvatarParameters p, ParameterNamespace ns, string s)
|
||||
{
|
||||
var tuple = (p, ns, s);
|
||||
|
||||
if (Remappings.TryGetValue(tuple, out var mapping)) return mapping;
|
||||
|
||||
return s + "$$Internal_" + internalParamIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class DefaultValues
|
||||
{
|
||||
public ImmutableDictionary<string, float> InitialValueOverrides;
|
||||
@ -609,6 +636,8 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
ref ImmutableDictionary<string, string> prefixRemaps
|
||||
)
|
||||
{
|
||||
var remapper = ParameterRenameMappings.Get(_context.PluginBuildContext);
|
||||
|
||||
ImmutableDictionary<string, ParameterInfo> parameterInfos = ImmutableDictionary<string, ParameterInfo>.Empty;
|
||||
|
||||
foreach (var param in p.parameters)
|
||||
@ -618,7 +647,9 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
var remapTo = param.remapTo;
|
||||
if (param.internalParameter)
|
||||
{
|
||||
remapTo = param.nameOrPrefix + "$$Internal_" + internalParamIndex++;
|
||||
remapTo = remapper.Remap(p,
|
||||
param.isPrefix ? ParameterNamespace.PhysBonesPrefix : ParameterNamespace.Animator,
|
||||
param.nameOrPrefix);
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(remapTo))
|
||||
{
|
||||
|
@ -6,4 +6,5 @@ using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("net.fushizen.xdress")]
|
||||
[assembly: InternalsVisibleTo("net.fushizen.xdress.editor")]
|
||||
[assembly: InternalsVisibleTo("nadena.dev.modular-avatar.harmony-patches")]
|
||||
[assembly: InternalsVisibleTo("nadena.dev.modular-avatar.harmony-patches")]
|
||||
[assembly: InternalsVisibleTo("nadena.dev.modular-avatar.param-introspection")]
|
@ -1,10 +1,12 @@
|
||||
{
|
||||
"name": "nadena.dev.modular-avatar.core.editor",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"nadena.dev.modular-avatar.core",
|
||||
"VRC.SDK3A",
|
||||
"VRC.SDKBase",
|
||||
"nadena.dev.ndmf"
|
||||
"nadena.dev.ndmf",
|
||||
"nadena.dev.ndmf.vrchat"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
|
@ -8,4 +8,5 @@ using System.Runtime.CompilerServices;
|
||||
[assembly: InternalsVisibleTo("net.fushizen.xdress")]
|
||||
[assembly: InternalsVisibleTo("net.fushizen.xdress.editor")]
|
||||
[assembly: InternalsVisibleTo("Tests")]
|
||||
[assembly: InternalsVisibleTo("nadena.dev.modular-avatar.harmony-patches")]
|
||||
[assembly: InternalsVisibleTo("nadena.dev.modular-avatar.harmony-patches")]
|
||||
[assembly: InternalsVisibleTo("nadena.dev.modular-avatar.param-introspection")]
|
@ -16,6 +16,6 @@
|
||||
},
|
||||
"vpmDependencies": {
|
||||
"com.vrchat.avatars": ">=3.4.0",
|
||||
"nadena.dev.ndmf": ">=1.3.6 <2.0.0-a"
|
||||
"nadena.dev.ndmf": ">=1.4.0-rc.0 <2.0.0-a"
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user