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

+WIP with materials; refactored channels.

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

+ 2 - 20
src/SharpGLTF.Core/Schema2/gltf.Material.cs

@@ -23,7 +23,7 @@ namespace SharpGLTF.Schema2
         public int LogicalIndex => this.LogicalParent.LogicalMaterials.IndexOfReference(this);
 
         /// <summary>
-        /// Gets or sets the <see cref="AlphaMode"/> of this <see cref="Material"/> instance.
+        /// Gets or sets the <see cref="AlphaMode"/>.
         /// </summary>
         public AlphaMode Alpha
         {
@@ -32,7 +32,7 @@ namespace SharpGLTF.Schema2
         }
 
         /// <summary>
-        /// Gets or sets the <see cref="AlphaCutoff"/> of this <see cref="Material"/> instance.
+        /// Gets or sets the <see cref="AlphaCutoff"/> value for <see cref="Alpha"/> = <see cref="AlphaMode.MASK"/>.
         /// </summary>
         public Single AlphaCutoff
         {
@@ -84,24 +84,6 @@ namespace SharpGLTF.Schema2
             return null;
         }
 
-        private MaterialNormalTextureInfo _GetNormalTexture(bool create)
-        {
-            if (create && _normalTexture == null) _normalTexture = new MaterialNormalTextureInfo();
-            return _normalTexture;
-        }
-
-        private MaterialOcclusionTextureInfo _GetOcclusionTexture(bool create)
-        {
-            if (create && _occlusionTexture == null) _occlusionTexture = new MaterialOcclusionTextureInfo();
-            return _occlusionTexture;
-        }
-
-        private TextureInfo _GetEmissiveTexture(bool create)
-        {
-            if (create && _emissiveTexture == null) _emissiveTexture = new TextureInfo();
-            return _emissiveTexture;
-        }
-
         #endregion
     }
 }

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

@@ -18,121 +18,40 @@ namespace SharpGLTF.Schema2
     {
         #region lifecycle
 
-        internal MaterialChannel(Material m, String key, Func<Boolean, TextureInfo> texInfo)
+        internal MaterialChannel(Material m, String key, Func<Boolean, TextureInfo> texInfo, Func<Single> cgetter, Action<Single> csetter)
+            : this(m, key, texInfo)
         {
-            Guard.NotNull(m, nameof(m));
-            Guard.NotNullOrEmpty(key, nameof(key));
-
-            Guard.NotNull(texInfo, nameof(texInfo));
-
-            _Key = key;
-            _Material = m;
-
-            _TextureInfo = texInfo;
-
-            _ColorGetter = () => Vector4.One;
-            _ColorSetter = val => { };
-
-            _AmountGetter = () => texInfo(false)?.Amount ?? 1;
-            _AmountSetter = val => texInfo(true).Amount = val;
-
-            IsAmountSupported = true;
-            IsColorSupported = false;
-        }
-
-        internal MaterialChannel(Material m, String key, Func<Boolean, TextureInfo> texInfo, Func<Single> agetter, Action<Single> asetter)
-        {
-            Guard.NotNull(m, nameof(m));
-            Guard.NotNullOrEmpty(key, nameof(key));
-
-            Guard.NotNull(texInfo, nameof(texInfo));
-            Guard.NotNull(agetter, nameof(agetter));
-            Guard.NotNull(asetter, nameof(asetter));
-
-            _Key = key;
-            _Material = m;
-
-            _TextureInfo = texInfo;
-
-            _ColorGetter = () => Vector4.One;
-            _ColorSetter = val => { };
-
-            _AmountGetter = agetter;
-            _AmountSetter = asetter;
+            Guard.NotNull(cgetter, nameof(cgetter));
+            Guard.NotNull(csetter, nameof(csetter));
 
-            IsAmountSupported = true;
-            IsColorSupported = false;
+            _ParameterGetter = () => new Vector4(cgetter(), 0, 0, 0);
+            _ParameterSetter = value => csetter(value.X);
         }
 
         internal MaterialChannel(Material m, String key, Func<Boolean, TextureInfo> texInfo, Func<Vector4> cgetter, Action<Vector4> csetter)
+            : this(m, key, texInfo)
         {
-            Guard.NotNull(m, nameof(m));
-            Guard.NotNullOrEmpty(key, nameof(key));
-
-            Guard.NotNull(texInfo, nameof(texInfo));
-
             Guard.NotNull(cgetter, nameof(cgetter));
             Guard.NotNull(csetter, nameof(csetter));
 
-            _Key = key;
-            _Material = m;
-
-            _TextureInfo = texInfo;
-
-            _ColorGetter = cgetter;
-            _ColorSetter = csetter;
-
-            _AmountGetter = () => texInfo(false)?.Amount ?? 1;
-            _AmountSetter = val => texInfo(true).Amount = val;
-
-            IsAmountSupported = false;
-            IsColorSupported = true;
-        }
-
-        internal MaterialChannel(Material m, String key, Func<Single> agetter, Action<Single> asetter)
-        {
-            Guard.NotNull(m, nameof(m));
-            Guard.NotNullOrEmpty(key, nameof(key));
-
-            Guard.NotNull(agetter, nameof(agetter));
-            Guard.NotNull(asetter, nameof(asetter));
-
-            _Key = key;
-            _Material = m;
-
-            _TextureInfo = null;
-
-            _ColorGetter = () => Vector4.One;
-            _ColorSetter = val => { };
-
-            _AmountGetter = agetter;
-            _AmountSetter = asetter;
-
-            IsAmountSupported = true;
-            IsColorSupported = false;
+            _ParameterGetter = cgetter;
+            _ParameterSetter = csetter;
         }
 
-        internal MaterialChannel(Material m, String key, Func<Vector4> cgetter, Action<Vector4> csetter)
+        private MaterialChannel(Material m, String key, Func<Boolean, TextureInfo> texInfo)
         {
             Guard.NotNull(m, nameof(m));
             Guard.NotNullOrEmpty(key, nameof(key));
 
-            Guard.NotNull(cgetter, nameof(cgetter));
-            Guard.NotNull(csetter, nameof(csetter));
+            Guard.NotNull(texInfo, nameof(texInfo));
 
             _Key = key;
             _Material = m;
 
-            _TextureInfo = null;
-
-            _ColorGetter = cgetter;
-            _ColorSetter = csetter;
-
-            _AmountGetter = () => 1;
-            _AmountSetter = val => { };
+            _TextureInfo = texInfo;
 
-            IsAmountSupported = false;
-            IsColorSupported = true;
+            _ParameterGetter = null;
+            _ParameterSetter = null;
         }
 
         #endregion
@@ -144,11 +63,8 @@ namespace SharpGLTF.Schema2
 
         private readonly Func<Boolean, TextureInfo> _TextureInfo;
 
-        private readonly Func<Single> _AmountGetter;
-        private readonly Action<Single> _AmountSetter;
-
-        private readonly Func<Vector4> _ColorGetter;
-        private readonly Action<Vector4> _ColorSetter;
+        private readonly Func<Vector4> _ParameterGetter;
+        private readonly Action<Vector4> _ParameterSetter;
 
         #endregion
 
@@ -159,38 +75,16 @@ namespace SharpGLTF.Schema2
         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.
+        /// Gets or sets the <see cref="Vector4"/> parameter of this channel.
+        /// The meaning of the <see cref="Vector4.X"/>, <see cref="Vector4.Y"/>. <see cref="Vector4.Z"/> and <see cref="Vector4.W"/>
+        /// depend on the type of channel.
         /// </summary>
-        /// <remarks>
-        /// Not all channels support this property.
-        /// </remarks>
-        public Single Amount
+        public Vector4 Parameter
         {
-            get => _AmountGetter();
-            set => _AmountSetter(value);
+            get => _ParameterGetter();
+            set => _ParameterSetter(value);
         }
 
-        /// <summary>
-        /// Gets a value indicating whether this channel supports a Color factor.
-        /// </summary>
-        public bool IsColorSupported { get; private set; }
-
-        public Vector4 Color
-        {
-            get => _ColorGetter();
-            set => _ColorSetter(value);
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether this channel supports textures.
-        /// </summary>
-        public bool IsTextureSupported => _TextureInfo != null;
-
         /// <summary>
         /// Gets the <see cref="Texture"/> instance used by this Material, or null.
         /// </summary>

+ 85 - 61
src/SharpGLTF.Core/Schema2/gltf.MaterialsFactory.cs

@@ -31,7 +31,10 @@ namespace SharpGLTF.Schema2
             {
                 if (this._pbrMetallicRoughness == null) this._pbrMetallicRoughness = new MaterialPBRMetallicRoughness();
             }
-            else this._pbrMetallicRoughness = null;
+            else
+            {
+                this._pbrMetallicRoughness = null;
+            }
 
             this.RemoveExtensions<MaterialUnlit>();
             this.SetExtension(new MaterialPBRSpecularGlossiness(this));
@@ -63,18 +66,35 @@ namespace SharpGLTF.Schema2
                 foreach (var c in channels) yield return c;
             }
 
-            yield return new MaterialChannel(this, "Normal", _GetNormalTexture);
+            yield return new MaterialChannel(this, "Normal", _GetNormalTexture, () => _GetNormalTexture(false)?.Scale ?? 0, value => _GetNormalTexture(true).Scale = value);
+
+            yield return new MaterialChannel(this, "Occlusion", _GetOcclusionTexture, () => _GetOcclusionTexture(false)?.Strength ?? 0, value => _GetOcclusionTexture(true).Strength = value);
+
+            yield return new MaterialChannel(this, "Emissive", _GetEmissiveTexture, () => this._EmissiveColor, value => this._EmissiveColor = value );
+        }
+
+        private Vector4 _EmissiveColor
+        {
+            get => new Vector4(_emissiveFactor.AsValue(_emissiveFactorDefault), 1);
+            set => _emissiveFactor = new Vector3(value.X, value.Y, value.Z).AsNullable(_emissiveFactorDefault, Vector3.Zero, Vector3.One);
+        }
+
+        private MaterialNormalTextureInfo _GetNormalTexture(bool create)
+        {
+            if (create && _normalTexture == null) _normalTexture = new MaterialNormalTextureInfo();
+            return _normalTexture;
+        }
 
-            yield return new MaterialChannel(this, "Occlusion", _GetOcclusionTexture);
+        private MaterialOcclusionTextureInfo _GetOcclusionTexture(bool create)
+        {
+            if (create && _occlusionTexture == null) _occlusionTexture = new MaterialOcclusionTextureInfo();
+            return _occlusionTexture;
+        }
 
-            yield return new MaterialChannel
-                (
-                this,
-                "Emissive",
-                _GetEmissiveTexture,
-                () => { var rgb = _emissiveFactor.AsValue(_emissiveFactorDefault); return new Vector4(rgb, 1); },
-                value => _emissiveFactor = new Vector3(value.X, value.Y, value.Z).AsNullable(_emissiveFactorDefault, Vector3.Zero, Vector3.One)
-                );
+        private TextureInfo _GetEmissiveTexture(bool create)
+        {
+            if (create && _emissiveTexture == null) _emissiveTexture = new TextureInfo();
+            return _emissiveTexture;
         }
 
         #endregion
@@ -118,33 +138,36 @@ namespace SharpGLTF.Schema2
             return _metallicRoughnessTexture;
         }
 
+        public Vector4 Color
+        {
+            get => _baseColorFactor.AsValue(_baseColorFactorDefault);
+            set => _baseColorFactor = value.AsNullable(_baseColorFactorDefault);
+        }
+
+        public Vector4 Parameter
+        {
+            get
+            {
+                return new Vector4
+                    (
+                    (float) _metallicFactor.AsValue( _metallicFactorDefault),
+                    (float)_roughnessFactor.AsValue(_roughnessFactorDefault),
+                    0,
+                    0
+                    );
+            }
+            set
+            {
+                _metallicFactor  = ((double)value.X).AsNullable( _metallicFactorDefault,  _metallicFactorMinimum,  _metallicFactorMaximum);
+                _roughnessFactor = ((double)value.Y).AsNullable(_roughnessFactorDefault, _roughnessFactorMinimum, _roughnessFactorMaximum);
+            }
+        }
+
         public IEnumerable<MaterialChannel> GetChannels(Material material)
         {
-            yield return new MaterialChannel
-                (
-                material,
-                "BaseColor",
-                _GetBaseTexture,
-                () => _baseColorFactor.AsValue(_baseColorFactorDefault),
-                value => _baseColorFactor = value.AsNullable(_baseColorFactorDefault)
-                );
-
-            yield return new MaterialChannel
-                (
-                material,
-                "Metallic",
-                _GetMetallicTexture,
-                () => (float)_metallicFactor.AsValue(_metallicFactorDefault),
-                value => _metallicFactor = ((double)value).AsNullable(_metallicFactorDefault, _metallicFactorMaximum, _metallicFactorMaximum)
-                );
-
-            yield return new MaterialChannel
-                (
-                material,
-                "Roughness",
-                () => (float)_roughnessFactor.AsValue(_roughnessFactorDefault),
-                value => _roughnessFactor = ((double)value).AsNullable(_roughnessFactorDefault, _roughnessFactorMinimum, _roughnessFactorMaximum)
-                );
+            yield return new MaterialChannel(material, "BaseColor", _GetBaseTexture, () => this.Color, value => this.Color = value);
+
+            yield return new MaterialChannel(material, "MetallicRoughness", _GetMetallicTexture, () => this.Parameter, value => this.Parameter = value);
         }
     }
 
@@ -170,33 +193,34 @@ namespace SharpGLTF.Schema2
             return _specularGlossinessTexture;
         }
 
+        public Vector4 Color
+        {
+            get => _diffuseFactor.AsValue(_diffuseFactorDefault);
+            set => _diffuseFactor = value.AsNullable(_diffuseFactorDefault);
+        }
+
+        public Vector4 Parameter
+        {
+            get
+            {
+                return new Vector4
+                    (
+                    _specularFactor.AsValue(_specularFactorDefault),
+                    (float)_glossinessFactor.AsValue(_glossinessFactorDefault)
+                    );
+            }
+            set
+            {
+                _specularFactor = new Vector3(value.X, value.Y, value.Z).AsNullable(_specularFactorDefault);
+                _glossinessFactor = ((double)value.W).AsNullable(_glossinessFactorDefault, _glossinessFactorMinimum, _glossinessFactorMaximum);
+            }
+        }
+
         public IEnumerable<MaterialChannel> GetChannels(Material material)
         {
-            yield return new MaterialChannel
-                (
-                material,
-                "Diffuse",
-                _GetDiffuseTexture,
-                () => _diffuseFactor.AsValue(_diffuseFactorDefault),
-                value => _diffuseFactor = value.AsNullable(_diffuseFactorDefault)
-                );
-
-            yield return new MaterialChannel
-                (
-                material,
-                "Glossiness",
-                _GetGlossinessTexture,
-                () => (float)_glossinessFactor.AsValue(_glossinessFactorDefault),
-                value => _glossinessFactor = ((double)value).AsNullable(_glossinessFactorDefault, _glossinessFactorMinimum, _glossinessFactorMaximum)
-                );
-
-            yield return new MaterialChannel
-                (
-                material,
-                "Specular",
-                () => { var rgb = _specularFactor.AsValue(_specularFactorDefault); return new Vector4(rgb, 1); },
-                value => _specularFactor = new Vector3(value.X, value.Y, value.Z).AsNullable(_specularFactorDefault)
-                );
+            yield return new MaterialChannel(material, "Diffuse", _GetDiffuseTexture, () => this.Color, value => this.Color = value);
+
+            yield return new MaterialChannel(material, "SpecularGlossiness", _GetGlossinessTexture, () => this.Parameter, value => this.Parameter = value);
         }
     }
 

+ 2 - 8
src/SharpGLTF.Core/Schema2/gltf.TextureInfo.cs

@@ -30,12 +30,6 @@ namespace SharpGLTF.Schema2
             get => this.GetExtension<TextureTransform>();
         }
 
-        public virtual Single Amount
-        {
-            get { return 1; }
-            set { }
-        }
-
         #endregion
 
         #region API
@@ -115,7 +109,7 @@ namespace SharpGLTF.Schema2
     {
         #region properties
 
-        public override Single Amount
+        public Single Scale
         {
             get => (Single)this._scale.AsValue(_scaleDefault);
             set => this._scale = ((Double)value).AsNullable(_scaleDefault);
@@ -129,7 +123,7 @@ namespace SharpGLTF.Schema2
     {
         #region properties
 
-        public override Single Amount
+        public Single Strength
         {
             get => (Single)this._strength.AsValue(_strengthDefault);
             set => this._strength = ((Double)value).AsNullable(_strengthDefault, _strengthMinimum, _strengthMaximum);

+ 25 - 9
src/SharpGLTF.Toolkit/Materials/Channel.cs

@@ -5,12 +5,32 @@ using System.Text;
 
 namespace SharpGLTF.Materials
 {
-    [System.Diagnostics.DebuggerDisplay("{_Key} {Amount}")]
+    [System.Diagnostics.DebuggerDisplay("{Key} {Parameter}")]
     public class MaterialChannelBuilder
     {
         #region lifecycle
 
-        internal MaterialChannelBuilder(MaterialBuilder parent, string key) { _Parent = parent; _Key = key; }
+        internal MaterialChannelBuilder(MaterialBuilder parent, string key)
+        {
+            _Parent = parent; _Key = key;
+
+            switch (_Key)
+            {
+                case "Emissive": Parameter = Vector4.Zero; break;
+
+                case "Normal":
+                case "Occlusion":
+                    Parameter = new Vector4(1, 0, 0, 0); break;
+
+                case "BaseColor":
+                case "Diffuse":
+                    Parameter = Vector4.One; break;
+
+                case "MetalicRoughness": Parameter = new Vector4(1, 1, 0, 0); break;
+
+                case "SpecularGlossiness": Parameter = Vector4.One; break;
+            }
+        }
 
         #endregion
 
@@ -26,9 +46,7 @@ namespace SharpGLTF.Materials
 
         public String Key => _Key;
 
-        public Single Amount { get; set; } = 1;
-
-        public Vector4 Color { get; set; } = Vector4.One;
+        public Vector4 Parameter { get; set; }
 
         public TextureBuilder Texture { get; private set; }
 
@@ -52,11 +70,9 @@ namespace SharpGLTF.Materials
         Emissive,
 
         BaseColor,
-        Metallic,
-        Roughness,
+        MetallicRoughness,
 
         Diffuse,
-        Specular,
-        Glosiness,
+        SpecularGlossiness,
     }
 }

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

@@ -22,6 +22,10 @@ namespace SharpGLTF.Materials
 
         #region data
 
+        public const string SHADERUNLIT = "Unlit";
+        public const string SHADERPBRMETALLICROUGHNESS = "PBRMetallicRoughness";
+        public const string SHADERPBRSPECULARGLOSSINESS = "PBRSpecularGlossiness";
+
         private readonly List<MaterialChannelBuilder> _Channels = new List<MaterialChannelBuilder>();
 
         private MaterialBuilder _CompatibilityFallbackMaterial;
@@ -40,7 +44,7 @@ namespace SharpGLTF.Materials
 
         public Boolean DoubleSided { get; set; } = false;
 
-        public String ShaderStyle { get; set; } = "PBRMetallicRoughness";
+        public String ShaderStyle { get; set; } = SHADERPBRMETALLICROUGHNESS;
 
         public MaterialBuilder CompatibilityFallback
         {
@@ -56,6 +60,19 @@ namespace SharpGLTF.Materials
 
         #region API
 
+        public MaterialBuilder WithUnlitShader() { return WithShade(SHADERUNLIT); }
+
+        public MaterialBuilder WithMetallicRoughnessShader() { return WithShade(SHADERPBRMETALLICROUGHNESS); }
+
+        public MaterialBuilder WithSpecularGlossinessShader() { return WithShade(SHADERPBRSPECULARGLOSSINESS); }
+
+        public MaterialBuilder WithShade(string shader)
+        {
+            Guard.NotNullOrEmpty(shader, nameof(shader));
+            ShaderStyle = shader;
+            return this;
+        }
+
         public MaterialChannelBuilder GetChannel(KnownChannels channelKey)
         {
             return GetChannel(channelKey.ToString());
@@ -100,15 +117,15 @@ namespace SharpGLTF.Materials
             return this;
         }
 
-        public MaterialBuilder WithChannelColor(KnownChannels channelKey, Vector4 color)
+        public MaterialBuilder WithChannelParam(KnownChannels channelKey, Vector4 parameter)
         {
-            this.UseChannel(channelKey).Color = color;
+            this.UseChannel(channelKey).Parameter = parameter;
             return this;
         }
 
-        public MaterialBuilder WithChannelColor(string channelKey, Vector4 color)
+        public MaterialBuilder WithChannelParam(string channelKey, Vector4 parameter)
         {
-            this.UseChannel(channelKey).Color = color;
+            this.UseChannel(channelKey).Parameter = parameter;
             return this;
         }
 
@@ -130,6 +147,12 @@ namespace SharpGLTF.Materials
             return this;
         }
 
+        public MaterialBuilder WithFallback(MaterialBuilder fallback)
+        {
+            this.CompatibilityFallback = fallback;
+            return this;
+        }
+
         #endregion
     }
 }

+ 26 - 0
src/SharpGLTF.Toolkit/Materials/readme.md

@@ -0,0 +1,26 @@
+# Toolkit material API
+
+#### Overview
+
+glTF materials specification has a rather complex architecture,
+with a number of speciallised objects, plus a good number of extensions.
+
+In order to streamline and simplify access to materials, a Material Build
+API is provided.
+
+By default, SharpGLTF supports the default PBRMetallicRoughness shader style,
+plus Unlit and PBRSpecularGlossiness extensions. Each shader style has its
+own unique list of channels.
+
+Each channel has a Texture, and a Vector4<X,Y,Z,W> parameter, where every
+element has a different meaning, depending on the kind of channel:
+
+|Channel|Shader Style|X|Y|Z|W|
+|-|
+|Normal|All|Scale
+|Occlussion|All|Strength
+|Emissive|All|Red|Green|Blue
+|BaseColor|Metallic Roughness & Unlit|Red|Green|Blue|Alpha
+|MetallicRoughness|Metallic Roughness|Metallic Factor|Roughness Factor
+|Diffuse|Specular Glossiness|Diffuse Red|Diffuse Green|Diffuse Blue|Alpha
+|SpecularGlossiness|Specular Glossiness|Specular Red|Specular Green|Specular Blue|Glossiness

+ 68 - 40
src/SharpGLTF.Toolkit/Schema2/MaterialExtensions.cs

@@ -30,7 +30,7 @@ namespace SharpGLTF.Schema2
         {
             var ch = material.WithPBRMetallicRoughness().FindChannel("BaseColor").Value;
 
-            ch.Color = diffuseColor;
+            ch.Parameter = diffuseColor;
 
             return material;
         }
@@ -41,30 +41,28 @@ namespace SharpGLTF.Schema2
             return material;
         }
 
-        public static Material WithChannelColor(this Material material, string channelName, Vector4 color)
+        public static Material WithChannelParameter(this Material material, string channelName, Vector4 parameter)
         {
             var channel = material.FindChannel(channelName).Value;
 
-            channel.Color = color;
+            channel.Parameter = parameter;
 
             return material;
         }
 
-        public static Material WithChannelTexture(this Material material, string channelName, int textureSet, string imageFilePath, float amount = 1)
+        public static Material WithChannelTexture(this Material material, string channelName, int textureSet, string imageFilePath)
         {
             var image = material.LogicalParent.UseImageWithFile(imageFilePath);
 
-            return material.WithChannelTexture(channelName, textureSet, image, amount);
+            return material.WithChannelTexture(channelName, textureSet, image);
         }
 
-        public static Material WithChannelTexture(this Material material, string channelName, int textureSet, Image image, float amount = 1)
+        public static Material WithChannelTexture(this Material material, string channelName, int textureSet, Image image)
         {
             var channel = material.FindChannel(channelName).Value;
 
             channel.SetTexture(textureSet, image);
 
-            channel.Amount = amount;
-
             return material;
         }
 
@@ -81,16 +79,20 @@ namespace SharpGLTF.Schema2
 
         public static Material WithPBRMetallicRoughness(
             this Material material,
-            Vector4 baseColor, string baseColorImageFilePath,
-            float metallicAmount = 1, string metallicImageFilePath = null,
-            float roughnessAmount = 1, string roughtnessImageFilePath = null
+            Vector4 baseColor,
+            string baseColorImageFilePath,
+            string metallicImageFilePath = null,
+            float metallicFactor = 1,
+            float roughnessFactor = 1
             )
         {
             material.WithPBRMetallicRoughness();
 
-            if (!string.IsNullOrWhiteSpace(baseColorImageFilePath)) material.WithChannelColor("BaseColor", baseColor).WithChannelTexture("BaseColor", 0, baseColorImageFilePath);
-            if (!string.IsNullOrWhiteSpace(metallicImageFilePath)) material.WithChannelTexture("Metallic", 0, baseColorImageFilePath, metallicAmount);
-            if (!string.IsNullOrWhiteSpace(roughtnessImageFilePath)) material.WithChannelTexture("Roughness", 0, baseColorImageFilePath, roughnessAmount);
+            material.WithChannelParameter("BaseColor", baseColor);
+            material.WithChannelParameter("MetallicRoughness", new Vector4(metallicFactor, roughnessFactor, 0, 0));
+
+            if (!string.IsNullOrWhiteSpace(baseColorImageFilePath)) material.WithChannelTexture("BaseColor", 0, baseColorImageFilePath);
+            if (!string.IsNullOrWhiteSpace(metallicImageFilePath)) material.WithChannelTexture("Metallic", 0, baseColorImageFilePath);
 
             return material;
         }
@@ -175,25 +177,48 @@ namespace SharpGLTF.Schema2
             dstMaterial.AlphaCutoff = srcMaterial.AlphaCutoff;
             dstMaterial.DoubleSided = srcMaterial.DoubleSided;
 
-            if (srcMaterial.Unlit) dstMaterial.ShaderStyle = "Unlit";
+            srcMaterial.CopyChannelsTo(dstMaterial, "Normal", "Occlusion", "Emissive");
+
+            if (srcMaterial.Unlit) dstMaterial.WithUnlitShader();
 
-            if (srcMaterial.FindChannel("Diffuse") != null || srcMaterial.FindChannel("Glossiness") != null)
+            if (srcMaterial.FindChannel("BaseColor") != null || srcMaterial.FindChannel("MetallicRoughness") != null)
             {
-                dstMaterial.ShaderStyle = "PBRSpecularGlossiness";
-                // TODO: create fallback material
+                dstMaterial.WithMetallicRoughnessShader();
+                srcMaterial.CopyChannelsTo(dstMaterial, "BaseColor", "MetallicRoughness");
             }
 
-            foreach (var channel in srcMaterial.Channels)
+            if (srcMaterial.FindChannel("Diffuse") != null || srcMaterial.FindChannel("SpecularGlossiness") != null)
+            {
+                dstMaterial = new Materials.MaterialBuilder(srcMaterial.Name).WithFallback(dstMaterial);
+
+                dstMaterial.Name = srcMaterial.Name;
+                dstMaterial.AlphaMode = srcMaterial.Alpha;
+                dstMaterial.AlphaCutoff = srcMaterial.AlphaCutoff;
+                dstMaterial.DoubleSided = srcMaterial.DoubleSided;
+
+                srcMaterial.CopyChannelsTo(dstMaterial, "Normal", "Occlusion", "Emissive");
+
+                dstMaterial.WithSpecularGlossinessShader();
+                srcMaterial.CopyChannelsTo(dstMaterial, "Diffuse", "SpecularGlossiness");
+            }
+        }
+
+        public static void CopyChannelsTo(this Material srcMaterial, Materials.MaterialBuilder dstMaterial, params string[] channelKeys)
+        {
+            foreach (var k in channelKeys)
             {
-                var ch = dstMaterial.UseChannel(channel.Key);
-                channel.CopyTo(ch);
+                var src = srcMaterial.FindChannel(k);
+                if (src == null) continue;
+
+                var dst = dstMaterial.UseChannel(k);
+
+                src.Value.CopyTo(dst);
             }
         }
 
         public static void CopyTo(this MaterialChannel srcChannel, Materials.MaterialChannelBuilder dstChannel)
         {
-            dstChannel.Color = srcChannel.Color;
-            dstChannel.Amount = srcChannel.Amount;
+            dstChannel.Parameter = srcChannel.Parameter;
 
             if (srcChannel.Texture == null) { return; }
 
@@ -222,44 +247,48 @@ namespace SharpGLTF.Schema2
             dstMaterial.AlphaCutoff = srcMaterial.AlphaCutoff;
             dstMaterial.DoubleSided = srcMaterial.DoubleSided;
 
-            srcMaterial.GetChannel("Normal").CopyTo(dstMaterial.FindChannel("Normal").Value);
-            srcMaterial.GetChannel("Occlusion").CopyTo(dstMaterial.FindChannel("Occlusion").Value);
-            srcMaterial.GetChannel("Emissive").CopyTo(dstMaterial.FindChannel("Emissive").Value);
+            srcMaterial.CopyChannelsTo(dstMaterial, "Normal", "Occlusion", "Emissive");
 
             Materials.MaterialBuilder defMaterial = null;
 
             if (srcMaterial.ShaderStyle == "Unlit")
             {
                 dstMaterial.InitializePBRMetallicRoughness();
-                srcMaterial.GetChannel("BaseColor").CopyTo(dstMaterial.FindChannel("BaseColor").Value);
+                srcMaterial.CopyChannelsTo(dstMaterial, "BaseColor");
                 return;
             }
 
             if (srcMaterial.ShaderStyle == "PBRMetallicRoughness")
             {
                 dstMaterial.InitializePBRMetallicRoughness();
-
                 defMaterial = srcMaterial;
             }
 
             if (srcMaterial.ShaderStyle == "PBRSpecularGlossiness")
             {
                 dstMaterial.InitializePBRSpecularGlossiness(srcMaterial.CompatibilityFallback != null);
-
-                srcMaterial.GetChannel("Diffuse").CopyTo(dstMaterial.FindChannel("Diffuse").Value);
-                srcMaterial.GetChannel("Specular").CopyTo(dstMaterial.FindChannel("Specular").Value);
-                srcMaterial.GetChannel("Glossiness").CopyTo(dstMaterial.FindChannel("Glossiness").Value);
-
+                srcMaterial.CopyChannelsTo(dstMaterial, "Diffuse", "SpecularGlossiness");
                 defMaterial = srcMaterial.CompatibilityFallback;
             }
 
             if (defMaterial != null)
             {
                 if (defMaterial.ShaderStyle != "PBRMetallicRoughness") throw new ArgumentException(nameof(srcMaterial.CompatibilityFallback.ShaderStyle));
+                srcMaterial.CopyChannelsTo(dstMaterial, "BaseColor", "MetallicRoughness");
+            }
+        }
+
+        public static void CopyChannelsTo(this Materials.MaterialBuilder srcMaterial, Material dstMaterial, params string[] channelKeys)
+        {
+            foreach (var k in channelKeys)
+            {
+                var src = srcMaterial.GetChannel(k);
+                if (src == null) continue;
+
+                var dst = dstMaterial.FindChannel(k);
+                if (dst == null) continue;
 
-                defMaterial.GetChannel("BaseColor").CopyTo(dstMaterial.FindChannel("BaseColor").Value);
-                defMaterial.GetChannel("Metallic").CopyTo(dstMaterial.FindChannel("Metallic").Value);
-                defMaterial.GetChannel("Roughness").CopyTo(dstMaterial.FindChannel("Roughness").Value);
+                src.CopyTo(dst.Value);
             }
         }
 
@@ -267,8 +296,7 @@ namespace SharpGLTF.Schema2
         {
             if (srcChannel == null) return;
 
-            dstChannel.Color = srcChannel.Color;
-            dstChannel.Amount = srcChannel.Amount;
+            dstChannel.Parameter = srcChannel.Parameter;
 
             var srcTex = srcChannel.Texture;
 
@@ -290,10 +318,10 @@ namespace SharpGLTF.Schema2
             if (material == null) return defaultColor;
 
             var channel = material.FindChannel("BaseColor");
-            if (channel.HasValue) return channel.Value.Color;
+            if (channel.HasValue) return channel.Value.Parameter;
 
             channel = material.FindChannel("Diffuse");
-            if (channel.HasValue) return channel.Value.Color;
+            if (channel.HasValue) return channel.Value.Parameter;
 
             return defaultColor;
         }

+ 22 - 21
tests/SharpGLTF.Tests/Schema2/Authoring/BasicSceneCreationTests.cs

@@ -223,30 +223,31 @@ namespace SharpGLTF.Schema2.Authoring
 
             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;
-
+            // first, create a default material
+            var material = new Materials.MaterialBuilder("material1 fallback")
+                .WithMetallicRoughnessShader()
+                .WithChannelImage(Materials.KnownChannels.Normal, System.IO.Path.Combine(basePath, "WaterBottle_normal.png"))
+                .WithChannelImage(Materials.KnownChannels.Emissive, System.IO.Path.Combine(basePath, "WaterBottle_emissive.png"))
+                .WithChannelImage(Materials.KnownChannels.Occlusion, System.IO.Path.Combine(basePath, "WaterBottle_occlusion.png"))
+                .WithChannelImage(Materials.KnownChannels.BaseColor, System.IO.Path.Combine(basePath, "WaterBottle_baseColor.png"))
+                .WithChannelImage(Materials.KnownChannels.MetallicRoughness, System.IO.Path.Combine(basePath, "WaterBottle_roughnessMetallic.png"));
+
+            // wrap the fallback material with a PBR Specular Glossiness material.
+            material = new Materials.MaterialBuilder("material1")
+                .WithFallback(material)
+                .WithSpecularGlossinessShader()
+                .WithChannelImage(Materials.KnownChannels.Normal, System.IO.Path.Combine(basePath, "WaterBottle_normal.png"))
+                .WithChannelImage(Materials.KnownChannels.Emissive, System.IO.Path.Combine(basePath, "WaterBottle_emissive.png"))
+                .WithChannelImage(Materials.KnownChannels.Occlusion, System.IO.Path.Combine(basePath, "WaterBottle_occlusion.png"))
+                .WithChannelImage(Materials.KnownChannels.Diffuse, System.IO.Path.Combine(basePath, "WaterBottle_diffuse.png"))
+                .WithChannelImage(Materials.KnownChannels.SpecularGlossiness, System.IO.Path.Combine(basePath, "WaterBottle_specularGlossiness.png"));                
 
             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))
+                ( (new Vector3(-10, 10, 0), new Vector2(1, 0))
+                , (new Vector3( 10, 10, 0), new Vector2(0, 0))
+                , (new Vector3( 10, -10, 0), new Vector2(0, 1))
+                , (new Vector3(-10, -10, 0), new Vector2(1, 1))
                 );
 
             var model = ModelRoot.CreateModel();

+ 7 - 7
tests/SharpGLTF.Tests/Schema2/Authoring/MeshBuilderCreationTests.cs

@@ -35,7 +35,7 @@ namespace SharpGLTF.Schema2.Authoring
             var v4 = new VPOSNRM(-10, -10, 0, -10, -10, 15);
 
             // create a material
-            var material1 = new MaterialBuilder("material1").WithChannelColor(KnownChannels.BaseColor, Vector4.One);
+            var material1 = new MaterialBuilder("material1").WithChannelParam(KnownChannels.BaseColor, Vector4.One);
 
             // create model
             var meshBuilder = new MeshBuilder<VPOSNRM>("mesh1");
@@ -61,8 +61,8 @@ namespace SharpGLTF.Schema2.Authoring
             TestContext.CurrentContext.AttachGltfValidatorLink();
 
             // create materials
-            var material1 = new MaterialBuilder("material1").WithChannelColor(KnownChannels.BaseColor, new Vector4(1, 1, 0, 1));
-            var material2 = new MaterialBuilder("material1").WithChannelColor(KnownChannels.BaseColor, new Vector4(1, 0, 1, 1));            
+            var material1 = new MaterialBuilder("material1").WithChannelParam(KnownChannels.BaseColor, new Vector4(1, 1, 0, 1));
+            var material2 = new MaterialBuilder("material1").WithChannelParam(KnownChannels.BaseColor, new Vector4(1, 0, 1, 1));            
 
             // create several meshes
             var meshBuilder1 = new MeshBuilder<VPOSNRM>("mesh1");
@@ -122,7 +122,7 @@ namespace SharpGLTF.Schema2.Authoring
             };
 
             // create a material
-            var material1 = new MaterialBuilder("material1").WithChannelColor(KnownChannels.BaseColor, Vector4.One);
+            var material1 = new MaterialBuilder("material1").WithChannelParam(KnownChannels.BaseColor, Vector4.One);
 
             // create a mesh
             var meshBuilder = new MeshBuilder<VPOSNRM>("mesh1");
@@ -157,8 +157,8 @@ namespace SharpGLTF.Schema2.Authoring
             };
 
             // create two materials
-            var pink = new MaterialBuilder("material1").WithChannelColor(KnownChannels.BaseColor, new Vector4(1, 0, 1, 1)).WithDoubleSide(true);
-            var yellow = new MaterialBuilder("material2").WithChannelColor(KnownChannels.BaseColor, new Vector4(1, 1, 0, 1)).WithDoubleSide(true);
+            var pink = new MaterialBuilder("material1").WithChannelParam(KnownChannels.BaseColor, new Vector4(1, 0, 1, 1)).WithDoubleSide(true);
+            var yellow = new MaterialBuilder("material2").WithChannelParam(KnownChannels.BaseColor, new Vector4(1, 1, 0, 1)).WithDoubleSide(true);
 
             // create the mesh
             var meshBuilder = new MeshBuilder<VPOS, VEMPTY, VSKIN4>("mesh1");
@@ -269,7 +269,7 @@ namespace SharpGLTF.Schema2.Authoring
             var materials = Enumerable
                 .Range(0, 10)
                 .Select(idx => new MaterialBuilder()
-                .WithChannelColor("BaseColor", new Vector4(rnd.NextVector3(),1)))
+                .WithChannelParam("BaseColor", new Vector4(rnd.NextVector3(),1)))
                 .ToList();
 
             // create a mesh