add package files

This commit is contained in:
VR164000
2023-06-15 09:52:08 +08:00
parent 06d41412be
commit a0b3b91879
382 changed files with 44971 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
//========= Copyright 2019, HTC Corporation. All rights reserved. ===========
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.XR.OpenXR;
namespace VIVE
{
namespace FacialTracking.Sample
{
public static class Eye
{
public const int WeightingCount = (int)XrEyeShapeHTC.XR_EYE_EXPRESSION_MAX_ENUM_HTC;
private static XrFacialExpressionsHTC EyeExpression_;
private static int LastUpdateFrame = -1;
private static Error LastUpdateResult = Error.FAILED;
private static Dictionary<XrEyeShapeHTC, float> Weightings;
private static float[] blendshapes = new float[60];
static Eye()
{
Weightings = new Dictionary<XrEyeShapeHTC, float>();
for (int i = 0; i < WeightingCount; ++i) Weightings.Add((XrEyeShapeHTC)i, 0.0f);
}
private static bool UpdateData()
{
if (Time.frameCount == LastUpdateFrame) return LastUpdateResult == Error.WORK;
else LastUpdateFrame = Time.frameCount;
EyeExpression_.expressionCount = 60;
EyeExpression_.type = XrStructureType.XR_TYPE_FACIAL_EXPRESSIONS_HTC;
EyeExpression_.blendShapeWeightings = Marshal.AllocCoTaskMem(sizeof(float) * EyeExpression_.expressionCount);
var feature = OpenXRSettings.Instance.GetFeature<VIVE_FacialTracking_OpenXR_API>();
int res = feature.xrGetFacialExpressionsHTC(OpenXRSettings.Instance.GetFeature<VIVE_FacialTracking_OpenXR_API>().m_expressionHandle, ref EyeExpression_);
if (res == (int)XrResult.XR_SUCCESS)
{
Marshal.Copy(EyeExpression_.blendShapeWeightings, blendshapes, 0, EyeExpression_.expressionCount);
LastUpdateResult = Error.WORK;
}
else
{
LastUpdateResult = Error.FAILED;
}
return LastUpdateResult == Error.WORK;
}
public static bool GetEyeWeightings(out Dictionary<XrEyeShapeHTC, float> shapes, XrFacialExpressionsHTC expression)
{
for (int i = 0; i < WeightingCount; ++i)
{
Weightings[(XrEyeShapeHTC)(i)] = blendshapes[i];
}
shapes = Weightings;
return true;
}
/// <summary>
/// Gets weighting values from Eye module.
/// </summary>
/// <param name="shapes">Weighting values obtained from Eye module.</param>
/// <returns>Indicates whether the values received are new.</returns>\
[Obsolete("Create FacialManager object and call member function GetWeightings instead")]
public static bool GetEyeWeightings(out Dictionary<XrEyeShapeHTC, float> shapes)
{
UpdateData();
return GetEyeWeightings(out shapes, EyeExpression_);
}
}
}
}

View File

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

View File

@@ -0,0 +1,32 @@
//========= Copyright 2018, HTC Corporation. All rights reserved. ===========
using System;
using System.Runtime.InteropServices;
using UnityEngine;
namespace VIVE
{
namespace FacialTracking.Sample
{
public enum EyeShape
{
None = -1,
Eye_Left_Blink = 0,
Eye_Left_Wide,
Eye_Left_Right,
Eye_Left_Left,
Eye_Left_Up,
Eye_Left_Down,
Eye_Right_Blink = 6,
Eye_Right_Wide,
Eye_Right_Right,
Eye_Right_Left,
Eye_Right_Up,
Eye_Right_Down,
Eye_Frown = 12,
Eye_Left_Squeeze,
Eye_Right_Squeeze,
Max = 15,
}
}
}

View File

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

View File

@@ -0,0 +1,72 @@
//========= Copyright 2018, HTC Corporation. All rights reserved. ===========
#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
using System;
namespace VIVE
{
namespace FacialTracking.Sample
{
[CustomPropertyDrawer(typeof(EyeShapeTable))]
public class EyeShapeTableDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
Rect newFieldPosition = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
newFieldPosition.height = EditorGUIUtility.singleLineHeight;
Rect newLabelPosition = position;
newLabelPosition.width -= newFieldPosition.width;
newLabelPosition.height = EditorGUIUtility.singleLineHeight;
SerializedProperty propSkinedMesh = property.FindPropertyRelative("skinnedMeshRenderer");
SerializedProperty propEyeShapes = property.FindPropertyRelative("eyeShapes");
EditorGUI.PropertyField(newFieldPosition, propSkinedMesh, GUIContent.none);
newFieldPosition.y += EditorGUIUtility.singleLineHeight;
SkinnedMeshRenderer skinnedMesh = propSkinedMesh.objectReferenceValue as SkinnedMeshRenderer;
if (skinnedMesh != null && skinnedMesh.sharedMesh.blendShapeCount > 0)
{
if (propEyeShapes.arraySize != skinnedMesh.sharedMesh.blendShapeCount)
{
propEyeShapes.arraySize = skinnedMesh.sharedMesh.blendShapeCount;
for (int i = 0; i < skinnedMesh.sharedMesh.blendShapeCount; ++i)
{
SerializedProperty propEyeShape = propEyeShapes.GetArrayElementAtIndex(i);
string elementName = skinnedMesh.sharedMesh.GetBlendShapeName(i);
propEyeShape.intValue = (int)XrEyeShapeHTC.XR_EYE_SHAPE_NONE_HTC;
foreach (XrEyeShapeHTC EyeShape in (XrEyeShapeHTC[])Enum.GetValues(typeof(XrEyeShapeHTC)))
{
if (elementName == EyeShape.ToString())
propEyeShape.intValue = (int)EyeShape;
}
}
}
for (int i = 0; i < skinnedMesh.sharedMesh.blendShapeCount; ++i)
{
SerializedProperty propEyeShape = propEyeShapes.GetArrayElementAtIndex(i);
newLabelPosition.y = newFieldPosition.y;
string elementName = skinnedMesh.sharedMesh.GetBlendShapeName(i);
EditorGUI.LabelField(newLabelPosition, " " + elementName);
EditorGUI.PropertyField(newFieldPosition, propEyeShape, GUIContent.none);
newFieldPosition.y += EditorGUIUtility.singleLineHeight;
}
}
EditorGUI.EndProperty();
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
int LineCount = 1;
SerializedProperty propSkinedMesh = property.FindPropertyRelative("skinnedMeshRenderer");
SkinnedMeshRenderer skinnedMesh = propSkinedMesh.objectReferenceValue as SkinnedMeshRenderer;
if (skinnedMesh != null) LineCount += skinnedMesh.sharedMesh.blendShapeCount;
return EditorGUIUtility.singleLineHeight * LineCount;
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,100 @@
//========= Copyright 2018, HTC Corporation. All rights reserved. ===========
using System;
using UnityEngine;
using UnityEngine.XR.OpenXR;
namespace VIVE
{
namespace FacialTracking.Sample
{
public class Eye_Framework : MonoBehaviour
{
public enum FrameworkStatus { STOP, START, WORKING, ERROR, NOT_SUPPORT }
/// <summary>
/// The status of the Eye engine.
/// </summary>
public static FrameworkStatus Status { get; protected set; }
/// <summary>
/// Whether to enable Eye module.
/// </summary>
public bool EnableEye = true;
private static Eye_Framework Mgr = null;
public static Eye_Framework Instance
{
get
{
if (Mgr == null)
{
Mgr = FindObjectOfType<Eye_Framework>();
}
if (Mgr == null)
{
Debug.LogError("Eye_Framework not found");
}
return Mgr;
}
}
void Start()
{
StartFramework();
}
void OnDestroy()
{
StopFramework();
}
[Obsolete("Create FacialManager object and call member function StartFramework instead")]
public void StartFramework()
{
if (!EnableEye) return;
if (Status == FrameworkStatus.WORKING || Status == FrameworkStatus.NOT_SUPPORT) return;
XrFacialTrackerCreateInfoHTC m_expressioncreateInfo = new XrFacialTrackerCreateInfoHTC(
XrStructureType.XR_TYPE_FACIAL_TRACKER_CREATE_INFO_HTC,
IntPtr.Zero,
XrFacialTrackingTypeHTC.XR_FACIAL_TRACKING_TYPE_EYE_DEFAULT_HTC);
var feature = OpenXRSettings.Instance.GetFeature<VIVE_FacialTracking_OpenXR_API>();
int res = feature.xrCreateFacialTrackerHTC(m_expressioncreateInfo, out OpenXRSettings.Instance.GetFeature<VIVE_FacialTracking_OpenXR_API>().m_expressionHandle);
if (res == (int)XrResult.XR_SUCCESS || res == (int)XrResult.XR_SESSION_LOSS_PENDING)
{
Debug.Log("Initial Eye success : " + res);
Status = FrameworkStatus.WORKING;
}
else
{
Debug.LogError("Initial Eye fail : " + res);
Status = FrameworkStatus.ERROR;
}
}
[Obsolete("Create FacialManager object and call member function StopFramework instead")]
public void StopFramework()
{
if (Status != FrameworkStatus.NOT_SUPPORT)
{
if (Status != FrameworkStatus.STOP)
{
var feature = OpenXRSettings.Instance.GetFeature<VIVE_FacialTracking_OpenXR_API>();
int res = feature.xrDestroyFacialTrackerHTC(OpenXRSettings.Instance.GetFeature<VIVE_FacialTracking_OpenXR_API>().m_expressionHandle);
if (res == (int)XrResult.XR_SUCCESS)
{
Debug.Log("Release Eye engine success : " + res);
}
else
{
Debug.LogError("Release Eye engine fail : " + res);
}
}
else
{
Debug.Log("Stop Eye Framework : module not on");
}
}
Status = FrameworkStatus.STOP;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 47613961cde0a8b4aa4f79219b7e523c
timeCreated: 1545374952
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2e820c013bcd10a4ea94b6d91672f893
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,234 @@
//========= Copyright 2018, HTC Corporation. All rights reserved. ===========
using System;
using System.Collections.Generic;
using UnityEngine;
namespace VIVE
{
namespace FacialTracking.Sample
{
[Serializable]
public class EyeShapeTable
{
public SkinnedMeshRenderer skinnedMeshRenderer;
public EyeShape[] eyeShapes;
}
public class AvatarEyeSample : MonoBehaviour
{
[SerializeField] private Transform[] EyesModels = new Transform[0];
[SerializeField] private List<EyeShapeTable> EyeShapeTables;
/// <summary>
/// Customize this curve to fit the blend shapes of your avatar.
/// </summary>
[SerializeField] private AnimationCurve EyebrowAnimationCurveUpper;
/// <summary>
/// Customize this curve to fit the blend shapes of your avatar.
/// </summary>
[SerializeField] private AnimationCurve EyebrowAnimationCurveLower;
/// <summary>
/// Customize this curve to fit the blend shapes of your avatar.
/// </summary>
[SerializeField] private AnimationCurve EyebrowAnimationCurveHorizontal;
public bool NeededToGetData = true;
private Dictionary<XrEyeShapeHTC, float> EyeWeightings = new Dictionary<XrEyeShapeHTC, float>();
//Map Openxr eye shape to Avatar eye blendshape
private static Dictionary<EyeShape, XrEyeShapeHTC> ShapeMap;
private AnimationCurve[] EyebrowAnimationCurves = new AnimationCurve[(int)EyeShape.Max];
private GameObject[] EyeAnchors;
private const int NUM_OF_EYES = 2;
private static XrFacialExpressionsHTC EyeExpression;
private FacialManager facialManager = new FacialManager();
static AvatarEyeSample()
{
ShapeMap = new Dictionary<EyeShape, XrEyeShapeHTC>();
ShapeMap.Add(EyeShape.Eye_Left_Blink, XrEyeShapeHTC.XR_EYE_EXPRESSION_LEFT_BLINK_HTC);
ShapeMap.Add(EyeShape.Eye_Left_Wide, XrEyeShapeHTC.XR_EYE_EXPRESSION_LEFT_WIDE_HTC);
ShapeMap.Add(EyeShape.Eye_Right_Blink, XrEyeShapeHTC.XR_EYE_EXPRESSION_RIGHT_BLINK_HTC);
ShapeMap.Add(EyeShape.Eye_Right_Wide, XrEyeShapeHTC.XR_EYE_EXPRESSION_RIGHT_WIDE_HTC);
ShapeMap.Add(EyeShape.Eye_Left_Squeeze, XrEyeShapeHTC.XR_EYE_EXPRESSION_LEFT_SQUEEZE_HTC);
ShapeMap.Add(EyeShape.Eye_Right_Squeeze, XrEyeShapeHTC.XR_EYE_EXPRESSION_RIGHT_SQUEEZE_HTC);
ShapeMap.Add(EyeShape.Eye_Left_Down, XrEyeShapeHTC.XR_EYE_EXPRESSION_LEFT_DOWN_HTC);
ShapeMap.Add(EyeShape.Eye_Right_Down, XrEyeShapeHTC.XR_EYE_EXPRESSION_RIGHT_DOWN_HTC);
ShapeMap.Add(EyeShape.Eye_Left_Left, XrEyeShapeHTC.XR_EYE_EXPRESSION_LEFT_OUT_HTC);
ShapeMap.Add(EyeShape.Eye_Right_Left, XrEyeShapeHTC.XR_EYE_EXPRESSION_RIGHT_IN_HTC);
ShapeMap.Add(EyeShape.Eye_Left_Right, XrEyeShapeHTC.XR_EYE_EXPRESSION_LEFT_IN_HTC);
ShapeMap.Add(EyeShape.Eye_Right_Right, XrEyeShapeHTC.XR_EYE_EXPRESSION_RIGHT_OUT_HTC);
ShapeMap.Add(EyeShape.Eye_Left_Up, XrEyeShapeHTC.XR_EYE_EXPRESSION_LEFT_UP_HTC);
ShapeMap.Add(EyeShape.Eye_Right_Up, XrEyeShapeHTC.XR_EYE_EXPRESSION_RIGHT_UP_HTC);
}
private void Start()
{
facialManager.StartFramework(XrFacialTrackingTypeHTC.XR_FACIAL_TRACKING_TYPE_EYE_DEFAULT_HTC);
SetEyesModels(EyesModels[0], EyesModels[1]);
SetEyeShapeTables(EyeShapeTables);
AnimationCurve[] curves = new AnimationCurve[(int)EyeShape.Max];
for (int i = 0; i < EyebrowAnimationCurves.Length; ++i)
{
if (i == (int)EyeShape.Eye_Left_Up || i == (int)EyeShape.Eye_Right_Up) curves[i] = EyebrowAnimationCurveUpper;
else if (i == (int)EyeShape.Eye_Left_Down || i == (int)EyeShape.Eye_Right_Down) curves[i] = EyebrowAnimationCurveLower;
else curves[i] = EyebrowAnimationCurveHorizontal;
}
SetEyeShapeAnimationCurves(curves);
}
private void Update()
{
if (NeededToGetData)
{
facialManager.GetWeightings(out EyeWeightings);
UpdateEyeShapes(EyeWeightings);
Vector3 GazeDirectionCombinedLocal = Vector3.zero;
if (EyeWeightings[XrEyeShapeHTC.XR_EYE_EXPRESSION_LEFT_IN_HTC] > EyeWeightings[XrEyeShapeHTC.XR_EYE_EXPRESSION_LEFT_OUT_HTC])
{
GazeDirectionCombinedLocal.x = -1 * EyeWeightings[XrEyeShapeHTC.XR_EYE_EXPRESSION_LEFT_IN_HTC];
}
else
{
GazeDirectionCombinedLocal.x = EyeWeightings[XrEyeShapeHTC.XR_EYE_EXPRESSION_LEFT_OUT_HTC];
}
if (EyeWeightings[XrEyeShapeHTC.XR_EYE_EXPRESSION_LEFT_UP_HTC] > EyeWeightings[XrEyeShapeHTC.XR_EYE_EXPRESSION_LEFT_DOWN_HTC])
{
GazeDirectionCombinedLocal.y = EyeWeightings[XrEyeShapeHTC.XR_EYE_EXPRESSION_LEFT_UP_HTC];
}
else
{
GazeDirectionCombinedLocal.y = -EyeWeightings[XrEyeShapeHTC.XR_EYE_EXPRESSION_LEFT_DOWN_HTC];
}
GazeDirectionCombinedLocal.z = (float)1.0;
Vector3 target = EyeAnchors[0].transform.TransformPoint(GazeDirectionCombinedLocal);
EyesModels[0].LookAt(target);
if (EyeWeightings[XrEyeShapeHTC.XR_EYE_EXPRESSION_RIGHT_IN_HTC] > EyeWeightings[XrEyeShapeHTC.XR_EYE_EXPRESSION_RIGHT_OUT_HTC])
{
GazeDirectionCombinedLocal.x = EyeWeightings[XrEyeShapeHTC.XR_EYE_EXPRESSION_RIGHT_IN_HTC];
}
else
{
GazeDirectionCombinedLocal.x = -1 * EyeWeightings[XrEyeShapeHTC.XR_EYE_EXPRESSION_RIGHT_OUT_HTC];
}
if (EyeWeightings[XrEyeShapeHTC.XR_EYE_EXPRESSION_RIGHT_UP_HTC] > EyeWeightings[XrEyeShapeHTC.XR_EYE_EXPRESSION_RIGHT_DOWN_HTC])
{
GazeDirectionCombinedLocal.y = EyeWeightings[XrEyeShapeHTC.XR_EYE_EXPRESSION_RIGHT_UP_HTC];
}
else
{
GazeDirectionCombinedLocal.y = -EyeWeightings[XrEyeShapeHTC.XR_EYE_EXPRESSION_RIGHT_DOWN_HTC];
}
GazeDirectionCombinedLocal.z = (float)1.0;
target = EyeAnchors[1].transform.TransformPoint(GazeDirectionCombinedLocal);
EyesModels[1].LookAt(target);
}
}
private void OnDestroy()
{
facialManager.StopFramework(XrFacialTrackingTypeHTC.XR_FACIAL_TRACKING_TYPE_EYE_DEFAULT_HTC);
DestroyEyeAnchors();
}
public void SetEyesModels(Transform leftEye, Transform rightEye)
{
if (leftEye != null && rightEye != null)
{
EyesModels = new Transform[NUM_OF_EYES] { leftEye, rightEye };
DestroyEyeAnchors();
CreateEyeAnchors();
}
}
public void SetEyeShapeTables(List<EyeShapeTable> eyeShapeTables)
{
bool valid = true;
if (eyeShapeTables == null)
{
valid = false;
}
else
{
for (int table = 0; table < eyeShapeTables.Count; ++table)
{
if (eyeShapeTables[table].skinnedMeshRenderer == null)
{
valid = false;
break;
}
for (int shape = 0; shape < eyeShapeTables[table].eyeShapes.Length; ++shape)
{
EyeShape eyeShape = eyeShapeTables[table].eyeShapes[shape];
if (eyeShape > EyeShape.Max || eyeShape < 0)
{
valid = false;
break;
}
}
}
}
if (valid)
EyeShapeTables = eyeShapeTables;
}
public void SetEyeShapeAnimationCurves(AnimationCurve[] eyebrowAnimationCurves)
{
if (eyebrowAnimationCurves.Length == (int)EyeShape.Max)
EyebrowAnimationCurves = eyebrowAnimationCurves;
}
public void UpdateEyeShapes(Dictionary<XrEyeShapeHTC, float> eyeWeightings)
{
foreach (var table in EyeShapeTables)
RenderModelEyeShape(table, eyeWeightings);
}
private void RenderModelEyeShape(EyeShapeTable eyeShapeTable, Dictionary<XrEyeShapeHTC, float> weighting)
{
for (int i = 0; i < eyeShapeTable.eyeShapes.Length; ++i)
{
EyeShape eyeShape = eyeShapeTable.eyeShapes[i];
if (eyeShape > EyeShape.Max || eyeShape < 0 || !ShapeMap.ContainsKey(eyeShape)) continue;
XrEyeShapeHTC xreyeshape = ShapeMap[eyeShape];
if (eyeShape == EyeShape.Eye_Left_Blink || eyeShape == EyeShape.Eye_Right_Blink)
{
eyeShapeTable.skinnedMeshRenderer.SetBlendShapeWeight(i, weighting[xreyeshape] * 100f);
}
else
{
AnimationCurve curve = EyebrowAnimationCurves[(int)eyeShape];
eyeShapeTable.skinnedMeshRenderer.SetBlendShapeWeight(i, curve.Evaluate(weighting[xreyeshape]) * 100f);
}
}
}
private void CreateEyeAnchors()
{
EyeAnchors = new GameObject[NUM_OF_EYES];
for (int i = 0; i < NUM_OF_EYES; ++i)
{
EyeAnchors[i] = new GameObject();
EyeAnchors[i].name = "EyeAnchor_" + i;
EyeAnchors[i].transform.SetParent(gameObject.transform);
EyeAnchors[i].transform.localPosition = EyesModels[i].localPosition;
EyeAnchors[i].transform.localRotation = EyesModels[i].localRotation;
EyeAnchors[i].transform.localScale = EyesModels[i].localScale;
}
}
private void DestroyEyeAnchors()
{
if (EyeAnchors != null)
{
foreach (var obj in EyeAnchors)
if (obj != null) Destroy(obj);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,35 @@
//========= Copyright 2018, HTC Corporation. All rights reserved. ===========
using System.Runtime.InteropServices;
using UnityEngine;
namespace VIVE
{
namespace FacialTracking.Sample
{
/// <summary>
/// A very basic mirror.
/// </summary>
[RequireComponent(typeof(Camera))]
public class MirrorCameraSample_Eye : MonoBehaviour
{
private const float Distance = 0.6f;
private void Update()
{
if (Eye_Framework.Status != Eye_Framework.FrameworkStatus.WORKING) return;
}
private void Release()
{
}
private void SetMirroTransform()
{
transform.position = Camera.main.transform.position + Camera.main.transform.forward * Distance;
transform.position = new Vector3(transform.position.x, Camera.main.transform.position.y, transform.position.z);
transform.LookAt(Camera.main.transform);
transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, 0);
}
}
}
}

View File

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