You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
610 lines
20 KiB
610 lines
20 KiB
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using Object = UnityEngine.Object;
|
|
using System.Globalization;
|
|
using TangentMode = UnityEditor.AnimationUtility.TangentMode;
|
|
|
|
namespace UnityEditor.VFX
|
|
{
|
|
[Serializable]
|
|
class SerializableType : ISerializationCallbackReceiver
|
|
{
|
|
public static implicit operator SerializableType(Type value)
|
|
{
|
|
return new SerializableType(value);
|
|
}
|
|
|
|
public static implicit operator Type(SerializableType value)
|
|
{
|
|
return !ReferenceEquals(value, null) ? value.m_Type : null;
|
|
}
|
|
|
|
private SerializableType() { }
|
|
public SerializableType(Type type)
|
|
{
|
|
m_Type = type;
|
|
}
|
|
|
|
public SerializableType(string typeText)
|
|
{
|
|
m_SerializableType = typeText;
|
|
OnAfterDeserialize();
|
|
}
|
|
|
|
public virtual void OnBeforeSerialize()
|
|
{
|
|
if (m_Type != null)
|
|
m_SerializableType = m_Type.AssemblyQualifiedName;
|
|
}
|
|
|
|
public virtual void OnAfterDeserialize()
|
|
{
|
|
m_Type = GetType(m_SerializableType);
|
|
}
|
|
|
|
public static Type GetType(string name)
|
|
{
|
|
var type = Type.GetType(name);
|
|
if (type == null
|
|
&& !string.IsNullOrEmpty(name)
|
|
&& !name.Contains("Unity.VisualEffectGraph.Runtime,", StringComparison.InvariantCulture)) //Don't log error from actual VFX package like IncrementStripIndexOnStart sanitization is handled automatically for those
|
|
Debug.LogErrorFormat("Unable to find type: {0}", name);
|
|
return type;
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (ReferenceEquals(this, obj))
|
|
return true;
|
|
|
|
Type otherType = null;
|
|
if (obj is SerializableType)
|
|
otherType = ((SerializableType)obj)?.m_Type;
|
|
else if (obj is Type)
|
|
otherType = (Type)obj;
|
|
else if (!ReferenceEquals(obj, null))
|
|
return false;
|
|
|
|
return m_Type == otherType;
|
|
}
|
|
|
|
public static bool operator ==(SerializableType left, SerializableType right)
|
|
{
|
|
if (!ReferenceEquals(left, null))
|
|
return left.Equals(right);
|
|
if (!ReferenceEquals(right, null))
|
|
return right.Equals(left);
|
|
|
|
return true; // both null
|
|
}
|
|
|
|
public static bool operator !=(SerializableType left, SerializableType right)
|
|
{
|
|
return !(left == right);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return m_Type != null ? m_Type.GetHashCode() : 0;
|
|
}
|
|
|
|
public string text
|
|
{
|
|
get { OnBeforeSerialize(); return m_SerializableType; }
|
|
}
|
|
|
|
[NonSerialized]
|
|
private Type m_Type;
|
|
[SerializeField]
|
|
private string m_SerializableType;
|
|
}
|
|
|
|
[Serializable]
|
|
class VFXSerializableObject
|
|
{
|
|
private VFXSerializableObject() { }
|
|
|
|
public VFXSerializableObject(Type type, object obj)
|
|
{
|
|
//In case of SerializedReference, the obj.GetType can be more specialized than storage type
|
|
m_Type = obj != null ? obj.GetType() : type;
|
|
Set(obj);
|
|
}
|
|
|
|
public object Get()
|
|
{
|
|
return VFXSerializer.Load(m_Type, m_SerializableObject, m_CachedValue);
|
|
}
|
|
|
|
public T Get<T>()
|
|
{
|
|
return (T)Get();
|
|
}
|
|
|
|
public bool Set(object obj)
|
|
{
|
|
var newValue = string.Empty;
|
|
if (obj != null)
|
|
{
|
|
Type type = m_Type;
|
|
|
|
if (!type.IsAssignableFrom(obj.GetType()))
|
|
{
|
|
if (obj is UnityEngine.Object && (obj as UnityEngine.Object == null))
|
|
{
|
|
// Some object couldn't be loaded. just ignore it.
|
|
}
|
|
else if (obj is Texture && typeof(Texture).IsAssignableFrom(type))
|
|
{
|
|
}
|
|
else
|
|
{
|
|
throw new ArgumentException(string.Format("Cannot assign an object of type {0} to VFXSerializedObject of type {1}", obj.GetType(), (Type)m_Type));
|
|
}
|
|
}
|
|
newValue = VFXSerializer.Save(obj);
|
|
}
|
|
m_CachedValue = obj;
|
|
if (m_SerializableObject != newValue)
|
|
{
|
|
m_SerializableObject = newValue;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public Type type
|
|
{
|
|
get { return m_Type; }
|
|
}
|
|
|
|
[SerializeField]
|
|
private SerializableType m_Type;
|
|
|
|
[SerializeField]
|
|
private string m_SerializableObject;
|
|
|
|
|
|
private object m_CachedValue;
|
|
}
|
|
|
|
|
|
static class VFXSerializer
|
|
{
|
|
[System.Serializable]
|
|
public struct TypedSerializedData
|
|
{
|
|
public string data;
|
|
public string type; // TODO This should have used SerializableType!
|
|
|
|
public static TypedSerializedData Null = new TypedSerializedData();
|
|
}
|
|
|
|
[Serializable]
|
|
private struct ObjectWrapper
|
|
{
|
|
public UnityEngine.Object obj;
|
|
}
|
|
|
|
|
|
[System.Serializable]
|
|
class AnimCurveWrapper
|
|
{
|
|
[System.Serializable]
|
|
public struct Keyframe
|
|
{
|
|
public float time;
|
|
public float value;
|
|
public float inTangent;
|
|
public float outTangent;
|
|
|
|
public int tangentMode;
|
|
public TangentMode leftTangentMode;
|
|
public TangentMode rightTangentMode;
|
|
|
|
public bool broken;
|
|
}
|
|
public Keyframe[] frames;
|
|
public WrapMode preWrapMode;
|
|
public WrapMode postWrapMode;
|
|
public int version;
|
|
}
|
|
|
|
[System.Serializable]
|
|
class GradientWrapper
|
|
{
|
|
[System.Serializable]
|
|
public struct ColorKey
|
|
{
|
|
public Color color;
|
|
public float time;
|
|
}
|
|
[System.Serializable]
|
|
public struct AlphaKey
|
|
{
|
|
public float alpha;
|
|
public float time;
|
|
}
|
|
public ColorKey[] colorKeys;
|
|
public AlphaKey[] alphaKeys;
|
|
|
|
public GradientMode gradientMode;
|
|
}
|
|
|
|
public static TypedSerializedData SaveWithType(object obj)
|
|
{
|
|
TypedSerializedData data = new TypedSerializedData();
|
|
data.data = VFXSerializer.Save(obj);
|
|
data.type = obj.GetType().AssemblyQualifiedName;
|
|
|
|
return data;
|
|
}
|
|
|
|
public static object LoadWithType(TypedSerializedData data, object oldValue)
|
|
{
|
|
if (!string.IsNullOrEmpty(data.data))
|
|
{
|
|
System.Type type = SerializableType.GetType(data.type);
|
|
if (type == null)
|
|
{
|
|
Debug.LogError("Can't find type " + data.type);
|
|
return null;
|
|
}
|
|
|
|
return VFXSerializer.Load(type, data.data, oldValue);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static string Save(object obj)
|
|
{
|
|
if (obj == null)
|
|
return string.Empty;
|
|
|
|
if (obj.GetType().IsPrimitive)
|
|
{
|
|
return string.Format(CultureInfo.InvariantCulture, "{0}", obj);
|
|
}
|
|
else if (obj is UnityEngine.Object) //type is a unity object
|
|
{
|
|
if ((obj as UnityEngine.Object) == null)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
ObjectWrapper wrapper = new ObjectWrapper { obj = obj as UnityEngine.Object };
|
|
var json = EditorJsonUtility.ToJson(wrapper);
|
|
return json;
|
|
}
|
|
else if (obj is AnimationCurve)
|
|
{
|
|
AnimCurveWrapper sac = new AnimCurveWrapper();
|
|
AnimationCurve curve = obj as AnimationCurve;
|
|
|
|
|
|
sac.frames = new AnimCurveWrapper.Keyframe[curve.keys.Length];
|
|
for (int i = 0; i < curve.keys.Length; ++i)
|
|
{
|
|
sac.frames[i].time = curve.keys[i].time;
|
|
sac.frames[i].value = curve.keys[i].value;
|
|
sac.frames[i].inTangent = curve.keys[i].inTangent;
|
|
sac.frames[i].outTangent = curve.keys[i].outTangent;
|
|
sac.frames[i].tangentMode = 0; // Not used
|
|
sac.frames[i].leftTangentMode = AnimationUtility.GetKeyLeftTangentMode(curve, i);
|
|
sac.frames[i].rightTangentMode = AnimationUtility.GetKeyRightTangentMode(curve, i);
|
|
sac.frames[i].broken = AnimationUtility.GetKeyBroken(curve, i);
|
|
}
|
|
sac.preWrapMode = curve.preWrapMode;
|
|
sac.postWrapMode = curve.postWrapMode;
|
|
sac.version = 1;
|
|
|
|
return JsonUtility.ToJson(sac);
|
|
}
|
|
else if (obj is Gradient)
|
|
{
|
|
GradientWrapper gw = new GradientWrapper();
|
|
Gradient gradient = obj as Gradient;
|
|
|
|
gw.gradientMode = gradient.mode;
|
|
gw.colorKeys = new GradientWrapper.ColorKey[gradient.colorKeys.Length];
|
|
for (int i = 0; i < gradient.colorKeys.Length; ++i)
|
|
{
|
|
gw.colorKeys[i].color = gradient.colorKeys[i].color;
|
|
gw.colorKeys[i].time = gradient.colorKeys[i].time;
|
|
}
|
|
gw.alphaKeys = new GradientWrapper.AlphaKey[gradient.alphaKeys.Length];
|
|
for (int i = 0; i < gradient.alphaKeys.Length; ++i)
|
|
{
|
|
gw.alphaKeys[i].alpha = gradient.alphaKeys[i].alpha;
|
|
gw.alphaKeys[i].time = gradient.alphaKeys[i].time;
|
|
}
|
|
return JsonUtility.ToJson(gw);
|
|
}
|
|
else if (obj is string)
|
|
{
|
|
return "\"" + ((string)obj).Replace("\"", "\\\"") + "\"";
|
|
}
|
|
else if (obj is SerializableType)
|
|
{
|
|
return "\"" + ((SerializableType)obj).text + "\"";
|
|
}
|
|
else if (obj.GetType().IsArrayOrList())
|
|
{
|
|
IList list = (IList)obj;
|
|
|
|
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
|
sb.Append('[');
|
|
for (int i = 0; i < list.Count; ++i)
|
|
{
|
|
sb.Append(Save(list[i]));
|
|
sb.Append(',');
|
|
}
|
|
sb.Length = sb.Length - 1;
|
|
sb.Append(']');
|
|
|
|
return sb.ToString();
|
|
}
|
|
else
|
|
{
|
|
return EditorJsonUtility.ToJson(obj);
|
|
}
|
|
}
|
|
|
|
const int kBrokenMask = 1 << 0;
|
|
const int kLeftTangentMask = 1 << 1 | 1 << 2 | 1 << 3 | 1 << 4;
|
|
const int kRightTangentMask = 1 << 5 | 1 << 6 | 1 << 7 | 1 << 8;
|
|
|
|
public static object Load(System.Type type, string text, object oldValue)
|
|
{
|
|
if (type == null)
|
|
return null;
|
|
|
|
if (type.IsPrimitive)
|
|
{
|
|
if (string.IsNullOrEmpty(text))
|
|
try
|
|
{
|
|
return Activator.CreateInstance(type);
|
|
}
|
|
catch (MissingMethodException)
|
|
{
|
|
Debug.LogError(type.Name + " Doesn't seem to have a default constructor");
|
|
|
|
throw;
|
|
}
|
|
|
|
return Convert.ChangeType(text, type, CultureInfo.InvariantCulture);
|
|
}
|
|
else if (typeof(UnityEngine.Object).IsAssignableFrom(type))
|
|
{
|
|
object obj = new ObjectWrapper();
|
|
EditorJsonUtility.FromJsonOverwrite(text, obj);
|
|
|
|
return ((ObjectWrapper)obj).obj;
|
|
}
|
|
else if (type.IsAssignableFrom(typeof(AnimationCurve)))
|
|
{
|
|
AnimCurveWrapper sac = new AnimCurveWrapper();
|
|
|
|
JsonUtility.FromJsonOverwrite(text, sac);
|
|
|
|
AnimationCurve curve = oldValue != null ? (AnimationCurve)oldValue : new AnimationCurve();
|
|
|
|
if (sac.frames != null)
|
|
{
|
|
Keyframe[] keys = new UnityEngine.Keyframe[sac.frames.Length];
|
|
for (int i = 0; i < sac.frames.Length; ++i)
|
|
{
|
|
keys[i].time = sac.frames[i].time;
|
|
keys[i].value = sac.frames[i].value;
|
|
keys[i].inTangent = sac.frames[i].inTangent;
|
|
keys[i].outTangent = sac.frames[i].outTangent;
|
|
if (sac.version == 1)
|
|
{
|
|
AnimationUtility.SetKeyLeftTangentMode(ref keys[i], sac.frames[i].leftTangentMode);
|
|
AnimationUtility.SetKeyRightTangentMode(ref keys[i], sac.frames[i].rightTangentMode);
|
|
AnimationUtility.SetKeyBroken(ref keys[i], sac.frames[i].broken);
|
|
}
|
|
else
|
|
{
|
|
AnimationUtility.SetKeyLeftTangentMode(ref keys[i], (TangentMode)((sac.frames[i].tangentMode & kLeftTangentMask) >> 1));
|
|
AnimationUtility.SetKeyRightTangentMode(ref keys[i], (TangentMode)((sac.frames[i].tangentMode & kRightTangentMask) >> 5));
|
|
AnimationUtility.SetKeyBroken(ref keys[i], (sac.frames[i].tangentMode & kBrokenMask) != 0);
|
|
}
|
|
}
|
|
curve.keys = keys;
|
|
curve.preWrapMode = sac.preWrapMode;
|
|
curve.postWrapMode = sac.postWrapMode;
|
|
}
|
|
|
|
return curve;
|
|
}
|
|
else if (type.IsAssignableFrom(typeof(Gradient)))
|
|
{
|
|
GradientWrapper gw = new GradientWrapper();
|
|
Gradient gradient = oldValue != null ? (Gradient)oldValue : new Gradient();
|
|
|
|
JsonUtility.FromJsonOverwrite(text, gw);
|
|
|
|
gradient.mode = gw.gradientMode;
|
|
|
|
GradientColorKey[] colorKeys = null;
|
|
if (gw.colorKeys != null)
|
|
{
|
|
colorKeys = new GradientColorKey[gw.colorKeys.Length];
|
|
for (int i = 0; i < gw.colorKeys.Length; ++i)
|
|
{
|
|
colorKeys[i].color = gw.colorKeys[i].color;
|
|
colorKeys[i].time = gw.colorKeys[i].time;
|
|
}
|
|
}
|
|
else
|
|
colorKeys = new GradientColorKey[0];
|
|
|
|
GradientAlphaKey[] alphaKeys = null;
|
|
|
|
if (gw.alphaKeys != null)
|
|
{
|
|
alphaKeys = new GradientAlphaKey[gw.alphaKeys.Length];
|
|
for (int i = 0; i < gw.alphaKeys.Length; ++i)
|
|
{
|
|
alphaKeys[i].alpha = gw.alphaKeys[i].alpha;
|
|
alphaKeys[i].time = gw.alphaKeys[i].time;
|
|
}
|
|
}
|
|
else
|
|
alphaKeys = new GradientAlphaKey[0];
|
|
|
|
gradient.SetKeys(colorKeys, alphaKeys);
|
|
return gradient;
|
|
}
|
|
else if (type == typeof(string))
|
|
{
|
|
if (string.IsNullOrEmpty(text))
|
|
return "";
|
|
return text.Substring(1, text.Length - 2).Replace("\\\"", "\"");
|
|
}
|
|
else if (type == typeof(SerializableType))
|
|
{
|
|
var obj = new SerializableType(text.Substring(1, text.Length - 2));
|
|
return obj;
|
|
}
|
|
else if (type.IsArrayOrList())
|
|
{
|
|
List<string> elements = ParseArray(text);
|
|
|
|
if (elements == null)
|
|
return null;
|
|
if (type.IsArray)
|
|
{
|
|
int listCount = elements.Count;
|
|
|
|
Array arrayObj = (Array)Activator.CreateInstance(type, new object[] { listCount });
|
|
|
|
for (int index = 0; index < listCount; index++)
|
|
{
|
|
arrayObj.SetValue(Load(type.GetElementType(), elements[index], null), index);
|
|
}
|
|
|
|
return arrayObj;
|
|
}
|
|
else //List
|
|
{
|
|
int listCount = elements.Count;
|
|
IList listObj = (IList)Activator.CreateInstance(type, new object[0]);
|
|
for (int index = 0; index < listCount; index++)
|
|
{
|
|
listObj.Add(Load(type.GetElementType(), elements[index], null));
|
|
}
|
|
|
|
return listObj;
|
|
}
|
|
}
|
|
else if (type == typeof(GraphicsBuffer))
|
|
{
|
|
//We can't desarialize or allocate a default GraphicsBuffer
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
object obj = Activator.CreateInstance(type);
|
|
EditorJsonUtility.FromJsonOverwrite(text, obj);
|
|
return obj;
|
|
}
|
|
catch (MissingMethodException)
|
|
{
|
|
Debug.LogError(type.Name + " Doesn't seem to have a default constructor");
|
|
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static List<string> ParseArray(string arrayText)
|
|
{
|
|
List<string> elements = new List<string>();
|
|
|
|
|
|
int cur = 0;
|
|
bool isInString = false;
|
|
bool ignoreNext = false;
|
|
int depth = 0; // depth of []
|
|
int bracketDepth = 0; //depth of {}
|
|
|
|
int prevElementStart = 0;
|
|
|
|
|
|
foreach (char c in arrayText)
|
|
{
|
|
switch (c)
|
|
{
|
|
case '{':
|
|
ignoreNext = false;
|
|
if (!isInString)
|
|
bracketDepth++;
|
|
break;
|
|
case '}':
|
|
ignoreNext = false;
|
|
if (!isInString)
|
|
bracketDepth--;
|
|
break;
|
|
case '[':
|
|
ignoreNext = false;
|
|
if (!isInString && bracketDepth == 0)
|
|
{
|
|
depth++;
|
|
if (depth == 1)
|
|
prevElementStart = cur + 1;
|
|
}
|
|
break;
|
|
case ']':
|
|
ignoreNext = false;
|
|
if (!isInString && bracketDepth == 0)
|
|
{
|
|
depth--;
|
|
if (depth < 0)
|
|
goto error;
|
|
if (depth == 0)
|
|
elements.Add(arrayText.Substring(prevElementStart, cur - prevElementStart));
|
|
}
|
|
return elements;
|
|
case ',':
|
|
ignoreNext = false;
|
|
if (!isInString && bracketDepth == 0)
|
|
{
|
|
elements.Add(arrayText.Substring(prevElementStart, cur - prevElementStart));
|
|
prevElementStart = cur + 1;
|
|
}
|
|
break;
|
|
case '"':
|
|
if (!isInString)
|
|
isInString = true;
|
|
else if (!ignoreNext)
|
|
isInString = false;
|
|
break;
|
|
case '\\':
|
|
if (isInString)
|
|
{
|
|
ignoreNext = !ignoreNext;
|
|
}
|
|
break;
|
|
default:
|
|
ignoreNext = false;
|
|
break;
|
|
}
|
|
++cur;
|
|
}
|
|
error:
|
|
Debug.LogError("Couln't parse array" + arrayText + " from " + cur);
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|