position/rotation constraint の共通部分を VRM10PostionRotationConstraintEditorBase にまとめた

This commit is contained in:
ousttrue
2021-05-17 16:20:42 +09:00
parent 1e80c61af6
commit 253846698c
13 changed files with 390 additions and 517 deletions

View File

@@ -3,9 +3,9 @@ using UnityEngine;
namespace UniVRM10
{
public static class IVRM10ConstraintSourceDestinationExtensions
public static class VRM10PositionRotationConstraintBaseExtensions
{
public static void DrawSourceCoords(this IVRM10ConstraintSourceDestination self)
public static void DrawSourceCoords(this VRM10RotationPositionConstraintBase self)
{
try
{
@@ -16,7 +16,7 @@ namespace UniVRM10
}
}
public static void DrawSourceCurrent(this IVRM10ConstraintSourceDestination self)
public static void DrawSourceCurrent(this VRM10RotationPositionConstraintBase self)
{
try
{
@@ -30,7 +30,7 @@ namespace UniVRM10
}
}
public static void DrawDstCoords(this IVRM10ConstraintSourceDestination self)
public static void DrawDstCoords(this VRM10RotationPositionConstraintBase self)
{
try
{
@@ -42,7 +42,7 @@ namespace UniVRM10
}
}
public static void DrawDstCurrent(this IVRM10ConstraintSourceDestination self)
public static void DrawDstCurrent(this VRM10RotationPositionConstraintBase self)
{
try
{

View File

@@ -5,67 +5,7 @@ using UnityEngine;
namespace UniVRM10
{
[CustomEditor(typeof(VRM10PositionConstraint))]
public class VRM10PositionConstraintEditor : Editor
public class VRM10PositionConstraintEditor : VRM10PositionRotationConstraintEditorBase
{
VRM10PositionConstraint m_target;
void OnEnable()
{
m_target = (VRM10PositionConstraint)target;
}
static GUIStyle s_style;
static GUIStyle Style
{
get
{
if (s_style == null)
{
s_style = new GUIStyle("box");
}
return s_style;
}
}
public void OnSceneGUI()
{
if (m_target.Source == null)
{
return;
}
// this to target line
Handles.color = Color.yellow;
Handles.DrawLine(m_target.Source.position, m_target.transform.position);
var delta = m_target.Delta;
// show source
{
var sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine();
sb.AppendLine($"source: {m_target.SourceCoordinate}");
sb.AppendLine($"{delta.x:0.00}");
sb.AppendLine($"{delta.y:0.00}");
sb.Append($"{delta.z:0.00}");
Handles.Label(m_target.Source.position, sb.ToString(), Style);
}
// show dst
{
var sb = new StringBuilder();
sb.AppendLine($"constraint: {m_target.DestinationCoordinate}");
sb.AppendLine(m_target.FreezeAxes.HasFlag(AxisMask.X) ? $"freeze" : $"{delta.x:0.}");
sb.AppendLine(m_target.FreezeAxes.HasFlag(AxisMask.Y) ? $"freeze" : $"{delta.y:0.}");
sb.Append(m_target.FreezeAxes.HasFlag(AxisMask.Z) ? $"freeze" : $"{delta.z:0.}");
Handles.Label(m_target.transform.position, sb.ToString(), Style);
}
m_target.DrawSourceCoords();
m_target.DrawSourceCurrent();
m_target.DrawDstCoords();
m_target.DrawDstCurrent();
}
}
}

View File

@@ -0,0 +1,113 @@
using System.Text;
using UnityEditor;
using UnityEngine;
namespace UniVRM10
{
public abstract class VRM10PositionRotationConstraintEditorBase : Editor
{
VRM10RotationPositionConstraintBase m_target;
void OnEnable()
{
m_target = (VRM10RotationPositionConstraintBase)target;
}
static GUIStyle s_style;
static GUIStyle Style
{
get
{
if (s_style == null)
{
s_style = new GUIStyle("box");
}
return s_style;
}
}
/// <summary>
/// Euler各を +- 180 にクランプする
/// </summary>
/// <param name="v"></param>
/// <returns></returns>
static Vector3 Clamp180(Vector3 v)
{
var x = v.x;
while (x < -180) x += 360;
while (x > 180) x -= 360;
var y = v.y;
while (y < -180) y += 360;
while (y > 180) y -= 360;
var z = v.z;
while (z < -180) z += 360;
while (z > 180) z -= 360;
return new Vector3(x, y, z);
}
public void OnSceneGUI()
{
if (m_target.GetSource() == null)
{
return;
}
// source offset
if (!Application.isPlaying)
{
EditorGUI.BeginChangeCheck();
Quaternion offset = Handles.RotationHandle(m_target.SourceOffset, m_target.GetSource().position);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(m_target.GetComponent(), "source offset");
m_target.SourceOffset = offset;
}
}
// dest offset
if (!Application.isPlaying)
{
EditorGUI.BeginChangeCheck();
Quaternion offset = Handles.RotationHandle(m_target.DestinationOffset, m_target.GetComponent().transform.position);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(m_target.GetComponent(), "dest offset");
m_target.DestinationOffset = offset;
}
}
// this to target line
Handles.color = Color.yellow;
Handles.DrawLine(m_target.GetSource().position, m_target.GetComponent().transform.position);
var delta = Clamp180(m_target.Delta);
// show source
{
var sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine();
sb.AppendLine($"source: {m_target.SourceCoordinate}");
sb.AppendLine($"{delta.x:0.}");
sb.AppendLine($"{delta.y:0.}");
sb.Append($"{delta.z:0.}");
Handles.Label(m_target.GetSource().position, sb.ToString(), Style);
}
// show dst
{
var sb = new StringBuilder();
sb.AppendLine($"constraint: {m_target.DestinationCoordinate}");
sb.AppendLine(m_target.FreezeAxes.HasFlag(AxisMask.X) ? $"freeze" : $"{delta.x:0.}");
sb.AppendLine(m_target.FreezeAxes.HasFlag(AxisMask.Y) ? $"freeze" : $"{delta.y:0.}");
sb.Append(m_target.FreezeAxes.HasFlag(AxisMask.Z) ? $"freeze" : $"{delta.z:0.}");
Handles.Label(m_target.GetComponent().transform.position, sb.ToString(), Style);
}
m_target.DrawSourceCoords();
m_target.DrawSourceCurrent();
m_target.DrawDstCoords();
m_target.DrawDstCurrent();
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 4d1ea0f0f4d95a2499f9aa5a1d7a2235
guid: 46c1f13688fee134aa37a39a72433217
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -5,110 +5,7 @@ using UnityEngine;
namespace UniVRM10
{
[CustomEditor(typeof(VRM10RotationConstraint))]
public class VRM10RotationConstraintEditor : Editor
public class VRM10RotationConstraintEditor : VRM10PositionRotationConstraintEditorBase
{
VRM10RotationConstraint m_target;
void OnEnable()
{
m_target = (VRM10RotationConstraint)target;
}
static GUIStyle s_style;
static GUIStyle Style
{
get
{
if (s_style == null)
{
s_style = new GUIStyle("box");
}
return s_style;
}
}
/// <summary>
/// Euler各を +- 180 にクランプする
/// </summary>
/// <param name="v"></param>
/// <returns></returns>
static Vector3 Clamp180(Vector3 v)
{
var x = v.x;
while (x < -180) x += 360;
while (x > 180) x -= 360;
var y = v.y;
while (y < -180) y += 360;
while (y > 180) y -= 360;
var z = v.z;
while (z < -180) z += 360;
while (z > 180) z -= 360;
return new Vector3(x, y, z);
}
public void OnSceneGUI()
{
if (m_target.Source == null)
{
return;
}
// source offset
if (!Application.isPlaying)
{
EditorGUI.BeginChangeCheck();
Quaternion offset = Handles.RotationHandle(m_target.SourceOffset.Rotation, m_target.Source.position);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(m_target, "source offset");
m_target.SourceOffset.Rotation = offset;
}
}
// dest offset
if (!Application.isPlaying)
{
EditorGUI.BeginChangeCheck();
Quaternion offset = Handles.RotationHandle(m_target.DestinationOffset.Rotation, m_target.transform.position);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(m_target, "dest offset");
m_target.DestinationOffset.Rotation = offset;
}
}
// this to target line
Handles.color = Color.yellow;
Handles.DrawLine(m_target.Source.position, m_target.transform.position);
var delta = Clamp180(m_target.Delta.eulerAngles);
// show source
{
var sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine();
sb.AppendLine($"source: {m_target.SourceCoordinate}");
sb.AppendLine($"{delta.x:0.}");
sb.AppendLine($"{delta.y:0.}");
sb.Append($"{delta.z:0.}");
Handles.Label(m_target.Source.position, sb.ToString(), Style);
}
// show dst
{
var sb = new StringBuilder();
sb.AppendLine($"constraint: {m_target.DestinationCoordinate}");
sb.AppendLine(m_target.FreezeAxes.HasFlag(AxisMask.X) ? $"freeze" : $"{delta.x:0.}");
sb.AppendLine(m_target.FreezeAxes.HasFlag(AxisMask.Y) ? $"freeze" : $"{delta.y:0.}");
sb.Append(m_target.FreezeAxes.HasFlag(AxisMask.Z) ? $"freeze" : $"{delta.z:0.}");
Handles.Label(m_target.transform.position, sb.ToString(), Style);
}
m_target.DrawSourceCoords();
m_target.DrawSourceCurrent();
m_target.DrawDstCoords();
m_target.DrawDstCurrent();
}
}
}

View File

@@ -4,7 +4,7 @@ using UnityEngine;
namespace UniVRM10
{
class ConstraintDestination
public class ConstraintDestination
{
readonly Transform m_transform;
readonly TR m_modelInitial;

View File

@@ -4,7 +4,7 @@ using UniGLTF.Extensions.VRMC_node_constraint;
namespace UniVRM10
{
class ConstraintSource
public class ConstraintSource
{
public readonly Transform ModelRoot;
readonly Transform m_transform;

View File

@@ -1,11 +0,0 @@
namespace UniVRM10
{
public interface IVRM10ConstraintSourceDestination
{
TR GetSourceCoords();
TR GetSourceCurrent();
TR GetDstCoords();
TR GetDstCurrent();
}
}

View File

@@ -5,9 +5,7 @@ namespace UniVRM10
{
public abstract class VRM10Constraint : MonoBehaviour
{
public virtual void Process()
{
public abstract void Process();
}
}
}

View File

@@ -8,169 +8,12 @@ namespace UniVRM10
/// 対象の初期位置と現在位置の差分(delta)を、自身の初期位置に対してWeightを乗算して加算する。
/// </summary>
[DisallowMultipleComponent]
public class VRM10PositionConstraint : VRM10Constraint, IVRM10ConstraintSourceDestination
public class VRM10PositionConstraint : VRM10RotationPositionConstraintBase
{
[SerializeField]
public Transform Source = default;
Vector3 m_delta;
public override Vector3 Delta => m_delta;
[SerializeField]
public ObjectSpace SourceCoordinate = default;
[SerializeField]
public VRM10RotationOffset SourceOffset = VRM10RotationOffset.Identity;
[SerializeField]
public ObjectSpace DestinationCoordinate = default;
[SerializeField]
public AxisMask FreezeAxes = default;
[SerializeField]
[Range(0, 10.0f)]
public float Weight = 1.0f;
[SerializeField]
public Transform ModelRoot = default;
ConstraintSource m_src;
public TR GetSourceCoords()
{
if (Source == null)
{
throw new ConstraintException(ConstraintException.ExceptionTypes.NoSource);
}
switch (SourceCoordinate)
{
case ObjectSpace.model:
{
if (ModelRoot == null)
{
throw new ConstraintException(ConstraintException.ExceptionTypes.NoModelWithModelSpace);
}
if (m_src == null)
{
return new TR(ModelRoot.rotation, Source.position);
}
// runtime
return new TR(ModelRoot.rotation, m_src.ModelInitial.Translation);
}
case ObjectSpace.local:
{
if (m_src == null)
{
return TR.FromWorld(Source);
}
// runtime
var parent = TR.Identity;
if (Source.parent != null)
{
parent = TR.FromWorld(Source.parent);
}
return parent * m_src.LocalInitial;
}
default:
throw new NotImplementedException();
}
}
public TR GetSourceCurrent()
{
var coords = GetSourceCoords();
if (m_src == null)
{
return coords;
}
return new TR(Delta) * coords;
}
public Vector3 Delta
{
get;
private set;
}
ConstraintDestination m_dst;
public TR GetDstCoords()
{
switch (DestinationCoordinate)
{
case ObjectSpace.model:
{
if (ModelRoot == null)
{
throw new ConstraintException(ConstraintException.ExceptionTypes.NoModelWithModelSpace);
}
return new TR(ModelRoot.rotation, transform.position);
}
case ObjectSpace.local:
{
if (m_src == null)
{
return TR.FromWorld(transform);
}
// runtime
var parent = TR.Identity;
if (transform.parent != null)
{
parent = TR.FromWorld(transform.parent);
}
return parent * m_dst.LocalInitial;
}
default:
throw new NotImplementedException();
}
}
public TR GetDstCurrent()
{
var coords = GetDstCoords();
if (m_src == null)
{
return coords;
}
return new TR(Delta) * coords;
}
/// <summary>
/// Editorで設定値の変更を反映するために、クリアする
/// </summary>
void OnValidate()
{
// Debug.Log("Validate");
if (m_src != null && m_src.ModelRoot != ModelRoot)
{
m_src = null;
}
if (m_dst != null && m_dst.ModelRoot != ModelRoot)
{
m_dst = null;
}
}
void Reset()
{
var current = transform;
while (current.parent != null)
{
current = current.parent;
}
ModelRoot = current;
}
public override void Process()
protected override void UpdateDelta()
{
if (Source == null)
{
@@ -187,8 +30,30 @@ namespace UniVRM10
m_dst = new ConstraintDestination(transform, ModelRoot);
}
Delta = FreezeAxes.Freeze(m_src.TranslationDelta(SourceCoordinate));
m_delta = FreezeAxes.Freeze(m_src.TranslationDelta(SourceCoordinate));
m_dst.ApplyTranslation(Delta, Weight, DestinationCoordinate, ModelRoot);
}
public override TR GetSourceCurrent()
{
var coords = GetSourceCoords();
if (m_src == null)
{
return coords;
}
return new TR(Delta) * coords;
}
public override TR GetDstCurrent()
{
var coords = GetDstCoords();
if (m_src == null)
{
return coords;
}
return new TR(Delta) * coords;
}
}
}

View File

@@ -1,6 +1,4 @@
using System;
using UniGLTF.Extensions.VRMC_node_constraint;
using UnityEngine;
using UnityEngine;
namespace UniVRM10
@@ -9,78 +7,18 @@ namespace UniVRM10
/// 対象の初期回転と現在回転の差分(delta)を、自身の初期回転と自身の初期回転にdeltaを乗算したものに対してWeightでSlerpする。
/// </summary>
[DisallowMultipleComponent]
public class VRM10RotationConstraint : VRM10Constraint, IVRM10ConstraintSourceDestination
public class VRM10RotationConstraint : VRM10RotationPositionConstraintBase
{
[SerializeField]
[EnumFlags]
public AxisMask FreezeAxes = default;
Quaternion m_delta;
[SerializeField]
[Range(0, 10.0f)]
public float Weight = 1.0f;
public override Vector3 Delta => m_delta.eulerAngles;
[SerializeField]
public Transform ModelRoot = default;
[Header("Source")]
[SerializeField]
public Transform Source = default;
[SerializeField]
public ObjectSpace SourceCoordinate = default;
[SerializeField]
public VRM10RotationOffset SourceOffset = VRM10RotationOffset.Identity;
[Header("Destination")]
[SerializeField]
public ObjectSpace DestinationCoordinate = default;
[SerializeField]
public VRM10RotationOffset DestinationOffset = VRM10RotationOffset.Identity;
ConstraintSource m_src;
public TR GetSourceCoords()
protected override void UpdateDelta()
{
if (Source == null)
{
throw new ConstraintException(ConstraintException.ExceptionTypes.NoSource);
}
switch (SourceCoordinate)
{
case ObjectSpace.model:
{
if (ModelRoot == null)
{
throw new ConstraintException(ConstraintException.ExceptionTypes.NoModelWithModelSpace);
}
return new TR(ModelRoot.rotation * SourceOffset.Rotation, Source.position);
}
case ObjectSpace.local:
{
if (m_src == null)
{
return new TR(Source.rotation * SourceOffset.Rotation, Source.position);
}
// runtime
var parent = Quaternion.identity;
if (Source.parent != null)
{
parent = Source.parent.rotation;
}
return new TR(parent * m_src.LocalInitial.Rotation * SourceOffset.Rotation, Source.position);
}
default:
throw new NotImplementedException();
}
m_delta = m_src.RotationDelta(SourceCoordinate, SourceOffset);
}
public TR GetSourceCurrent()
public override TR GetSourceCurrent()
{
var coords = GetSourceCoords();
if (m_src == null)
@@ -88,51 +26,10 @@ namespace UniVRM10
return coords;
}
return coords * new TR(Delta);
return coords * new TR(m_delta);
}
public Quaternion Delta
{
get;
private set;
}
ConstraintDestination m_dst;
public TR GetDstCoords()
{
switch (DestinationCoordinate)
{
case ObjectSpace.model:
{
if (ModelRoot == null)
{
throw new ConstraintException(ConstraintException.ExceptionTypes.NoModelWithModelSpace);
}
return new TR(ModelRoot.rotation * DestinationOffset.Rotation, transform.position);
}
case ObjectSpace.local:
{
if (m_src == null)
{
return new TR(transform.rotation * DestinationOffset.Rotation, transform.position);
}
// runtime
var parent = Quaternion.identity;
if (transform.parent != null)
{
parent = transform.parent.rotation;
}
return new TR(parent * m_dst.LocalInitial.Rotation * DestinationOffset.Rotation, transform.position);
}
default:
throw new NotImplementedException();
}
}
public TR GetDstCurrent()
public override TR GetDstCurrent()
{
var coords = GetDstCoords();
if (m_src == null)
@@ -140,62 +37,7 @@ namespace UniVRM10
return coords;
}
return coords * new TR(Delta);
}
/// <summary>
/// Editorで設定値の変更を反映するために、クリアする
/// </summary>
void OnValidate()
{
// Debug.Log("Validate");
if (m_src != null && m_src.ModelRoot != ModelRoot)
{
m_src = null;
}
if (m_dst != null && m_dst.ModelRoot != ModelRoot)
{
m_dst = null;
}
}
void Reset()
{
var current = transform;
while (current.parent != null)
{
current = current.parent;
}
ModelRoot = current;
}
public override void Process()
{
if (Source == null)
{
enabled = false;
return;
}
if (m_src == null)
{
m_src = new ConstraintSource(Source, ModelRoot);
}
if (m_dst == null)
{
m_dst = new ConstraintDestination(transform, ModelRoot);
}
// 回転差分
Delta = m_src.RotationDelta(SourceCoordinate, SourceOffset.Rotation);
// 軸制限
var fleezed = FreezeAxes.Freeze(Delta.eulerAngles);
var rotation = Quaternion.Euler(fleezed);
// Debug.Log($"{delta} => {rotation}");
// オイラー角を再度Quaternionへ。weight を加味してSlerpする
m_dst.ApplyRotation(DestinationOffset.Rotation * rotation, Weight, DestinationCoordinate, ModelRoot);
return coords * new TR(m_delta);
}
}
}

View File

@@ -0,0 +1,218 @@
using System;
using UniGLTF.Extensions.VRMC_node_constraint;
using UnityEngine;
namespace UniVRM10
{
public abstract class VRM10RotationPositionConstraintBase : VRM10Constraint
{
[SerializeField]
[EnumFlags]
AxisMask m_freezeAxes = default;
public AxisMask FreezeAxes
{
get => m_freezeAxes;
set => m_freezeAxes = value;
}
[SerializeField]
[Range(0, 10.0f)]
public float Weight = 1.0f;
[SerializeField]
public Transform ModelRoot = default;
#region Source
[Header("Source")]
[SerializeField]
public Transform Source = default;
public Transform GetSource() => Source;
[SerializeField]
ObjectSpace m_sourceCoordinate = default;
public ObjectSpace SourceCoordinate
{
get => m_sourceCoordinate;
set => m_sourceCoordinate = value;
}
[SerializeField]
VRM10RotationOffset m_sourceOffset = VRM10RotationOffset.Identity;
public Quaternion SourceOffset
{
get => m_sourceOffset.Rotation;
set => m_sourceOffset.Rotation = value;
}
#endregion
#region Destination
[Header("Destination")]
[SerializeField]
ObjectSpace m_destinationCoordinate = default;
public ObjectSpace DestinationCoordinate
{
get => m_destinationCoordinate;
set => m_destinationCoordinate = value;
}
[SerializeField]
public VRM10RotationOffset m_destinationOffset = VRM10RotationOffset.Identity;
public Quaternion DestinationOffset
{
get => m_destinationOffset.Rotation;
set => m_destinationOffset.Rotation = value;
}
#endregion
public abstract Vector3 Delta { get; }
protected ConstraintSource m_src;
public TR GetSourceCoords()
{
if (Source == null)
{
throw new ConstraintException(ConstraintException.ExceptionTypes.NoSource);
}
switch (SourceCoordinate)
{
case ObjectSpace.model:
{
if (ModelRoot == null)
{
throw new ConstraintException(ConstraintException.ExceptionTypes.NoModelWithModelSpace);
}
return new TR(ModelRoot.rotation * SourceOffset, Source.position);
}
case ObjectSpace.local:
{
if (m_src == null)
{
return new TR(Source.rotation * SourceOffset, Source.position);
}
// runtime
var parent = Quaternion.identity;
if (Source.parent != null)
{
parent = Source.parent.rotation;
}
return new TR(parent * m_src.LocalInitial.Rotation * SourceOffset, Source.position);
}
default:
throw new NotImplementedException();
}
}
public abstract TR GetSourceCurrent();
protected ConstraintDestination m_dst;
public TR GetDstCoords()
{
switch (DestinationCoordinate)
{
case ObjectSpace.model:
{
if (ModelRoot == null)
{
throw new ConstraintException(ConstraintException.ExceptionTypes.NoModelWithModelSpace);
}
return new TR(ModelRoot.rotation * DestinationOffset, transform.position);
}
case ObjectSpace.local:
{
if (m_src == null)
{
return new TR(transform.rotation * DestinationOffset, transform.position);
}
// runtime
var parent = Quaternion.identity;
if (transform.parent != null)
{
parent = transform.parent.rotation;
}
return new TR(parent * m_dst.LocalInitial.Rotation * DestinationOffset, transform.position);
}
default:
throw new NotImplementedException();
}
}
public abstract TR GetDstCurrent();
/// <summary>
/// Editorで設定値の変更を反映するために、クリアする
/// </summary>
void OnValidate()
{
// Debug.Log("Validate");
if (m_src != null && m_src.ModelRoot != ModelRoot)
{
m_src = null;
}
if (m_dst != null && m_dst.ModelRoot != ModelRoot)
{
m_dst = null;
}
}
void Reset()
{
var current = transform;
while (current.parent != null)
{
current = current.parent;
}
ModelRoot = current;
}
public Component GetComponent()
{
return this;
}
protected abstract void UpdateDelta();
public override void Process()
{
if (Source == null)
{
enabled = false;
return;
}
if (m_src == null)
{
m_src = new ConstraintSource(Source, ModelRoot);
}
if (m_dst == null)
{
m_dst = new ConstraintDestination(transform, ModelRoot);
}
// 回転差分
UpdateDelta();
// 軸制限
var fleezed = FreezeAxes.Freeze(Delta);
var rotation = Quaternion.Euler(fleezed);
// Debug.Log($"{delta} => {rotation}");
// オイラー角を再度Quaternionへ。weight を加味してSlerpする
m_dst.ApplyRotation(DestinationOffset * rotation, Weight, DestinationCoordinate, ModelRoot);
}
}
}

View File

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