Просмотр исходного кода

Added basic support for "Extras" property

Vicente Penades 6 лет назад
Родитель
Сommit
ee05602593

+ 81 - 6
src/SharpGLTF/IO/JsonSerializable.cs

@@ -28,6 +28,13 @@ namespace SharpGLTF.IO
 
 
         protected abstract void SerializeProperties(JsonWriter writer);
         protected abstract void SerializeProperties(JsonWriter writer);
 
 
+        protected static void SerializeProperty(JsonWriter writer, string name, Object value)
+        {
+            if (value == null) return;
+            writer.WritePropertyName(name);
+            _Serialize(writer, value);
+        }
+
         protected static void SerializeProperty(JsonWriter writer, string name, string value)
         protected static void SerializeProperty(JsonWriter writer, string name, string value)
         {
         {
             if (value == null) return;
             if (value == null) return;
@@ -144,6 +151,7 @@ namespace SharpGLTF.IO
             if (minItems.HasValue && collection.Count < minItems.Value) return;
             if (minItems.HasValue && collection.Count < minItems.Value) return;
 
 
             writer.WritePropertyName(name);
             writer.WritePropertyName(name);
+
             writer.WriteStartArray();
             writer.WriteStartArray();
             foreach (var item in collection)
             foreach (var item in collection)
             {
             {
@@ -203,18 +211,42 @@ namespace SharpGLTF.IO
                 }
                 }
 
 
                 writer.WriteEndObject();
                 writer.WriteEndObject();
+                return;
+            }
 
 
+            if (value is IReadOnlyDictionary<String, Object> vdso)
+            {
+                writer.WriteStartObject();
+                foreach (var item in vdso)
+                {
+                    writer.WritePropertyName(item.Key);
+                    _Serialize(writer, item.Value);
+                }
+
+                writer.WriteEndObject();
+                return;
+            }
+
+            if (value is Array array)
+            {
+                writer.WriteStartArray();
+                foreach (var item in array)
+                {
+                    _Serialize(writer, item);
+                }
+
+                writer.WriteEndArray();
                 return;
                 return;
             }
             }
 
 
-            throw new NotImplementedException();
+            throw new NotImplementedException($"Serialization of {value.GetType().Name} types is not supported.");
         }
         }
 
 
         #endregion
         #endregion
 
 
         #region deserialization
         #region deserialization
 
 
-        public void DeserializeObject(JsonReader reader)
+        public void Deserialize(JsonReader reader)
         {
         {
             while (reader.TokenType != JsonToken.StartObject)
             while (reader.TokenType != JsonToken.StartObject)
             {
             {
@@ -237,6 +269,51 @@ namespace SharpGLTF.IO
 
 
         protected abstract void DeserializeProperty(JsonReader reader, string property);
         protected abstract void DeserializeProperty(JsonReader reader, string property);
 
 
+        protected static Object DeserializeObject(JsonReader reader)
+        {
+            reader.Read();
+
+            if (reader.TokenType == JsonToken.StartObject)
+            {
+                var dict = new Dictionary<string, object>();
+
+                while (true)
+                {
+                    reader.Read();
+
+                    if (reader.TokenType == JsonToken.StartObject) continue;
+                    if (reader.TokenType == JsonToken.EndObject) break;
+
+                    System.Diagnostics.Debug.Assert(reader.TokenType == JsonToken.PropertyName);
+                    var key = reader.Value as String;
+                    var val = DeserializeObject(reader);
+
+                    dict[key] = val;
+                }
+
+                return dict;
+            }
+
+            if (reader.TokenType == JsonToken.StartArray)
+            {
+                var items = new List<Object>();
+
+                while (true)
+                {
+                    reader.Read();
+
+                    if (reader.TokenType == JsonToken.StartArray) continue;
+                    if (reader.TokenType == JsonToken.EndArray) break;
+
+                    items.Add(reader.Value);
+                }
+
+                return items.ToArray();
+            }
+
+            return reader.Value;
+        }
+
         protected static T DeserializeValue<T>(JsonReader reader)
         protected static T DeserializeValue<T>(JsonReader reader)
         {
         {
             reader.Read();
             reader.Read();
@@ -272,8 +349,6 @@ namespace SharpGLTF.IO
 
 
         protected static void DeserializeDictionary<T>(JsonReader reader, IDictionary<string, T> dict)
         protected static void DeserializeDictionary<T>(JsonReader reader, IDictionary<string, T> dict)
         {
         {
-            // System.Diagnostics.Debug.Assert(typeof(T) != typeof(MeshPrimitive));
-
             while (true)
             while (true)
             {
             {
                 reader.Read();
                 reader.Read();
@@ -367,9 +442,9 @@ namespace SharpGLTF.IO
             {
             {
                 System.Diagnostics.Debug.Assert(reader.TokenType == JsonToken.StartObject);
                 System.Diagnostics.Debug.Assert(reader.TokenType == JsonToken.StartObject);
 
 
-                var item = System.Activator.CreateInstance(vtype, true) as JsonSerializable;
+                var item = Activator.CreateInstance(vtype, true) as JsonSerializable;
 
 
-                item.DeserializeObject(reader);
+                item.Deserialize(reader);
 
 
                 value = item;
                 value = item;
 
 

+ 2 - 1
src/SharpGLTF/IO/Serialization.cs

@@ -15,7 +15,8 @@ namespace SharpGLTF.IO
                 reader.TokenType == JsonToken.Boolean ||
                 reader.TokenType == JsonToken.Boolean ||
                 reader.TokenType == JsonToken.String ||
                 reader.TokenType == JsonToken.String ||
                 reader.TokenType == JsonToken.Integer ||
                 reader.TokenType == JsonToken.Integer ||
-                reader.TokenType == JsonToken.Float
+                reader.TokenType == JsonToken.Float,
+                $"invalid JSON token {reader.TokenType}"
                 );
                 );
 
 
             return Convert.ChangeType(reader.Value, vtype, System.Globalization.CultureInfo.InvariantCulture);
             return Convert.ChangeType(reader.Value, vtype, System.Globalization.CultureInfo.InvariantCulture);

+ 30 - 0
src/SharpGLTF/Schema2/gltf.Extras.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Newtonsoft.Json;
+
+namespace SharpGLTF.Schema2
+{
+    using IO;
+
+    class Extras : JsonSerializable
+    {
+        private readonly Dictionary<string, Object> _Properties = new Dictionary<string, object>();
+
+        public IDictionary<String, Object> Properties => _Properties;
+
+        protected override void DeserializeProperty(JsonReader reader, string property)
+        {
+            _Properties[property] = DeserializeObject(reader);
+        }
+
+        protected override void SerializeProperties(JsonWriter writer)
+        {
+            foreach (var kvp in _Properties)
+            {
+                SerializeProperty(writer, kvp.Key, kvp.Value);
+            }
+        }
+    }
+}

+ 20 - 6
src/SharpGLTF/Schema2/gltf.Property.cs

@@ -14,14 +14,29 @@ namespace SharpGLTF.Schema2
 
 
         private readonly List<JsonSerializable> _extensions = new List<JsonSerializable>();
         private readonly List<JsonSerializable> _extensions = new List<JsonSerializable>();
 
 
-        private Object _extras;
+        private Extras _extras;
 
 
         #endregion
         #endregion
 
 
         #region properties
         #region properties
 
 
+        /// <summary>
+        /// Gets a collection of <see cref="JsonSerializable"/> instances.
+        /// </summary>
         public IReadOnlyCollection<JsonSerializable> Extensions => _extensions;
         public IReadOnlyCollection<JsonSerializable> Extensions => _extensions;
 
 
+        /// <summary>
+        /// Gets a collection of extra dynamic properties.
+        /// </summary>
+        public IDictionary<String, Object> Extras
+        {
+            get
+            {
+                if (_extras == null) _extras = new Extras();
+                return _extras.Properties;
+            }
+        }
+
         #endregion
         #endregion
 
 
         #region API
         #region API
@@ -67,8 +82,7 @@ namespace SharpGLTF.Schema2
         protected override void SerializeProperties(JsonWriter writer)
         protected override void SerializeProperties(JsonWriter writer)
         {
         {
             SerializeProperty(writer, "extensions", _extensions);
             SerializeProperty(writer, "extensions", _extensions);
-
-            // SerializeProperty(writer, "extras", _extras);
+            if (_extras != null) SerializeProperty(writer, "extras", _extras);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -80,10 +94,10 @@ namespace SharpGLTF.Schema2
         {
         {
             switch (property)
             switch (property)
             {
             {
-                case "extras": reader.Skip(); break;
                 case "extensions": _DeserializeExtensions(reader, _extensions); break;
                 case "extensions": _DeserializeExtensions(reader, _extensions); break;
 
 
-                // case "extras": _extras = DeserializeValue<Object>(reader); break;
+                // case "extras": reader.Skip(); break;
+                case "extras": _extras = DeserializeValue<Extras>(reader); break;
 
 
                 default: reader.Skip(); break;
                 default: reader.Skip(); break;
             }
             }
@@ -123,7 +137,7 @@ namespace SharpGLTF.Schema2
                 }
                 }
                 else
                 else
                 {
                 {
-                    val.DeserializeObject(reader);
+                    val.Deserialize(reader);
                     extensions.Add(val);
                     extensions.Add(val);
                 }
                 }
             }
             }

+ 1 - 1
src/SharpGLTF/Schema2/gltf.Serialization.cs

@@ -217,7 +217,7 @@ namespace SharpGLTF.Schema2
             using (var reader = new JsonTextReader(textReader))
             using (var reader = new JsonTextReader(textReader))
             {
             {
                 var root = new MODEL();
                 var root = new MODEL();
-                root.DeserializeObject(reader);
+                root.Deserialize(reader);
 
 
                 var ex = root.Validate().FirstOrDefault();
                 var ex = root.Validate().FirstOrDefault();
                 if (ex != null) throw ex;
                 if (ex != null) throw ex;

+ 38 - 0
tests/SharpGLTF.Tests/Schema2/Authoring/CreateModelTests.cs

@@ -21,6 +21,44 @@ namespace SharpGLTF.Schema2.Authoring
             Assert.AreEqual("Empty Scene", root.DefaultScene.Name);            
             Assert.AreEqual("Empty Scene", root.DefaultScene.Name);            
         }
         }
 
 
+        [Test(Description = "Creates an empty model")]
+        public void CreateSceneWithExtras()
+        {
+            var root = ModelRoot.CreateModel();
+            var scene = root.UseScene("Empty Scene");
+
+            root.Extras["author"] = "me";
+
+            root.Extras["value1"] = 17;
+            root.Extras["array1"] = new Single[] { 1, 2, 3 };
+            root.Extras["dict1"] = new Dictionary<string, Object>
+            {
+                ["A"] = 16,
+                ["B"] = "delta",
+                ["C"] = new Single[] { 4, 6, 7 },
+                ["D"] = new Dictionary<string, Object> { ["S"]= 1, ["T"] = 2 }
+            };
+
+            var json = root.GetJSON(Newtonsoft.Json.Formatting.Indented);
+            var bytes = root.GetGLB();
+
+            var rootBis = ModelRoot.ParseGLB(bytes);
+
+            Assert.AreEqual(root.Extras["author"], rootBis.Extras["author"]);
+            Assert.AreEqual(root.Extras["value1"], rootBis.Extras["value1"]);
+            CollectionAssert.AreEqual
+                (
+                root.Extras["array1"] as Array,
+                rootBis.Extras["array1"] as Array
+                );
+
+            CollectionAssert.AreEqual
+                (
+                root.Extras["dict1"] as Dictionary<string, Object>,
+                rootBis.Extras["dict1"] as Dictionary<string, Object>
+                );
+        }
+
         [Test(Description ="Creates a model with a triangle mesh")]
         [Test(Description ="Creates a model with a triangle mesh")]
         public void CreateSolidTriangleScene()
         public void CreateSolidTriangleScene()
         {
         {

+ 8 - 1
tests/SharpGLTF.Tests/Schema2/LoadAndSave/LoadModelTests.cs

@@ -86,9 +86,16 @@ namespace SharpGLTF.Schema2.LoadAndSave
                 if (!f.Contains(section)) continue;
                 if (!f.Contains(section)) continue;
 
 
                 var model = GltfUtils.LoadModel(f);
                 var model = GltfUtils.LoadModel(f);
-                Assert.NotNull(model);
+                Assert.NotNull(model);                
 
 
+                // evaluate and save all the triangles to a Wavefront Object
                 model.AttachToCurrentTest(System.IO.Path.ChangeExtension(System.IO.Path.GetFileName(f), ".obj"));
                 model.AttachToCurrentTest(System.IO.Path.ChangeExtension(System.IO.Path.GetFileName(f), ".obj"));
+
+                // do a model roundtrip                
+                model.MergeBuffers();
+                var bytes = model.GetGLB();
+
+                var modelBis = ModelRoot.ParseGLB(bytes);
             }
             }
         }
         }