first commit

This commit is contained in:
lethanhsonvsp
2025-11-17 15:16:36 +07:00
commit a40d0921eb
17012 changed files with 2652386 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
using UnityEngine;
namespace UnityEditor.Animations.Rigging
{
internal interface IRigEffector
{
Transform transform { get; }
bool visible { get; set; }
void OnSceneGUI();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5aa7c79e34bb34aab80024d95fafe526
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
using System;
namespace UnityEditor.Animations.Rigging
{
interface IRigEffectorOverlay : IDisposable
{
bool IsValid();
void OnSceneGUIOverlay();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4231b7e96c3094051a12f91bae5c7c90
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,307 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Animations.Rigging;
namespace UnityEditor.Animations.Rigging
{
[Serializable]
internal class RigEffector : ScriptableObject, IRigEffector
{
[SerializeField] private RigEffectorData m_Data;
public Transform transform
{
get => m_Data.transform;
}
public bool visible
{
get => m_Data.visible;
set => m_Data.visible = value;
}
private static int s_ButtonHash = "RigEffector".GetHashCode();
public void Initialize(RigEffectorData data)
{
m_Data = data;
}
private static Material s_Material;
public static Material material
{
get
{
if (!s_Material)
{
var shader = EditorHelper.LoadShader("BoneHandles.shader");
s_Material = new Material(shader);
s_Material.hideFlags = HideFlags.HideAndDontSave;
}
return s_Material;
}
}
public static RigEffectorData.Style defaultStyle
{
get
{
var style = new RigEffectorData.Style()
{
shape = EditorHelper.LoadShape("LocatorEffector.asset"),
color = new Color(1f, 0f, 0f, 0.5f),
size = 0.10f,
position = Vector3.zero,
rotation = Vector3.zero
};
return style;
}
}
public void OnSceneGUI()
{
if (!m_Data.visible)
return;
// Might happen if we delete transform while effector still exists.
if (transform == null)
return;
var style = m_Data.style;
// Disregard effectors without shapes.
if (style.shape == null)
return;
if (SceneVisibilityManager.instance.IsHidden(transform.gameObject, false))
return;
var mask = UnityEditor.Tools.visibleLayers;
if ((mask & (1 << transform.gameObject.layer)) == 0)
return;
int id = GUIUtility.GetControlID(s_ButtonHash, FocusType.Passive);
Event evt = Event.current;
switch (evt.GetTypeForControl(id))
{
case EventType.Layout:
{
HandleUtility.AddControl(id, DistanceToEffector(transform, style.shape, style.position, style.rotation, style.size));
break;
}
case EventType.MouseDown:
{
if (evt.alt)
break;
if (HandleUtility.nearestControl == id && evt.button == 0)
{
GameObject targetGameObject = transform.gameObject;
if (!SceneVisibilityManager.instance.IsPickingDisabled(targetGameObject, false))
{
GUIUtility.hotControl = id; // Grab mouse focus
EditorHelper.HandleClickSelection(targetGameObject, evt);
evt.Use();
}
}
break;
}
case EventType.MouseDrag:
{
if (!evt.alt && GUIUtility.hotControl == id)
{
GameObject targetGameObject = transform.gameObject;
if (!SceneVisibilityManager.instance.IsPickingDisabled(targetGameObject, false))
{
DragAndDrop.PrepareStartDrag();
DragAndDrop.objectReferences = new UnityEngine.Object[] {transform};
DragAndDrop.StartDrag(ObjectNames.GetDragAndDropTitle(transform));
GUIUtility.hotControl = 0;
evt.Use();
}
}
break;
}
case EventType.MouseUp:
{
if (GUIUtility.hotControl == id && (evt.button == 0 || evt.button == 2))
{
GUIUtility.hotControl = 0;
evt.Use();
}
break;
}
case EventType.Repaint:
{
Matrix4x4 matrix = GetEffectorMatrix(transform, style.position, style.rotation, style.size);
Color highlight = style.color;
bool hoveringEffector = GUIUtility.hotControl == 0 && HandleUtility.nearestControl == id;
hoveringEffector = hoveringEffector &&
!SceneVisibilityManager.instance.IsPickingDisabled(transform.gameObject, false);
if (hoveringEffector)
{
highlight = Handles.preselectionColor;
}
else if (Selection.Contains(transform.gameObject) || Selection.activeObject == transform.gameObject)
{
highlight = Handles.selectedColor;
}
try
{
Material mat = material;
var shapeHighlight = MeshHasWireframeShapes(style.shape) ? style.color : highlight;
var wireHighlight = new Color(highlight.r, highlight.g, highlight.b, 1f);
if (style.shape.subMeshCount > 0)
{
// Draw every sub meshes separately to control highlight vs shape colors.
for (int i = 0; i < style.shape.subMeshCount; ++i)
{
MeshTopology topology = style.shape.GetTopology(i);
bool isFilled = (topology == MeshTopology.Triangles || topology == MeshTopology.Quads);
mat.SetColor("_Color", isFilled ? shapeHighlight : wireHighlight);
mat.SetPass(0);
Graphics.DrawMeshNow(style.shape, matrix, i);
}
}
else
{
MeshTopology topology = style.shape.GetTopology(0);
bool isFilled = (topology == MeshTopology.Triangles || topology == MeshTopology.Quads);
mat.SetColor("_Color", isFilled ? shapeHighlight : wireHighlight);
mat.SetPass(0);
Graphics.DrawMeshNow(style.shape, matrix);
}
}
catch (Exception exception)
{
Debug.LogException(exception);
}
}
break;
}
}
private bool FindMeshTopology(Mesh shape, IEnumerable<MeshTopology> topologies)
{
if (shape.subMeshCount > 0)
{
for (int i = 0; i < shape.subMeshCount; ++i)
{
MeshTopology topology = shape.GetTopology(i);
foreach (var topologyQuery in topologies)
{
if (topologyQuery == topology)
return true;
}
}
}
else
{
var topology = shape.GetTopology(0);
foreach (var topologyQuery in topologies)
{
if (topologyQuery == topology)
return true;
}
}
return false;
}
private bool MeshHasFilledShapes(Mesh shape) =>
FindMeshTopology(shape, new MeshTopology[] {MeshTopology.Triangles, MeshTopology.Quads});
private bool MeshHasWireframeShapes(Mesh shape) =>
FindMeshTopology(shape, new MeshTopology[] {MeshTopology.Lines, MeshTopology.LineStrip});
private Matrix4x4 GetEffectorMatrix(Transform transform, Vector3 position, Vector3 rotation, float size)
{
return Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one) * Matrix4x4.TRS(position, Quaternion.Euler(rotation), new Vector3(size, size, size));
}
private float DistanceToEffector(Transform transform, Mesh shape, Vector3 position, Vector3 rotation, float size)
{
Matrix4x4 matrix = GetEffectorMatrix(transform, position, rotation, size);
Vector3 origin = matrix.MultiplyPoint(Vector3.zero);
if (shape == null)
return HandleUtility.DistanceToCircle(origin, size * 0.5f);
if (MeshHasFilledShapes(shape))
{
var bounds = shape.bounds;
var extents = Mathf.Max( Mathf.Max(bounds.extents.x, bounds.extents.y), bounds.extents.z);
return HandleUtility.DistanceToCircle(origin + bounds.center * size, extents * size);
}
float nearestDistance = Mathf.Infinity;
Vector3[] vertices = shape.vertices;
for (int i = 0; i < shape.subMeshCount; ++i)
{
MeshTopology topology = shape.GetTopology(i);
if (topology == MeshTopology.Lines)
{
int[] indices = shape.GetIndices(i);
int count = 0;
while (count < indices.Length)
{
float d = HandleUtility.DistanceToLine(matrix.MultiplyPoint(vertices[indices[count]]), matrix.MultiplyPoint(vertices[indices[count+1]]));
if (d < nearestDistance)
nearestDistance = d;
count += 2;
}
}
else if (topology == MeshTopology.LineStrip)
{
int[] indices = shape.GetIndices(i);
if (indices.Length > 0)
{
int count = 0;
float d = 0f;
Vector3 v0 = matrix.MultiplyPoint(vertices[indices[count]]);
Vector3 v1 = v0;
while (++count < indices.Length)
{
Vector3 v2 = matrix.MultiplyPoint(vertices[indices[count]]);
d = HandleUtility.DistanceToLine(v1, v2);
if (d < nearestDistance)
nearestDistance = d;
v1 = v2;
}
d = HandleUtility.DistanceToLine(v1, v0);
if (d < nearestDistance)
nearestDistance = d;
}
}
}
return nearestDistance;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ff03b96c5bd3745c28814ed6eb4341cb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,190 @@
using UnityEngine;
using UnityEngine.Animations.Rigging;
namespace UnityEditor.Animations.Rigging
{
[CustomOverlay(typeof(RigEffector))]
class RigEffectorOverlay : IRigEffectorOverlay
{
private Object[] m_TargetObjects;
private SerializedObject m_SerializedObject;
private SerializedProperty m_Visible;
private SerializedProperty m_Shape;
private SerializedProperty m_Color;
private SerializedProperty m_Size;
private SerializedProperty m_Position;
private SerializedProperty m_Rotation;
private bool m_ExpandOverlay;
private static GUIContent s_VisibleLabel = new GUIContent("Visible");
private static GUIContent s_ShapeLabel = new GUIContent("Shape");
private static GUIContent s_ColorLabel = new GUIContent("Color");
private static GUIContent s_SizeLabel = new GUIContent("Size");
private static GUIContent s_PositionLabel = new GUIContent("Position");
private static GUIContent s_RotationLabel = new GUIContent("Rotation");
private static GUIContent s_RemoveLabel = new GUIContent("Remove");
private static string s_ExpandOverlayPrefKey = "AnimationRigging.ExpandOverlay";
private static GUILayoutOption s_FixedWidth = GUILayout.Width(210f);
public void Initialize(Object[] effectors)
{
m_TargetObjects = effectors;
m_SerializedObject = new SerializedObject(effectors);
SerializedProperty data = m_SerializedObject.FindProperty("m_Data");
m_Visible = data.FindPropertyRelative("m_Visible");
SerializedProperty style = data.FindPropertyRelative("m_Style");
m_Shape = style.FindPropertyRelative("shape");
m_Color = style.FindPropertyRelative("color");
m_Size = style.FindPropertyRelative("size");
m_Position = style.FindPropertyRelative("position");
m_Rotation = style.FindPropertyRelative("rotation");
m_ExpandOverlay = EditorPrefs.GetBool(s_ExpandOverlayPrefKey, true);
}
private IRigEffectorHolder FetchRigEffectorHolder(Transform transform)
{
var rigBuilder = EditorHelper.GetClosestComponent<RigBuilder>(transform);
var rig = EditorHelper.GetClosestComponent<Rig>(transform, (rigBuilder != null) ? rigBuilder.transform : null);
if (rigBuilder.ContainsEffector(transform))
{
return rigBuilder;
}
else if (rig.ContainsEffector(transform))
{
return rig;
}
return null;
}
public bool IsValid() => m_SerializedObject.targetObject != null;
public void OnSceneGUIOverlay()
{
if (!IsValid())
return;
m_SerializedObject.Update();
GameObject targetGameObject = null;
if (!m_SerializedObject.isEditingMultipleObjects)
{
RigEffector rigEffector = m_SerializedObject.targetObject as RigEffector;
if (rigEffector != null && rigEffector.transform != null)
{
targetGameObject = rigEffector.transform.gameObject;
}
}
GUILayout.BeginHorizontal(s_FixedWidth);
EditorGUI.BeginChangeCheck();
m_ExpandOverlay = EditorGUILayout.Toggle(m_ExpandOverlay, EditorStyles.foldout, GUILayout.Width(12));
if (EditorGUI.EndChangeCheck())
{
EditorPrefs.SetBool(s_ExpandOverlayPrefKey, m_ExpandOverlay);
}
GUILayout.BeginHorizontal(EditorStyles.toolbar);
EditorGUILayout.PropertyField(m_Visible, GUIContent.none, GUILayout.Width(17));
GUILayout.Label((targetGameObject != null) ? targetGameObject.name : "(Multiple objects)");
if (GUILayout.Button(GUIContent.none, "OL Minus", GUILayout.Width(17)))
{
UnityEngine.Object[] targetObjects = m_SerializedObject.targetObjects;
foreach(var targetObject in targetObjects)
{
var effector = targetObject as IRigEffector;
Transform transform = effector.transform;
IRigEffectorHolder holder = FetchRigEffectorHolder(transform);
if (holder != null)
{
var holderObject = holder as UnityEngine.Object;
Undo.RecordObject(holderObject, "Remove Effector");
if (PrefabUtility.IsPartOfPrefabInstance(holderObject))
EditorUtility.SetDirty(holderObject);
holder.RemoveEffector(transform);
}
}
}
GUILayout.EndHorizontal();
GUILayout.EndHorizontal();
if (m_ExpandOverlay)
{
EditorGUILayout.LabelField(s_ShapeLabel, s_FixedWidth);
EditorGUILayout.PropertyField(m_Shape, GUIContent.none, s_FixedWidth);
Rect rect = GUILayoutUtility.GetRect(s_ColorLabel, EditorStyles.colorField, s_FixedWidth);
// Shenanigans to bypass color picker bug.
var evt = Event.current;
if (evt.type == EventType.MouseUp)
{
if (rect.Contains(evt.mousePosition))
{
GUIUtility.hotControl = 0;
}
}
EditorGUI.BeginProperty(rect, s_ColorLabel, m_Color);
EditorGUI.BeginChangeCheck();
Color newColor = EditorGUI.ColorField(rect, s_ColorLabel, m_Color.colorValue, false, true, false);
if (EditorGUI.EndChangeCheck())
{
m_Color.colorValue = newColor;
}
EditorGUI.EndProperty();
EditorGUILayout.PropertyField(m_Size, s_SizeLabel, s_FixedWidth);
EditorGUILayout.PropertyField(m_Position, s_PositionLabel, s_FixedWidth);
EditorGUILayout.PropertyField(m_Rotation, s_RotationLabel, s_FixedWidth);
}
if (m_SerializedObject.hasModifiedProperties)
{
UnityEngine.Object[] targetObjects = m_SerializedObject.targetObjects;
foreach(var targetObject in targetObjects)
{
var effector = targetObject as IRigEffector;
Transform transform = effector.transform;
IRigEffectorHolder holder = FetchRigEffectorHolder(transform);
if (holder != null)
{
var holderObject = holder as UnityEngine.Object;
Undo.RecordObject(holderObject, "Edit Effector");
if (PrefabUtility.IsPartOfPrefabInstance(holderObject))
EditorUtility.SetDirty(holderObject);
}
}
m_SerializedObject.ApplyModifiedProperties();
}
}
public void Dispose()
{
m_SerializedObject?.Dispose();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 128d92bb7c7e949569b14ae35b4b229e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,235 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Animations.Rigging;
#if SUPPORTS_SCENE_VIEW_OVERLAYS
using UnityEditor.Overlays;
#endif
using UnityEditor.Experimental.SceneManagement; // required for 2020.2
using UnityEditor.SceneManagement;
namespace UnityEditor.Animations.Rigging
{
[InitializeOnLoad]
static class RigEffectorRenderer
{
const string k_OverlayId = "Scene View/Animation Rigging";
const string k_DisplayName = "Animation Rigging";
static GUIContent s_OverlayTitle = new GUIContent(k_DisplayName);
static List<RigBuilder> s_RigBuilders = new List<RigBuilder>();
static Dictionary<RigEffectorData, RigEffector> s_Effectors = new Dictionary<RigEffectorData, RigEffector>();
static Transform[] s_ActiveSelection = null;
static List<RigEffector> s_ActiveEffectors = null;
static IRigEffectorOverlay s_ActiveOverlay = null;
static bool s_ActiveOverlayDirtied = true;
static RigEffectorRenderer()
{
RigBuilder.onAddRigBuilder += OnAddRigBuilder;
RigBuilder.onRemoveRigBuilder += OnRemoveRigBuilder;
SceneView.duringSceneGui += OnSceneGUI;
Selection.selectionChanged += OnSelectionChange;
ObjectFactory.componentWasAdded += OnComponentAdded;
}
static void OnSelectionChange()
{
s_ActiveOverlayDirtied = true;
}
static void OnComponentAdded(Component component)
{
if (!(component is Rig) && !(component is RigBuilder))
return;
s_ActiveOverlayDirtied = true;
}
static void FetchOrCreateEffectors(IRigEffectorHolder holder)
{
foreach(var effectorData in holder.effectors)
{
if (s_Effectors.ContainsKey(effectorData))
{
s_ActiveEffectors.Add(s_Effectors[effectorData]);
}
else
{
var newEffector = ScriptableObject.CreateInstance<RigEffector>();
newEffector.Initialize(effectorData);
s_Effectors.Add(effectorData, newEffector);
s_ActiveEffectors.Add(newEffector);
}
}
}
static void FetchOrCreateEffectors()
{
s_ActiveEffectors = new List<RigEffector>();
PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
for (int i = 0; i < s_RigBuilders.Count; i++)
{
var rigBuilder = s_RigBuilders[i];
if (rigBuilder == null)
continue;
if (prefabStage != null)
{
StageHandle stageHandle = prefabStage.stageHandle;
if (stageHandle.IsValid() && !stageHandle.Contains(rigBuilder.gameObject))
continue;
}
FetchOrCreateEffectors(rigBuilder);
var rigs = rigBuilder.GetComponentsInChildren<Rig>();
if (rigs != null)
{
foreach(var rig in rigs)
{
FetchOrCreateEffectors(rig);
}
}
}
}
static IRigEffectorOverlay FetchOrCreateEffectorOverlay()
{
if (!s_ActiveOverlayDirtied && s_ActiveOverlay != null && s_ActiveOverlay.IsValid())
return s_ActiveOverlay;
s_ActiveOverlay?.Dispose();
Transform[] transforms = Selection.GetTransforms(SelectionMode.ExcludePrefab | SelectionMode.Editable);
var inspectedEffectors = new List<Object>();
for (int i = 0; i < s_ActiveEffectors.Count; ++i)
{
var effector = s_ActiveEffectors[i];
if (effector != null && effector.transform != null)
{
if (Selection.Contains(effector.transform) || Selection.Contains(effector.transform.gameObject))
{
inspectedEffectors.Add(s_ActiveEffectors[i]);
}
}
}
if (inspectedEffectors.Count > 0)
{
var overlay = new RigEffectorOverlay();
overlay.Initialize(inspectedEffectors.ToArray());
s_ActiveOverlay = overlay;
}
else
{
RigEffectorWizard wizard = null;
foreach(var transform in transforms)
{
RigBuilder rigBuilder = EditorHelper.GetClosestComponent<RigBuilder>(transform);
Rig rig = EditorHelper.GetClosestComponent<Rig>(transform, (rigBuilder != null) ? rigBuilder.transform : null);
IRigEffectorHolder holder = (rig != null) ? (IRigEffectorHolder)rig : (IRigEffectorHolder)rigBuilder;
if (holder == null)
continue;
if (wizard == null)
wizard = new RigEffectorWizard();
wizard.Add(holder, transform);
}
if (wizard != null)
{
s_ActiveOverlay = wizard;
}
else
{
s_ActiveOverlay = null;
}
}
s_ActiveSelection = transforms;
s_ActiveOverlayDirtied = false;
return s_ActiveOverlay;
}
static void OnSceneGUI(SceneView sceneView)
{
// Fetch effectors and overlay once in Layout before processing events.
if (Event.current.type == EventType.Layout)
{
FetchOrCreateEffectors();
FetchOrCreateEffectorOverlay();
}
// Process effector events.
if (s_ActiveEffectors != null)
{
for (int i = 0; i < s_ActiveEffectors.Count; ++i)
{
var effector = s_ActiveEffectors[i];
if (effector == null)
continue;
effector.OnSceneGUI();
}
}
#if !SUPPORTS_SCENE_VIEW_OVERLAYS
// Process overlay events.
if (s_ActiveOverlay != null)
{
SceneViewOverlay.Begin(sceneView);
SceneViewOverlay.Window(s_OverlayTitle, SceneViewGUICallback, 1200);
SceneViewOverlay.End();
}
#endif
}
static void OnAddRigBuilder(RigBuilder rigBuilder)
{
s_RigBuilders.Add(rigBuilder);
}
static void OnRemoveRigBuilder(RigBuilder rigBuilder)
{
s_RigBuilders.Remove(rigBuilder);
s_Effectors.Clear();
}
private static void SceneViewGUICallback(UnityEngine.Object target, SceneView sceneView)
{
if (s_ActiveOverlay != null)
s_ActiveOverlay.OnSceneGUIOverlay();
}
#if SUPPORTS_SCENE_VIEW_OVERLAYS
[Overlay(typeof(SceneView), k_OverlayId, k_DisplayName)]
class Overlay : IMGUIOverlay, ITransientOverlay
{
public bool visible
{
get => s_ActiveOverlay != null;
}
public override void OnGUI()
{
if (s_ActiveOverlay != null)
s_ActiveOverlay.OnSceneGUIOverlay();
}
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aedff7ad35cc7477a9786519f4791c00
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,68 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Animations.Rigging;
namespace UnityEditor.Animations.Rigging
{
class RigEffectorWizard : IRigEffectorOverlay
{
private static GUIContent s_CreateEffectorLabel = new GUIContent("Create Effector");
private struct HolderTransformPair
{
public IRigEffectorHolder holder;
public Transform transform;
}
private List<HolderTransformPair> m_Transforms = new List<HolderTransformPair>();
public void Add(IRigEffectorHolder holder, Transform transform)
{
m_Transforms.Add(new HolderTransformPair() { holder = holder, transform = transform });
}
public bool IsValid() => true;
public void OnSceneGUIOverlay()
{
string labelName = "(no selection)";
if (m_Transforms.Count > 1)
{
labelName = "(Multiple objects)";
}
else if (m_Transforms.Count > 0)
{
if (m_Transforms[0].transform.gameObject != null)
{
labelName = m_Transforms[0].transform.gameObject.name;
}
}
GUILayout.BeginHorizontal(EditorStyles.toolbar, GUILayout.Width(210.0f));
GUILayout.Label(labelName);
if (GUILayout.Button(GUIContent.none, "OL Plus", GUILayout.Width(17)))
{
foreach (var pair in m_Transforms)
{
var targetObject = pair.holder as UnityEngine.Object;
Undo.RecordObject(targetObject, "Add Effector");
if (PrefabUtility.IsPartOfPrefabInstance(targetObject))
EditorUtility.SetDirty(targetObject);
pair.holder.AddEffector(pair.transform, RigEffector.defaultStyle);
}
}
GUILayout.EndHorizontal();
}
public void Dispose()
{
// nothing to do.
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 51459fd0b87be44cbb33be7842186a5e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: