浏览代码

improving API dump
+progress with materials API

Vicente Penades 6 年之前
父节点
当前提交
8626b75f48

+ 22 - 22
src/SharpGLTF.Core/Schema2/gltf.MaterialChannel.cs

@@ -36,7 +36,7 @@ namespace SharpGLTF.Schema2
             _AmountGetter = () => texInfo(false)?.Amount ?? 1;
             _AmountGetter = () => texInfo(false)?.Amount ?? 1;
             _AmountSetter = val => texInfo(true).Amount = val;
             _AmountSetter = val => texInfo(true).Amount = val;
 
 
-            IsTextureAmountSupported = true;
+            IsAmountSupported = true;
             IsColorSupported = false;
             IsColorSupported = false;
         }
         }
 
 
@@ -60,7 +60,7 @@ namespace SharpGLTF.Schema2
             _AmountGetter = agetter;
             _AmountGetter = agetter;
             _AmountSetter = asetter;
             _AmountSetter = asetter;
 
 
-            IsTextureAmountSupported = true;
+            IsAmountSupported = true;
             IsColorSupported = false;
             IsColorSupported = false;
         }
         }
 
 
@@ -85,7 +85,7 @@ namespace SharpGLTF.Schema2
             _AmountGetter = () => texInfo(false)?.Amount ?? 1;
             _AmountGetter = () => texInfo(false)?.Amount ?? 1;
             _AmountSetter = val => texInfo(true).Amount = val;
             _AmountSetter = val => texInfo(true).Amount = val;
 
 
-            IsTextureAmountSupported = false;
+            IsAmountSupported = false;
             IsColorSupported = true;
             IsColorSupported = true;
         }
         }
 
 
@@ -108,7 +108,7 @@ namespace SharpGLTF.Schema2
             _AmountGetter = agetter;
             _AmountGetter = agetter;
             _AmountSetter = asetter;
             _AmountSetter = asetter;
 
 
-            IsTextureAmountSupported = true;
+            IsAmountSupported = true;
             IsColorSupported = false;
             IsColorSupported = false;
         }
         }
 
 
@@ -131,7 +131,7 @@ namespace SharpGLTF.Schema2
             _AmountGetter = () => 1;
             _AmountGetter = () => 1;
             _AmountSetter = val => { };
             _AmountSetter = val => { };
 
 
-            IsTextureAmountSupported = false;
+            IsAmountSupported = false;
             IsColorSupported = true;
             IsColorSupported = true;
         }
         }
 
 
@@ -158,6 +158,23 @@ namespace SharpGLTF.Schema2
 
 
         public String Key => _Key;
         public String Key => _Key;
 
 
+        /// <summary>
+        /// Gets a value indicating whether this channel supports amount factor.
+        /// </summary>
+        public bool IsAmountSupported { get; private set; }
+
+        /// <summary>
+        /// Gets or sets the Texture weight in the final shader.
+        /// </summary>
+        /// <remarks>
+        /// Not all channels support this property.
+        /// </remarks>
+        public Single Amount
+        {
+            get => _AmountGetter();
+            set => _AmountSetter(value);
+        }
+
         /// <summary>
         /// <summary>
         /// Gets a value indicating whether this channel supports a Color factor.
         /// Gets a value indicating whether this channel supports a Color factor.
         /// </summary>
         /// </summary>
@@ -188,23 +205,6 @@ namespace SharpGLTF.Schema2
 
 
         public TextureSampler TextureSampler => Texture?.Sampler;
         public TextureSampler TextureSampler => Texture?.Sampler;
 
 
-        /// <summary>
-        /// Gets a value indicating whether this channel supports texture amount factor.
-        /// </summary>
-        public bool IsTextureAmountSupported { get; private set; }
-
-        /// <summary>
-        /// Gets or sets the Texture weight in the final shader.
-        /// </summary>
-        /// <remarks>
-        /// Not all channels support this property.
-        /// </remarks>
-        public Single TextureAmount
-        {
-            get => _AmountGetter();
-            set => _AmountSetter(value);
-        }
-
         #endregion
         #endregion
 
 
         #region API
         #region API

+ 11 - 2
src/SharpGLTF.Core/Schema2/gltf.MaterialsFactory.cs

@@ -15,7 +15,7 @@ namespace SharpGLTF.Schema2
         /// </summary>
         /// </summary>
         public void InitializePBRMetallicRoughness()
         public void InitializePBRMetallicRoughness()
         {
         {
-            this._pbrMetallicRoughness = new MaterialPBRMetallicRoughness();
+            if (this._pbrMetallicRoughness == null) this._pbrMetallicRoughness = new MaterialPBRMetallicRoughness();
 
 
             this.RemoveExtensions<MaterialPBRSpecularGlossiness>();
             this.RemoveExtensions<MaterialPBRSpecularGlossiness>();
             this.RemoveExtensions<MaterialUnlit>();
             this.RemoveExtensions<MaterialUnlit>();
@@ -24,8 +24,15 @@ namespace SharpGLTF.Schema2
         /// <summary>
         /// <summary>
         /// Initializes this <see cref="Material"/> instance with PBR Specular Glossiness attributes.
         /// Initializes this <see cref="Material"/> instance with PBR Specular Glossiness attributes.
         /// </summary>
         /// </summary>
-        public void InitializePBRSpecularGlossiness()
+        /// <param name="useFallback">true to add a PBRMetallicRoughness fallback material.</param>
+        public void InitializePBRSpecularGlossiness(bool useFallback = false)
         {
         {
+            if (useFallback)
+            {
+                if (this._pbrMetallicRoughness == null) this._pbrMetallicRoughness = new MaterialPBRMetallicRoughness();
+            }
+            else this._pbrMetallicRoughness = null;
+
             this.RemoveExtensions<MaterialUnlit>();
             this.RemoveExtensions<MaterialUnlit>();
             this.SetExtension(new MaterialPBRSpecularGlossiness(this));
             this.SetExtension(new MaterialPBRSpecularGlossiness(this));
         }
         }
@@ -35,6 +42,8 @@ namespace SharpGLTF.Schema2
         /// </summary>
         /// </summary>
         public void InitializeUnlit()
         public void InitializeUnlit()
         {
         {
+            if (this._pbrMetallicRoughness == null) this._pbrMetallicRoughness = new MaterialPBRMetallicRoughness();
+
             this.RemoveExtensions<MaterialPBRSpecularGlossiness>();
             this.RemoveExtensions<MaterialPBRSpecularGlossiness>();
             this.SetExtension(new MaterialUnlit(this));
             this.SetExtension(new MaterialUnlit(this));
         }
         }

+ 1 - 1
src/SharpGLTF.Toolkit/IO/WavefrontWriter.cs

@@ -231,7 +231,7 @@ namespace SharpGLTF.IO
         {
         {
             foreach (var triangle in Schema2.Schema2Toolkit.Triangulate<POSITION, TEXCOORD, VEMPTY>(model.DefaultScene))
             foreach (var triangle in Schema2.Schema2Toolkit.Triangulate<POSITION, TEXCOORD, VEMPTY>(model.DefaultScene))
             {
             {
-                var dstMaterial = new Material();
+                var dstMaterial = default(Material);
 
 
                 var srcMaterial = triangle.Item4;
                 var srcMaterial = triangle.Item4;
                 if (srcMaterial != null)
                 if (srcMaterial != null)

+ 8 - 4
src/SharpGLTF.Toolkit/Materials/Channel.cs

@@ -5,17 +5,19 @@ using System.Text;
 
 
 namespace SharpGLTF.Materials
 namespace SharpGLTF.Materials
 {
 {
-    [System.Diagnostics.DebuggerDisplay("{_Key} {Factor}")]
+    [System.Diagnostics.DebuggerDisplay("{_Key} {Amount}")]
     public class MaterialChannelBuilder
     public class MaterialChannelBuilder
     {
     {
         #region lifecycle
         #region lifecycle
 
 
-        internal MaterialChannelBuilder(string key) { _Key = key; }
+        internal MaterialChannelBuilder(MaterialBuilder parent, string key) { _Parent = parent; _Key = key; }
 
 
         #endregion
         #endregion
 
 
         #region data
         #region data
 
 
+        private readonly MaterialBuilder _Parent;
+
         private readonly String _Key;
         private readonly String _Key;
 
 
         #endregion
         #endregion
@@ -24,9 +26,11 @@ namespace SharpGLTF.Materials
 
 
         public String Key => _Key;
         public String Key => _Key;
 
 
+        public Single Amount { get; set; } = 1;
+
         public Vector4 Color { get; set; } = Vector4.One;
         public Vector4 Color { get; set; } = Vector4.One;
 
 
-        public TextureBuilder Texture { get; set; }
+        public TextureBuilder Texture { get; private set; }
 
 
         #endregion
         #endregion
 
 
@@ -34,7 +38,7 @@ namespace SharpGLTF.Materials
 
 
         public TextureBuilder UseTexture()
         public TextureBuilder UseTexture()
         {
         {
-            if (Texture == null) Texture = new TextureBuilder();
+            if (Texture == null) Texture = new TextureBuilder(this);
             return Texture;
             return Texture;
         }
         }
 
 

+ 3 - 5
src/SharpGLTF.Toolkit/Materials/Material.cs

@@ -8,7 +8,7 @@ namespace SharpGLTF.Materials
 {
 {
     using ALPHA = Schema2.AlphaMode;
     using ALPHA = Schema2.AlphaMode;
 
 
-    [System.Diagnostics.DebuggerDisplay("{Name} {Style}")]
+    [System.Diagnostics.DebuggerDisplay("{Name} {ShaderStyle}")]
     public class MaterialBuilder
     public class MaterialBuilder
     {
     {
         #region lifecycle
         #region lifecycle
@@ -40,9 +40,7 @@ namespace SharpGLTF.Materials
 
 
         public Boolean DoubleSided { get; set; } = false;
         public Boolean DoubleSided { get; set; } = false;
 
 
-        public Boolean Unlit { get; set; } = false;
-
-        public String Style { get; set; } = "PBRMetallicRoughness";
+        public String ShaderStyle { get; set; } = "PBRMetallicRoughness";
 
 
         public MaterialBuilder CompatibilityFallback
         public MaterialBuilder CompatibilityFallback
         {
         {
@@ -83,7 +81,7 @@ namespace SharpGLTF.Materials
 
 
             if (ch != null) return ch;
             if (ch != null) return ch;
 
 
-            ch = new MaterialChannelBuilder(channelKey);
+            ch = new MaterialChannelBuilder(this, channelKey);
             _Channels.Add(ch);
             _Channels.Add(ch);
 
 
             return ch;
             return ch;

+ 7 - 3
src/SharpGLTF.Toolkit/Materials/Texture.cs

@@ -16,7 +16,13 @@ namespace SharpGLTF.Materials
     {
     {
         #region lifecycle
         #region lifecycle
 
 
-        internal TextureBuilder() { }
+        internal TextureBuilder(MaterialChannelBuilder parent) { _Parent = parent; }
+
+        #endregion
+
+        #region data
+
+        private readonly MaterialChannelBuilder _Parent;
 
 
         #endregion
         #endregion
 
 
@@ -32,8 +38,6 @@ namespace SharpGLTF.Materials
         public Vector2 Scale { get; set; } = Vector2.One;
         public Vector2 Scale { get; set; } = Vector2.One;
         */
         */
 
 
-        public Single Amount { get; set; } = 1;
-
         public BYTES ImageContent { get; set; }
         public BYTES ImageContent { get; set; }
 
 
         public TEXMIPMAP MinFilter { get; set; } = TEXMIPMAP.DEFAULT;
         public TEXMIPMAP MinFilter { get; set; } = TEXMIPMAP.DEFAULT;

+ 46 - 19
src/SharpGLTF.Toolkit/Schema2/MaterialExtensions.cs

@@ -63,7 +63,7 @@ namespace SharpGLTF.Schema2
 
 
             channel.SetTexture(textureSet, image);
             channel.SetTexture(textureSet, image);
 
 
-            channel.TextureAmount = amount;
+            channel.Amount = amount;
 
 
             return material;
             return material;
         }
         }
@@ -138,6 +138,8 @@ namespace SharpGLTF.Schema2
         /// <returns>A <see cref="Image"/> instance.</returns>
         /// <returns>A <see cref="Image"/> instance.</returns>
         public static Image UseImageWithContent(this ModelRoot root, Byte[] imageContent)
         public static Image UseImageWithContent(this ModelRoot root, Byte[] imageContent)
         {
         {
+            Guard.NotNullOrEmpty(imageContent, nameof(imageContent));
+
             foreach (var img in root.LogicalImages)
             foreach (var img in root.LogicalImages)
             {
             {
                 var existingContent = img.GetImageContent();
                 var existingContent = img.GetImageContent();
@@ -166,17 +168,24 @@ namespace SharpGLTF.Schema2
 
 
         #region transfer API
         #region transfer API
 
 
-        public static void CopyTo(this Material srcMaterial, Materials.MaterialBuilder mb)
+        public static void CopyTo(this Material srcMaterial, Materials.MaterialBuilder dstMaterial)
         {
         {
-            mb.Name = srcMaterial.Name;
-            mb.Unlit = srcMaterial.Unlit;
-            mb.AlphaMode = srcMaterial.Alpha;
-            mb.AlphaCutoff = srcMaterial.AlphaCutoff;
-            mb.DoubleSided = srcMaterial.DoubleSided;
+            dstMaterial.Name = srcMaterial.Name;
+            dstMaterial.AlphaMode = srcMaterial.Alpha;
+            dstMaterial.AlphaCutoff = srcMaterial.AlphaCutoff;
+            dstMaterial.DoubleSided = srcMaterial.DoubleSided;
+
+            if (srcMaterial.Unlit) dstMaterial.ShaderStyle = "Unlit";
+
+            if (srcMaterial.FindChannel("Diffuse") != null || srcMaterial.FindChannel("Glossiness") != null)
+            {
+                dstMaterial.ShaderStyle = "PBRSpecularGlossiness";
+                // TODO: create fallback material
+            }
 
 
             foreach (var channel in srcMaterial.Channels)
             foreach (var channel in srcMaterial.Channels)
             {
             {
-                var ch = mb.UseChannel(channel.Key);
+                var ch = dstMaterial.UseChannel(channel.Key);
                 channel.CopyTo(ch);
                 channel.CopyTo(ch);
             }
             }
         }
         }
@@ -184,12 +193,11 @@ namespace SharpGLTF.Schema2
         public static void CopyTo(this MaterialChannel srcChannel, Materials.MaterialChannelBuilder dstChannel)
         public static void CopyTo(this MaterialChannel srcChannel, Materials.MaterialChannelBuilder dstChannel)
         {
         {
             dstChannel.Color = srcChannel.Color;
             dstChannel.Color = srcChannel.Color;
+            dstChannel.Amount = srcChannel.Amount;
 
 
-            if (srcChannel.Texture == null) { dstChannel.Texture = null; return; }
-
-            if (dstChannel.Texture == null) dstChannel.Texture = new Materials.TextureBuilder();
+            if (srcChannel.Texture == null) { return; }
 
 
-            dstChannel.Texture.Amount = srcChannel.TextureAmount;
+            if (dstChannel.Texture == null) dstChannel.UseTexture();
 
 
             dstChannel.Texture.CoordinateSet = srcChannel.TextureCoordinate;
             dstChannel.Texture.CoordinateSet = srcChannel.TextureCoordinate;
             dstChannel.Texture.MinFilter = srcChannel.TextureSampler.MinFilter;
             dstChannel.Texture.MinFilter = srcChannel.TextureSampler.MinFilter;
@@ -218,21 +226,40 @@ namespace SharpGLTF.Schema2
             srcMaterial.GetChannel("Occlusion").CopyTo(dstMaterial.FindChannel("Occlusion").Value);
             srcMaterial.GetChannel("Occlusion").CopyTo(dstMaterial.FindChannel("Occlusion").Value);
             srcMaterial.GetChannel("Emissive").CopyTo(dstMaterial.FindChannel("Emissive").Value);
             srcMaterial.GetChannel("Emissive").CopyTo(dstMaterial.FindChannel("Emissive").Value);
 
 
-            if (srcMaterial.Style == "PBRMetallicRoughness")
+            Materials.MaterialBuilder defMaterial = null;
+
+            if (srcMaterial.ShaderStyle == "Unlit")
             {
             {
                 dstMaterial.InitializePBRMetallicRoughness();
                 dstMaterial.InitializePBRMetallicRoughness();
-
                 srcMaterial.GetChannel("BaseColor").CopyTo(dstMaterial.FindChannel("BaseColor").Value);
                 srcMaterial.GetChannel("BaseColor").CopyTo(dstMaterial.FindChannel("BaseColor").Value);
-                srcMaterial.GetChannel("Metallic").CopyTo(dstMaterial.FindChannel("Metallic").Value);
-                srcMaterial.GetChannel("Roughness").CopyTo(dstMaterial.FindChannel("Roughness").Value);
+                return;
+            }
+
+            if (srcMaterial.ShaderStyle == "PBRMetallicRoughness")
+            {
+                dstMaterial.InitializePBRMetallicRoughness();
+
+                defMaterial = srcMaterial;
             }
             }
-            else if (srcMaterial.Style == "PBRSpecularGlossiness")
+
+            if (srcMaterial.ShaderStyle == "PBRSpecularGlossiness")
             {
             {
-                dstMaterial.InitializePBRSpecularGlossiness();
+                dstMaterial.InitializePBRSpecularGlossiness(srcMaterial.CompatibilityFallback != null);
 
 
                 srcMaterial.GetChannel("Diffuse").CopyTo(dstMaterial.FindChannel("Diffuse").Value);
                 srcMaterial.GetChannel("Diffuse").CopyTo(dstMaterial.FindChannel("Diffuse").Value);
                 srcMaterial.GetChannel("Specular").CopyTo(dstMaterial.FindChannel("Specular").Value);
                 srcMaterial.GetChannel("Specular").CopyTo(dstMaterial.FindChannel("Specular").Value);
                 srcMaterial.GetChannel("Glossiness").CopyTo(dstMaterial.FindChannel("Glossiness").Value);
                 srcMaterial.GetChannel("Glossiness").CopyTo(dstMaterial.FindChannel("Glossiness").Value);
+
+                defMaterial = srcMaterial.CompatibilityFallback;
+            }
+
+            if (defMaterial != null)
+            {
+                if (defMaterial.ShaderStyle != "PBRMetallicRoughness") throw new ArgumentException(nameof(srcMaterial.CompatibilityFallback.ShaderStyle));
+
+                defMaterial.GetChannel("BaseColor").CopyTo(dstMaterial.FindChannel("BaseColor").Value);
+                defMaterial.GetChannel("Metallic").CopyTo(dstMaterial.FindChannel("Metallic").Value);
+                defMaterial.GetChannel("Roughness").CopyTo(dstMaterial.FindChannel("Roughness").Value);
             }
             }
         }
         }
 
 
@@ -241,6 +268,7 @@ namespace SharpGLTF.Schema2
             if (srcChannel == null) return;
             if (srcChannel == null) return;
 
 
             dstChannel.Color = srcChannel.Color;
             dstChannel.Color = srcChannel.Color;
+            dstChannel.Amount = srcChannel.Amount;
 
 
             var srcTex = srcChannel.Texture;
             var srcTex = srcChannel.Texture;
 
 
@@ -281,7 +309,6 @@ namespace SharpGLTF.Schema2
             if (channel.HasValue) return channel.Value.Texture;
             if (channel.HasValue) return channel.Value.Texture;
 
 
             return null;
             return null;
-
         }
         }
 
 
         #endregion
         #endregion

+ 24 - 0
tests/SharpGLTF.Tests/AssemblyAPITests.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Reflection;
 using System.Text;
 using System.Text;
 
 
 using NUnit.Framework;
 using NUnit.Framework;
@@ -10,6 +11,29 @@ namespace SharpGLTF
     [TestFixture]
     [TestFixture]
     public class AssemblyAPITests
     public class AssemblyAPITests
     {
     {
+        public class TestClass
+        {
+            public int xyz(int x, out int y, ref int z) { y = 0; return x + 7; }
+        }
+
+        [Test]
+        public void DumpTestAPI()
+        {
+            TestContext.CurrentContext.AttachShowDirLink();
+
+            var type = typeof(TestClass);
+
+            var API = DumpAssemblyAPI.GetTypeSignature(type.GetTypeInfo()).OrderBy(item => item).ToArray();
+
+            TestContext.CurrentContext.AttachText("TestAPI.txt", API);
+
+            foreach (var l in API)
+            {
+                TestContext.WriteLine(l);
+            }
+        }
+
+
         [Test]
         [Test]
         public void DumpCoreAPI()
         public void DumpCoreAPI()
         {
         {

+ 72 - 55
tests/SharpGLTF.Tests/DumpAssemblyAPI.cs

@@ -40,25 +40,25 @@ namespace SharpGLTF
             if (tinfo.IsNestedPrivate) yield break;
             if (tinfo.IsNestedPrivate) yield break;
             if (tinfo.IsNestedAssembly) yield break;
             if (tinfo.IsNestedAssembly) yield break;
 
 
-            string baseName = string.Empty;
+            string baseName = string.Empty;            
 
 
-            if (tinfo.IsNestedFamily) baseName += "PROTECTED";                        
-
-            if (tinfo.IsInterface) baseName += "INTERFACE";
-            else if (tinfo.IsEnum) baseName += "ENUM";
-            else if (tinfo.IsValueType) baseName += "STRUCT";
+            if (tinfo.IsInterface) baseName = "INTERFACE";
+            else if (tinfo.IsEnum) baseName = "ENUM";
+            else if (tinfo.IsValueType) baseName = "STRUCT";
             else if (tinfo.IsClass)
             else if (tinfo.IsClass)
             {
             {
-                if (tinfo.IsSealed && tinfo.IsAbstract) baseName += "STATIC";
+                baseName = "CLASS";
+
+                if (tinfo.IsSealed && tinfo.IsAbstract) baseName += ":STATIC";
                 else
                 else
                 {
                 {
-                    if (tinfo.IsSealed) baseName += "SEALED";
-                    if (tinfo.IsAbstract) baseName += "ABSTRACT";
+                    if (tinfo.IsSealed) baseName += ":SEALED";
+                    if (tinfo.IsAbstract) baseName += ":ABSTRACT";
                 }
                 }
-
-                baseName += "CLASS";
             }
             }
 
 
+            if (tinfo.IsNestedFamily) baseName += ":PROTECTED";
+
             baseName += " " + tinfo.GetQualifiedName();
             baseName += " " + tinfo.GetQualifiedName();
 
 
             if (tinfo.IsClass)
             if (tinfo.IsClass)
@@ -115,24 +115,22 @@ namespace SharpGLTF
         {
         {
             if (!IsVisible(finfo)) yield break;
             if (!IsVisible(finfo)) yield break;
 
 
-            var name = string.Empty;
+            var name = "FIELD";
 
 
-            if (finfo.IsLiteral) name += "CONST";
-            else name += "FIELD";
+            if (finfo.IsLiteral) name += ":CONST";
+            if (finfo.IsStatic) name += ":STATIC";
 
 
             name += $" {finfo.Name} {finfo.FieldType.GetQualifiedName()}";
             name += $" {finfo.Name} {finfo.FieldType.GetQualifiedName()}";
 
 
             if (finfo.IsStatic)
             if (finfo.IsStatic)
             {
             {
-                name = "STATIC" + name;
-
                 var v = finfo.GetValue(null);
                 var v = finfo.GetValue(null);
-                if (v != null) name += Invariant($"= {v}");
+                if (v != null) name += Invariant($"={v}");
             }
             }
             else if (instance != null)
             else if (instance != null)
             {
             {
                 var v = finfo.GetValue(instance);
                 var v = finfo.GetValue(instance);
-                if (v != null) name += Invariant($"= {v}");
+                if (v != null) name += Invariant($"={v}");
             }
             }
 
 
             yield return name;
             yield return name;
@@ -143,29 +141,38 @@ namespace SharpGLTF
             var pname = $"{pinfo.Name} {pinfo.PropertyType.GetQualifiedName()}";
             var pname = $"{pinfo.Name} {pinfo.PropertyType.GetQualifiedName()}";
 
 
             var getter = pinfo.GetGetMethod();
             var getter = pinfo.GetGetMethod();
-            if (IsVisible(getter,true)) yield return GetMethodModifiers(getter)+ "PROPERTYGET " + pname;
+            if (IsVisible(getter,true)) yield return "METHOD:GET"+ GetMethodModifiers(getter) + " " + pname;
 
 
             var setter = pinfo.GetSetMethod();
             var setter = pinfo.GetSetMethod();
-            if (IsVisible(setter, true)) yield return GetMethodModifiers(getter) + "PROPERTYSET " + pname;
+            if (IsVisible(setter, true)) yield return "METHOD:SET"+ GetMethodModifiers(setter) +" " + pname;
         }
         }
 
 
         public static IEnumerable<string> GetMethodSignature(Object instance, MethodBase minfo)
         public static IEnumerable<string> GetMethodSignature(Object instance, MethodBase minfo)
         {
         {
-            // TODO: if parameters have default values, dump the same method multiple times with one parameter less each time.            
+            // TODO: if parameters have default values, dump the same method multiple times with one parameter less each time.
 
 
-            var mname = GetMethodModifiers(minfo);
+            string mname = "METHOD";
 
 
             if (minfo is MethodInfo mminfo)
             if (minfo is MethodInfo mminfo)
             {
             {
+                var isExtension = mminfo.IsDefined(typeof(System.Runtime.CompilerServices.ExtensionAttribute), true);
+
+                if (isExtension) mname += ":EXTENSION";
+                mname += GetMethodModifiers(minfo);
+
+                mname += " ";
+
                 if (!IsVisible(minfo)) yield break;
                 if (!IsVisible(minfo)) yield break;
-                mname += "METHOD " + mminfo.Name + $" {mminfo.ReturnType.GetQualifiedName()} ";
+                mname += mminfo.Name + $" {mminfo.ReturnType.GetQualifiedName()}";
             }
             }
 
 
             if (minfo is ConstructorInfo cinfo)
             if (minfo is ConstructorInfo cinfo)
             {
             {
                 if (!IsVisible(minfo,true)) yield break;
                 if (!IsVisible(minfo,true)) yield break;
-                mname += "CONSTRUCTOR ";
-            }             
+                mname += ":CONSTRUCTOR";
+            }
+
+            mname += " ";
 
 
             var mparams = minfo.GetParameters()
             var mparams = minfo.GetParameters()
                 .Select(item => GetParameterSignature(item))
                 .Select(item => GetParameterSignature(item))
@@ -176,9 +183,18 @@ namespace SharpGLTF
 
 
         public static string GetParameterSignature(ParameterInfo pinfo)
         public static string GetParameterSignature(ParameterInfo pinfo)
         {
         {
-            var isParams = pinfo.GetCustomAttribute(typeof(ParamArrayAttribute)) != null;
+            var prefix = string.Empty;
+
+            if (pinfo.GetCustomAttribute(typeof(ParamArrayAttribute)) != null) prefix += "params ";
 
 
-            return (isParams ? "params " : "") + pinfo.ParameterType.GetQualifiedName();
+            if (pinfo.ParameterType.IsByRef)
+            {
+                if (pinfo.IsIn) prefix += "in ";
+                else if (pinfo.IsOut) prefix += "out ";
+                else prefix += "ref ";
+            }
+
+            return prefix + pinfo.ParameterType.GetQualifiedName();
         }
         }
 
 
         public static bool IsVisible(FieldInfo finfo)
         public static bool IsVisible(FieldInfo finfo)
@@ -203,7 +219,7 @@ namespace SharpGLTF
             if (minfo.IsFamilyOrAssembly) return false;
             if (minfo.IsFamilyOrAssembly) return false;
 
 
             return true;
             return true;
-        }
+        }        
 
 
         public static string GetMethodModifiers(MethodBase minfo)
         public static string GetMethodModifiers(MethodBase minfo)
         {
         {
@@ -211,11 +227,11 @@ namespace SharpGLTF
 
 
             var mod = string.Empty;
             var mod = string.Empty;
 
 
-            if (minfo.IsFamily) mod += "PROTECTED";
-            if (minfo.IsStatic) mod += "STATIC";            
+            if (minfo.IsFamily) mod += ":PROTECTED";
+            if (minfo.IsStatic) mod += ":STATIC";            
 
 
-            if (minfo.IsAbstract) mod += "ABSTRACT";
-            else if (minfo.IsVirtual) mod += "VIRTUAL";
+            if (minfo.IsAbstract) mod += ":ABSTRACT";
+            else if (minfo.IsVirtual) mod += ":VIRTUAL";
 
 
             return mod;
             return mod;
         }
         }
@@ -227,37 +243,38 @@ namespace SharpGLTF
 
 
         public static string GetQualifiedName(this System.Reflection.TypeInfo tinfo)
         public static string GetQualifiedName(this System.Reflection.TypeInfo tinfo)
         {
         {
-            var name = tinfo.Name;
-
             if (tinfo.IsArray)
             if (tinfo.IsArray)
             {
             {
-                var fname = tinfo.GetElementType().GetQualifiedName();
-                fname += "[" + string.Join("", Enumerable.Repeat(",", tinfo.GetArrayRank() - 1)) + "]";                
-
-                return fname; 
+                var itemName = tinfo.GetElementType().GetQualifiedName();
+                itemName += "[" + string.Join("", Enumerable.Repeat(",", tinfo.GetArrayRank() - 1)) + "]";
+                return itemName;
             }
             }
 
 
-            if (!tinfo.IsGenericType && !tinfo.IsGenericTypeDefinition) return tinfo.Name;            
-
-            name = name.Replace("`1","");
-            name = name.Replace("`2", "");
-            name = name.Replace("`3", "");
-            name = name.Replace("`4", "");
-            name = name.Replace("`5", "");
-            name = name.Replace("`6", "");
-            name = name.Replace("`7", "");
-            name = name.Replace("`8", "");
+            var name = tinfo.Name;
 
 
-            var gpm = tinfo
-                .GenericTypeArguments
-                .Concat(tinfo.GenericTypeParameters)
-                .Select(item => GetQualifiedName(item.GetTypeInfo()))
-                .ToList();
+            name = name.Replace("&", ""); // remove PTR semantics
 
 
-            if (gpm.Count > 0) name += "<" + string.Join(", ", gpm) + ">";
+            if (tinfo.IsGenericType || tinfo.IsGenericTypeDefinition)
+            {
+                name = name.Replace("`1", "");
+                name = name.Replace("`2", "");
+                name = name.Replace("`3", "");
+                name = name.Replace("`4", "");
+                name = name.Replace("`5", "");
+                name = name.Replace("`6", "");
+                name = name.Replace("`7", "");
+                name = name.Replace("`8", "");
+
+                var gpm = tinfo
+                    .GenericTypeArguments
+                    .Concat(tinfo.GenericTypeParameters)
+                    .Select(item => GetQualifiedName(item))
+                    .ToList();
+
+                if (gpm.Count > 0) name += "<" + string.Join(",", gpm) + ">";                
+            }
 
 
             return name;
             return name;
-            
         }
         }
     }
     }
 }
 }

+ 47 - 1
tests/SharpGLTF.Tests/Schema2/Authoring/BasicSceneCreationTests.cs

@@ -8,7 +8,10 @@ using NUnit.Framework;
 
 
 namespace SharpGLTF.Schema2.Authoring
 namespace SharpGLTF.Schema2.Authoring
 {
 {
-    using VPOSNRM = Geometry.VertexTypes.VertexPositionNormal;    
+    using VPOS = Geometry.VertexTypes.VertexPosition;
+    using VTEX = Geometry.VertexTypes.VertexTexture1;
+    using VPOSNRM = Geometry.VertexTypes.VertexPositionNormal;
+    
 
 
     [TestFixture]
     [TestFixture]
     public class BasicSceneCreationTests
     public class BasicSceneCreationTests
@@ -211,5 +214,48 @@ namespace SharpGLTF.Schema2.Authoring
             model.AttachToCurrentTest("result.glb");
             model.AttachToCurrentTest("result.glb");
             model.AttachToCurrentTest("result.gltf");
             model.AttachToCurrentTest("result.gltf");
         }
         }
+
+        [Test(Description = "Creates a quad mesh with a complex material")]
+        public void CreateFallbackMaterialScene()
+        {
+            TestContext.CurrentContext.AttachShowDirLink();
+            TestContext.CurrentContext.AttachGltfValidatorLink();
+
+            var basePath = System.IO.Path.Combine(TestContext.CurrentContext.WorkDirectory, "glTF-Sample-Models", "2.0", "SpecGlossVsMetalRough", "glTF");
+
+            var material = new Materials.MaterialBuilder("material1");
+            material.ShaderStyle = "PBRSpecularGlossiness";
+            material.UseChannel("Normal").UseTexture().WithImage(System.IO.Path.Combine(basePath, "WaterBottle_normal.png"));
+            material.UseChannel("Emissive").UseTexture().WithImage(System.IO.Path.Combine(basePath, "WaterBottle_emissive.png"));
+            material.UseChannel("Occlusion").UseTexture().WithImage(System.IO.Path.Combine(basePath, "WaterBottle_occlusion.png"));
+            material.UseChannel("Diffuse").UseTexture().WithImage(System.IO.Path.Combine(basePath, "WaterBottle_diffuse.png"));
+            material.UseChannel("Glossiness").UseTexture().WithImage(System.IO.Path.Combine(basePath, "WaterBottle_specularGlossiness.png"));
+            material.UseChannel("Specular").Color = Vector4.One * 0.3f;
+
+            var fallback = material.CompatibilityFallback = new Materials.MaterialBuilder("material1 fallback");
+            fallback.UseChannel("Normal").UseTexture().WithImage(System.IO.Path.Combine(basePath, "WaterBottle_normal.png"));
+            fallback.UseChannel("Emissive").UseTexture().WithImage(System.IO.Path.Combine(basePath, "WaterBottle_emissive.png"));
+            fallback.UseChannel("Occlusion").UseTexture().WithImage(System.IO.Path.Combine(basePath, "WaterBottle_occlusion.png"));
+            fallback.UseChannel("BaseColor").UseTexture().WithImage(System.IO.Path.Combine(basePath, "WaterBottle_baseColor.png"));
+            fallback.UseChannel("Metallic").UseTexture().WithImage(System.IO.Path.Combine(basePath, "WaterBottle_roughnessMetallic.png"));
+            fallback.UseChannel("Roughness").Amount = 0.3f;
+
+
+            var mesh = new Geometry.MeshBuilder<VPOS, VTEX>("mesh1");
+            mesh.UsePrimitive(material).AddPolygon
+                ( (new Vector3(-10, 10, 0), new Vector2(0, 0))
+                , (new Vector3( 10, 10, 0), new Vector2(1, 0))
+                , (new Vector3( 10, -10, 0), new Vector2(1, 1))
+                , (new Vector3(-10, -10, 0), new Vector2(0, 1))
+                );
+
+            var model = ModelRoot.CreateModel();
+            var scene = model.UseScene("Default");
+            var rnode = scene.CreateNode("RootNode").WithMesh( model.CreateMesh(mesh) );
+
+            model.AttachToCurrentTest("result.glb");
+            model.AttachToCurrentTest("result.gltf");
+
+        }
     }
     }
 }
 }