Browse Source

+WIP with materials

Vicente Penades 6 years ago
parent
commit
2dafd5b2e8

+ 107 - 0
src/SharpGLTF.Core/Schema2/gltf.Material.cs

@@ -0,0 +1,107 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Text;
+
+namespace SharpGLTF.Schema2
+{
+    [System.Diagnostics.DebuggerDisplay("Material[{LogicalIndex}] {Name}")]
+    public sealed partial class Material
+    {
+        #region lifecycle
+
+        internal Material() { }
+
+        #endregion
+
+        #region properties
+
+        /// <summary>
+        /// Gets the zero-based index of this <see cref="Material"/> at <see cref="ModelRoot.LogicalMaterials"/>
+        /// </summary>
+        public int LogicalIndex => this.LogicalParent.LogicalMaterials.IndexOfReference(this);
+
+        /// <summary>
+        /// Gets or sets the <see cref="AlphaMode"/> of this <see cref="Material"/> instance.
+        /// </summary>
+        public AlphaMode Alpha
+        {
+            get => _alphaMode.AsValue(_alphaModeDefault);
+            set => _alphaMode = value.AsNullable(_alphaModeDefault);
+        }
+
+        /// <summary>
+        /// Gets or sets the <see cref="AlphaCutoff"/> of this <see cref="Material"/> instance.
+        /// </summary>
+        public Single AlphaCutoff
+        {
+            get => (Single)_alphaCutoff.AsValue(_alphaCutoffDefault);
+            set => _alphaCutoff = ((Double)value).AsNullable(_alphaCutoffDefault, _alphaCutoffMinimum, double.MaxValue);
+        }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this <see cref="Material"/> will render as Double Sided.
+        /// </summary>
+        public Boolean DoubleSided
+        {
+            get => _doubleSided.AsValue(_doubleSidedDefault);
+            set => _doubleSided = value.AsNullable(_doubleSidedDefault);
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether this <see cref="Material"/> instance has Unlit extension.
+        /// </summary>
+        public Boolean Unlit => this.GetExtension<MaterialUnlit>() != null;
+
+        /// <summary>
+        /// Gets a collection of <see cref="MaterialChannel"/> elements available in this <see cref="Material"/> instance.
+        /// </summary>
+        public IEnumerable<MaterialChannel> Channels => _GetChannels();
+
+        #endregion
+
+        #region API
+
+        /// <inheritdoc />
+        protected override IEnumerable<ExtraProperties> GetLogicalChildren()
+        {
+            return base.GetLogicalChildren().ConcatItems(_normalTexture, _emissiveTexture, _occlusionTexture, _pbrMetallicRoughness);
+        }
+
+        /// <summary>
+        /// Finds an instance of <see cref="MaterialChannel"/>
+        /// </summary>
+        /// <param name="channelKey">the channel key</param>
+        /// <returns>A <see cref="MaterialChannel"/> structure. or null if it does not exist</returns>
+        public MaterialChannel? FindChannel(string channelKey)
+        {
+            foreach (var ch in Channels)
+            {
+                if (ch.Key.Equals(channelKey, StringComparison.OrdinalIgnoreCase)) return ch;
+            }
+
+            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
+    }
+}

+ 245 - 0
src/SharpGLTF.Core/Schema2/gltf.MaterialChannel.cs

@@ -0,0 +1,245 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Text;
+
+namespace SharpGLTF.Schema2
+{
+    /// <summary>
+    /// Represents a material sub-channel, which usually contains a texture.
+    /// </summary>
+    /// <remarks>
+    /// This structure is not part of the gltf schema,
+    /// but wraps several components of the material
+    /// to have an homogeneous and easy to use API.
+    /// </remarks>
+    [System.Diagnostics.DebuggerDisplay("Channel {_Key}")]
+    public struct MaterialChannel
+    {
+        #region lifecycle
+
+        internal MaterialChannel(Material m, String key, Func<Boolean, TextureInfo> 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;
+
+            IsTextureAmountSupported = 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;
+
+            IsTextureAmountSupported = true;
+            IsColorSupported = false;
+        }
+
+        internal MaterialChannel(Material m, String key, Func<Boolean, TextureInfo> texInfo, Func<Vector4> cgetter, Action<Vector4> csetter)
+        {
+            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;
+
+            IsTextureAmountSupported = 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;
+
+            IsTextureAmountSupported = true;
+            IsColorSupported = false;
+        }
+
+        internal MaterialChannel(Material m, String key, Func<Vector4> cgetter, Action<Vector4> csetter)
+        {
+            Guard.NotNull(m, nameof(m));
+            Guard.NotNullOrEmpty(key, nameof(key));
+
+            Guard.NotNull(cgetter, nameof(cgetter));
+            Guard.NotNull(csetter, nameof(csetter));
+
+            _Key = key;
+            _Material = m;
+
+            _TextureInfo = null;
+
+            _ColorGetter = cgetter;
+            _ColorSetter = csetter;
+
+            _AmountGetter = () => 1;
+            _AmountSetter = val => { };
+
+            IsTextureAmountSupported = false;
+            IsColorSupported = true;
+        }
+
+        #endregion
+
+        #region data
+
+        private readonly String _Key;
+        private readonly Material _Material;
+
+        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;
+
+        #endregion
+
+        #region properties
+
+        public Material LogicalParent => _Material;
+
+        public String Key => _Key;
+
+        /// <summary>
+        /// Gets the <see cref="Texture"/> instance used by this Material, or null.
+        /// </summary>
+        public Texture Texture => _GetTexture();
+
+        /// <summary>
+        /// Gets the index of texture's TEXCOORD_[index] attribute used for texture coordinate mapping.
+        /// </summary>
+        public int TextureCoordinate => _TextureInfo(false)?.TextureCoordinate ?? 0;
+
+        public TextureTransform TextureTransform => _TextureInfo(false)?.Transform;
+
+        public TextureSampler TextureSampler => Texture?.Sampler;
+
+        public bool IsTextureAmountSupported { get; private set; }
+
+        public Single TextureAmount
+        {
+            get => _AmountGetter();
+            set => _AmountSetter(value);
+        }
+
+        public bool IsColorSupported { get; private set; }
+
+        public Vector4 Color
+        {
+            get => _ColorGetter();
+            set => _ColorSetter(value);
+        }
+
+        #endregion
+
+        #region API
+
+        private Texture _GetTexture()
+        {
+            var texInfo = _TextureInfo?.Invoke(false);
+            if (texInfo == null) return null;
+
+            return _Material.LogicalParent.LogicalTextures[texInfo._LogicalTextureIndex];
+        }
+
+        public void SetTexture(
+            int texCoord,
+            Image texImg,
+            TextureMipMapMode min = TextureMipMapMode.LINEAR_MIPMAP_LINEAR,
+            TextureInterpolationMode mag = TextureInterpolationMode.LINEAR,
+            TextureWrapMode ws = TextureWrapMode.REPEAT,
+            TextureWrapMode wt = TextureWrapMode.REPEAT)
+        {
+            if (texImg == null) return; // in theory, we should completely remove the TextureInfo
+
+            if (_Material == null) throw new InvalidOperationException();
+
+            var sampler = _Material.LogicalParent.UseSampler(mag, min, ws, wt);
+            var texture = _Material.LogicalParent.UseTexture(texImg, sampler);
+
+            SetTexture(texCoord, texture);
+        }
+
+        public void SetTexture(int texSet, Texture tex)
+        {
+            Guard.NotNull(tex, nameof(tex));
+            Guard.MustShareLogicalParent(_Material, tex, nameof(tex));
+
+            if (_TextureInfo == null) throw new InvalidOperationException();
+
+            var texInfo = _TextureInfo(true);
+
+            texInfo.TextureCoordinate = texSet;
+            texInfo._LogicalTextureIndex = tex.LogicalIndex;
+        }
+
+        public void SetTransform(Vector2 offset, Vector2 scale, float rotation = 0, int? texCoordOverride = null)
+        {
+            if (_TextureInfo == null) throw new InvalidOperationException();
+
+            var texInfo = _TextureInfo(true);
+
+            texInfo.SetTransform(offset, scale, rotation, texCoordOverride);
+        }
+
+        #endregion
+    }
+}

+ 0 - 206
src/SharpGLTF.Core/Schema2/gltf.Materials.cs

@@ -1,206 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Numerics;
-using System.Text;
-
-namespace SharpGLTF.Schema2
-{
-    [System.Diagnostics.DebuggerDisplay("Material[{LogicalIndex}] {Name}")]
-    public sealed partial class Material
-    {
-        #region lifecycle
-
-        internal Material() { }
-
-        #endregion
-
-        #region properties
-
-        /// <summary>
-        /// Gets the zero-based index of this <see cref="Material"/> at <see cref="ModelRoot.LogicalMaterials"/>
-        /// </summary>
-        public int LogicalIndex => this.LogicalParent.LogicalMaterials.IndexOfReference(this);
-
-        /// <summary>
-        /// Gets or sets the <see cref="AlphaMode"/> of this <see cref="Material"/> instance.
-        /// </summary>
-        public AlphaMode Alpha
-        {
-            get => _alphaMode.AsValue(_alphaModeDefault);
-            set => _alphaMode = value.AsNullable(_alphaModeDefault);
-        }
-
-        /// <summary>
-        /// Gets or sets the <see cref="AlphaCutoff"/> of this <see cref="Material"/> instance.
-        /// </summary>
-        public Single AlphaCutoff
-        {
-            get => (Single)_alphaCutoff.AsValue(_alphaCutoffDefault);
-            set => _alphaCutoff = ((Double)value).AsNullable(_alphaCutoffDefault, _alphaCutoffMinimum, double.MaxValue);
-        }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this <see cref="Material"/> will render as Double Sided.
-        /// </summary>
-        public Boolean DoubleSided
-        {
-            get => _doubleSided.AsValue(_doubleSidedDefault);
-            set => _doubleSided = value.AsNullable(_doubleSidedDefault);
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether this <see cref="Material"/> instance has Unlit extension.
-        /// </summary>
-        public Boolean Unlit => this.GetExtension<MaterialUnlit>() != null;
-
-        /// <summary>
-        /// Gets a collection of <see cref="MaterialChannelView"/> elements available in this <see cref="Material"/> instance.
-        /// </summary>
-        public IEnumerable<MaterialChannelView> Channels => _GetChannels();
-
-        #endregion
-
-        #region API
-
-        /// <inheritdoc />
-        protected override IEnumerable<ExtraProperties> GetLogicalChildren()
-        {
-            return base.GetLogicalChildren().ConcatItems(_normalTexture, _emissiveTexture, _occlusionTexture, _pbrMetallicRoughness);
-        }
-
-        /// <summary>
-        /// Finds an instance of <see cref="MaterialChannelView"/>
-        /// </summary>
-        /// <param name="key">the channel key</param>
-        /// <returns>A <see cref="MaterialChannelView"/> instance, or null if <paramref name="key"/> does not exist.</returns>
-        public MaterialChannelView FindChannel(string key)
-        {
-            return Channels.FirstOrDefault(item => item.Key == key);
-        }
-
-        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
-    }
-
-    [System.Diagnostics.DebuggerDisplay("Channel {_Key}")]
-    public struct MaterialChannelView
-    {
-        #region lifecycle
-
-        internal MaterialChannelView(Material m, String key, Func<Boolean, TextureInfo> texInfo, Func<Vector4> fg, Action<Vector4> fs)
-        {
-            _Key = key;
-            _Material = m;
-            _TextureInfoGetter = texInfo;
-            _FactorGetter = fg;
-            _FactorSetter = fs;
-        }
-
-        #endregion
-
-        #region data
-
-        private readonly String _Key;
-        private readonly Material _Material;
-
-        private readonly Func<Boolean, TextureInfo> _TextureInfoGetter;
-
-        private readonly Func<Vector4> _FactorGetter;
-        private readonly Action<Vector4> _FactorSetter;
-
-        #endregion
-
-        #region properties
-
-        public Material LogicalParent => _Material;
-
-        public Boolean Exists => _Material != null;
-
-        public String Key => _Key;
-
-        public Texture Texture => _TextureInfoGetter?.Invoke(false) == null ? null : _Material.LogicalParent.LogicalTextures[_TextureInfoGetter(false)._LogicalTextureIndex];
-
-        public int Set => _TextureInfoGetter?.Invoke(false)?.TextureSet ?? 0;
-
-        public Image Image => Texture?.Image;
-
-        public TextureSampler Sampler => Texture?.Sampler;
-
-        public Vector4 Factor => _FactorGetter?.Invoke() ?? Vector4.One;
-
-        public TextureTransform Transform => _TextureInfoGetter?.Invoke(false)?.Transform;
-
-        #endregion
-
-        #region API
-
-        public void SetFactor(float value) { SetFactor(new Vector4(1, 1, 1, value)); }
-
-        public void SetFactor(Vector4 value)
-        {
-            if (_FactorSetter == null) throw new InvalidOperationException();
-
-            _FactorSetter?.Invoke(value);
-        }
-
-        public void SetTexture(
-            int texSet,
-            Image texImg,
-            TextureMipMapMode min = TextureMipMapMode.LINEAR_MIPMAP_LINEAR,
-            TextureInterpolationMode mag = TextureInterpolationMode.LINEAR,
-            TextureWrapMode ws = TextureWrapMode.REPEAT,
-            TextureWrapMode wt = TextureWrapMode.REPEAT)
-        {
-            if (texImg == null) return; // in theory, we should completely remove the TextureInfo
-
-            if (_Material == null) throw new InvalidOperationException();
-
-            var sampler = _Material.LogicalParent.UseSampler(mag, min, ws, wt);
-            var texture = _Material.LogicalParent.UseTexture(texImg, sampler);
-
-            SetTexture(texSet, texture);
-        }
-
-        public void SetTexture(int texSet, Texture tex)
-        {
-            Guard.NotNull(tex, nameof(tex));
-            Guard.MustShareLogicalParent(_Material, tex, nameof(tex));
-
-            if (_TextureInfoGetter == null) throw new InvalidOperationException();
-
-            var texInfo = _TextureInfoGetter(true);
-
-            texInfo.TextureSet = texSet;
-            texInfo._LogicalTextureIndex = tex.LogicalIndex;
-        }
-
-        public void SetTransform(int texCoord, Vector2 offset, Vector2 scale, float rotation)
-        {
-            if (_TextureInfoGetter == null) throw new InvalidOperationException();
-
-            var texInfo = _TextureInfoGetter(true);
-
-            texInfo.SetTransform(texCoord, offset, scale, rotation);
-        }
-
-        #endregion
-    }
-}

+ 18 - 34
src/SharpGLTF.Core/Schema2/gltf.MaterialsFactory.cs

@@ -39,7 +39,7 @@ namespace SharpGLTF.Schema2
             this.SetExtension(new MaterialUnlit(this));
             this.SetExtension(new MaterialUnlit(this));
         }
         }
 
 
-        private IEnumerable<MaterialChannelView> _GetChannels()
+        private IEnumerable<MaterialChannel> _GetChannels()
         {
         {
             if (_pbrMetallicRoughness != null)
             if (_pbrMetallicRoughness != null)
             {
             {
@@ -54,25 +54,11 @@ namespace SharpGLTF.Schema2
                 foreach (var c in channels) yield return c;
                 foreach (var c in channels) yield return c;
             }
             }
 
 
-            yield return new MaterialChannelView
-                (
-                this,
-                "Normal",
-                _GetNormalTexture,
-                () => _GetNormalTexture(false) == null ? Vector4.One : new Vector4(1, 1, 1, _GetNormalTexture(false).Factor),
-                value => _GetNormalTexture(true).Factor = value.W
-                );
+            yield return new MaterialChannel(this, "Normal", _GetNormalTexture);
 
 
-            yield return new MaterialChannelView
-                (
-                this,
-                "Occlusion",
-                _GetOcclusionTexture,
-                () => _GetOcclusionTexture(false) == null ? Vector4.One : new Vector4(1, 1, 1, _GetOcclusionTexture(false).Factor),
-                value => _GetOcclusionTexture(true).Factor = value.W
-                );
+            yield return new MaterialChannel(this, "Occlusion", _GetOcclusionTexture);
 
 
-            yield return new MaterialChannelView
+            yield return new MaterialChannel
                 (
                 (
                 this,
                 this,
                 "Emissive",
                 "Emissive",
@@ -123,9 +109,9 @@ namespace SharpGLTF.Schema2
             return _metallicRoughnessTexture;
             return _metallicRoughnessTexture;
         }
         }
 
 
-        public IEnumerable<MaterialChannelView> GetChannels(Material material)
+        public IEnumerable<MaterialChannel> GetChannels(Material material)
         {
         {
-            yield return new MaterialChannelView
+            yield return new MaterialChannel
                 (
                 (
                 material,
                 material,
                 "BaseColor",
                 "BaseColor",
@@ -134,22 +120,21 @@ namespace SharpGLTF.Schema2
                 value => _baseColorFactor = value.AsNullable(_baseColorFactorDefault)
                 value => _baseColorFactor = value.AsNullable(_baseColorFactorDefault)
                 );
                 );
 
 
-            yield return new MaterialChannelView
+            yield return new MaterialChannel
                 (
                 (
                 material,
                 material,
                 "Metallic",
                 "Metallic",
                 _GetMetallicTexture,
                 _GetMetallicTexture,
-                () => new Vector4(1, 1, 1, (float)(_metallicFactor ?? _metallicFactorDefault)),
-                value => _metallicFactor = ((double)value.W).AsNullable(_metallicFactorDefault, _metallicFactorMaximum, _metallicFactorMaximum)
+                () => (float)_metallicFactor.AsValue(_metallicFactorDefault),
+                value => _metallicFactor = ((double)value).AsNullable(_metallicFactorDefault, _metallicFactorMaximum, _metallicFactorMaximum)
                 );
                 );
 
 
-            yield return new MaterialChannelView
+            yield return new MaterialChannel
                 (
                 (
                 material,
                 material,
                 "Roughness",
                 "Roughness",
-                null,
-                () => new Vector4(1, 1, 1, (float)(_roughnessFactor ?? _roughnessFactorDefault)),
-                value => _roughnessFactor = ((double)value.W).AsNullable(_roughnessFactorDefault, _roughnessFactorMinimum, _roughnessFactorMaximum)
+                () => (float)_roughnessFactor.AsValue(_roughnessFactorDefault),
+                value => _roughnessFactor = ((double)value).AsNullable(_roughnessFactorDefault, _roughnessFactorMinimum, _roughnessFactorMaximum)
                 );
                 );
         }
         }
     }
     }
@@ -176,9 +161,9 @@ namespace SharpGLTF.Schema2
             return _specularGlossinessTexture;
             return _specularGlossinessTexture;
         }
         }
 
 
-        public IEnumerable<MaterialChannelView> GetChannels(Material material)
+        public IEnumerable<MaterialChannel> GetChannels(Material material)
         {
         {
-            yield return new MaterialChannelView
+            yield return new MaterialChannel
                 (
                 (
                 material,
                 material,
                 "Diffuse",
                 "Diffuse",
@@ -187,20 +172,19 @@ namespace SharpGLTF.Schema2
                 value => _diffuseFactor = value.AsNullable(_diffuseFactorDefault)
                 value => _diffuseFactor = value.AsNullable(_diffuseFactorDefault)
                 );
                 );
 
 
-            yield return new MaterialChannelView
+            yield return new MaterialChannel
                 (
                 (
                 material,
                 material,
                 "Glossiness",
                 "Glossiness",
                 _GetGlossinessTexture,
                 _GetGlossinessTexture,
-                () => new Vector4(1, 1, 1, (float)_glossinessFactor.AsValue(_glossinessFactorDefault)),
-                value => _glossinessFactor = ((double)value.W).AsNullable(_glossinessFactorDefault, _glossinessFactorMinimum, _glossinessFactorMaximum)
+                () => (float)_glossinessFactor.AsValue(_glossinessFactorDefault),
+                value => _glossinessFactor = ((double)value).AsNullable(_glossinessFactorDefault, _glossinessFactorMinimum, _glossinessFactorMaximum)
                 );
                 );
 
 
-            yield return new MaterialChannelView
+            yield return new MaterialChannel
                 (
                 (
                 material,
                 material,
                 "Specular",
                 "Specular",
-                null,
                 () => { var rgb = _specularFactor.AsValue(_specularFactorDefault); return new Vector4(rgb, 1); },
                 () => { var rgb = _specularFactor.AsValue(_specularFactorDefault); return new Vector4(rgb, 1); },
                 value => _specularFactor = new Vector3(value.X, value.Y, value.Z).AsNullable(_specularFactorDefault)
                 value => _specularFactor = new Vector3(value.X, value.Y, value.Z).AsNullable(_specularFactorDefault)
                 );
                 );

+ 63 - 7
src/SharpGLTF.Core/Schema2/gltf.TextureInfo.cs

@@ -16,9 +16,12 @@ namespace SharpGLTF.Schema2
             set => _index = value;
             set => _index = value;
         }
         }
 
 
-        public int TextureSet
+        /// <summary>
+        /// Gets or sets the index of texture's TEXCOORD_[index] attribute used for texture coordinate mapping.
+        /// </summary>
+        public int TextureCoordinate
         {
         {
-            get => _texCoord ?? _texCoordDefault;
+            get => _texCoord.AsValue(_texCoordDefault);
             set => _texCoord = value.AsNullable(_texCoordDefault, _texCoordMinimum, int.MaxValue);
             set => _texCoord = value.AsNullable(_texCoordDefault, _texCoordMinimum, int.MaxValue);
         }
         }
 
 
@@ -27,7 +30,7 @@ namespace SharpGLTF.Schema2
             get => this.GetExtension<TextureTransform>();
             get => this.GetExtension<TextureTransform>();
         }
         }
 
 
-        public virtual Single Factor
+        public virtual Single Amount
         {
         {
             get { return 1; }
             get { return 1; }
             set { }
             set { }
@@ -37,11 +40,11 @@ namespace SharpGLTF.Schema2
 
 
         #region API
         #region API
 
 
-        public void SetTransform(int texCoord, Vector2 offset, Vector2 scale, float rotation)
+        public void SetTransform(Vector2 offset, Vector2 scale, float rotation, int? texCoordOverride = null)
         {
         {
             var xform = new TextureTransform(this)
             var xform = new TextureTransform(this)
             {
             {
-                TextureCoordinate = texCoord,
+                TextureCoordinateOverride = texCoordOverride,
                 Offset = offset,
                 Offset = offset,
                 Scale = scale,
                 Scale = scale,
                 Rotation = rotation
                 Rotation = rotation
@@ -54,12 +57,65 @@ namespace SharpGLTF.Schema2
         #endregion
         #endregion
     }
     }
 
 
+    [System.Diagnostics.DebuggerDisplay("TextureTransform {Offset} {Scale} {Rotation} {TextureCoordinate}")]
+    public partial class TextureTransform
+    {
+        #region lifecycle
+
+        internal TextureTransform(TextureInfo parent) { }
+
+        #endregion
+
+        #region properties
+
+        public Vector2 Offset
+        {
+            get => _offset.AsValue(_offsetDefault);
+            set => _offset = value.AsNullable(_offsetDefault);
+        }
+
+        public Vector2 Scale
+        {
+            get => _scale.AsValue(_scaleDefault);
+            set => _scale = value.AsNullable(_scaleDefault);
+        }
+
+        public float Rotation
+        {
+            get => (float)_rotation.AsValue(_rotationDefault);
+            set => _rotation = ((double)value).AsNullable(_rotationDefault);
+        }
+
+        /// <summary>
+        /// Gets or sets a value that overrides <see cref="TextureInfo.TextureCoordinate"/> if supplied, and if this extension is supported.
+        /// </summary>
+        public int? TextureCoordinateOverride
+        {
+            get => _texCoord;
+            set => _texCoord = value;
+        }
+
+        internal bool IsDefault
+        {
+            get
+            {
+                if (_texCoord.HasValue) return false;
+                if (_offset.HasValue) return false;
+                if (_scale.HasValue) return false;
+                if (_rotation.HasValue) return false;
+                return true;
+            }
+        }
+
+        #endregion
+    }
+
     [System.Diagnostics.DebuggerDisplay("Normal Texture[{LogicalIndex}] {Name}")]
     [System.Diagnostics.DebuggerDisplay("Normal Texture[{LogicalIndex}] {Name}")]
     internal sealed partial class MaterialNormalTextureInfo
     internal sealed partial class MaterialNormalTextureInfo
     {
     {
         #region properties
         #region properties
 
 
-        public override Single Factor
+        public override Single Amount
         {
         {
             get => (Single)this._scale.AsValue(_scaleDefault);
             get => (Single)this._scale.AsValue(_scaleDefault);
             set => this._scale = ((Double)value).AsNullable(_scaleDefault);
             set => this._scale = ((Double)value).AsNullable(_scaleDefault);
@@ -73,7 +129,7 @@ namespace SharpGLTF.Schema2
     {
     {
         #region properties
         #region properties
 
 
-        public override Single Factor
+        public override Single Amount
         {
         {
             get => (Single)this._strength.AsValue(_strengthDefault);
             get => (Single)this._strength.AsValue(_strengthDefault);
             set => this._strength = ((Double)value).AsNullable(_strengthDefault, _strengthMinimum, _strengthMaximum);
             set => this._strength = ((Double)value).AsNullable(_strengthDefault, _strengthMinimum, _strengthMaximum);

+ 2 - 50
src/SharpGLTF.Core/Schema2/gltf.Textures.cs

@@ -80,55 +80,7 @@ namespace SharpGLTF.Schema2
         #endregion
         #endregion
     }
     }
 
 
-    [System.Diagnostics.DebuggerDisplay("TextureTransform {TextureCoordinate} {Offset} {Scale} {Rotation}")]
-    public partial class TextureTransform
-    {
-        #region lifecycle
-
-        internal TextureTransform(TextureInfo parent) { }
-
-        #endregion
-
-        #region properties
-
-        public Vector2 Offset
-        {
-            get => _offset.AsValue(_offsetDefault);
-            set => _offset = value.AsNullable(_offsetDefault);
-        }
-
-        public Vector2 Scale
-        {
-            get => _scale.AsValue(_scaleDefault);
-            set => _scale = value.AsNullable(_scaleDefault);
-        }
-
-        public float Rotation
-        {
-            get => (float)_rotation.AsValue(_rotationDefault);
-            set => _rotation = ((double)value).AsNullable(_rotationDefault);
-        }
-
-        public int TextureCoordinate
-        {
-            get => _texCoord.AsValue(_texCoordMinimum);
-            set => _texCoord = value.AsNullable(_texCoordMinimum, _texCoordMinimum, int.MaxValue);
-        }
-
-        internal bool IsDefault
-        {
-            get
-            {
-                if (_texCoord.HasValue) return false;
-                if (_offset.HasValue) return false;
-                if (_scale.HasValue) return false;
-                if (_rotation.HasValue) return false;
-                return true;
-            }
-        }
-
-        #endregion
-    }
+    
 
 
     public partial class ModelRoot
     public partial class ModelRoot
     {
     {
@@ -190,7 +142,7 @@ namespace SharpGLTF.Schema2
             return new T
             return new T
             {
             {
                 _LogicalTextureIndex = tex.LogicalIndex,
                 _LogicalTextureIndex = tex.LogicalIndex,
-                TextureSet = textureSet
+                TextureCoordinate = textureSet
             };
             };
         }
         }
     }
     }

+ 1 - 1
src/SharpGLTF.Toolkit/Geometry/StaticMeshBuilder.cs

@@ -35,7 +35,7 @@ namespace SharpGLTF.Geometry
     /// <see cref="VertexJoints16x4"/>,
     /// <see cref="VertexJoints16x4"/>,
     /// <see cref="VertexJoints16x8"/>.
     /// <see cref="VertexJoints16x8"/>.
     /// </typeparam>
     /// </typeparam>
-    public class MeshBuilder<TvP, TvM, TvJ> : MeshBuilder<Materials.MaterialBuilder,TvP,TvM,TvJ>
+    public class MeshBuilder<TvP, TvM, TvJ> : MeshBuilder<Materials.MaterialBuilder, TvP, TvM, TvJ>
         where TvP : struct, IVertexPosition
         where TvP : struct, IVertexPosition
         where TvM : struct, IVertexMaterial
         where TvM : struct, IVertexMaterial
         where TvJ : struct, IVertexJoints
         where TvJ : struct, IVertexJoints

+ 6 - 5
src/SharpGLTF.Toolkit/IO/WavefrontWriter.cs

@@ -9,6 +9,8 @@ using static System.FormattableString;
 
 
 namespace SharpGLTF.IO
 namespace SharpGLTF.IO
 {
 {
+    using Schema2;
+
     using BYTES = ArraySegment<Byte>;
     using BYTES = ArraySegment<Byte>;
 
 
     using POSITION = Geometry.VertexTypes.VertexPositionNormal;
     using POSITION = Geometry.VertexTypes.VertexPositionNormal;
@@ -234,15 +236,14 @@ namespace SharpGLTF.IO
                 var srcMaterial = triangle.Item4;
                 var srcMaterial = triangle.Item4;
                 if (srcMaterial != null)
                 if (srcMaterial != null)
                 {
                 {
-                    var baseColor = srcMaterial.FindChannel("BaseColor");
+                    // https://stackoverflow.com/questions/36510170/how-to-calculate-specular-contribution-in-pbr
 
 
-                    var clr = new Vector3(baseColor.Factor.X, baseColor.Factor.Y, baseColor.Factor.Z);
+                    var diffuse = srcMaterial.GetDiffuseColor(Vector4.One);
 
 
-                    // https://stackoverflow.com/questions/36510170/how-to-calculate-specular-contribution-in-pbr
-                    dstMaterial.DiffuseColor = clr;
+                    dstMaterial.DiffuseColor = new Vector3(diffuse.X, diffuse.Y, diffuse.Z);
                     dstMaterial.SpecularColor = new Vector3(0.2f);
                     dstMaterial.SpecularColor = new Vector3(0.2f);
 
 
-                    dstMaterial.DiffuseTexture = baseColor.Image?.GetImageContent() ?? default;
+                    dstMaterial.DiffuseTexture = srcMaterial.GetDiffuseTexture()?.Image?.GetImageContent() ?? default;
                 }
                 }
 
 
                 this.AddTriangle(dstMaterial, triangle.Item1, triangle.Item2, triangle.Item3);
                 this.AddTriangle(dstMaterial, triangle.Item1, triangle.Item2, triangle.Item3);

+ 1 - 1
src/SharpGLTF.Toolkit/Materials/Channel.cs

@@ -24,7 +24,7 @@ namespace SharpGLTF.Materials
 
 
         public String Key => _Key;
         public String Key => _Key;
 
 
-        public Vector4 Factor { get; set; } = Vector4.One;
+        public Vector4 Color { get; set; } = Vector4.One;
 
 
         public TextureBuilder Texture { get; set; }
         public TextureBuilder Texture { get; set; }
 
 

+ 2 - 2
src/SharpGLTF.Toolkit/Materials/Material.cs

@@ -104,13 +104,13 @@ namespace SharpGLTF.Materials
 
 
         public MaterialBuilder WithChannelColor(KnownChannels channelKey, Vector4 color)
         public MaterialBuilder WithChannelColor(KnownChannels channelKey, Vector4 color)
         {
         {
-            this.UseChannel(channelKey).Factor = color;
+            this.UseChannel(channelKey).Color = color;
             return this;
             return this;
         }
         }
 
 
         public MaterialBuilder WithChannelColor(string channelKey, Vector4 color)
         public MaterialBuilder WithChannelColor(string channelKey, Vector4 color)
         {
         {
-            this.UseChannel(channelKey).Factor = color;
+            this.UseChannel(channelKey).Color = color;
             return this;
             return this;
         }
         }
 
 

+ 2 - 0
src/SharpGLTF.Toolkit/Materials/Texture.cs

@@ -32,6 +32,8 @@ 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.LINEAR;
         public TEXMIPMAP MinFilter { get; set; } = TEXMIPMAP.LINEAR;

+ 81 - 37
src/SharpGLTF.Toolkit/Schema2/MaterialExtensions.cs

@@ -28,9 +28,9 @@ namespace SharpGLTF.Schema2
         /// <returns>This <see cref="Material"/> instance.</returns>
         /// <returns>This <see cref="Material"/> instance.</returns>
         public static Material WithDefault(this Material material, Vector4 diffuseColor)
         public static Material WithDefault(this Material material, Vector4 diffuseColor)
         {
         {
-            material.WithPBRMetallicRoughness()
-                .FindChannel("BaseColor")
-                .SetFactor(diffuseColor);
+            var ch = material.WithPBRMetallicRoughness().FindChannel("BaseColor").Value;
+
+            ch.Color = diffuseColor;
 
 
             return material;
             return material;
         }
         }
@@ -41,15 +41,30 @@ namespace SharpGLTF.Schema2
             return material;
             return material;
         }
         }
 
 
-        public static Material WithChannelTexture(this Material material, string channelName, int textureSet, string imageFilePath)
+        public static Material WithChannelColor(this Material material, string channelName, Vector4 color)
         {
         {
-            material.FindChannel(channelName).SetTexture(textureSet, material.LogicalParent.UseImageWithFile(imageFilePath));
+            var channel = material.FindChannel(channelName).Value;
+
+            channel.Color = color;
+
             return material;
             return material;
         }
         }
 
 
-        public static Material WithChannelTexture(this Material material, string channelName, int textureSet, Image image)
+        public static Material WithChannelTexture(this Material material, string channelName, int textureSet, string imageFilePath, float amount = 1)
+        {
+            var image = material.LogicalParent.UseImageWithFile(imageFilePath);
+
+            return material.WithChannelTexture(channelName, textureSet, image, amount);
+        }
+
+        public static Material WithChannelTexture(this Material material, string channelName, int textureSet, Image image, float amount = 1)
         {
         {
-            material.FindChannel(channelName).SetTexture(textureSet, image);
+            var channel = material.FindChannel(channelName).Value;
+
+            channel.SetTexture(textureSet, image);
+
+            channel.TextureAmount = amount;
+
             return material;
             return material;
         }
         }
 
 
@@ -66,20 +81,16 @@ namespace SharpGLTF.Schema2
 
 
         public static Material WithPBRMetallicRoughness(
         public static Material WithPBRMetallicRoughness(
             this Material material,
             this Material material,
-            Vector4 baseColorFactor, string baseColorImageFilePath,
-            float metallicFactor = 1, string metallicImageFilePath = null,
-            float roughnessFactor = 1, string roughtnessImageFilePath = null
+            Vector4 baseColor, string baseColorImageFilePath,
+            float metallicAmount = 1, string metallicImageFilePath = null,
+            float roughnessAmount = 1, string roughtnessImageFilePath = null
             )
             )
         {
         {
             material.WithPBRMetallicRoughness();
             material.WithPBRMetallicRoughness();
 
 
-            if (!string.IsNullOrWhiteSpace(baseColorImageFilePath)) material.WithChannelTexture("BaseColor", 0, baseColorImageFilePath);
-            if (!string.IsNullOrWhiteSpace(metallicImageFilePath)) material.WithChannelTexture("Metallic", 0, baseColorImageFilePath);
-            if (!string.IsNullOrWhiteSpace(roughtnessImageFilePath)) material.WithChannelTexture("Roughness", 0, baseColorImageFilePath);
-
-            material.FindChannel("BaseColor").SetFactor(baseColorFactor);
-            material.FindChannel("Metallic").SetFactor(metallicFactor);
-            material.FindChannel("Roughness").SetFactor(roughnessFactor);
+            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);
 
 
             return material;
             return material;
         }
         }
@@ -170,19 +181,21 @@ namespace SharpGLTF.Schema2
             }
             }
         }
         }
 
 
-        public static void CopyTo(this MaterialChannelView srcChannel, Materials.MaterialChannelBuilder dstChannel)
+        public static void CopyTo(this MaterialChannel srcChannel, Materials.MaterialChannelBuilder dstChannel)
         {
         {
-            dstChannel.Factor = srcChannel.Factor;
+            dstChannel.Color = srcChannel.Color;
 
 
             if (srcChannel.Texture == null) { dstChannel.Texture = null; return; }
             if (srcChannel.Texture == null) { dstChannel.Texture = null; return; }
 
 
-            if (srcChannel.Texture == null) dstChannel.Texture = new Materials.TextureBuilder();
+            if (dstChannel.Texture == null) dstChannel.Texture = new Materials.TextureBuilder();
 
 
-            dstChannel.Texture.CoordinateSet = srcChannel.Set;
-            dstChannel.Texture.MinFilter = srcChannel.Sampler.MinFilter;
-            dstChannel.Texture.MagFilter = srcChannel.Sampler.MagFilter;
-            dstChannel.Texture.WrapS = srcChannel.Sampler.WrapS;
-            dstChannel.Texture.WrapT = srcChannel.Sampler.WrapT;
+            dstChannel.Texture.Amount = srcChannel.TextureAmount;
+
+            dstChannel.Texture.CoordinateSet = srcChannel.TextureCoordinate;
+            dstChannel.Texture.MinFilter = srcChannel.TextureSampler.MinFilter;
+            dstChannel.Texture.MagFilter = srcChannel.TextureSampler.MagFilter;
+            dstChannel.Texture.WrapS = srcChannel.TextureSampler.WrapS;
+            dstChannel.Texture.WrapT = srcChannel.TextureSampler.WrapT;
 
 
             /*
             /*
             dstChannel.Texture.Rotation = srcChannel.Transform?.Rotation ?? 0;
             dstChannel.Texture.Rotation = srcChannel.Transform?.Rotation ?? 0;
@@ -190,7 +203,7 @@ namespace SharpGLTF.Schema2
             dstChannel.Texture.Scale = srcChannel.Transform?.Scale ?? Vector2.One;
             dstChannel.Texture.Scale = srcChannel.Transform?.Scale ?? Vector2.One;
             */
             */
 
 
-            dstChannel.Texture.ImageContent = srcChannel.Image.GetImageContent();
+            dstChannel.Texture.ImageContent = srcChannel.Texture.Image.GetImageContent();
         }
         }
 
 
         public static void CopyTo(this Materials.MaterialBuilder srcMaterial, Material dstMaterial)
         public static void CopyTo(this Materials.MaterialBuilder srcMaterial, Material dstMaterial)
@@ -201,33 +214,33 @@ namespace SharpGLTF.Schema2
             dstMaterial.AlphaCutoff = srcMaterial.AlphaCutoff;
             dstMaterial.AlphaCutoff = srcMaterial.AlphaCutoff;
             dstMaterial.DoubleSided = srcMaterial.DoubleSided;
             dstMaterial.DoubleSided = srcMaterial.DoubleSided;
 
 
-            srcMaterial.GetChannel("Normal").CopyTo(dstMaterial.FindChannel("Normal"));
-            srcMaterial.GetChannel("Occlusion").CopyTo(dstMaterial.FindChannel("Occlusion"));
-            srcMaterial.GetChannel("Emissive").CopyTo(dstMaterial.FindChannel("Emissive"));
+            srcMaterial.GetChannel("Normal").CopyTo(dstMaterial.FindChannel("Normal").Value);
+            srcMaterial.GetChannel("Occlusion").CopyTo(dstMaterial.FindChannel("Occlusion").Value);
+            srcMaterial.GetChannel("Emissive").CopyTo(dstMaterial.FindChannel("Emissive").Value);
 
 
             if (srcMaterial.Style == "PBRMetallicRoughness")
             if (srcMaterial.Style == "PBRMetallicRoughness")
             {
             {
                 dstMaterial.InitializePBRMetallicRoughness();
                 dstMaterial.InitializePBRMetallicRoughness();
 
 
-                srcMaterial.GetChannel("BaseColor").CopyTo(dstMaterial.FindChannel("BaseColor"));
-                srcMaterial.GetChannel("Metallic").CopyTo(dstMaterial.FindChannel("Metallic"));
-                srcMaterial.GetChannel("Roughness").CopyTo(dstMaterial.FindChannel("Roughness"));
+                srcMaterial.GetChannel("BaseColor").CopyTo(dstMaterial.FindChannel("BaseColor").Value);
+                srcMaterial.GetChannel("Metallic").CopyTo(dstMaterial.FindChannel("Metallic").Value);
+                srcMaterial.GetChannel("Roughness").CopyTo(dstMaterial.FindChannel("Roughness").Value);
             }
             }
             else if (srcMaterial.Style == "PBRSpecularGlossiness")
             else if (srcMaterial.Style == "PBRSpecularGlossiness")
             {
             {
                 dstMaterial.InitializePBRSpecularGlossiness();
                 dstMaterial.InitializePBRSpecularGlossiness();
 
 
-                srcMaterial.GetChannel("Diffuse").CopyTo(dstMaterial.FindChannel("Diffuse"));
-                srcMaterial.GetChannel("Specular").CopyTo(dstMaterial.FindChannel("Specular"));
-                srcMaterial.GetChannel("Glossiness").CopyTo(dstMaterial.FindChannel("Glossiness"));
+                srcMaterial.GetChannel("Diffuse").CopyTo(dstMaterial.FindChannel("Diffuse").Value);
+                srcMaterial.GetChannel("Specular").CopyTo(dstMaterial.FindChannel("Specular").Value);
+                srcMaterial.GetChannel("Glossiness").CopyTo(dstMaterial.FindChannel("Glossiness").Value);
             }
             }
         }
         }
 
 
-        public static void CopyTo(this Materials.MaterialChannelBuilder srcChannel, MaterialChannelView dstChannel)
+        public static void CopyTo(this Materials.MaterialChannelBuilder srcChannel, MaterialChannel dstChannel)
         {
         {
             if (srcChannel == null) return;
             if (srcChannel == null) return;
 
 
-            dstChannel.SetFactor(srcChannel.Factor);
+            dstChannel.Color = srcChannel.Color;
 
 
             var srcTex = srcChannel.Texture;
             var srcTex = srcChannel.Texture;
 
 
@@ -241,5 +254,36 @@ namespace SharpGLTF.Schema2
         }
         }
 
 
         #endregion
         #endregion
+
+        #region evaluation API
+
+        public static Vector4 GetDiffuseColor(this Material material, Vector4 defaultColor)
+        {
+            if (material == null) return defaultColor;
+
+            var channel = material.FindChannel("BaseColor");
+            if (channel.HasValue) return channel.Value.Color;
+
+            channel = material.FindChannel("Diffuse");
+            if (channel.HasValue) return channel.Value.Color;
+
+            return defaultColor;
+        }
+
+        public static Texture GetDiffuseTexture(this Material material)
+        {
+            if (material == null) return null;
+
+            var channel = material.FindChannel("BaseColor");
+            if (channel.HasValue) return channel.Value.Texture;
+
+            channel = material.FindChannel("Diffuse");
+            if (channel.HasValue) return channel.Value.Texture;
+
+            return null;
+
+        }
+
+        #endregion
     }
     }
 }
 }

+ 0 - 3
tests/SharpGLTF.Tests/Schema2/Authoring/BasicSceneCreationTests.cs

@@ -177,9 +177,6 @@ namespace SharpGLTF.Schema2.Authoring
             model.AttachToCurrentTest("result.glb");
             model.AttachToCurrentTest("result.glb");
             model.AttachToCurrentTest("result.obj");
             model.AttachToCurrentTest("result.obj");
             model.AttachToCurrentTest("result.gltf");
             model.AttachToCurrentTest("result.gltf");
-
-            material.FindChannel("BaseColor").SetTransform(0, Vector2.Zero, Vector2.One, 1.5f);
-            model.AttachToCurrentTest("result_withTransform.glb");
         }
         }
 
 
         [Test(Description = "Creates an interleaved scene using a toolkit utilities")]
         [Test(Description = "Creates an interleaved scene using a toolkit utilities")]