Browse Source

refactored extensions support to handle "one to many" extensions cases

Vicente Penades 6 years ago
parent
commit
7d7d9736b4

+ 3 - 3
src/SharpGLTF/Geometry/MemoryAccessor.cs

@@ -84,10 +84,10 @@ namespace SharpGLTF.Geometry
                 if (this.ByteOffset < 0) return false;
                 if (this.ItemsCount < 0) return false;
                 if (this.ByteStride < 0) return false;
-                var len = this.Dimensions.DimCount() * this.Encoding.ByteLength();
-                if (len == 0 || (len & 3) != 0) return false;
+                var blen = this.ByteLength;
+                if (blen == 0 || (blen & 3) != 0) return false;
 
-                if (this.ByteStride > 0 && this.ByteStride < len) return false;
+                if (this.ByteStride > 0 && this.ByteStride < blen) return false;
                 if ((this.ByteStride & 3) != 0) return false;
 
                 return true;

+ 3 - 0
src/SharpGLTF/Schema2/gltf.Animations.cs

@@ -300,6 +300,9 @@ namespace SharpGLTF.Schema2
             var root = LogicalParent.LogicalParent;
 
             var buffer = root.UseBufferView(new Byte[output.Count * 4 * 3]);
+
+            System.Diagnostics.Debug.Assert(buffer.ByteStride == 0);
+
             var accessor = root.CreateAccessor("Animation.Output")
                 .WithData(buffer, 0, output.Count, ElementType.VEC3, ComponentType.FLOAT, false);
 

+ 3 - 2
src/SharpGLTF/Schema2/gltf.BufferView.cs

@@ -28,9 +28,10 @@ namespace SharpGLTF.Schema2
             Guard.MustBeGreaterThanOrEqualTo(byteLength.AsValue(0), _byteLengthMinimum, nameof(byteLength));
             Guard.MustBeGreaterThanOrEqualTo(byteOffset, _byteOffsetMinimum, nameof(byteOffset));
 
-            if (target == BufferMode.ELEMENT_ARRAY_BUFFER)
+            if (target == BufferMode.ELEMENT_ARRAY_BUFFER || byteStride == 0)
             {
                 Guard.IsTrue(byteStride == 0, nameof(byteStride));
+                this._byteStride = null;
             }
             else if (byteStride > 0)
             {
@@ -38,6 +39,7 @@ namespace SharpGLTF.Schema2
 
                 Guard.IsTrue(byteStride.IsMultipleOf(4), nameof(byteStride));
                 Guard.MustBeBetweenOrEqualTo(byteStride, _byteStrideMinimum, _byteStrideMaximum, nameof(byteStride));
+                this._byteStride = byteStride.AsNullable(0, _byteStrideMinimum, _byteStrideMaximum);
             }
 
             this._buffer = buffer.LogicalIndex;
@@ -45,7 +47,6 @@ namespace SharpGLTF.Schema2
             this._byteLength = byteLength.AsValue(buffer.Content.Length);
 
             this._byteOffset = byteOffset.AsNullable(_byteOffsetDefault, _byteOffsetMinimum, int.MaxValue);
-            this._byteStride = byteStride.AsNullable(0, _byteStrideMinimum, _byteStrideMaximum);
 
             this._target = target;
         }

+ 28 - 17
src/SharpGLTF/Schema2/gltf.ExtensionsFactory.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Text;
+using System.Linq;
 
 namespace SharpGLTF.Schema2
 {
@@ -15,48 +16,58 @@ namespace SharpGLTF.Schema2
 
         static ExtensionsFactory()
         {
-            RegisterExtension<MaterialPBRSpecularGlossiness_KHR>("KHR_materials_pbrSpecularGlossiness");
-            RegisterExtension<MaterialUnlit_KHR>("KHR_materials_unlit");
+            RegisterExtension<Material, MaterialPBRSpecularGlossiness_KHR>("KHR_materials_pbrSpecularGlossiness");
 
-            // if found in model:
-            // RegisterExtension<KHR_lights_punctualglTFextension>("KHR_lights_punctual");
+            RegisterExtension<Material, MaterialUnlit_KHR>("KHR_materials_unlit");
 
-            // if found in node
-            // RegisterExtension<KHR_lights_punctualnodeextension>("KHR_lights_punctual");
+            RegisterExtension<ModelRoot, KHR_lights_punctualglTFextension>("KHR_lights_punctual");
+            RegisterExtension<Node, KHR_lights_punctualnodeextension>("KHR_lights_punctual");
         }
 
         #endregion
 
         #region data
 
-        private static readonly Dictionary<string, Type> _Extensions = new Dictionary<string, Type>();
+        private static readonly List<(string, Type, Type)> _Extensions = new List<(string, Type, Type)>();
 
         #endregion
 
         #region API
 
-        public static IEnumerable<string> SupportedExtensions => _Extensions.Keys;
+        public static IEnumerable<string> SupportedExtensions => _Extensions.Select(item => item.Item1);
 
-        public static void RegisterExtension<T>(string persistentName)
-            where T : JsonSerializable
+        public static void RegisterExtension<TParent, TExtension>(string persistentName)
+            where TParent : JsonSerializable
+            where TExtension : JsonSerializable
         {
-            _Extensions[persistentName] = typeof(T);
+            _Extensions.Add( (persistentName, typeof(TParent), typeof(TExtension)) );
         }
 
-        internal static JsonSerializable Create(string key)
+        internal static JsonSerializable Create(JsonSerializable parent, string key)
         {
-            if (!_Extensions.TryGetValue(key, out Type t)) return null;
+            var ptype = parent.GetType();
 
-            var instance = Activator.CreateInstance(t);
+            var entry = _Extensions.FirstOrDefault(item => item.Item1 == key && item.Item2 == ptype);
+
+            if (entry.Item1 == null) return null;
+
+            var instance = Activator.CreateInstance
+                (
+                entry.Item3,
+                System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance,
+                null,
+                new Object[] { parent },
+                null
+                );
 
             return instance as JsonSerializable;
         }
 
-        internal static string Identify(Type type)
+        internal static string Identify(Type parentType, Type extensionType)
         {
-            foreach (var kvp in _Extensions)
+            foreach (var entry in _Extensions)
             {
-                if (kvp.Value == type) return kvp.Key;
+                if (entry.Item2 == parentType && entry.Item3 == extensionType) return entry.Item1;
             }
 
             return null;

+ 5 - 3
src/SharpGLTF/Schema2/gltf.MaterialsFactory.cs

@@ -54,7 +54,7 @@ namespace SharpGLTF.Schema2
         public Material WithPBRSpecularGlossiness()
         {
             this.RemoveExtensions<MaterialUnlit_KHR>();
-            this.SetExtension(new MaterialPBRSpecularGlossiness_KHR());
+            this.SetExtension(new MaterialPBRSpecularGlossiness_KHR(this));
 
             return this;
         }
@@ -66,7 +66,7 @@ namespace SharpGLTF.Schema2
         public Material WithUnlit()
         {
             this.RemoveExtensions<MaterialPBRSpecularGlossiness_KHR>();
-            this.SetExtension(new MaterialUnlit_KHR());
+            this.SetExtension(new MaterialUnlit_KHR(this));
 
             return this;
         }
@@ -110,7 +110,7 @@ namespace SharpGLTF.Schema2
                 "Emissive",
                 _GetEmissiveTexture,
                 () => { var rgb = _emissiveFactor.AsValue(_emissiveFactorDefault); return new Vector4(rgb, 1); },
-                value => _emissiveFactor = new Vector3(value.X, value.Y, value.Z).AsNullable(_emissiveFactorDefault)
+                value => _emissiveFactor = new Vector3(value.X, value.Y, value.Z).AsNullable(_emissiveFactorDefault, Vector3.Zero, Vector3.One)
                 );
         }
 
@@ -182,6 +182,7 @@ namespace SharpGLTF.Schema2
 
     internal sealed partial class MaterialPBRSpecularGlossiness_KHR
     {
+        internal MaterialPBRSpecularGlossiness_KHR(Material material) { }
 
         private TextureInfo _GetDiffuseTexture(bool create)
         {
@@ -228,5 +229,6 @@ namespace SharpGLTF.Schema2
 
     internal sealed partial class MaterialUnlit_KHR
     {
+        internal MaterialUnlit_KHR(Material material) { }
     }
 }

+ 5 - 5
src/SharpGLTF/Schema2/gltf.Property.cs

@@ -88,7 +88,7 @@ namespace SharpGLTF.Schema2
                 foreach (var val in this._extensions)
                 {
                     if (val == null) continue;
-                    var key = ExtensionsFactory.Identify(val.GetType());
+                    var key = ExtensionsFactory.Identify(this.GetType(), val.GetType());
                     if (key == null) continue;
 
                     dict[key] = val;
@@ -109,7 +109,7 @@ namespace SharpGLTF.Schema2
         {
             switch (property)
             {
-                case "extensions": _DeserializeExtensions(reader, _extensions); break;
+                case "extensions": _DeserializeExtensions(this, reader, _extensions); break;
 
                 // case "extras": reader.Skip(); break;
                 case "extras": _extras = DeserializeValue<Extras>(reader); break;
@@ -118,7 +118,7 @@ namespace SharpGLTF.Schema2
             }
         }
 
-        private static void _DeserializeExtensions(JsonReader reader, IList<JsonSerializable> extensions)
+        private static void _DeserializeExtensions(JsonSerializable parent, JsonReader reader, IList<JsonSerializable> extensions)
         {
             while (true)
             {
@@ -133,7 +133,7 @@ namespace SharpGLTF.Schema2
                     {
                         if (reader.TokenType == JsonToken.EndArray) break;
 
-                        _DeserializeExtensions(reader, extensions);
+                        _DeserializeExtensions(parent, reader, extensions);
                     }
 
                     break;
@@ -144,7 +144,7 @@ namespace SharpGLTF.Schema2
                 System.Diagnostics.Debug.Assert(reader.TokenType == JsonToken.PropertyName);
                 var key = reader.Value as String;
 
-                var val = ExtensionsFactory.Create(key);
+                var val = ExtensionsFactory.Create(parent, key);
 
                 if (val == null)
                 {

+ 41 - 16
src/SharpGLTF/Schema2/khr.lights.cs

@@ -9,14 +9,30 @@ namespace SharpGLTF.Schema2
 
     partial class KHR_lights_punctualglTFextension
     {
-        public ChildrenCollection<PunctualLight, ModelRoot> GetLightsCollection(ModelRoot root)
+        internal KHR_lights_punctualglTFextension(ModelRoot root)
         {
-            if (_lights == null) _lights = new ChildrenCollection<PunctualLight, ModelRoot>(root);
+            _lights = new ChildrenCollection<PunctualLight, ModelRoot>(root);
+        }
+
+        public IReadOnlyList<PunctualLight> Lights => _lights;
+
+        public PunctualLight CreateLight(string name = null)
+        {
+            var light = new PunctualLight();
+            light.Name = name;
 
-            return _lights;
+            _lights.Add(light);
+
+            return light;
         }
     }
 
+    public enum PunctualLightType { Directional, Point, Spot }
+
+    /// <remarks>
+    /// This is part of <see cref="https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual"/> extension.
+    /// </remarks>
+    [System.Diagnostics.DebuggerDisplay("{LightType} {Color} {Intensity} {Range}")]
     public partial class PunctualLight
     {
         /// <summary>
@@ -27,22 +43,26 @@ namespace SharpGLTF.Schema2
         public Vector3 Color
         {
             get => _color.AsValue(_colorDefault);
-            set => _color = value.AsNullable(_colorDefault);
+            set => _color = value.AsNullable(_colorDefault, Vector3.Zero, Vector3.One);
         }
 
         public Double Intensity
         {
             get => _intensity.AsValue(_intensityDefault);
-            set => _intensity = value.AsNullable(_intensityDefault, _intensityMinimum, double.MaxValue);
+            set => _intensity = value.AsNullable(_intensityDefault, _intensityMinimum, float.MaxValue);
         }
 
         public Double Range
         {
             get => _range.AsValue(0);
-            set => _range = value.AsNullable(0, _rangeMinimum, double.MaxValue);
+            set => _range = value.AsNullable(0, _rangeMinimum, float.MaxValue);
         }
 
-        public string LightType => _type;
+        public PunctualLightType LightType
+        {
+            get => (PunctualLightType)Enum.Parse(typeof(PunctualLightType), _type, true);
+            set => this._type = value.ToString().ToLower();
+        }
     }
 
     partial class PunctualLightSpot
@@ -62,6 +82,12 @@ namespace SharpGLTF.Schema2
 
     partial class ModelRoot
     {
+        /// <summary>
+        /// A collection of <see cref="PunctualLight"/> instances.
+        /// </summary>
+        /// <remarks>
+        /// This is part of <see cref="https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual">KHR_lights_punctual</see> extension.
+        /// </remarks>
         public IReadOnlyList<PunctualLight> LogicalPunctualLights
         {
             get
@@ -69,7 +95,7 @@ namespace SharpGLTF.Schema2
                 var ext = this.GetExtension<KHR_lights_punctualglTFextension>();
                 if (ext == null) return new PunctualLight[0];
 
-                return ext.GetLightsCollection(this);
+                return ext.Lights;
             }
         }
 
@@ -84,21 +110,20 @@ namespace SharpGLTF.Schema2
             var ext = this.GetExtension<KHR_lights_punctualglTFextension>();
             if (ext == null)
             {
-                ext = new KHR_lights_punctualglTFextension();
+                ext = new KHR_lights_punctualglTFextension(this);
                 this.SetExtension(ext);
             }
 
-            var light = new PunctualLight();
-            light.Name = name;
-
-            ext.GetLightsCollection(this).Add(light);
-
-            return light;
+            return ext.CreateLight(name);
         }
     }
 
     partial class KHR_lights_punctualnodeextension
     {
+        internal KHR_lights_punctualnodeextension(Node node)
+        {
+        }
+
         public int LightIndex
         {
             get => _light;
@@ -123,7 +148,7 @@ namespace SharpGLTF.Schema2
 
                 Guard.MustShareLogicalParent(this, value, nameof(value));
 
-                var ext = new KHR_lights_punctualnodeextension();
+                var ext = new KHR_lights_punctualnodeextension(this);
                 ext.LightIndex = value.LogicalIndex;
 
                 this.SetExtension(ext);

+ 18 - 4
src/SharpGLTF/_Extensions.cs

@@ -220,12 +220,26 @@ namespace SharpGLTF
         internal static T? AsNullable<T>(this T value, T defval, T minval, T maxval)
             where T : struct, IEquatable<T>, IComparable<T>
         {
-            if (value.Equals(defval)) return null;
+            if (value.CompareTo(minval) < 0) value = minval;
+            if (value.CompareTo(maxval) > 0) value = maxval;
 
-            if (value.CompareTo(minval) < 0) return minval;
-            if (value.CompareTo(maxval) > 0) return maxval;
+            return value.Equals(defval) ? (T?)null : value;
+        }
+
+        internal static Vector3? AsNullable(this Vector3 value, Vector3 defval, Vector3 minval, Vector3 maxval)
+        {
+            value = Vector3.Min(value, minval);
+            value = Vector3.Max(value, maxval);
+
+            return value.Equals(defval) ? (Vector3?)null : value;
+        }
+
+        internal static Vector4? AsNullable(this Vector4 value, Vector4 defval, Vector4 minval, Vector4 maxval)
+        {
+            value = Vector4.Min(value, minval);
+            value = Vector4.Max(value, maxval);
 
-            return value;
+            return value.Equals(defval) ? (Vector4?)null : value;
         }
 
         internal static String AsNullable(this string value)

+ 5 - 0
tests/SharpGLTF.Tests/Schema2/LoadAndSave/LoadModelTests.cs

@@ -152,6 +152,11 @@ namespace SharpGLTF.Schema2.LoadAndSave
 
             var model = GltfUtils.LoadModel(f);
             Assert.NotNull(model);
+
+            Assert.AreEqual(3, model.LogicalPunctualLights.Count);
+
+            Assert.AreEqual(1, model.DefaultScene.VisualChildren.ElementAt(0).PunctualLight.LogicalIndex);
+            Assert.AreEqual(0, model.DefaultScene.VisualChildren.ElementAt(1).PunctualLight.LogicalIndex);
         }
 
         #endregion