Browse Source

Merge remote-tracking branch 'origin/master' into agi-extensions

Ed Mackey 4 years ago
parent
commit
d5d4e74fd4

+ 44 - 25
src/SharpGLTF.Core/Schema2/gltf.ExtensionsFactory.cs

@@ -7,7 +7,14 @@ using SharpGLTF.IO;
 
 namespace SharpGLTF.Schema2
 {
-    static class ExtensionsFactory
+    /// <summary>
+    /// Global extensions manager.
+    /// </summary>
+    /// <remarks>
+    /// Extensions must be registered at the beginning of the application<br/>
+    /// Before using the APIs.
+    /// </remarks>
+    public static class ExtensionsFactory
     {
         // extensions design inconsistencies:
         // https://github.com/KhronosGroup/glTF/issues/1491
@@ -48,15 +55,34 @@ namespace SharpGLTF.Schema2
 
         public static IEnumerable<string> SupportedExtensions => _Extensions
             .Select(item => item.Name)
-            .Concat(new[] { "KHR_mesh_quantization" });
+            .Concat(new[] { "KHR_mesh_quantization" }); // special case because it's a "typeless" extension.
 
+        /// <summary>
+        /// Registers a new extensions to be used globally.
+        /// </summary>
+        /// <typeparam name="TParent">The parent type to which this extension is attached.</typeparam>
+        /// <typeparam name="TExtension">The extension type.</typeparam>
+        /// <param name="persistentName">The extension name.</param>
+        /// <remarks>
+        /// The <paramref name="persistentName"/> is the value used for serialization<br/>
+        /// and it must meet <see href="https://github.com/KhronosGroup/glTF/blob/master/extensions/Prefixes.md">extension naming constraints</see>.
+        /// </remarks>
         public static void RegisterExtension<TParent, TExtension>(string persistentName)
             where TParent : JsonSerializable
             where TExtension : JsonSerializable
         {
+            Guard.NotNullOrEmpty(persistentName, nameof(persistentName));
+            Guard.MustBeNull(Identify(typeof(TParent), typeof(TExtension)), $"{nameof(TExtension)} already registered for {nameof(TParent)}");
+
+            // TODO: check that persistentName has a valid extension name.
+
             _Extensions.Add( (persistentName, typeof(TParent), typeof(TExtension)) );
         }
 
+        #endregion
+
+        #region internals
+
         /// <summary>
         /// Creates an extension object based on the parent object and the extension code.
         /// </summary>
@@ -84,11 +110,14 @@ namespace SharpGLTF.Schema2
         }
 
         /// <summary>
-        /// Given a parentType and an extensionType, it identifies the extension code.
+        /// Given a parentType and an extensionType, it identifies the extension code name.
         /// </summary>
         /// <param name="parentType">The type of the parent object.</param>
         /// <param name="extensionType">The type of the extension object.</param>
         /// <returns>An extension identifier code, like "KHR_texture_transform".</returns>
+        /// <remarks>
+        /// Extensions must be registered in advanced using <see cref="RegisterExtension{TParent, TExtension}(string)"/>.
+        /// </remarks>
         internal static string Identify(Type parentType, Type extensionType)
         {
             foreach (var (name, baseType, extType) in _Extensions)
@@ -114,7 +143,7 @@ namespace SharpGLTF.Schema2
 
         internal void UpdateExtensionsSupport()
         {
-            var used = RetrieveUsedExtensions();
+            var used = GatherUsedExtensions();
 
             // update the used list
             this._extensionsUsed.Clear();
@@ -123,20 +152,7 @@ namespace SharpGLTF.Schema2
             _SetExtensionUsage("KHR_mesh_quantization", this._extensionsUsed.Contains("KHR_mesh_quantization"), true);
         }
 
-        private void _SetExtensionUsage(string extension, bool used, bool required)
-        {
-            if (!used)
-            {
-                this._extensionsUsed.Remove(extension);
-                this._extensionsRequired.Remove(extension);
-                return;
-            }
-
-            if (!this._extensionsUsed.Contains(extension)) this._extensionsUsed.Add(extension);
-            if (required && !this._extensionsRequired.Contains(extension)) this._extensionsRequired.Add(extension);
-        }
-
-        internal IEnumerable<string> RetrieveUsedExtensions()
+        internal IEnumerable<string> GatherUsedExtensions()
         {
             // retrieve ALL the property based objects of the whole model.
             var allObjects = new[] { this }
@@ -170,14 +186,17 @@ namespace SharpGLTF.Schema2
             return used;
         }
 
-        internal void UsingExtension(Type parentType, Type extensionType)
+        private void _SetExtensionUsage(string extension, bool used, bool required)
         {
-            var id = ExtensionsFactory.Identify(parentType, extensionType);
-            if (string.IsNullOrWhiteSpace(id)) return;
-
-            if (this._extensionsUsed.Contains(id)) return;
+            if (!used)
+            {
+                this._extensionsUsed.Remove(extension);
+                this._extensionsRequired.Remove(extension);
+                return;
+            }
 
-            this._extensionsUsed.Add(id);
+            if (!this._extensionsUsed.Contains(extension)) this._extensionsUsed.Add(extension);
+            if (required && !this._extensionsRequired.Contains(extension)) this._extensionsRequired.Add(extension);
         }
 
         internal void _ValidateExtensions(Validation.ValidationContext validate)
@@ -187,7 +206,7 @@ namespace SharpGLTF.Schema2
                 validate._LinkThrow("Extensions", iex);
             }
 
-            foreach (var ext in RetrieveUsedExtensions())
+            foreach (var ext in GatherUsedExtensions())
             {
                 if (!this._extensionsUsed.Contains(ext)) validate._LinkThrow("Extensions", ext);
             }

+ 21 - 0
src/SharpGLTF.Core/Schema2/gltf.ExtraProperties.cs

@@ -61,6 +61,23 @@ namespace SharpGLTF.Schema2
             return _extensions.OfType<T>().FirstOrDefault();
         }
 
+        public T UseExtension<T>()
+            where T : JsonSerializable
+        {
+            var value = GetExtension<T>();
+            if (value != null) return value;
+
+            var name = ExtensionsFactory.Identify(this.GetType(), typeof(T));
+            Guard.NotNull(name, nameof(T));
+
+            value = ExtensionsFactory.Create(this, name) as T;
+            Guard.NotNull(value, nameof(T));
+
+            _extensions.Add(value);
+
+            return value;
+        }
+
         public void SetExtension<T>(T value)
             where T : JsonSerializable
         {
@@ -89,6 +106,10 @@ namespace SharpGLTF.Schema2
         /// Gets a collection of <see cref="ExtraProperties"/> instances stored by this object.
         /// </summary>
         /// <returns>A collection of <see cref="ExtraProperties"/> instances.</returns>
+        /// <remarks>
+        /// This is used to traverse the whole glTF document tree and gather all the objects<br/>
+        /// So we can identify which extensions are used anywhere in the document.
+        /// </remarks>
         protected virtual IEnumerable<ExtraProperties> GetLogicalChildren()
         {
             return _extensions.OfType<ExtraProperties>();

+ 0 - 5
src/SharpGLTF.Core/Schema2/gltf.LogicalChildOfRoot.cs

@@ -62,11 +62,6 @@ namespace SharpGLTF.Schema2
 
         #region API
 
-        internal void UsingExtension(Type extensionType)
-        {
-            LogicalParent.UsingExtension(this.GetType(), extensionType);
-        }
-
         /// <summary>
         /// Renames all the unnamed and duplicate name items in the collection so all the items have a unique valid name.
         /// </summary>

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

@@ -27,7 +27,7 @@ namespace SharpGLTF.Schema2
             if (this._pbrMetallicRoughness == null) this._pbrMetallicRoughness = new MaterialPBRMetallicRoughness();
 
             ClearExtensions();
-            this.SetExtension(new MaterialUnlit(this));
+            this.UseExtension<MaterialUnlit>();
         }
 
         /// <summary>
@@ -45,9 +45,9 @@ namespace SharpGLTF.Schema2
 
             foreach (var extn in extensionNames)
             {
-                if (extn == "ClearCoat") this.SetExtension(new MaterialClearCoat(this));
-                if (extn == "Transmission") this.SetExtension(new MaterialTransmission(this));
-                if (extn == "Sheen") this.SetExtension(new MaterialSheen(this));
+                if (extn == "ClearCoat") this.UseExtension<MaterialClearCoat>();
+                if (extn == "Transmission") this.UseExtension<MaterialTransmission>();
+                if (extn == "Sheen") this.UseExtension<MaterialSheen>();
             }
         }
 
@@ -67,7 +67,7 @@ namespace SharpGLTF.Schema2
             }
 
             ClearExtensions();
-            this.SetExtension(new MaterialPBRSpecularGlossiness(this));
+            this.UseExtension<MaterialPBRSpecularGlossiness>();
         }
 
         #endregion

+ 2 - 13
src/SharpGLTF.Core/Schema2/gltf.Root.PunctualLights.cs

@@ -45,9 +45,7 @@ namespace SharpGLTF.Schema2
             get
             {
                 var ext = this.GetExtension<_ModelPunctualLights>();
-                if (ext == null) return Array.Empty<PunctualLight>();
-
-                return ext.Lights;
+                return ext == null ? Array.Empty<PunctualLight>() : ext.Lights;
             }
         }
 
@@ -71,16 +69,7 @@ namespace SharpGLTF.Schema2
         /// <returns>A <see cref="PunctualLight"/> instance.</returns>
         public PunctualLight CreatePunctualLight(string name, PunctualLightType lightType)
         {
-            var ext = this.GetExtension<_ModelPunctualLights>();
-            if (ext == null)
-            {
-                this.UsingExtension(typeof(ModelRoot), typeof(_ModelPunctualLights));
-
-                ext = new _ModelPunctualLights(this);
-                this.SetExtension(ext);
-            }
-
-            return ext.CreateLight(name, lightType);
+            return UseExtension<_ModelPunctualLights>().CreateLight(name, lightType);
         }
     }
 }

+ 17 - 22
src/SharpGLTF.Core/Schema2/gltf.Textures.cs

@@ -62,14 +62,6 @@ namespace SharpGLTF.Schema2
             return _GetPrimaryImage() == img ? null : img;
         }
 
-        public void ClearImages()
-        {
-            _source = null;
-            this.RemoveExtensions<TextureDDS>();
-            this.RemoveExtensions<TextureWEBP>();
-            this.RemoveExtensions<TextureKTX2>();
-        }
-
         public void SetImage(Image primaryImage)
         {
             Guard.NotNull(primaryImage, nameof(primaryImage));
@@ -96,8 +88,6 @@ namespace SharpGLTF.Schema2
             Guard.IsTrue(primaryImage.Content.IsExtendedFormat, "Primary image must be DDS, WEBP or KTX2");
             Guard.IsTrue(fallbackImage.Content.IsJpg || fallbackImage.Content.IsPng, nameof(fallbackImage), "Fallback image must be PNG or JPEG");
 
-            ClearImages();
-
             if (primaryImage.Content.IsDds) { _UseDDSTexture().Image = primaryImage; }
             if (primaryImage.Content.IsWebp) { _UseWEBPTexture().Image = primaryImage; }
             if (primaryImage.Content.IsKtx2) { _UseKTX2Texture().Image = primaryImage; }
@@ -105,28 +95,33 @@ namespace SharpGLTF.Schema2
             _source = fallbackImage.LogicalIndex;
         }
 
-        private TextureDDS _UseDDSTexture()
+        public void ClearImages()
         {
-            var primary = this.GetExtension<TextureDDS>();
-            if (primary == null) { primary = new TextureDDS(this); this.SetExtension(primary); }
+            _source = null;
+            this.RemoveExtensions<TextureDDS>();
+            this.RemoveExtensions<TextureWEBP>();
+            this.RemoveExtensions<TextureKTX2>();
+        }
 
-            return primary;
+        private TextureDDS _UseDDSTexture()
+        {
+            this.RemoveExtensions<TextureWEBP>();
+            this.RemoveExtensions<TextureKTX2>();
+            return this.UseExtension<TextureDDS>();
         }
 
         private TextureWEBP _UseWEBPTexture()
         {
-            var primary = this.GetExtension<TextureWEBP>();
-            if (primary == null) { primary = new TextureWEBP(this); this.SetExtension(primary); }
-
-            return primary;
+            this.RemoveExtensions<TextureDDS>();
+            this.RemoveExtensions<TextureKTX2>();
+            return this.UseExtension<TextureWEBP>();
         }
 
         private TextureKTX2 _UseKTX2Texture()
         {
-            var primary = this.GetExtension<TextureKTX2>();
-            if (primary == null) { primary = new TextureKTX2(this); this.SetExtension(primary); }
-
-            return primary;
+            this.RemoveExtensions<TextureDDS>();
+            this.RemoveExtensions<TextureWEBP>();
+            return this.UseExtension<TextureKTX2>();
         }
 
         internal bool _IsEqualentTo(Image primary, Image fallback, TextureSampler sampler)

+ 1 - 1
tests/SharpGLTF.Tests/Schema2/LoadAndSave/LoadSampleTests.cs

@@ -58,7 +58,7 @@ namespace SharpGLTF.Schema2.LoadAndSave
             // check extensions used
             if (unsupportedExtensions.All(uex => !model.ExtensionsUsed.Contains(uex)))
             {
-                var detectedExtensions = model.RetrieveUsedExtensions().ToArray();
+                var detectedExtensions = model.GatherUsedExtensions().ToArray();
                 CollectionAssert.AreEquivalent(model.ExtensionsUsed, detectedExtensions);
             }
 

+ 1 - 1
tests/SharpGLTF.Tests/Schema2/LoadAndSave/LoadSpecialModelsTest.cs

@@ -117,7 +117,7 @@ namespace SharpGLTF.Schema2.LoadAndSave
             var model = ModelRoot.Load(path);
             Assert.NotNull(model);
 
-            var flattenExtensions = model.RetrieveUsedExtensions().ToArray();
+            var flattenExtensions = model.GatherUsedExtensions().ToArray();
 
             model.AttachToCurrentTest("AliceModel.glb");
         }