Преглед на файлове

Progress with materials...

Vicente Penades преди 6 години
родител
ревизия
48408f8fa8

+ 2 - 2
build/SharpGLTF.CodeGen/SharpGLTF.CodeGen.csproj

@@ -8,8 +8,8 @@
 
   <ItemGroup>
     <PackageReference Include="LibGit2Sharp" Version="0.26.0" />
-    <PackageReference Include="NJsonSchema.CodeGeneration" Version="9.13.26" />
-    <PackageReference Include="NJsonSchema.CodeGeneration.CSharp" Version="9.13.26" />
+    <PackageReference Include="NJsonSchema.CodeGeneration" Version="9.13.27" />
+    <PackageReference Include="NJsonSchema.CodeGeneration.CSharp" Version="9.13.27" />
   </ItemGroup>
 
 </Project>

+ 25 - 25
src/Shared/_Extensions.cs

@@ -318,27 +318,27 @@ namespace SharpGLTF
 
         public static IEnumerable<(int, int, int)> GetTrianglesIndices(this PrimitiveType ptype, int count)
         {
-            return ptype.GetTrianglesIndices(Enumerable.Range(0, count).Select(item => (UInt32)item));
+            return ptype.GetTrianglesIndices(Enumerable.Range(0, count).Select(item => (UInt32)item), IndexEncodingType.UNSIGNED_INT);
         }
 
-        public static IEnumerable<(int, int, int)> GetTrianglesIndices(this PrimitiveType ptype, IEnumerable<UInt32> indices)
+        public static IEnumerable<(int, int, int)> GetTrianglesIndices(this PrimitiveType ptype, IEnumerable<UInt32> sourceIndices, IndexEncodingType sourceEncoding)
         {
             switch (ptype)
             {
                 case PrimitiveType.TRIANGLES:
                     {
-                        using (var ator = indices.GetEnumerator())
+                        using (var ptr = sourceIndices.GetEnumerator())
                         {
                             while (true)
                             {
-                                if (!ator.MoveNext()) break;
-                                var a = ator.Current;
-                                if (!ator.MoveNext()) break;
-                                var b = ator.Current;
-                                if (!ator.MoveNext()) break;
-                                var c = ator.Current;
-
-                                if (!_IsDegenerated(a,b,c)) yield return ((int)a, (int)b, (int)c);
+                                if (!ptr.MoveNext()) break;
+                                var a = ptr.Current;
+                                if (!ptr.MoveNext()) break;
+                                var b = ptr.Current;
+                                if (!ptr.MoveNext()) break;
+                                var c = ptr.Current;
+
+                                if (!_IsDegenerated(a, b, c)) yield return ((int)a, (int)b, (int)c);
                             }
                         }
 
@@ -347,17 +347,17 @@ namespace SharpGLTF
 
                 case PrimitiveType.TRIANGLE_FAN:
                     {
-                        using (var ator = indices.GetEnumerator())
+                        using (var ptr = sourceIndices.GetEnumerator())
                         {
-                            if (!ator.MoveNext()) break;
-                            var a = ator.Current;
-                            if (!ator.MoveNext()) break;
-                            var b = ator.Current;
+                            if (!ptr.MoveNext()) break;
+                            var a = ptr.Current;
+                            if (!ptr.MoveNext()) break;
+                            var b = ptr.Current;
 
                             while (true)
                             {
-                                if (!ator.MoveNext()) break;
-                                var c = ator.Current;
+                                if (!ptr.MoveNext()) break;
+                                var c = ptr.Current;
 
                                 if (!_IsDegenerated(a, b, c)) yield return ((int)a, (int)b, (int)c);
 
@@ -370,19 +370,19 @@ namespace SharpGLTF
 
                 case PrimitiveType.TRIANGLE_STRIP:
                     {
-                        using (var ator = indices.GetEnumerator())
+                        using (var ptr = sourceIndices.GetEnumerator())
                         {
-                            if (!ator.MoveNext()) break;
-                            var a = ator.Current;
-                            if (!ator.MoveNext()) break;
-                            var b = ator.Current;
+                            if (!ptr.MoveNext()) break;
+                            var a = ptr.Current;
+                            if (!ptr.MoveNext()) break;
+                            var b = ptr.Current;
 
                             bool reversed = false;
 
                             while (true)
                             {
-                                if (!ator.MoveNext()) break;
-                                var c = ator.Current;
+                                if (!ptr.MoveNext()) break;
+                                var c = ptr.Current;
 
                                 if (!_IsDegenerated(a, b, c))
                                 {

+ 7 - 1
src/SharpGLTF.Core/Schema2/gltf.Asset.cs

@@ -13,9 +13,15 @@ namespace SharpGLTF.Schema2
 
         internal static Asset CreateDefault(string copyright)
         {
+            var av = typeof(Asset).Assembly.GetCustomAttributes(true)
+                .OfType<System.Reflection.AssemblyInformationalVersionAttribute>()
+                .FirstOrDefault();
+
+            var generator = av == null ? "SharpGLTF" : "SharpGLTF " + av.InformationalVersion;
+
             return new Asset()
             {
-                _generator = "SharpGLTF",
+                _generator = generator,
                 _copyright = copyright,
                 _version = MAXVERSION.ToString(),
                 _minVersion = MINVERSION.ToString()

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

@@ -104,11 +104,20 @@ namespace SharpGLTF.Schema2
             // check all the extensions used by each object
             var used = new HashSet<string>();
 
+            // search for known extensions
             foreach (var c in allObjects)
             {
-                var ids = c.Extensions.Select(item => ExtensionsFactory.Identify(c.GetType(), item.GetType()));
+                var ids = c.Extensions
+                    .Select(item => ExtensionsFactory.Identify(c.GetType(), item.GetType()))
+                    .Where(item => !string.IsNullOrWhiteSpace(item));
 
-                foreach (var id in ids) used.Add(id);
+                used.UnionWith(ids);
+            }
+
+            // search for unknown extensions
+            foreach (var unk in allObjects.SelectMany(item => item.Extensions).OfType<Unknown>())
+            {
+                used.Add(unk.Name);
             }
 
             return used;

+ 14 - 3
src/SharpGLTF.Core/Schema2/gltf.Materials.cs

@@ -34,10 +34,10 @@ namespace SharpGLTF.Schema2
         /// <summary>
         /// Gets or sets the <see cref="AlphaCutoff"/> of this <see cref="Material"/> instance.
         /// </summary>
-        public Double AlphaCutoff
+        public Single AlphaCutoff
         {
-            get => _alphaCutoff.AsValue(_alphaCutoffDefault);
-            set => _alphaCutoff = value.AsNullable(_alphaCutoffDefault, _alphaCutoffMinimum, double.MaxValue);
+            get => (Single)_alphaCutoff.AsValue(_alphaCutoffDefault);
+            set => _alphaCutoff = ((Double)value).AsNullable(_alphaCutoffDefault, _alphaCutoffMinimum, double.MaxValue);
         }
 
         /// <summary>
@@ -130,6 +130,8 @@ namespace SharpGLTF.Schema2
 
         #region properties
 
+        public Material LogicalParent => _Material;
+
         public Boolean Exists => _Material != null;
 
         public String Key => _Key;
@@ -190,6 +192,15 @@ namespace SharpGLTF.Schema2
             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
     }
 }

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

@@ -56,6 +56,8 @@ namespace SharpGLTF.Schema2
             var dict = new Dictionary<string, ArraySegment<Byte>>();
             var settings = WriteSettings.ForDeepClone(dict);
 
+            System.Diagnostics.Debug.Assert(settings._NoCloneWatchdog, "invalid clone settings");
+
             this.Write(settings, "deepclone");
 
             return ModelRoot.ReadFromDictionary(dict, "deepclone.gltf");

+ 19 - 8
src/SharpGLTF.Core/Schema2/gltf.Serialization.cs

@@ -95,6 +95,7 @@ namespace SharpGLTF.Schema2
                 ImageWriting = ImageWriteMode.SatelliteFile,
                 MergeBuffers = false,
                 JsonFormatting = Formatting.None,
+                _UpdateSupportedExtensions = false,
                 _NoCloneWatchdog = true,
 
                 FileWriter = (fn, buff) => dict[fn] = buff
@@ -115,6 +116,7 @@ namespace SharpGLTF.Schema2
                 ImageWriting = ImageWriteMode.SatelliteFile,
                 MergeBuffers = true,
                 JsonFormatting = Formatting.Indented,
+                _UpdateSupportedExtensions = true,
 
                 FileWriter = (fn, d) => File.WriteAllBytes(Path.Combine(dir, fn), d.ToArray())
             };
@@ -130,6 +132,7 @@ namespace SharpGLTF.Schema2
                 ImageWriting = ImageWriteMode.SatelliteFile,
                 MergeBuffers = false,
                 JsonFormatting = Formatting.None,
+                _UpdateSupportedExtensions = true,
 
                 FileWriter = (fn, buff) => dict[fn] = buff
             };
@@ -149,6 +152,7 @@ namespace SharpGLTF.Schema2
                 ImageWriting = ImageWriteMode.BufferView,
                 MergeBuffers = true,
                 JsonFormatting = Formatting.None,
+                _UpdateSupportedExtensions = true,
 
                 FileWriter = (fn, d) => File.WriteAllBytes(Path.Combine(dir, fn), d.ToArray())
             };
@@ -167,6 +171,7 @@ namespace SharpGLTF.Schema2
                 ImageWriting = ImageWriteMode.BufferView,
                 MergeBuffers = true,
                 JsonFormatting = Formatting.None,
+                _UpdateSupportedExtensions = true,
 
                 FileWriter = (fn, d) => stream.Write(d.Array, d.Offset, d.Count)
             };
@@ -181,12 +186,12 @@ namespace SharpGLTF.Schema2
         #region data
 
         /// <summary>
-        /// Gets or sets a value indicating whether to write a GLTF or a GLB file
+        /// Gets or sets a value indicating whether to write a GLTF or a GLB file.
         /// </summary>
         public Boolean BinaryMode { get; set; }
 
         /// <summary>
-        /// Gets or sets a value indicating how to write the images of the model
+        /// Gets or sets a value indicating how to write the images of the model.
         /// </summary>
         public ImageWriteMode ImageWriting { get; set; }
 
@@ -196,7 +201,7 @@ namespace SharpGLTF.Schema2
         public Boolean MergeBuffers { get; set; }
 
         /// <summary>
-        /// Gets or sets a value indicating how to format the JSON document of the glTF
+        /// Gets or sets a value indicating how to format the JSON document of the glTF.
         /// </summary>
         public Formatting JsonFormatting { get; set; }
 
@@ -205,7 +210,15 @@ namespace SharpGLTF.Schema2
         /// </summary>
         public AssetWriter FileWriter { get; set; }
 
-        internal bool _NoCloneWatchdog = false;
+        /// <summary>
+        /// Gets a value indicating whether to scan the whole model for used extensions.
+        /// </summary>
+        internal Boolean _UpdateSupportedExtensions { get; private set; } = true;
+
+        /// <summary>
+        /// Gets a value indicating whether cloning the model upon serialization is not allowed.
+        /// </summary>
+        internal bool _NoCloneWatchdog { get; private set; } = false;
 
         #endregion
 
@@ -451,10 +464,6 @@ namespace SharpGLTF.Schema2
 
             var settings = WriteSettings.ForText(dict);
 
-            // WriteToDictionary is usually called by DeepClone, so allowing a setting that
-            // would imply cloning the model would cause an infine loop and a StackOverflow
-            settings._NoCloneWatchdog = true;
-
             _Write(settings, fileName, this);
 
             return dict;
@@ -533,6 +542,8 @@ namespace SharpGLTF.Schema2
 
             model = settings.FilterModel(model);
 
+            if (settings._UpdateSupportedExtensions) model.UpdateExtensionsSupport();
+
             if (settings.BinaryMode)
             {
                 var ex = glb.IsBinaryCompatible(model);

+ 3 - 3
src/SharpGLTF.Core/Schema2/gltf.Skin.cs

@@ -118,9 +118,9 @@ namespace SharpGLTF.Schema2
         /// <summary>
         /// Returns true if this <see cref="Skin"/> matches the input values.
         /// </summary>
-        /// <param name="skeleton"></param>
-        /// <param name="joints"></param>
-        /// <returns></returns>
+        /// <param name="skeleton">A <see cref="Node"/> instance that represents the skeleton root.</param>
+        /// <param name="joints">A key value pair collection of <see cref="Node"/> joints and their binding matrices.</param>
+        /// <returns>True if the input values match this <see cref="Skin"/>.</returns>
         public bool IsMatch(Node skeleton, KeyValuePair<Node, Matrix4x4>[] joints)
         {
             if (!ReferenceEquals(skeleton, this.Skeleton)) return false;

+ 42 - 9
src/SharpGLTF.Core/Schema2/gltf.Textures.cs

@@ -26,12 +26,24 @@ namespace SharpGLTF.Schema2
         public TextureTransform Transform
         {
             get => this.GetExtension<TextureTransform>();
-            set
+        }
+
+        #endregion
+
+        #region API
+
+        public void SetTransform(int texCoord, Vector2 offset, Vector2 scale, float rotation)
+        {
+            var xform = new TextureTransform(this)
             {
-                if (value == null) { this.RemoveExtensions<TextureTransform>(); return; }
+                TextureCoordinate = texCoord,
+                Offset = offset,
+                Scale = scale,
+                Rotation = rotation
+            };
 
-                this.SetExtension(value);
-            }
+            if (xform.IsDefault) this.RemoveExtensions<TextureTransform>();
+            else this.SetExtension(xform);
         }
 
         #endregion
@@ -128,21 +140,28 @@ namespace SharpGLTF.Schema2
         /// </summary>
         public int LogicalIndex => this.LogicalParent.LogicalTextureSamplers.IndexOfReference(this);
 
-        public TextureInterpolationMode MagFilter => _magFilter ?? TextureInterpolationMode.LINEAR;
+        public TextureInterpolationMode MagFilter => _magFilter.AsValue(TextureInterpolationMode.LINEAR);
 
-        public TextureMipMapMode MinFilter => _minFilter ?? TextureMipMapMode.LINEAR;
+        public TextureMipMapMode MinFilter => _minFilter.AsValue(TextureMipMapMode.LINEAR);
 
-        public TextureWrapMode WrapS => _wrapS ?? _wrapSDefault;
+        public TextureWrapMode WrapS => _wrapS.AsValue(_wrapSDefault);
 
-        public TextureWrapMode WrapT => _wrapT ?? _wrapSDefault;
+        public TextureWrapMode WrapT => _wrapT.AsValue(_wrapTDefault);
 
         #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);
@@ -164,8 +183,22 @@ namespace SharpGLTF.Schema2
         public int TextureCoordinate
         {
             get => _texCoord.AsValue(_texCoordMinimum);
-            set => _texCoord = value.AsNullable(_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

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

@@ -227,7 +227,7 @@ namespace SharpGLTF.IO
 
         public void AddModel(Schema2.ModelRoot model)
         {
-            foreach (var triangle in Schema2.Toolkit.Triangulate<POSITION, TEXCOORD, VEMPTY>(model.DefaultScene))
+            foreach (var triangle in Schema2.Schema2Toolkit.Triangulate<POSITION, TEXCOORD, VEMPTY>(model.DefaultScene))
             {
                 var dstMaterial = new Material();
 

+ 21 - 0
src/SharpGLTF.Toolkit/Materials/Channel.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Text;
+
+namespace SharpGLTF.Materials
+{
+    [System.Diagnostics.DebuggerDisplay("{_key} {Factor}")]
+    public class Channel
+    {
+        internal Channel(string key) { _Key = key; }
+
+        private readonly String _Key;
+
+        public String Key => _Key;
+
+        public Vector4 Factor { get; set; }
+
+        public Texture Texture { get; set; }
+    }
+}

+ 59 - 0
src/SharpGLTF.Toolkit/Materials/Material.cs

@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace SharpGLTF.Materials
+{
+    public class MaterialBuilder
+    {
+        #region data
+
+        private readonly List<Channel> _Channels = new List<Channel>();
+
+        #endregion
+
+        #region properties
+
+        public string Name { get; set; }
+
+        public IReadOnlyCollection<Channel> Channels => _Channels;
+
+        public Schema2.AlphaMode Alpha { get; set; } = Schema2.AlphaMode.OPAQUE;
+
+        public Single AlphaCutoff { get; set; } = 0.5f;
+
+        public Boolean DoubleSided { get; set; } = false;
+
+        public Boolean Unlit { get; set; } = false;
+
+        public String Style { get; set; } = "PBRMetallicRoughness";
+
+        #endregion
+
+        #region API
+
+        public Channel GetChannel(string key)
+        {
+            Guard.NotNullOrEmpty(key, nameof(key));
+
+            key = key.ToLower();
+
+            return _Channels.FirstOrDefault(item => string.Equals(key, item.Key, StringComparison.OrdinalIgnoreCase));
+        }
+
+        public Channel UseChannel(string key)
+        {
+            var ch = GetChannel(key);
+
+            if (ch != null) return ch;
+
+            ch = new Channel(key);
+            _Channels.Add(ch);
+
+            return ch;
+        }
+
+        #endregion
+    }
+}

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

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Text;
+
+namespace SharpGLTF.Materials
+{
+    [System.Diagnostics.DebuggerDisplay("Texture {CoordinateSet} {MinFilter} {MagFilter} {WrapS} {WrapT} {Rotation} {Offset} {Scale}")]
+    public class Texture
+    {
+        #region properties
+
+        public int CoordinateSet { get; set; }
+
+        public Single Rotation { get; set; }
+
+        public Vector2 Offset { get; set; }
+
+        public Vector2 Scale { get; set; }
+
+        public ArraySegment<Byte> ImageContent { get; set; }
+
+        public Schema2.TextureMipMapMode MinFilter { get; set; }
+
+        public Schema2.TextureInterpolationMode MagFilter { get; set; }
+
+        public Schema2.TextureWrapMode WrapS { get; set; } = Schema2.TextureWrapMode.REPEAT;
+
+        public Schema2.TextureWrapMode WrapT { get; set; } = Schema2.TextureWrapMode.REPEAT;
+
+        #endregion
+    }
+}

+ 3 - 1
src/SharpGLTF.Toolkit/Schema2/AccessorExtensions.cs

@@ -5,10 +5,12 @@ using System.Numerics;
 
 namespace SharpGLTF.Schema2
 {
-    public static partial class Toolkit
+    public static partial class Schema2Toolkit
     {
         public static Accessor CreateVertexAccessor(this ModelRoot root, Memory.MemoryAccessor memAccessor)
         {
+            Guard.NotNull(memAccessor, nameof(memAccessor));
+
             var accessor = root.CreateAccessor(memAccessor.Attribute.Name);
 
             accessor.SetVertexData(memAccessor);

+ 1 - 1
src/SharpGLTF.Toolkit/Schema2/AnimationExtensions.cs

@@ -6,7 +6,7 @@ using System.Text;
 
 namespace SharpGLTF.Schema2
 {
-    public static partial class Toolkit
+    public static partial class Schema2Toolkit
     {
         public static Animation UseAnimation(this ModelRoot root, string name)
         {

+ 1 - 1
src/SharpGLTF.Toolkit/Schema2/LightExtensions.cs

@@ -5,7 +5,7 @@ using System.Text;
 
 namespace SharpGLTF.Schema2
 {
-    public static partial class Toolkit
+    public static partial class Schema2Toolkit
     {
         /// <summary>
         /// Sets the cone angles for the <see cref="PunctualLightType.Spot"/> light.

+ 94 - 1
src/SharpGLTF.Toolkit/Schema2/MaterialExtensions.cs

@@ -6,8 +6,10 @@ using System.Text;
 
 namespace SharpGLTF.Schema2
 {
-    public static partial class Toolkit
+    public static partial class Schema2Toolkit
     {
+        #region Fluent API
+
         /// <summary>
         /// Initializes this <see cref="Material"/> instance with default material attributes.
         /// </summary>
@@ -135,5 +137,96 @@ namespace SharpGLTF.Schema2
             image.SetSatelliteContent(imageContent);
             return image;
         }
+
+        #endregion
+
+        #region transfer API
+
+        public static void CopyTo(this Material srcMaterial, Materials.MaterialBuilder mb)
+        {
+            mb.Name = srcMaterial.Name;
+            mb.Unlit = srcMaterial.Unlit;
+            mb.Alpha = srcMaterial.Alpha;
+            mb.AlphaCutoff = srcMaterial.AlphaCutoff;
+            mb.DoubleSided = srcMaterial.DoubleSided;
+
+            foreach (var channel in srcMaterial.Channels)
+            {
+                var ch = mb.UseChannel(channel.Key);
+                channel.CopyTo(ch);
+            }
+        }
+
+        public static void CopyTo(this MaterialChannelView srcChannel, Materials.Channel dstChannel)
+        {
+            dstChannel.Factor = srcChannel.Factor;
+
+            if (srcChannel.Texture == null) { dstChannel.Texture = null; return; }
+
+            if (srcChannel.Texture == null) dstChannel.Texture = new Materials.Texture();
+
+            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.Rotation = srcChannel.Transform?.Rotation ?? 0;
+            dstChannel.Texture.Offset = srcChannel.Transform?.Offset ?? Vector2.Zero;
+            dstChannel.Texture.Scale = srcChannel.Transform?.Scale ?? Vector2.One;
+
+            dstChannel.Texture.ImageContent = srcChannel.Image.GetImageContent();
+        }
+
+        public static Material CreateMaterial(this ModelRoot mdl, Materials.MaterialBuilder srcMaterial)
+        {
+            var dstMaterial = mdl.CreateMaterial(srcMaterial.Name);
+
+            dstMaterial.Alpha = srcMaterial.Alpha;
+            dstMaterial.AlphaCutoff = srcMaterial.AlphaCutoff;
+            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"));
+
+            if (srcMaterial.Style == "PBRMetallicRoughness")
+            {
+                dstMaterial.InitializePBRMetallicRoughness();
+
+                srcMaterial.GetChannel("BaseColor").CopyTo(dstMaterial.FindChannel("BaseColor"));
+                srcMaterial.GetChannel("Metallic").CopyTo(dstMaterial.FindChannel("Metallic"));
+                srcMaterial.GetChannel("Roughness").CopyTo(dstMaterial.FindChannel("Roughness"));
+            }
+            else if (srcMaterial.Style == "PBRSpecularGlossiness")
+            {
+                dstMaterial.InitializePBRSpecularGlossiness();
+
+                srcMaterial.GetChannel("Diffuse").CopyTo(dstMaterial.FindChannel("Diffuse"));
+                srcMaterial.GetChannel("Specular").CopyTo(dstMaterial.FindChannel("Specular"));
+                srcMaterial.GetChannel("Glossiness").CopyTo(dstMaterial.FindChannel("Glossiness"));
+            }
+
+            return dstMaterial;
+        }
+
+        public static void CopyTo(this Materials.Channel srcChannel, MaterialChannelView dstChannel)
+        {
+            if (srcChannel == null) return;
+
+            dstChannel.SetFactor(dstChannel.Factor);
+
+            var srcTex = srcChannel.Texture;
+
+            if (srcTex == null) return;
+
+            var image = dstChannel.LogicalParent.LogicalParent.UseImageWithContent(srcTex.ImageContent.ToArray());
+
+            dstChannel.SetTexture(srcTex.CoordinateSet, image, srcTex.MagFilter, srcTex.MinFilter, srcTex.WrapS, srcTex.WrapT);
+
+            dstChannel.SetTransform(srcTex.CoordinateSet, srcTex.Offset, srcTex.Scale, srcTex.Rotation);
+        }
+
+        #endregion
     }
 }

+ 2 - 2
src/SharpGLTF.Toolkit/Schema2/MeshExtensions.cs

@@ -8,7 +8,7 @@ namespace SharpGLTF.Schema2
 {
     using Memory;
 
-    public static partial class Toolkit
+    public static partial class Schema2Toolkit
     {
         #region meshes
 
@@ -310,7 +310,7 @@ namespace SharpGLTF.Schema2
         {
             if (primitive.IndexAccessor == null) return primitive.DrawPrimitiveType.GetTrianglesIndices(primitive.GetVertexAccessor("POSITION").Count);
 
-            return primitive.DrawPrimitiveType.GetTrianglesIndices(primitive.IndexAccessor.AsIndicesArray());
+            return primitive.DrawPrimitiveType.GetTrianglesIndices(primitive.IndexAccessor.AsIndicesArray(), primitive.IndexAccessor.Encoding.ToIndex());
         }
 
         /// <summary>

+ 1 - 1
src/SharpGLTF.Toolkit/Schema2/SceneExtensions.cs

@@ -6,7 +6,7 @@ using System.Text;
 
 namespace SharpGLTF.Schema2
 {
-    public static partial class Toolkit
+    public static partial class Schema2Toolkit
     {
         #region fluent creation
 

+ 5 - 1
tests/SharpGLTF.Tests/Schema2/Authoring/CreateModelTests.cs

@@ -177,7 +177,11 @@ namespace SharpGLTF.Schema2.Authoring
                 .WithMaterial(material);
 
             model.AttachToCurrentTest("result.glb");
-            model.AttachToCurrentTest("result.gltf");            
+            model.AttachToCurrentTest("result.obj");
+            model.AttachToCurrentTest("result.gltf");
+
+            material.FindChannel("BaseColor").SetTransform(0, Vector2.Zero, Vector2.One, 1.5f);
+            model.AttachToCurrentTest("result_withTransform.glb");
         }
 
         [Test(Description = "Creates a simple scene using a mesh builder helper class")]

+ 1 - 1
tests/SharpGLTF.Tests/TestUtils.cs

@@ -54,7 +54,7 @@ namespace SharpGLTF
             }
             else if (fileName.ToLower().EndsWith(".obj"))
             {
-                Schema2.Toolkit.SaveAsWavefront(model, fileName);
+                Schema2.Schema2Toolkit.SaveAsWavefront(model, fileName);
             }
 
             // Attach the saved file to the current test