mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2024-12-29 18:55:06 +08:00
feat: add Mesh Settings component
This commit is contained in:
parent
095c2667a0
commit
3c7634e4ea
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c217d78c5408b04489548a5823bed3d4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,29 +0,0 @@
|
||||
using nadena.dev.modular_avatar.core.editor;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace modular_avatar_tests
|
||||
{
|
||||
internal class ProbeAnchorTests : TestBase
|
||||
{
|
||||
[Test]
|
||||
public void TestProbeAnchor()
|
||||
{
|
||||
var prefab = CreatePrefab("ProbeAnchorTests.prefab");
|
||||
AvatarProcessor.ProcessAvatar(prefab);
|
||||
|
||||
var root = prefab.transform.Find("RendererRoot");
|
||||
var target = prefab.transform.Find("ProbeTarget");
|
||||
var obj1 = prefab.transform.Find("RendererRoot/SkinnedMeshRenderer").GetComponent<Renderer>();
|
||||
var obj2 = prefab.transform.Find("RendererRoot/MeshRenderer").GetComponent<Renderer>();
|
||||
var obj3 = prefab.transform.Find("RendererRoot/ParticleSystemRenderer").GetComponent<Renderer>();
|
||||
var obj4 = prefab.transform.Find("RendererRoot/TrailRenderer").GetComponent<Renderer>();
|
||||
|
||||
Assert.AreEqual(target, obj1.probeAnchor);
|
||||
Assert.AreEqual(target, obj2.probeAnchor);
|
||||
Assert.AreEqual(target, obj3.probeAnchor);
|
||||
Assert.AreEqual(target, obj4.probeAnchor);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f3adfcc27e73a249badd8f42ca5a57c
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -201,6 +201,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
new ProbeAnchorProcessor().OnPreprocessAvatar(avatarGameObject);
|
||||
new VisibleHeadAccessoryProcessor(vrcAvatarDescriptor).Process(context);
|
||||
new BoundsOverrideProcessor().OnProcessAvatar(avatarGameObject);
|
||||
new MeshSettingsPass(context).OnPreprocessAvatar();
|
||||
new RemapAnimationPass(vrcAvatarDescriptor).Process(context.AnimationDatabase);
|
||||
new BlendshapeSyncAnimationProcessor().OnPreprocessAvatar(avatarGameObject, context);
|
||||
PhysboneBlockerPass.Process(avatarGameObject);
|
||||
|
@ -17,6 +17,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
out var avatarRoot, out var avatarHips, out var outfitHips)
|
||||
) return;
|
||||
|
||||
var outfitRoot = cmd.context as GameObject;
|
||||
var avatarArmature = avatarHips.transform.parent;
|
||||
var outfitArmature = outfitHips.transform.parent;
|
||||
|
||||
@ -28,6 +29,95 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
merge.InferPrefixSuffix();
|
||||
HeuristicBoneMapper.RenameBonesByHeuristic(merge);
|
||||
}
|
||||
|
||||
if (outfitRoot != null
|
||||
&& outfitRoot.GetComponent<ModularAvatarMeshSettings>() == null
|
||||
&& outfitRoot.GetComponentInParent<ModularAvatarMeshSettings>() == null)
|
||||
{
|
||||
var meshSettings = Undo.AddComponent<ModularAvatarMeshSettings>(outfitRoot.gameObject);
|
||||
Transform rootBone = null, probeAnchor = null;
|
||||
Bounds bounds = ModularAvatarMeshSettings.DEFAULT_BOUNDS;
|
||||
|
||||
FindConsistentSettings(avatarRoot, ref probeAnchor, ref rootBone, ref bounds);
|
||||
|
||||
if (probeAnchor == null)
|
||||
{
|
||||
probeAnchor = avatarHips.transform;
|
||||
}
|
||||
|
||||
if (rootBone == null)
|
||||
{
|
||||
rootBone = avatarRoot.transform;
|
||||
}
|
||||
|
||||
meshSettings.InheritProbeAnchor = ModularAvatarMeshSettings.InheritMode.Set;
|
||||
meshSettings.InheritBounds = ModularAvatarMeshSettings.InheritMode.Set;
|
||||
|
||||
meshSettings.ProbeAnchor = new AvatarObjectReference();
|
||||
meshSettings.ProbeAnchor.referencePath = RuntimeUtil.RelativePath(avatarRoot, probeAnchor.gameObject);
|
||||
|
||||
meshSettings.RootBone = new AvatarObjectReference();
|
||||
meshSettings.RootBone.referencePath = RuntimeUtil.RelativePath(avatarRoot, rootBone.gameObject);
|
||||
meshSettings.Bounds = bounds;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FindConsistentSettings(
|
||||
GameObject avatarRoot,
|
||||
ref Transform probeAnchor,
|
||||
ref Transform rootBone,
|
||||
ref Bounds bounds
|
||||
)
|
||||
{
|
||||
// We assume the renderers directly under the avatar root came from the original avatar and are _probably_
|
||||
// set consistently. If so, we use this as a basis for the new outfit's settings.
|
||||
|
||||
bool firstRenderer = true;
|
||||
bool firstSkinnedMeshRenderer = true;
|
||||
|
||||
foreach (Transform directChild in avatarRoot.transform)
|
||||
{
|
||||
var renderer = directChild.GetComponent<Renderer>();
|
||||
if (renderer == null) continue;
|
||||
|
||||
if (firstRenderer)
|
||||
{
|
||||
probeAnchor = renderer.probeAnchor;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (renderer.probeAnchor != probeAnchor)
|
||||
{
|
||||
probeAnchor = null; // inconsistent configuration
|
||||
}
|
||||
}
|
||||
|
||||
firstRenderer = false;
|
||||
|
||||
var skinnedMeshRenderer = renderer as SkinnedMeshRenderer;
|
||||
if (skinnedMeshRenderer == null) continue;
|
||||
|
||||
if (firstSkinnedMeshRenderer)
|
||||
{
|
||||
rootBone = skinnedMeshRenderer.rootBone;
|
||||
bounds = skinnedMeshRenderer.localBounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rootBone != skinnedMeshRenderer.rootBone)
|
||||
{
|
||||
rootBone = null; // inconsistent configuration
|
||||
bounds = ModularAvatarMeshSettings.DEFAULT_BOUNDS;
|
||||
}
|
||||
else if (Vector3.Distance(bounds.center, skinnedMeshRenderer.bounds.center) > 0.01f
|
||||
|| Vector3.Distance(bounds.extents, skinnedMeshRenderer.bounds.extents) > 0.01f)
|
||||
{
|
||||
bounds = ModularAvatarMeshSettings.DEFAULT_BOUNDS;
|
||||
}
|
||||
}
|
||||
|
||||
firstSkinnedMeshRenderer = false;
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/ModularAvatar/Setup Outfit", true, PRIORITY)]
|
||||
|
@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static nadena.dev.modular_avatar.core.editor.Localization;
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(ModularAvatarMeshSettings.InheritMode))]
|
||||
class MeshSettingsInheritMode : EnumDrawer<ModularAvatarMeshSettings.InheritMode>
|
||||
{
|
||||
protected override string localizationPrefix => "mesh_settings.inherit_mode";
|
||||
}
|
||||
|
||||
[CustomEditor(typeof(ModularAvatarMeshSettings))]
|
||||
[CanEditMultipleObjects]
|
||||
internal class MeshSettingsEditor : MAEditorBase
|
||||
{
|
||||
private SerializedProperty _prop_inherit_probe_anchor;
|
||||
private SerializedProperty _prop_probe_anchor;
|
||||
|
||||
private SerializedProperty _prop_inherit_bounds;
|
||||
private SerializedProperty _prop_root_bone;
|
||||
private SerializedProperty _prop_bounds;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_prop_inherit_probe_anchor =
|
||||
serializedObject.FindProperty(nameof(ModularAvatarMeshSettings.InheritProbeAnchor));
|
||||
_prop_probe_anchor = serializedObject.FindProperty(nameof(ModularAvatarMeshSettings.ProbeAnchor));
|
||||
|
||||
_prop_inherit_bounds = serializedObject.FindProperty(nameof(ModularAvatarMeshSettings.InheritBounds));
|
||||
_prop_root_bone = serializedObject.FindProperty(nameof(ModularAvatarMeshSettings.RootBone));
|
||||
_prop_bounds = serializedObject.FindProperty(nameof(ModularAvatarMeshSettings.Bounds));
|
||||
}
|
||||
|
||||
protected override void OnInnerInspectorGUI()
|
||||
{
|
||||
MeshSettingsPass.MergedSettings merged = new MeshSettingsPass.MergedSettings();
|
||||
bool haveMerged = false;
|
||||
|
||||
ModularAvatarMeshSettings settings = null;
|
||||
if (targets.Length == 1)
|
||||
{
|
||||
settings = (ModularAvatarMeshSettings) target;
|
||||
var avatar = RuntimeUtil.FindAvatarInParents(settings.transform);
|
||||
if (avatar != null)
|
||||
{
|
||||
Component mesh = (Component) target;
|
||||
merged = MeshSettingsPass.MergeSettings(avatar.transform, mesh.transform);
|
||||
haveMerged = true;
|
||||
}
|
||||
}
|
||||
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.LabelField(G("mesh_settings.header_probe_anchor"), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_prop_inherit_probe_anchor, G("mesh_settings.inherit_probe_anchor"));
|
||||
if (_prop_inherit_probe_anchor.enumValueIndex == (int) ModularAvatarMeshSettings.InheritMode.Set)
|
||||
{
|
||||
EditorGUILayout.PropertyField(_prop_probe_anchor, G("mesh_settings.probe_anchor"));
|
||||
}
|
||||
else if (_prop_inherit_probe_anchor.enumValueIndex == (int) ModularAvatarMeshSettings.InheritMode.Inherit &&
|
||||
haveMerged)
|
||||
{
|
||||
using (new EditorGUI.DisabledScope(true))
|
||||
{
|
||||
EditorGUILayout.ObjectField(G("mesh_settings.probe_anchor"), merged.ProbeAnchor, typeof(Transform),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Separator();
|
||||
|
||||
EditorGUILayout.LabelField(G("mesh_settings.header_bounds"), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_prop_inherit_bounds, G("mesh_settings.inherit_bounds"));
|
||||
if (_prop_inherit_bounds.enumValueIndex == (int) ModularAvatarMeshSettings.InheritMode.Set)
|
||||
{
|
||||
EditorGUILayout.PropertyField(_prop_root_bone, G("mesh_settings.root_bone"));
|
||||
EditorGUILayout.PropertyField(_prop_bounds, G("mesh_settings.bounds"));
|
||||
}
|
||||
else if (_prop_inherit_bounds.enumValueIndex == (int) ModularAvatarMeshSettings.InheritMode.Inherit &&
|
||||
haveMerged)
|
||||
{
|
||||
using (new EditorGUI.DisabledScope(true))
|
||||
{
|
||||
EditorGUILayout.ObjectField(G("mesh_settings.root_bone"), merged.RootBone, typeof(Transform), true);
|
||||
EditorGUILayout.PropertyField(_prop_bounds, G("mesh_settings.bounds"));
|
||||
}
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
ShowLanguageUI();
|
||||
}
|
||||
|
||||
[DrawGizmo(GizmoType.Selected)]
|
||||
private static void DrawGizmo(ModularAvatarMeshSettings component, GizmoType gizmoType)
|
||||
{
|
||||
if (component.InheritBounds != ModularAvatarMeshSettings.InheritMode.Set) return;
|
||||
|
||||
Matrix4x4 oldMatrix = Gizmos.matrix;
|
||||
|
||||
Vector3 center = component.Bounds.center;
|
||||
Vector3 size = component.Bounds.size;
|
||||
|
||||
Transform rootBone = component.RootBone.Get(component)?.transform;
|
||||
try
|
||||
{
|
||||
if (rootBone != null)
|
||||
{
|
||||
Gizmos.matrix *= rootBone.localToWorldMatrix;
|
||||
}
|
||||
|
||||
Gizmos.DrawWireCube(center, size);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Gizmos.matrix = oldMatrix;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 441e32d18e0f4971be8f1853a7ceacae
|
||||
timeCreated: 1685960604
|
@ -70,6 +70,19 @@
|
||||
"bounds_override.root_bone": "Root Bone",
|
||||
"bounds_override.bounds": "Bounds",
|
||||
"bounds_override_blocker.help": "Within this object will no longer be affected by the parent's BoundsOverride.",
|
||||
"mesh_settings.header_probe_anchor": "Anchor Override Configuration",
|
||||
"mesh_settings.inherit_probe_anchor": "Anchor Override Mode",
|
||||
"mesh_settings.probe_anchor": "Anchor Override",
|
||||
"mesh_settings.probe_anchor.tooltip": "Sets the anchor override used for lighting calculations for renderers inside this object",
|
||||
"mesh_settings.header_bounds": "Bounds Override Configuration",
|
||||
"mesh_settings.inherit_bounds": "Bounds Override Mode",
|
||||
"mesh_settings.root_bone": "Root Bone",
|
||||
"mesh_settings.root_bone.tooltip": "The root bone of the mesh. This is used as a reference point from which to calculate the bounds of the mesh.",
|
||||
"mesh_settings.bounds": "Bounds",
|
||||
"mesh_settings.bounds.tooltip": "The bounds of the mesh. This is used to determine when rendering can be skipped for an offscreen mesh.",
|
||||
"mesh_settings.inherit_mode.Inherit": "Inherit",
|
||||
"mesh_settings.inherit_mode.Set": "Set",
|
||||
"mesh_settings.inherit_mode.DontSet": "Don't Set (use mesh as-is)",
|
||||
"pb_blocker.help": "This object will not be affected by PhysBones attached to parents.",
|
||||
"probe_anchor.help": "Sets the Anchor Override for the inner renderer object.",
|
||||
"hint.bad_vrcsdk": "Incompatible version of VRCSDK detected.\n\nPlease try upgrading your VRCSDK; if this does not work, check for a newer version of Modular Avatar as well.",
|
||||
|
@ -68,6 +68,19 @@
|
||||
"bounds_override.root_bone": "ルートボーン",
|
||||
"bounds_override.bounds": "バウンズ",
|
||||
"bounds_override_blocker.help": "このオブジェクト内は親のBoundsOverrideの影響を受けなくなります。",
|
||||
"mesh_settings.header_probe_anchor": "Anchor Override 設定",
|
||||
"mesh_settings.inherit_probe_anchor": "設定モード",
|
||||
"mesh_settings.probe_anchor": "Anchor Override",
|
||||
"mesh_settings.probe_anchor.tooltip": "このオブジェクトとその子のレンダラーのAnchorOverrideを設定します。",
|
||||
"mesh_settings.header_bounds": "Bounds 設定",
|
||||
"mesh_settings.inherit_bounds": "設定モード",
|
||||
"mesh_settings.root_bone": "Root Bone",
|
||||
"mesh_settings.root_bone.tooltip": "このオブジェクトとその子のメッシュで設定されるルートボーン。メッシュのバウンズを計算するための参照点として使用されます。",
|
||||
"mesh_settings.bounds": "Bounds",
|
||||
"mesh_settings.bounds.tooltip": "このオブジェクトとその子のメッシュで設定されるバウンズ。画面外のメッシュのレンダリングを省略するかどうかを決定するために使用されます。",
|
||||
"mesh_settings.inherit_mode.Inherit": "継承",
|
||||
"mesh_settings.inherit_mode.Set": "設定",
|
||||
"mesh_settings.inherit_mode.DontSet": "設定しない(メッシュ本体の設定のまま)",
|
||||
"pb_blocker.help": "このオブジェクトは親のPhysBoneから影響を受けなくなります。",
|
||||
"probe_anchor.help": "このオブジェクトに含まれるレンダラーのAnchorOverrideを設定します。",
|
||||
"hint.bad_vrcsdk": "使用中のVRCSDKのバージョンとは互換性がありません。\n\nVRCSDKを更新してみてください。それでもだめでしたら、Modular Avatarにも最新版が出てないかチェックしてください。",
|
||||
|
110
Packages/nadena.dev.modular-avatar/Editor/MeshSettingsPass.cs
Normal file
110
Packages/nadena.dev.modular-avatar/Editor/MeshSettingsPass.cs
Normal file
@ -0,0 +1,110 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
internal static class InheritModeExtension
|
||||
{
|
||||
internal static bool NotFinal(this ModularAvatarMeshSettings.InheritMode mode)
|
||||
{
|
||||
return mode == ModularAvatarMeshSettings.InheritMode.Inherit;
|
||||
}
|
||||
}
|
||||
|
||||
internal class MeshSettingsPass
|
||||
{
|
||||
private readonly BuildContext context;
|
||||
|
||||
public MeshSettingsPass(BuildContext context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void OnPreprocessAvatar()
|
||||
{
|
||||
foreach (var mesh in context.AvatarDescriptor.GetComponentsInChildren<Renderer>(true))
|
||||
{
|
||||
ProcessMesh(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
internal struct MergedSettings
|
||||
{
|
||||
public bool SetAnchor, SetBounds;
|
||||
|
||||
public Transform ProbeAnchor;
|
||||
public Transform RootBone;
|
||||
public Bounds Bounds;
|
||||
}
|
||||
|
||||
private static bool Inherit(ref ModularAvatarMeshSettings.InheritMode mode,
|
||||
ModularAvatarMeshSettings.InheritMode srcmode)
|
||||
{
|
||||
if (mode != ModularAvatarMeshSettings.InheritMode.Inherit ||
|
||||
srcmode == ModularAvatarMeshSettings.InheritMode.Inherit)
|
||||
return false;
|
||||
|
||||
mode = srcmode;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static MergedSettings MergeSettings(Transform avatarRoot, Transform referenceObject)
|
||||
{
|
||||
MergedSettings merged = new MergedSettings();
|
||||
|
||||
Transform current = referenceObject;
|
||||
|
||||
ModularAvatarMeshSettings.InheritMode inheritProbeAnchor = ModularAvatarMeshSettings.InheritMode.Inherit;
|
||||
ModularAvatarMeshSettings.InheritMode inheritBounds = ModularAvatarMeshSettings.InheritMode.Inherit;
|
||||
|
||||
do
|
||||
{
|
||||
var settings = current.GetComponent<ModularAvatarMeshSettings>();
|
||||
if (current == avatarRoot)
|
||||
{
|
||||
current = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
current = current.transform.parent;
|
||||
}
|
||||
|
||||
if (settings == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Inherit(ref inheritProbeAnchor, settings.InheritProbeAnchor))
|
||||
{
|
||||
merged.ProbeAnchor = settings.ProbeAnchor.Get(settings)?.transform;
|
||||
}
|
||||
|
||||
if (Inherit(ref inheritBounds, settings.InheritBounds))
|
||||
{
|
||||
merged.RootBone = settings.RootBone.Get(settings)?.transform;
|
||||
merged.Bounds = settings.Bounds;
|
||||
}
|
||||
} while (current != null && (inheritProbeAnchor.NotFinal() || inheritBounds.NotFinal()));
|
||||
|
||||
merged.SetAnchor = inheritProbeAnchor == ModularAvatarMeshSettings.InheritMode.Set;
|
||||
merged.SetBounds = inheritBounds == ModularAvatarMeshSettings.InheritMode.Set;
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
private void ProcessMesh(Renderer mesh)
|
||||
{
|
||||
MergedSettings settings = MergeSettings(context.AvatarDescriptor.transform, mesh.transform);
|
||||
|
||||
if (settings.SetAnchor)
|
||||
{
|
||||
mesh.probeAnchor = settings.ProbeAnchor;
|
||||
}
|
||||
|
||||
if (settings.SetBounds && mesh is SkinnedMeshRenderer smr)
|
||||
{
|
||||
smr.rootBone = settings.RootBone;
|
||||
smr.localBounds = settings.Bounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06b7e7286f434327b1d0ef4b60496055
|
||||
timeCreated: 1685960001
|
@ -15,7 +15,7 @@ namespace nadena.dev.modular_avatar.core
|
||||
|
||||
public GameObject Get(Component container)
|
||||
{
|
||||
if (_cacheValid && _cachedPath == referencePath) return _cachedReference;
|
||||
if (_cacheValid && _cachedPath == referencePath && _cachedReference != null) return _cachedReference;
|
||||
|
||||
_cacheValid = true;
|
||||
_cachedPath = referencePath;
|
||||
|
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace nadena.dev.modular_avatar.core
|
||||
{
|
||||
[AddComponentMenu("Modular Avatar/MA Mesh Settings")]
|
||||
[DisallowMultipleComponent]
|
||||
public class ModularAvatarMeshSettings : AvatarTagComponent
|
||||
{
|
||||
internal static readonly Bounds DEFAULT_BOUNDS = new Bounds(Vector3.zero, Vector3.one * 2);
|
||||
|
||||
[Serializable]
|
||||
public enum InheritMode
|
||||
{
|
||||
Inherit,
|
||||
Set,
|
||||
DontSet
|
||||
}
|
||||
|
||||
//[Header("Probe anchor configuration")]
|
||||
public InheritMode InheritProbeAnchor = InheritMode.Inherit;
|
||||
public AvatarObjectReference ProbeAnchor;
|
||||
|
||||
//[Header("Bounds configuration")]
|
||||
public InheritMode InheritBounds = InheritMode.Inherit;
|
||||
public AvatarObjectReference RootBone;
|
||||
public Bounds Bounds = DEFAULT_BOUNDS;
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c3ea8cc803f79c45a629964d7316a3f
|
||||
guid: 560fdafd46c74b2db6422fdf0e7f2363
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
icon: {fileID: 2800000, guid: a8edd5bd1a0a64a40aa99cc09fb5f198, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Reference in New Issue
Block a user