using System; using System.Collections.Generic; using GLTF.Math; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using GLTF.Schema; namespace GLTF.Extensions { public static class JsonWriterExtensions { public static void WriteTexture(this JObject token, string name, TextureInfo texture) { var jsonWriter = token.CreateWriter(); jsonWriter.WritePropertyName(name); texture.Serialize(jsonWriter); } } public static class JsonReaderExtensions { public static List ReadStringList(this JsonReader reader) { if (reader.Read() && reader.TokenType != JsonToken.StartArray) { throw new Exception(string.Format("Invalid array at: {0}", reader.Path)); } var list = new List(); while (reader.Read() && reader.TokenType != JsonToken.EndArray) { var value = reader.Value; if (value != null) list.Add(value.ToString()); } return list; } public static List ReadDoubleList(this JsonReader reader) { if (reader.Read() && reader.TokenType != JsonToken.StartArray) { throw new Exception(string.Format("Invalid array at: {0}", reader.Path)); } var list = new List(); while (reader.Read() && reader.TokenType != JsonToken.EndArray) { var value = reader.Value; if (value != null) list.Add(double.Parse(value.ToString())); } return list; } public static List ReadInt32List(this JsonReader reader) { if (reader.Read() && reader.TokenType != JsonToken.StartArray) { throw new Exception(string.Format("Invalid array at: {0}", reader.Path)); } var list = new List(); while (reader.Read() && reader.TokenType != JsonToken.EndArray) { var value = reader.Value; if (value != null) list.Add(int.Parse(value.ToString())); } return list; } public static List ReadList(this JsonReader reader, Func deserializerFunc) { if (reader.Read() && reader.TokenType != JsonToken.StartArray) { throw new Exception(string.Format("Invalid array at: {0}", reader.Path)); } var list = new List(); while (reader.Read() && reader.TokenType != JsonToken.EndArray) { list.Add(deserializerFunc()); // deserializerFunc can advance to EndArray. We need to check for this case as well. if (reader.TokenType == JsonToken.EndArray) { break; } } return list; } public static TextureInfo DeserializeAsTexture(this JToken token, GLTFRoot root) { TextureInfo textureInfo = null; if (token != null) { JObject textureObject = token as JObject; if (textureObject == null) { throw new Exception("JToken used for Texture deserialization was not a JObject. It was a " + token.Type.ToString()); } #if DEBUG // Broken on il2cpp. Don't ship debug DLLs there. System.Diagnostics.Debug.WriteLine("textureObject is " + textureObject.Type + " with a value of: " + textureObject[TextureInfo.INDEX].Type + " " + textureObject.ToString()); #endif int indexVal = textureObject[TextureInfo.INDEX].DeserializeAsInt(); textureInfo = new TextureInfo() { Index = new TextureId() { Id = indexVal, Root = root } }; if (textureObject.ContainsKey(TextureInfo.TEXCOORD)) { textureInfo.TexCoord = textureObject[TextureInfo.TEXCOORD].DeserializeAsInt(); } if (textureObject.ContainsKey("extensions")) { textureInfo.Extensions = GLTFProperty.DeserializeExtensions(root, textureObject["extensions"].CreateReader()); } } return textureInfo; } public static NormalTextureInfo DeserializeAsNormalTexture(this JToken token, GLTFRoot root) { var tex = DeserializeAsTexture(token, root); if (tex != null) { var normalTex = new NormalTextureInfo() { Index = tex.Index, TexCoord = tex.TexCoord }; JObject textureObject = token as JObject; if (textureObject != null && textureObject.ContainsKey(NormalTextureInfo.SCALE)) { normalTex.Scale = textureObject[NormalTextureInfo.SCALE].DeserializeAsDouble(); } return normalTex; } return null; } public static int DeserializeAsInt(this JToken token) { if (token != null) { JValue intValue = token as JValue; if (intValue == null) { throw new Exception("JToken used for int deserialization was not a JValue. It was a " + token.Type.ToString()); } return (int)intValue; } return 0; } public static double DeserializeAsDouble(this JToken token) { if (token != null) { JValue doubleValue = token as JValue; if (doubleValue == null) { throw new Exception("JToken used for double deserialization was not a JValue. It was a " + token.Type.ToString()); } return (double)doubleValue; } return 0d; } public static Color ReadAsRGBAColor(this JsonReader reader) { if (reader.Read() && reader.TokenType != JsonToken.StartArray) { throw new Exception(string.Format("Invalid color value at: {0}", reader.Path)); } var color = new Color { R = (float) reader.ReadAsDouble().Value, G = (float) reader.ReadAsDouble().Value, B = (float) reader.ReadAsDouble().Value, A = (float) reader.ReadAsDouble().Value }; if (reader.Read() && reader.TokenType != JsonToken.EndArray) { throw new Exception(string.Format("Invalid color value at: {0}", reader.Path)); } return color; } public static Color DeserializeAsColor(this JToken token) { Color color = Color.White; if (token != null) { JArray colorArray = token as JArray; if (colorArray == null) { throw new Exception("JToken used for Color deserialization was not a JArray. It was a " + token.Type.ToString()); } if (colorArray.Count < 3) { throw new Exception("JArray used for Color deserialization did have less than 3 entries (needs to be RGB or RGBA). It had " + colorArray.Count); } color = new Color { R = (float)colorArray[0].DeserializeAsDouble(), G = (float)colorArray[1].DeserializeAsDouble(), B = (float)colorArray[2].DeserializeAsDouble(), A = (float)(colorArray.Count == 4 ? colorArray[3].DeserializeAsDouble() : 1.0), }; } return color; } public static Color ReadAsRGBColor(this JsonReader reader) { if (reader.Read() && reader.TokenType != JsonToken.StartArray) { throw new Exception(string.Format("Invalid vector value at: {0}", reader.Path)); } var color = new Color { R = (float) reader.ReadAsDouble().Value, G = (float) reader.ReadAsDouble().Value, B = (float) reader.ReadAsDouble().Value, A = 1.0f }; if (reader.Read() && reader.TokenType != JsonToken.EndArray) { throw new Exception(string.Format("Invalid color value at: {0}", reader.Path)); } return color; } public static Vector3 ReadAsVector3(this JsonReader reader) { if (reader.Read() && reader.TokenType != JsonToken.StartArray) { throw new Exception(string.Format("Invalid vector value at: {0}", reader.Path)); } var vector = new Vector3 { X = (float) reader.ReadAsDouble().Value, Y = (float) reader.ReadAsDouble().Value, Z = (float) reader.ReadAsDouble().Value }; if (reader.Read() && reader.TokenType != JsonToken.EndArray) { throw new Exception(string.Format("Invalid vector value at: {0}", reader.Path)); } return vector; } public static Vector2 DeserializeAsVector2(this JToken token) { Vector2 vector = new Vector2(); if (token != null) { JArray vectorArray = token as JArray; if (vectorArray == null) { throw new Exception("JToken used for Vector2 deserialization was not a JArray. It was a " + token.Type.ToString()); } if (vectorArray.Count != 2) { throw new Exception("JArray used for Vector2 deserialization did not have 2 entries for XY. It had " + vectorArray.Count); } vector = new Vector2 { X = (float)vectorArray[0].DeserializeAsDouble(), Y = (float)vectorArray[1].DeserializeAsDouble() }; } return vector; } public static Vector3 DeserializeAsVector3(this JToken token) { Vector3 vector = new Vector3(); if (token != null) { JArray vectorArray = token as JArray; if (vectorArray == null) { throw new Exception("JToken used for Vector3 deserialization was not a JArray. It was a " + token.Type.ToString()); } if (vectorArray.Count != 3) { throw new Exception("JArray used for Vector3 deserialization did not have 3 entries for XYZ. It had " + vectorArray.Count); } vector = new Vector3 { X = (float)vectorArray[0].DeserializeAsDouble(), Y = (float)vectorArray[1].DeserializeAsDouble(), Z = (float)vectorArray[2].DeserializeAsDouble() }; } return vector; } public static Quaternion ReadAsQuaternion(this JsonReader reader) { if (reader.Read() && reader.TokenType != JsonToken.StartArray) { throw new Exception(string.Format("Invalid vector value at: {0}", reader.Path)); } var quat = new Quaternion { X = (float) reader.ReadAsDouble().Value, Y = (float) reader.ReadAsDouble().Value, Z = (float) reader.ReadAsDouble().Value, W = (float) reader.ReadAsDouble().Value }; if (reader.Read() && reader.TokenType != JsonToken.EndArray) { throw new Exception(string.Format("Invalid vector value at: {0}", reader.Path)); } return quat; } public static Dictionary ReadAsDictionary(this JsonReader reader, Func deserializerFunc, bool skipStartObjectRead = false) { if (!skipStartObjectRead && reader.Read() && reader.TokenType != JsonToken.StartObject) { throw new Exception(string.Format("Dictionary must be an object at: {0}.", reader.Path)); } var dict = new Dictionary(); while (reader.Read() && reader.TokenType != JsonToken.EndObject) { var value = reader.Value; if (value != null) dict.Add(value.ToString(), deserializerFunc()); } return dict; } public static Dictionary ReadAsObjectDictionary(this JsonReader reader, bool skipStartObjectRead = false) { if (!skipStartObjectRead && reader.Read() && reader.TokenType != JsonToken.StartObject) { throw new Exception(string.Format("Dictionary must be an object at: {0}", reader.Path)); } var dict = new Dictionary(); while (reader.Read() && reader.TokenType != JsonToken.EndObject) { var value = reader.Value; if (value != null) dict.Add(value.ToString(), ReadDictionaryValue(reader)); } return dict; } private static object ReadDictionaryValue(JsonReader reader) { if (!reader.Read()) { return null; } switch (reader.TokenType) { case JsonToken.StartArray: return reader.ReadObjectList(); case JsonToken.StartObject: return reader.ReadAsObjectDictionary(true); default: return reader.Value; } } private static List ReadObjectList(this JsonReader reader) { var list = new List(); while (reader.Read() && reader.TokenType != JsonToken.EndArray) { list.Add(reader.Value); } return list; } public static T ReadStringEnum(this JsonReader reader) { return (T) Enum.Parse(typeof(T), reader.ReadAsString()); } } }