Browse Source

Added KHR_material_dispersion

vpenades 8 months ago
parent
commit
8aafa16f8c

+ 29 - 0
build/SharpGLTF.CodeGen/Ext.KHR_Dispersion.cs

@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using SharpGLTF.SchemaReflection;
+
+namespace SharpGLTF
+{
+    class DispersionExtension : SchemaProcessor
+    {
+        private static string SchemaUri => Constants.KhronosExtensionPath("KHR_materials_dispersion", "material.KHR_materials_dispersion.schema.json");
+
+        private const string ExtensionRootClassName = "KHR_materials_dispersion glTF Material Extension";
+
+        public override IEnumerable<(string, SchemaType.Context)> Process()
+        {
+            var ctx = SchemaProcessing.LoadSchemaContext(SchemaUri);
+            ctx.IgnoredByCodeEmitter("glTF Property");
+            ctx.IgnoredByCodeEmitter("glTF Child of Root Property");            
+
+            yield return ("ext.Dispersion.g", ctx);
+        }
+
+        public override void PrepareTypes(CodeGen.CSharpEmitter newEmitter, SchemaType.Context ctx)
+        {
+            newEmitter.SetRuntimeName(ExtensionRootClassName, "MaterialDispersion");
+        }
+    }
+}

+ 2 - 1
build/SharpGLTF.CodeGen/Program.cs

@@ -28,10 +28,11 @@ namespace SharpGLTF
             // material extensions       
             // material extensions       
             processors.Add(new UnlitExtension());
             processors.Add(new UnlitExtension());
             processors.Add(new IorExtension());
             processors.Add(new IorExtension());
-            processors.Add(new SheenExtension());
+            processors.Add(new SheenExtension());            
             processors.Add(new VolumeExtension());
             processors.Add(new VolumeExtension());
             processors.Add(new SpecularExtension());
             processors.Add(new SpecularExtension());
             processors.Add(new ClearCoatExtension());
             processors.Add(new ClearCoatExtension());
+            processors.Add(new DispersionExtension());
             processors.Add(new AnisotropyExtension());
             processors.Add(new AnisotropyExtension());
             processors.Add(new IridescenceExtension());            
             processors.Add(new IridescenceExtension());            
             processors.Add(new TransmissionExtension());
             processors.Add(new TransmissionExtension());

+ 95 - 0
src/SharpGLTF.Core/Schema2/Generated/ext.Dispersion.g.cs

@@ -0,0 +1,95 @@
+// <auto-generated/>
+
+//------------------------------------------------------------------------------------------------
+//      This file has been programatically generated; DON´T EDIT!
+//------------------------------------------------------------------------------------------------
+
+#pragma warning disable SA1001
+#pragma warning disable SA1027
+#pragma warning disable SA1028
+#pragma warning disable SA1121
+#pragma warning disable SA1205
+#pragma warning disable SA1309
+#pragma warning disable SA1402
+#pragma warning disable SA1505
+#pragma warning disable SA1507
+#pragma warning disable SA1508
+#pragma warning disable SA1652
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Numerics;
+using System.Text.Json;
+
+using JSONREADER = System.Text.Json.Utf8JsonReader;
+using JSONWRITER = System.Text.Json.Utf8JsonWriter;
+using FIELDINFO = SharpGLTF.Reflection.FieldInfo;
+
+
+namespace SharpGLTF.Schema2
+{
+	using Collections;
+
+	/// <summary>
+	/// glTF extension that defines the strength of dispersion.
+	/// </summary>
+	#if NET6_0_OR_GREATER
+	[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
+	#endif
+	[global::System.CodeDom.Compiler.GeneratedCodeAttribute("SharpGLTF.CodeGen", "1.0.0.0")]
+	partial class MaterialDispersion : ExtraProperties
+	{
+	
+		#region reflection
+	
+		public const string SCHEMANAME = "KHR_materials_dispersion";
+		protected override string GetSchemaName() => SCHEMANAME;
+	
+		protected override IEnumerable<string> ReflectFieldsNames()
+		{
+			yield return "dispersion";
+			foreach(var f in base.ReflectFieldsNames()) yield return f;
+		}
+		protected override bool TryReflectField(string name, out FIELDINFO value)
+		{
+			switch(name)
+			{
+				case "dispersion": value = FIELDINFO.From("dispersion",this, instance => instance._dispersion ?? 0); return true;
+				default: return base.TryReflectField(name, out value);
+			}
+		}
+	
+		#endregion
+	
+		#region data
+	
+		private const Double _dispersionDefault = 0;
+		private const Double _dispersionMinimum = 0;
+		private Double? _dispersion = _dispersionDefault;
+		
+		#endregion
+	
+		#region serialization
+	
+		protected override void SerializeProperties(JSONWRITER writer)
+		{
+			base.SerializeProperties(writer);
+			SerializeProperty(writer, "dispersion", _dispersion, _dispersionDefault);
+		}
+	
+		protected override void DeserializeProperty(string jsonPropertyName, ref JSONREADER reader)
+		{
+			switch (jsonPropertyName)
+			{
+				case "dispersion": DeserializePropertyValue<MaterialDispersion, Double?>(ref reader, this, out _dispersion); break;
+				default: base.DeserializeProperty(jsonPropertyName,ref reader); break;
+			}
+		}
+	
+		#endregion
+	
+	}
+
+}

+ 13 - 0
src/SharpGLTF.Core/Schema2/gltf.ExtensionsFactory.cs

@@ -40,6 +40,7 @@ namespace SharpGLTF.Schema2
             RegisterExtension<Material, MaterialUnlit>("KHR_materials_unlit", p => new MaterialUnlit(p));
             RegisterExtension<Material, MaterialUnlit>("KHR_materials_unlit", p => new MaterialUnlit(p));
             RegisterExtension<Material, MaterialSheen>("KHR_materials_sheen", p => new MaterialSheen(p));
             RegisterExtension<Material, MaterialSheen>("KHR_materials_sheen", p => new MaterialSheen(p));
             RegisterExtension<Material, MaterialIOR>("KHR_materials_ior", p => new MaterialIOR(p));
             RegisterExtension<Material, MaterialIOR>("KHR_materials_ior", p => new MaterialIOR(p));
+            RegisterExtension<Material, MaterialDispersion>("KHR_materials_dispersion", p => new MaterialDispersion(p));
             RegisterExtension<Material, MaterialSpecular>("KHR_materials_specular", p => new MaterialSpecular(p));
             RegisterExtension<Material, MaterialSpecular>("KHR_materials_specular", p => new MaterialSpecular(p));
             RegisterExtension<Material, MaterialClearCoat>("KHR_materials_clearcoat", p => new MaterialClearCoat(p));
             RegisterExtension<Material, MaterialClearCoat>("KHR_materials_clearcoat", p => new MaterialClearCoat(p));
             RegisterExtension<Material, MaterialTransmission>("KHR_materials_transmission", p => new MaterialTransmission(p));
             RegisterExtension<Material, MaterialTransmission>("KHR_materials_transmission", p => new MaterialTransmission(p));
@@ -174,8 +175,10 @@ namespace SharpGLTF.Schema2
             return null;
             return null;
         }
         }
 
 
+        [System.Diagnostics.DebuggerDisplay("{Name} {ParentType} {ExtType}")]
         internal readonly struct ExtensionEntry
         internal readonly struct ExtensionEntry
         {
         {
+            #region lifecycle
             public static ExtensionEntry Create
             public static ExtensionEntry Create
                 <
                 <
                 TParent,
                 TParent,
@@ -213,11 +216,19 @@ namespace SharpGLTF.Schema2
                 Factory = f;
                 Factory = f;
             }
             }
 
 
+            #endregion
+
+            #region data
+
             public readonly string Name;
             public readonly string Name;
             public readonly Type ParentType;
             public readonly Type ParentType;
             public readonly Type ExtType;
             public readonly Type ExtType;
             public readonly Func<JsonSerializable, JsonSerializable> Factory;
             public readonly Func<JsonSerializable, JsonSerializable> Factory;
 
 
+            #endregion
+
+            #region API
+
             public readonly bool IsMatch(Type parentType, string extensionName)
             public readonly bool IsMatch(Type parentType, string extensionName)
             {
             {
                 return this.ParentType.IsAssignableFrom(parentType) && this.Name == extensionName;
                 return this.ParentType.IsAssignableFrom(parentType) && this.Name == extensionName;
@@ -227,6 +238,8 @@ namespace SharpGLTF.Schema2
             {
             {
                 return this.ParentType.IsAssignableFrom(parentType) && this.ExtType == extensionType;
                 return this.ParentType.IsAssignableFrom(parentType) && this.ExtType == extensionType;
             }
             }
+
+            #endregion
         }
         }
 
 
         #endregion
         #endregion

+ 29 - 3
src/SharpGLTF.Core/Schema2/gltf.Material.cs

@@ -77,8 +77,8 @@ namespace SharpGLTF.Schema2
                 if (this.GetExtension<MaterialUnlit>() != null) return;
                 if (this.GetExtension<MaterialUnlit>() != null) return;
                 if (this.GetExtension<MaterialPBRSpecularGlossiness>() != null) return;
                 if (this.GetExtension<MaterialPBRSpecularGlossiness>() != null) return;
 
 
-                // setting the IOR to its default value essentially
-                // makes the extension unneccesary
+                // setting the IOR to its default value
+                // essentially makes the extension unneccesary.
                 if (value == MaterialIOR.DefaultIndexOfRefraction)
                 if (value == MaterialIOR.DefaultIndexOfRefraction)
                 {
                 {
                     this.RemoveExtensions<MaterialIOR>();
                     this.RemoveExtensions<MaterialIOR>();
@@ -89,10 +89,36 @@ namespace SharpGLTF.Schema2
             }
             }
         }
         }
 
 
+        /// <summary>
+        /// Gets or sets the Dispersion.
+        /// </summary>
+        /// <remarks>
+        /// This property backs KHR_Materials_Dispersion extension.
+        /// </remarks>
+        public float Dispersion
+        {
+            get => this.GetExtension<MaterialDispersion>()?.Dispersion ?? MaterialDispersion.DefaultDispersion;
+            set
+            {
+                if (this.GetExtension<MaterialUnlit>() != null) return;
+                if (this.GetExtension<MaterialPBRSpecularGlossiness>() != null) return;
+
+                // setting the Dispersion to its default value
+                // essentially makes the extension unneccesary.
+                if (value == MaterialDispersion.DefaultDispersion)
+                {
+                    this.RemoveExtensions<MaterialDispersion>();
+                    return;
+                }
+
+                this.UseExtension<MaterialDispersion>().Dispersion = value;
+            }
+        }
+
         #endregion
         #endregion
 
 
         #region API
         #region API
-        
+
         /// <summary>
         /// <summary>
         /// Finds an instance of <see cref="MaterialChannel"/>
         /// Finds an instance of <see cref="MaterialChannel"/>
         /// </summary>
         /// </summary>

+ 25 - 0
src/SharpGLTF.Core/Schema2/gltf.MaterialsFactory.cs

@@ -477,6 +477,31 @@ namespace SharpGLTF.Schema2
         }
         }
     }
     }
 
 
+    /// <remarks>
+    /// Mapped straight away to <see cref="Material.Dispersion"/>
+    /// </remarks>    
+    internal sealed partial class MaterialDispersion
+    {
+        #pragma warning disable CA1801 // Review unused parameters
+        internal MaterialDispersion(Material material) { }
+        #pragma warning restore CA1801 // Review unused parameters
+
+        protected override void OnValidateContent(ValidationContext validate)
+        {
+            base.OnValidateContent(validate);
+            
+            if (_dispersion < _dispersionMinimum) throw new ArgumentOutOfRangeException(nameof(Dispersion));
+        }
+
+        public static float DefaultDispersion => (float)_dispersionDefault;
+
+        public float Dispersion
+        {
+            get => (float)(this._dispersion ?? _dispersionDefault);
+            set => this._dispersion = Math.Max(_dispersionMinimum, value).AsNullable(_dispersionDefault);
+        }
+    }
+
     internal sealed partial class MaterialSpecular
     internal sealed partial class MaterialSpecular
     {
     {
         #pragma warning disable CA1801 // Review unused parameters
         #pragma warning disable CA1801 // Review unused parameters

+ 10 - 4
src/SharpGLTF.Toolkit/Materials/MaterialBuilder.cs

@@ -61,7 +61,8 @@ namespace SharpGLTF.Materials
             this.AlphaCutoff = other.AlphaCutoff;
             this.AlphaCutoff = other.AlphaCutoff;
             this.DoubleSided = other.DoubleSided;
             this.DoubleSided = other.DoubleSided;
             this.ShaderStyle = other.ShaderStyle;
             this.ShaderStyle = other.ShaderStyle;
-            this.IndexOfRefraction = other.IndexOfRefraction;
+            this.Dispersion = other.Dispersion;
+            this.IndexOfRefraction = other.IndexOfRefraction;            
 
 
             this._CompatibilityFallbackMaterial = other._CompatibilityFallbackMaterial == null
             this._CompatibilityFallbackMaterial = other._CompatibilityFallbackMaterial == null
                 ? null
                 ? null
@@ -106,6 +107,8 @@ namespace SharpGLTF.Materials
 
 
         public float IndexOfRefraction { get; set; } = 1.5f;
         public float IndexOfRefraction { get; set; } = 1.5f;
 
 
+        public float Dispersion { get; set; } = 0;
+
         /// <inheritdoc/>
         /// <inheritdoc/>
         /// <remarks>
         /// <remarks>
         /// Material builder uses default class equality, but NUnit4 does some weird trickery on equality
         /// Material builder uses default class equality, but NUnit4 does some weird trickery on equality
@@ -132,7 +135,8 @@ namespace SharpGLTF.Materials
             // AlphaCutoff only has meaning when AlphaMode = Mask
             // AlphaCutoff only has meaning when AlphaMode = Mask
             if (x.AlphaMode == AlphaMode.MASK && x.AlphaCutoff != y.AlphaCutoff) return false;
             if (x.AlphaMode == AlphaMode.MASK && x.AlphaCutoff != y.AlphaCutoff) return false;
             if (x.DoubleSided != y.DoubleSided) return false;
             if (x.DoubleSided != y.DoubleSided) return false;
-            if (x.IndexOfRefraction != y.IndexOfRefraction) return false;
+            if (x.Dispersion != y.Dispersion) return false;
+            if (x.IndexOfRefraction != y.IndexOfRefraction) return false;            
             if (x._ShaderStyle != y._ShaderStyle) return false;
             if (x._ShaderStyle != y._ShaderStyle) return false;
 
 
             if (!AreEqualByContent(x._CompatibilityFallbackMaterial, y._CompatibilityFallbackMaterial)) return false;
             if (!AreEqualByContent(x._CompatibilityFallbackMaterial, y._CompatibilityFallbackMaterial)) return false;
@@ -164,7 +168,8 @@ namespace SharpGLTF.Materials
             h ^= x.AlphaMode.GetHashCode();
             h ^= x.AlphaMode.GetHashCode();
             h ^= x.AlphaCutoff.GetHashCode();
             h ^= x.AlphaCutoff.GetHashCode();
             h ^= x.DoubleSided.GetHashCode();
             h ^= x.DoubleSided.GetHashCode();
-            h ^= x.IndexOfRefraction.GetHashCode();
+            h ^= x.Dispersion.GetHashCode();
+            h ^= x.IndexOfRefraction.GetHashCode();            
             h ^= x.ShaderStyle.GetHashCode(StringComparison.InvariantCulture);
             h ^= x.ShaderStyle.GetHashCode(StringComparison.InvariantCulture);
 
 
             h ^= x._Channels
             h ^= x._Channels
@@ -285,7 +290,8 @@ namespace SharpGLTF.Materials
                     Guard.IsTrue(this.AlphaMode == this.CompatibilityFallback.AlphaMode, nameof(AlphaMode));
                     Guard.IsTrue(this.AlphaMode == this.CompatibilityFallback.AlphaMode, nameof(AlphaMode));
                     Guard.MustBeEqualTo(this.AlphaCutoff, this.CompatibilityFallback.AlphaCutoff, nameof(AlphaCutoff));
                     Guard.MustBeEqualTo(this.AlphaCutoff, this.CompatibilityFallback.AlphaCutoff, nameof(AlphaCutoff));
                     Guard.MustBeEqualTo(this.DoubleSided, this.CompatibilityFallback.DoubleSided, nameof(DoubleSided));
                     Guard.MustBeEqualTo(this.DoubleSided, this.CompatibilityFallback.DoubleSided, nameof(DoubleSided));
-                    Guard.MustBeEqualTo(this.IndexOfRefraction, this.CompatibilityFallback.IndexOfRefraction, nameof(IndexOfRefraction));
+                    Guard.MustBeEqualTo(this.Dispersion, this.CompatibilityFallback.Dispersion, nameof(Dispersion));
+                    Guard.MustBeEqualTo(this.IndexOfRefraction, this.CompatibilityFallback.IndexOfRefraction, nameof(IndexOfRefraction));                    
 
 
                     foreach (var chKey in new[] { KnownChannel.Normal, KnownChannel.Occlusion, KnownChannel.Emissive })
                     foreach (var chKey in new[] { KnownChannel.Normal, KnownChannel.Occlusion, KnownChannel.Emissive })
                     {
                     {

+ 7 - 0
src/SharpGLTF.Toolkit/Schema2/MaterialExtensions.cs

@@ -310,7 +310,9 @@ namespace SharpGLTF.Schema2
             dstMaterial.AlphaMode = srcMaterial.Alpha.ToToolkit();
             dstMaterial.AlphaMode = srcMaterial.Alpha.ToToolkit();
             dstMaterial.AlphaCutoff = srcMaterial.AlphaCutoff;
             dstMaterial.AlphaCutoff = srcMaterial.AlphaCutoff;
             dstMaterial.DoubleSided = srcMaterial.DoubleSided;
             dstMaterial.DoubleSided = srcMaterial.DoubleSided;
+            dstMaterial.Dispersion = srcMaterial.Dispersion;
             dstMaterial.IndexOfRefraction = srcMaterial.IndexOfRefraction;
             dstMaterial.IndexOfRefraction = srcMaterial.IndexOfRefraction;
+            
 
 
             srcMaterial.CopyChannelsTo(dstMaterial, "Normal", "Occlusion", "Emissive");
             srcMaterial.CopyChannelsTo(dstMaterial, "Normal", "Occlusion", "Emissive");
         }
         }
@@ -457,6 +459,10 @@ namespace SharpGLTF.Schema2
                 defMaterial = srcMaterial.CompatibilityFallback;
                 defMaterial = srcMaterial.CompatibilityFallback;
             }
             }
 
 
+            // Dispersion must be set after dst material initialization,
+            // otherwise it's erased since it's stored in an extension
+            dstMaterial.Dispersion = srcMaterial.Dispersion;
+
             // IOR must be set after dst material initialization,
             // IOR must be set after dst material initialization,
             // otherwise it's erased since it's stored in an extension
             // otherwise it's erased since it's stored in an extension
             dstMaterial.IndexOfRefraction = srcMaterial.IndexOfRefraction;
             dstMaterial.IndexOfRefraction = srcMaterial.IndexOfRefraction;
@@ -477,6 +483,7 @@ namespace SharpGLTF.Schema2
 
 
             // final validation
             // final validation
 
 
+            System.Diagnostics.Debug.Assert(dstMaterial.Dispersion == srcMaterial.Dispersion, "set Dispersion after dst material initialization");
             System.Diagnostics.Debug.Assert(dstMaterial.IndexOfRefraction == srcMaterial.IndexOfRefraction, "set IOR after dst material initialization");            
             System.Diagnostics.Debug.Assert(dstMaterial.IndexOfRefraction == srcMaterial.IndexOfRefraction, "set IOR after dst material initialization");            
         }
         }
 
 

+ 2 - 0
tests/SharpGLTF.Toolkit.Tests/Materials/MaterialBuilderTests.cs

@@ -110,6 +110,8 @@ namespace SharpGLTF.Materials
             // https://github.com/vpenades/SharpGLTF/issues/246
             // https://github.com/vpenades/SharpGLTF/issues/246
             material.IndexOfRefraction = 7;
             material.IndexOfRefraction = 7;
 
 
+            material.Dispersion = 2;
+
             Assert.That(MaterialBuilder.AreEqualByContent(material, _Schema2Roundtrip(material)));
             Assert.That(MaterialBuilder.AreEqualByContent(material, _Schema2Roundtrip(material)));
             Assert.That(MaterialBuilder.AreEqualByContent(material, material.Clone()));
             Assert.That(MaterialBuilder.AreEqualByContent(material, material.Clone()));