소스 검색

WIP on Materials Builder.
Added Terrain scene example.

Vicente Penades 6 년 전
부모
커밋
a0b4c92995

+ 2 - 2
src/SharpGLTF.Core/Schema2/gltf.Materials.cs

@@ -140,7 +140,7 @@ namespace SharpGLTF.Schema2
 
 
         public int Set => _TextureInfoGetter?.Invoke(false)?.TextureSet ?? 0;
         public int Set => _TextureInfoGetter?.Invoke(false)?.TextureSet ?? 0;
 
 
-        public Image Image => Texture?.Source;
+        public Image Image => Texture?.Image;
 
 
         public TextureSampler Sampler => Texture?.Sampler;
         public TextureSampler Sampler => Texture?.Sampler;
 
 
@@ -164,8 +164,8 @@ namespace SharpGLTF.Schema2
         public void SetTexture(
         public void SetTexture(
             int texSet,
             int texSet,
             Image texImg,
             Image texImg,
-            TextureInterpolationMode mag = TextureInterpolationMode.LINEAR,
             TextureMipMapMode min = TextureMipMapMode.LINEAR_MIPMAP_LINEAR,
             TextureMipMapMode min = TextureMipMapMode.LINEAR_MIPMAP_LINEAR,
+            TextureInterpolationMode mag = TextureInterpolationMode.LINEAR,
             TextureWrapMode ws = TextureWrapMode.REPEAT,
             TextureWrapMode ws = TextureWrapMode.REPEAT,
             TextureWrapMode wt = TextureWrapMode.REPEAT)
             TextureWrapMode wt = TextureWrapMode.REPEAT)
         {
         {

+ 4 - 4
src/SharpGLTF.Core/Schema2/gltf.MaterialsFactory.cs

@@ -59,8 +59,8 @@ namespace SharpGLTF.Schema2
                 this,
                 this,
                 "Normal",
                 "Normal",
                 _GetNormalTexture,
                 _GetNormalTexture,
-                () => _GetNormalTexture(false) == null ? Vector4.One : new Vector4(1, 1, 1, (float)_GetNormalTexture(false).Scale),
-                value => _GetNormalTexture(true).Scale = (double)value.W
+                () => _GetNormalTexture(false) == null ? Vector4.One : new Vector4(1, 1, 1, _GetNormalTexture(false).Factor),
+                value => _GetNormalTexture(true).Factor = value.W
                 );
                 );
 
 
             yield return new MaterialChannelView
             yield return new MaterialChannelView
@@ -68,8 +68,8 @@ namespace SharpGLTF.Schema2
                 this,
                 this,
                 "Occlusion",
                 "Occlusion",
                 _GetOcclusionTexture,
                 _GetOcclusionTexture,
-                () => _GetOcclusionTexture(false) == null ? Vector4.One : new Vector4(1, 1, 1, (float)_GetOcclusionTexture(false).Strength),
-                value => _GetOcclusionTexture(true).Strength = (double)value.W
+                () => _GetOcclusionTexture(false) == null ? Vector4.One : new Vector4(1, 1, 1, _GetOcclusionTexture(false).Factor),
+                value => _GetOcclusionTexture(true).Factor = value.W
                 );
                 );
 
 
             yield return new MaterialChannelView
             yield return new MaterialChannelView

+ 84 - 0
src/SharpGLTF.Core/Schema2/gltf.TextureInfo.cs

@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Text;
+
+namespace SharpGLTF.Schema2
+{
+    [System.Diagnostics.DebuggerDisplay("Texture[{LogicalIndex}] {Name}")]
+    internal partial class TextureInfo
+    {
+        #region properties
+
+        internal int _LogicalTextureIndex
+        {
+            get => _index;
+            set => _index = value;
+        }
+
+        public int TextureSet
+        {
+            get => _texCoord ?? _texCoordDefault;
+            set => _texCoord = value.AsNullable(_texCoordDefault, _texCoordMinimum, int.MaxValue);
+        }
+
+        public TextureTransform Transform
+        {
+            get => this.GetExtension<TextureTransform>();
+        }
+
+        public virtual Single Factor
+        {
+            get { return 1; }
+            set { }
+        }
+
+        #endregion
+
+        #region API
+
+        public void SetTransform(int texCoord, Vector2 offset, Vector2 scale, float rotation)
+        {
+            var xform = new TextureTransform(this)
+            {
+                TextureCoordinate = texCoord,
+                Offset = offset,
+                Scale = scale,
+                Rotation = rotation
+            };
+
+            if (xform.IsDefault) this.RemoveExtensions<TextureTransform>();
+            else this.SetExtension(xform);
+        }
+
+        #endregion
+    }
+
+    [System.Diagnostics.DebuggerDisplay("Normal Texture[{LogicalIndex}] {Name}")]
+    internal sealed partial class MaterialNormalTextureInfo
+    {
+        #region properties
+
+        public override Single Factor
+        {
+            get => (Single)this._scale.AsValue(_scaleDefault);
+            set => this._scale = ((Double)value).AsNullable(_scaleDefault);
+        }
+
+        #endregion
+    }
+
+    [System.Diagnostics.DebuggerDisplay("Occlusion Texture[{LogicalIndex}] {Name}")]
+    internal sealed partial class MaterialOcclusionTextureInfo
+    {
+        #region properties
+
+        public override Single Factor
+        {
+            get => (Single)this._strength.AsValue(_strengthDefault);
+            set => this._strength = ((Double)value).AsNullable(_strengthDefault, _strengthMinimum, _strengthMaximum);
+        }
+
+        #endregion
+    }
+}

+ 3 - 74
src/SharpGLTF.Core/Schema2/gltf.Textures.cs

@@ -6,77 +6,6 @@ using System.Text;
 
 
 namespace SharpGLTF.Schema2
 namespace SharpGLTF.Schema2
 {
 {
-    [System.Diagnostics.DebuggerDisplay("Texture[{LogicalIndex}] {Name}")]
-    internal partial class TextureInfo
-    {
-        #region properties
-
-        internal int _LogicalTextureIndex
-        {
-            get => _index;
-            set => _index = value;
-        }
-
-        public int TextureSet
-        {
-            get => _texCoord ?? _texCoordDefault;
-            set => _texCoord = value.AsNullable(_texCoordDefault, _texCoordMinimum, int.MaxValue);
-        }
-
-        public TextureTransform Transform
-        {
-            get => this.GetExtension<TextureTransform>();
-        }
-
-        #endregion
-
-        #region API
-
-        public void SetTransform(int texCoord, Vector2 offset, Vector2 scale, float rotation)
-        {
-            var xform = new TextureTransform(this)
-            {
-                TextureCoordinate = texCoord,
-                Offset = offset,
-                Scale = scale,
-                Rotation = rotation
-            };
-
-            if (xform.IsDefault) this.RemoveExtensions<TextureTransform>();
-            else this.SetExtension(xform);
-        }
-
-        #endregion
-    }
-
-    [System.Diagnostics.DebuggerDisplay("Normal Texture[{LogicalIndex}] {Name}")]
-    internal sealed partial class MaterialNormalTextureInfo
-    {
-        #region properties
-
-        public Double Scale
-        {
-            get => this._scale.AsValue(_scaleDefault);
-            set => this._scale = value.AsNullable(_scaleDefault);
-        }
-
-        #endregion
-    }
-
-    [System.Diagnostics.DebuggerDisplay("Occlusion Texture[{LogicalIndex}] {Name}")]
-    internal sealed partial class MaterialOcclusionTextureInfo
-    {
-        #region properties
-
-        public Double Strength
-        {
-            get => this._strength ?? _strengthDefault;
-            set => this._strength = value.AsNullable(_strengthDefault, _strengthMinimum, _strengthMaximum);
-        }
-
-        #endregion
-    }
-
     [System.Diagnostics.DebuggerDisplay("Texture[{LogicalIndex}] {Name}")]
     [System.Diagnostics.DebuggerDisplay("Texture[{LogicalIndex}] {Name}")]
     public sealed partial class Texture
     public sealed partial class Texture
     {
     {
@@ -103,7 +32,7 @@ namespace SharpGLTF.Schema2
             }
             }
         }
         }
 
 
-        public Image Source
+        public Image Image
         {
         {
             get => _source.HasValue ? LogicalParent.LogicalImages[_source.Value] : null;
             get => _source.HasValue ? LogicalParent.LogicalImages[_source.Value] : null;
             set
             set
@@ -240,13 +169,13 @@ namespace SharpGLTF.Schema2
             if (image != null) Guard.MustShareLogicalParent(this, image, nameof(image));
             if (image != null) Guard.MustShareLogicalParent(this, image, nameof(image));
             if (sampler != null) Guard.MustShareLogicalParent(this, sampler, nameof(sampler));
             if (sampler != null) Guard.MustShareLogicalParent(this, sampler, nameof(sampler));
 
 
-            var tex = _textures.FirstOrDefault(item => item.Source == image && item.Sampler == sampler);
+            var tex = _textures.FirstOrDefault(item => item.Image == image && item.Sampler == sampler);
             if (tex != null) return tex;
             if (tex != null) return tex;
 
 
             tex = new Texture();
             tex = new Texture();
             _textures.Add(tex);
             _textures.Add(tex);
 
 
-            tex.Source = image;
+            tex.Image = image;
             tex.Sampler = sampler;
             tex.Sampler = sampler;
 
 
             return tex;
             return tex;

+ 18 - 0
src/SharpGLTF.Toolkit/Geometry/PackedMeshBuilder.cs

@@ -7,10 +7,25 @@ namespace SharpGLTF.Geometry
 {
 {
     using Schema2;
     using Schema2;
 
 
+    /// <summary>
+    /// Used internally to convert a <see cref="MeshBuilder{TMaterial, TvP, TvM, TvJ}"/>
+    /// to <see cref="Schema2.Mesh"/>.
+    /// </summary>
+    /// <typeparam name="TMaterial">A material key to split primitives by material.</typeparam>
     class PackedMeshBuilder<TMaterial>
     class PackedMeshBuilder<TMaterial>
     {
     {
         #region lifecycle
         #region lifecycle
 
 
+        /// <summary>
+        /// Converts a collection of <see cref="MeshBuilder{TMaterial, TvP, TvM, TvJ}"/>
+        /// to a collection of <see cref="PackedMeshBuilder{TMaterial}"/>, trying to use
+        /// a single vertex buffer and a single index buffer shared by all meshes.
+        /// </summary>
+        /// <typeparam name="TvP">The vertex fragment type with Position, Normal and Tangent.</typeparam>
+        /// <typeparam name="TvM">The vertex fragment type with Colors and Texture Coordinates.</typeparam>
+        /// <typeparam name="TvJ">The vertex fragment type with Skin Joint Weights.</typeparam>
+        /// <param name="meshBuilders">A collection of <see cref="MeshBuilder{TMaterial, TvP, TvM, TvJ}"/> instances.</param>
+        /// <returns>A collection of <see cref="PackedMeshBuilder{TMaterial}"/> instances.</returns>
         internal static IEnumerable<PackedMeshBuilder<TMaterial>> PackMeshes<TvP, TvM, TvJ>(IEnumerable<MeshBuilder<TMaterial, TvP, TvM, TvJ>> meshBuilders)
         internal static IEnumerable<PackedMeshBuilder<TMaterial>> PackMeshes<TvP, TvM, TvJ>(IEnumerable<MeshBuilder<TMaterial, TvP, TvM, TvJ>> meshBuilders)
             where TvP : struct, VertexTypes.IVertexPosition
             where TvP : struct, VertexTypes.IVertexPosition
             where TvM : struct, VertexTypes.IVertexMaterial
             where TvM : struct, VertexTypes.IVertexMaterial
@@ -63,6 +78,7 @@ namespace SharpGLTF.Geometry
         #region data
         #region data
 
 
         private readonly string _MeshName;
         private readonly string _MeshName;
+
         private readonly List<PackedPrimitiveBuilder<TMaterial>> _Primitives = new List<PackedPrimitiveBuilder<TMaterial>>();
         private readonly List<PackedPrimitiveBuilder<TMaterial>> _Primitives = new List<PackedPrimitiveBuilder<TMaterial>>();
 
 
         #endregion
         #endregion
@@ -106,7 +122,9 @@ namespace SharpGLTF.Geometry
         #region data
         #region data
 
 
         private readonly TMaterial _Material;
         private readonly TMaterial _Material;
+
         private readonly Memory.MemoryAccessor[] _VertexAccessors;
         private readonly Memory.MemoryAccessor[] _VertexAccessors;
+
         private readonly Memory.MemoryAccessor _IndexAccessors;
         private readonly Memory.MemoryAccessor _IndexAccessors;
 
 
         #endregion
         #endregion

+ 27 - 5
src/SharpGLTF.Toolkit/Materials/Channel.cs

@@ -5,17 +5,39 @@ using System.Text;
 
 
 namespace SharpGLTF.Materials
 namespace SharpGLTF.Materials
 {
 {
-    [System.Diagnostics.DebuggerDisplay("{_key} {Factor}")]
-    public class Channel
+    [System.Diagnostics.DebuggerDisplay("{_Key} {Factor}")]
+    public class MaterialChannelBuilder
     {
     {
-        internal Channel(string key) { _Key = key; }
+        #region lifecycle
+
+        internal MaterialChannelBuilder(string key) { _Key = key; }
+
+        #endregion
+
+        #region data
 
 
         private readonly String _Key;
         private readonly String _Key;
 
 
+        #endregion
+
+        #region properties
+
         public String Key => _Key;
         public String Key => _Key;
 
 
-        public Vector4 Factor { get; set; }
+        public Vector4 Factor { get; set; } = Vector4.One;
+
+        public TextureBuilder Texture { get; set; }
+
+        #endregion
+
+        #region API
+
+        public TextureBuilder UseTexture()
+        {
+            if (Texture == null) Texture = new TextureBuilder();
+            return Texture;
+        }
 
 
-        public Texture Texture { get; set; }
+        #endregion
     }
     }
 }
 }

+ 63 - 10
src/SharpGLTF.Toolkit/Materials/Material.cs

@@ -1,15 +1,30 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Numerics;
 using System.Text;
 using System.Text;
 
 
 namespace SharpGLTF.Materials
 namespace SharpGLTF.Materials
 {
 {
+    using ALPHA = Schema2.AlphaMode;
+
+    [System.Diagnostics.DebuggerDisplay("{Name} {Style}")]
     public class MaterialBuilder
     public class MaterialBuilder
     {
     {
+        #region lifecycle
+
+        public MaterialBuilder(string name = null)
+        {
+            Name = name;
+        }
+
+        #endregion
+
         #region data
         #region data
 
 
-        private readonly List<Channel> _Channels = new List<Channel>();
+        private readonly List<MaterialChannelBuilder> _Channels = new List<MaterialChannelBuilder>();
+
+        private MaterialBuilder _CompatibilityFallbackMaterial;
 
 
         #endregion
         #endregion
 
 
@@ -17,9 +32,9 @@ namespace SharpGLTF.Materials
 
 
         public string Name { get; set; }
         public string Name { get; set; }
 
 
-        public IReadOnlyCollection<Channel> Channels => _Channels;
+        public IReadOnlyCollection<MaterialChannelBuilder> Channels => _Channels;
 
 
-        public Schema2.AlphaMode Alpha { get; set; } = Schema2.AlphaMode.OPAQUE;
+        public ALPHA AlphaMode { get; set; } = ALPHA.OPAQUE;
 
 
         public Single AlphaCutoff { get; set; } = 0.5f;
         public Single AlphaCutoff { get; set; } = 0.5f;
 
 
@@ -29,31 +44,69 @@ namespace SharpGLTF.Materials
 
 
         public String Style { get; set; } = "PBRMetallicRoughness";
         public String Style { get; set; } = "PBRMetallicRoughness";
 
 
+        public MaterialBuilder CompatibilityFallback
+        {
+            get => _CompatibilityFallbackMaterial;
+            set
+            {
+                if (_CompatibilityFallbackMaterial == this) throw new ArgumentException(nameof(value));
+                _CompatibilityFallbackMaterial = value;
+            }
+        }
+
         #endregion
         #endregion
 
 
         #region API
         #region API
 
 
-        public Channel GetChannel(string key)
+        public MaterialChannelBuilder GetChannel(string channelKey)
         {
         {
-            Guard.NotNullOrEmpty(key, nameof(key));
+            Guard.NotNullOrEmpty(channelKey, nameof(channelKey));
 
 
-            key = key.ToLower();
+            channelKey = channelKey.ToLower();
 
 
-            return _Channels.FirstOrDefault(item => string.Equals(key, item.Key, StringComparison.OrdinalIgnoreCase));
+            return _Channels.FirstOrDefault(item => string.Equals(channelKey, item.Key, StringComparison.OrdinalIgnoreCase));
         }
         }
 
 
-        public Channel UseChannel(string key)
+        public MaterialChannelBuilder UseChannel(string channelKey)
         {
         {
-            var ch = GetChannel(key);
+            var ch = GetChannel(channelKey);
 
 
             if (ch != null) return ch;
             if (ch != null) return ch;
 
 
-            ch = new Channel(key);
+            ch = new MaterialChannelBuilder(channelKey);
             _Channels.Add(ch);
             _Channels.Add(ch);
 
 
             return ch;
             return ch;
         }
         }
 
 
+        public MaterialBuilder WithAlpha(ALPHA alphaMode = ALPHA.OPAQUE, Single alphaCutoff = 0.5f)
+        {
+            this.AlphaMode = alphaMode;
+            this.AlphaCutoff = alphaCutoff;
+            return this;
+        }
+
+        public MaterialBuilder WithDoubleSide(bool enabled)
+        {
+            this.DoubleSided = enabled;
+            return this;
+        }
+
+        public MaterialBuilder WithChannelColor(string channelKey, Vector4 color)
+        {
+            this.UseChannel(channelKey).Factor = color;
+            return this;
+        }
+
+        public MaterialBuilder WithChannelTexture(string channelKey, int textureSet, string imageFilePath)
+        {
+            this.UseChannel(channelKey).UseTexture()
+                .WithCoordinateSet(textureSet)
+                .WithImage(imageFilePath);
+
+            return this;
+        }
+
         #endregion
         #endregion
     }
     }
 }
 }

+ 48 - 10
src/SharpGLTF.Toolkit/Materials/Texture.cs

@@ -5,28 +5,66 @@ using System.Text;
 
 
 namespace SharpGLTF.Materials
 namespace SharpGLTF.Materials
 {
 {
+    using BYTES = ArraySegment<Byte>;
+
+    using TEXLERP = Schema2.TextureInterpolationMode;
+    using TEXMIPMAP = Schema2.TextureMipMapMode;
+    using TEXWRAP = Schema2.TextureWrapMode;
+
     [System.Diagnostics.DebuggerDisplay("Texture {CoordinateSet} {MinFilter} {MagFilter} {WrapS} {WrapT} {Rotation} {Offset} {Scale}")]
     [System.Diagnostics.DebuggerDisplay("Texture {CoordinateSet} {MinFilter} {MagFilter} {WrapS} {WrapT} {Rotation} {Offset} {Scale}")]
-    public class Texture
+    public class TextureBuilder
     {
     {
+        #region lifecycle
+
+        internal TextureBuilder() { }
+
+        #endregion
+
         #region properties
         #region properties
 
 
-        public int CoordinateSet { get; set; }
+        public int CoordinateSet { get; set; } = 0;
+
+        /*
+        public Single Rotation { get; set; } = 0;
 
 
-        public Single Rotation { get; set; }
+        public Vector2 Offset { get; set; } = Vector2.Zero;
 
 
-        public Vector2 Offset { get; set; }
+        public Vector2 Scale { get; set; } = Vector2.One;
+        */
+
+        public BYTES ImageContent { get; set; }
+
+        public TEXMIPMAP MinFilter { get; set; } = TEXMIPMAP.LINEAR;
+
+        public TEXLERP MagFilter { get; set; } = TEXLERP.LINEAR;
+
+        public TEXWRAP WrapS { get; set; } = TEXWRAP.REPEAT;
+
+        public TEXWRAP WrapT { get; set; } = TEXWRAP.REPEAT;
+
+        #endregion
 
 
-        public Vector2 Scale { get; set; }
+        #region API
 
 
-        public ArraySegment<Byte> ImageContent { get; set; }
+        public TextureBuilder WithCoordinateSet(int cset) { CoordinateSet = cset; return this; }
 
 
-        public Schema2.TextureMipMapMode MinFilter { get; set; }
+        public TextureBuilder WithImage(string imageFilePath)
+        {
+            var data = System.IO.File.ReadAllBytes(imageFilePath).Slice(0);
+            return WithImage(data);
+        }
 
 
-        public Schema2.TextureInterpolationMode MagFilter { get; set; }
+        public TextureBuilder WithImage(BYTES image) { this.ImageContent = image; return this; }
 
 
-        public Schema2.TextureWrapMode WrapS { get; set; } = Schema2.TextureWrapMode.REPEAT;
+        public TextureBuilder WithSampler(TEXMIPMAP min = TEXMIPMAP.LINEAR, TEXLERP mag = TEXLERP.LINEAR, TEXWRAP ws = TEXWRAP.REPEAT, TEXWRAP wt = TEXWRAP.REPEAT)
+        {
+            this.MinFilter = min;
+            this.MagFilter = mag;
+            this.WrapS = ws;
+            this.WrapT = wt;
 
 
-        public Schema2.TextureWrapMode WrapT { get; set; } = Schema2.TextureWrapMode.REPEAT;
+            return this;
+        }
 
 
         #endregion
         #endregion
     }
     }

+ 24 - 11
src/SharpGLTF.Toolkit/Schema2/MaterialExtensions.cs

@@ -140,13 +140,26 @@ namespace SharpGLTF.Schema2
 
 
         #endregion
         #endregion
 
 
+        #region creation API
+
+        public static Material CreateMaterial(this ModelRoot root, Materials.MaterialBuilder mb)
+        {
+            var m = root.CreateMaterial(mb.Name);
+
+            mb.CopyTo(m);
+
+            return m;
+        }
+
+        #endregion
+
         #region transfer API
         #region transfer API
 
 
         public static void CopyTo(this Material srcMaterial, Materials.MaterialBuilder mb)
         public static void CopyTo(this Material srcMaterial, Materials.MaterialBuilder mb)
         {
         {
             mb.Name = srcMaterial.Name;
             mb.Name = srcMaterial.Name;
             mb.Unlit = srcMaterial.Unlit;
             mb.Unlit = srcMaterial.Unlit;
-            mb.Alpha = srcMaterial.Alpha;
+            mb.AlphaMode = srcMaterial.Alpha;
             mb.AlphaCutoff = srcMaterial.AlphaCutoff;
             mb.AlphaCutoff = srcMaterial.AlphaCutoff;
             mb.DoubleSided = srcMaterial.DoubleSided;
             mb.DoubleSided = srcMaterial.DoubleSided;
 
 
@@ -157,13 +170,13 @@ namespace SharpGLTF.Schema2
             }
             }
         }
         }
 
 
-        public static void CopyTo(this MaterialChannelView srcChannel, Materials.Channel dstChannel)
+        public static void CopyTo(this MaterialChannelView srcChannel, Materials.MaterialChannelBuilder dstChannel)
         {
         {
             dstChannel.Factor = srcChannel.Factor;
             dstChannel.Factor = srcChannel.Factor;
 
 
             if (srcChannel.Texture == null) { dstChannel.Texture = null; return; }
             if (srcChannel.Texture == null) { dstChannel.Texture = null; return; }
 
 
-            if (srcChannel.Texture == null) dstChannel.Texture = new Materials.Texture();
+            if (srcChannel.Texture == null) dstChannel.Texture = new Materials.TextureBuilder();
 
 
             dstChannel.Texture.CoordinateSet = srcChannel.Set;
             dstChannel.Texture.CoordinateSet = srcChannel.Set;
             dstChannel.Texture.MinFilter = srcChannel.Sampler.MinFilter;
             dstChannel.Texture.MinFilter = srcChannel.Sampler.MinFilter;
@@ -171,18 +184,20 @@ namespace SharpGLTF.Schema2
             dstChannel.Texture.WrapS = srcChannel.Sampler.WrapS;
             dstChannel.Texture.WrapS = srcChannel.Sampler.WrapS;
             dstChannel.Texture.WrapT = srcChannel.Sampler.WrapT;
             dstChannel.Texture.WrapT = srcChannel.Sampler.WrapT;
 
 
+            /*
             dstChannel.Texture.Rotation = srcChannel.Transform?.Rotation ?? 0;
             dstChannel.Texture.Rotation = srcChannel.Transform?.Rotation ?? 0;
             dstChannel.Texture.Offset = srcChannel.Transform?.Offset ?? Vector2.Zero;
             dstChannel.Texture.Offset = srcChannel.Transform?.Offset ?? Vector2.Zero;
             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.Image.GetImageContent();
         }
         }
 
 
-        public static Material CreateMaterial(this ModelRoot mdl, Materials.MaterialBuilder srcMaterial)
+        public static void CopyTo(this Materials.MaterialBuilder srcMaterial, Material dstMaterial)
         {
         {
-            var dstMaterial = mdl.CreateMaterial(srcMaterial.Name);
+            // dstMaterial.Name = srcMaterial.Name;
 
 
-            dstMaterial.Alpha = srcMaterial.Alpha;
+            dstMaterial.Alpha = srcMaterial.AlphaMode;
             dstMaterial.AlphaCutoff = srcMaterial.AlphaCutoff;
             dstMaterial.AlphaCutoff = srcMaterial.AlphaCutoff;
             dstMaterial.DoubleSided = srcMaterial.DoubleSided;
             dstMaterial.DoubleSided = srcMaterial.DoubleSided;
 
 
@@ -206,11 +221,9 @@ namespace SharpGLTF.Schema2
                 srcMaterial.GetChannel("Specular").CopyTo(dstMaterial.FindChannel("Specular"));
                 srcMaterial.GetChannel("Specular").CopyTo(dstMaterial.FindChannel("Specular"));
                 srcMaterial.GetChannel("Glossiness").CopyTo(dstMaterial.FindChannel("Glossiness"));
                 srcMaterial.GetChannel("Glossiness").CopyTo(dstMaterial.FindChannel("Glossiness"));
             }
             }
-
-            return dstMaterial;
         }
         }
 
 
-        public static void CopyTo(this Materials.Channel srcChannel, MaterialChannelView dstChannel)
+        public static void CopyTo(this Materials.MaterialChannelBuilder srcChannel, MaterialChannelView dstChannel)
         {
         {
             if (srcChannel == null) return;
             if (srcChannel == null) return;
 
 
@@ -222,9 +235,9 @@ namespace SharpGLTF.Schema2
 
 
             var image = dstChannel.LogicalParent.LogicalParent.UseImageWithContent(srcTex.ImageContent.ToArray());
             var image = dstChannel.LogicalParent.LogicalParent.UseImageWithContent(srcTex.ImageContent.ToArray());
 
 
-            dstChannel.SetTexture(srcTex.CoordinateSet, image, srcTex.MagFilter, srcTex.MinFilter, srcTex.WrapS, srcTex.WrapT);
+            dstChannel.SetTexture(srcTex.CoordinateSet, image, srcTex.MinFilter, srcTex.MagFilter, srcTex.WrapS, srcTex.WrapT);
 
 
-            dstChannel.SetTransform(srcTex.CoordinateSet, srcTex.Offset, srcTex.Scale, srcTex.Rotation);
+            // dstChannel.SetTransform(srcTex.CoordinateSet, srcTex.Offset, srcTex.Scale, srcTex.Rotation);
         }
         }
 
 
         #endregion
         #endregion

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

@@ -33,7 +33,15 @@ namespace SharpGLTF.Schema2
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
             where TvJ : struct, Geometry.VertexTypes.IVertexJoints
             where TvJ : struct, Geometry.VertexTypes.IVertexJoints
         {
         {
-            return root.CreateMeshes(k => k, meshBuilders);
+            return root.CreateMeshes(m => m, meshBuilders);
+        }
+
+        public static IReadOnlyList<Mesh> CreateMeshes<TvP, TvM, TvJ>(this ModelRoot root, params Geometry.MeshBuilder<Materials.MaterialBuilder, TvP, TvM, TvJ>[] meshBuilders)
+            where TvP : struct, Geometry.VertexTypes.IVertexPosition
+            where TvM : struct, Geometry.VertexTypes.IVertexMaterial
+            where TvJ : struct, Geometry.VertexTypes.IVertexJoints
+        {
+            return root.CreateMeshes(mb => root.CreateMaterial(mb), meshBuilders);
         }
         }
 
 
         public static IReadOnlyList<Mesh> CreateMeshes<TMaterial, TvP, TvM, TvJ>(this ModelRoot root, Func<TMaterial, Material> materialEvaluator, params Geometry.MeshBuilder<TMaterial, TvP, TvM, TvJ>[] meshBuilders)
         public static IReadOnlyList<Mesh> CreateMeshes<TMaterial, TvP, TvM, TvJ>(this ModelRoot root, Func<TMaterial, Material> materialEvaluator, params Geometry.MeshBuilder<TMaterial, TvP, TvM, TvJ>[] meshBuilders)
@@ -46,7 +54,7 @@ namespace SharpGLTF.Schema2
                 .SelectMany(item => item.Primitives)
                 .SelectMany(item => item.Primitives)
                 .Select(item => item.Material)
                 .Select(item => item.Material)
                 .Distinct()
                 .Distinct()
-                .ToDictionary(k => k, k => materialEvaluator(k));
+                .ToDictionary(m => m, m => materialEvaluator(m));
 
 
             // creates meshes and primitives using MemoryAccessors using a single, shared vertex and index buffer
             // creates meshes and primitives using MemoryAccessors using a single, shared vertex and index buffer
             var srcMeshes = Geometry.PackedMeshBuilder<TMaterial>
             var srcMeshes = Geometry.PackedMeshBuilder<TMaterial>

+ 19 - 0
src/SharpGLTF.Toolkit/Schema2/SceneExtensions.cs

@@ -47,6 +47,15 @@ namespace SharpGLTF.Schema2
 
 
         #region evaluation
         #region evaluation
 
 
+        /// <summary>
+        /// Yield a collection of triangles representing the geometry
+        /// of the input <see cref="Scene"/> in world space.
+        /// </summary>
+        /// <typeparam name="TvP">The vertex fragment type with Position, Normal and Tangent.</typeparam>
+        /// <typeparam name="TvM">The vertex fragment type with Colors and Texture Coordinates.</typeparam>
+        /// <typeparam name="TvJ">The vertex fragment type with Skin Joint Weights.</typeparam>
+        /// <param name="scene">A <see cref="Scene"/> instance.</param>
+        /// <returns>A collection of triangles in world space.</returns>
         public static IEnumerable<((TvP, TvM, TvJ), (TvP, TvM, TvJ), (TvP, TvM, TvJ), Material)> Triangulate<TvP, TvM, TvJ>(this Scene scene)
         public static IEnumerable<((TvP, TvM, TvJ), (TvP, TvM, TvJ), (TvP, TvM, TvJ), Material)> Triangulate<TvP, TvM, TvJ>(this Scene scene)
             where TvP : struct, Geometry.VertexTypes.IVertexPosition
             where TvP : struct, Geometry.VertexTypes.IVertexPosition
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
@@ -55,6 +64,16 @@ namespace SharpGLTF.Schema2
             return Node.Flatten(scene).SelectMany(item => item.Triangulate<TvP, TvM, TvJ>(true));
             return Node.Flatten(scene).SelectMany(item => item.Triangulate<TvP, TvM, TvJ>(true));
         }
         }
 
 
+        /// <summary>
+        /// Yield a collection of triangles representing the geometry
+        /// of the input <see cref="Node"/> in local or world space.
+        /// </summary>
+        /// <typeparam name="TvP">The vertex fragment type with Position, Normal and Tangent.</typeparam>
+        /// <typeparam name="TvM">The vertex fragment type with Colors and Texture Coordinates.</typeparam>
+        /// <typeparam name="TvJ">The vertex fragment type with Skin Joint Weights.</typeparam>
+        /// <param name="node">A <see cref="Node"/> instance.</param>
+        /// <param name="inWorldSpace">A value indicating whether the returned triangles must be in local (false) or world (true) space.</param>
+        /// <returns>A collection of triangles in local or world space.</returns>
         public static IEnumerable<((TvP, TvM, TvJ), (TvP, TvM, TvJ), (TvP, TvM, TvJ), Material)> Triangulate<TvP, TvM, TvJ>(this Node node, bool inWorldSpace)
         public static IEnumerable<((TvP, TvM, TvJ), (TvP, TvM, TvJ), (TvP, TvM, TvJ), Material)> Triangulate<TvP, TvM, TvJ>(this Node node, bool inWorldSpace)
             where TvP : struct, Geometry.VertexTypes.IVertexPosition
             where TvP : struct, Geometry.VertexTypes.IVertexPosition
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial
             where TvM : struct, Geometry.VertexTypes.IVertexMaterial

BIN
tests/SharpGLTF.Tests/Assets/Texture1.jpg


+ 50 - 29
tests/SharpGLTF.Tests/Schema2/Authoring/CreateModelTests.cs

@@ -13,6 +13,8 @@ namespace SharpGLTF.Schema2.Authoring
     using VEMPTY = Geometry.VertexTypes.VertexEmpty;
     using VEMPTY = Geometry.VertexTypes.VertexEmpty;
     using VPOSNRM = Geometry.VertexTypes.VertexPositionNormal;
     using VPOSNRM = Geometry.VertexTypes.VertexPositionNormal;
     using VPOS = Geometry.VertexTypes.VertexPosition;
     using VPOS = Geometry.VertexTypes.VertexPosition;
+    using VCLR1 = Geometry.VertexTypes.VertexColor1;
+    using VTEX1 = Geometry.VertexTypes.VertexTexture1;
     using VSKIN4 = Geometry.VertexTypes.VertexJoints8x4;
     using VSKIN4 = Geometry.VertexTypes.VertexJoints8x4;
 
 
     [TestFixture]
     [TestFixture]
@@ -184,35 +186,6 @@ namespace SharpGLTF.Schema2.Authoring
             model.AttachToCurrentTest("result_withTransform.glb");
             model.AttachToCurrentTest("result_withTransform.glb");
         }
         }
 
 
-        [Test(Description = "Creates a simple scene using a mesh builder helper class")]
-        public void CreateSimpleMeshBuilderScene()
-        {
-            TestContext.CurrentContext.AttachShowDirLink();
-            TestContext.CurrentContext.AttachGltfValidatorLink();
-
-            var meshBuilder = new SimpleSceneBuilder<Vector4>();
-            meshBuilder.AddPolygon(new Vector4(1, 1, 1, 1), (-10, 10,  0), (10, 10,  0), (10, -10,  0), (-10, -10,  0));
-            meshBuilder.AddPolygon(new Vector4(1, 1, 0, 1), (-10, 10, 10), (10, 10, 10), (10, -10, 10), (-10, -10, 10));
-            meshBuilder.AddPolygon(new Vector4(1, 0, 0, 1), (-10, 10, 20), (10, 10, 20), (10, -10, 20), (-10, -10, 20));
-
-            var model = ModelRoot.CreateModel();
-            var scene = model.UseScene("Default");
-            var rnode = scene.CreateNode("RootNode");            
-
-            // setup a lambda function that creates a material for a given color
-            Material createMaterialForColor(Vector4 color)
-            {
-                return model.CreateMaterial().WithDefault(color).WithDoubleSide(true);
-            };
-
-            // fill our node with the mesh
-            meshBuilder.CopyToNode(rnode, createMaterialForColor);
-
-            model.AttachToCurrentTest("result.gltf");
-            model.AttachToCurrentTest("result.glb");            
-        }
-
-
         [Test(Description = "Creates an interleaved scene using a toolkit utilities")]
         [Test(Description = "Creates an interleaved scene using a toolkit utilities")]
         public void CreateInterleavedQuadScene()
         public void CreateInterleavedQuadScene()
         {
         {
@@ -421,5 +394,53 @@ namespace SharpGLTF.Schema2.Authoring
             model.AttachToCurrentTest("result.glb");
             model.AttachToCurrentTest("result.glb");
             model.AttachToCurrentTest("result.gltf");
             model.AttachToCurrentTest("result.gltf");
         }
         }
+
+        [Test]
+        public void CreateTerrainScene()
+        {
+            TestContext.CurrentContext.AttachShowDirLink();
+            TestContext.CurrentContext.AttachGltfValidatorLink();
+
+            // texture path
+            var imagePath = System.IO.Path.Combine(TestContext.CurrentContext.WorkDirectory, "Assets", "Texture1.jpg");
+
+            // fancy height function; can be easily replaced with a bitmap sampler.
+            float heightFunction(int xx, int yy)
+            {
+                float x = xx;
+                float y = yy;
+
+                double h = 0;
+
+                h += Math.Sin(x / 45);
+                h += Math.Sin(3 + x / 13) * 0.5f;
+
+                h += Math.Sin(2 + y / 31);
+                h += Math.Sin(y / 13) * 0.5f;
+
+                h += Math.Sin((x + y * 2) / 19);
+
+                h *= 5;
+
+                return (float)h;
+            }
+
+            var terrain = SolidMeshUtils.CreateTerrainMesh(128,128, heightFunction, imagePath);
+
+            // create a new gltf model
+            var model = ModelRoot.CreateModel();
+            
+            // add all meshes (just one in this case) to the model
+            model.CreateMeshes(terrain);
+
+            // create a scene, a node, and assign the first mesh (the terrain)
+            model.UseScene("Default")
+                .CreateNode().WithMesh(model.LogicalMeshes[0]);
+
+            // save the model as GLB
+            model.AttachToCurrentTest("terrain.glb");
+        }
+
+        
     }
     }
 }
 }

+ 0 - 116
tests/SharpGLTF.Tests/Schema2/Authoring/SimpleMeshBuilder.cs

@@ -1,116 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Numerics;
-using System.Text;
-using System.Linq;
-
-using SharpGLTF.Collections;
-
-namespace SharpGLTF.Schema2.Authoring
-{
-    class SimpleSceneBuilder<TMaterial>
-    {
-        #region data
-
-        private readonly VertexColumn<Vector3> _Positions = new VertexColumn<Vector3>();        
-        private readonly Dictionary<TMaterial, List<int>> _Indices = new Dictionary<TMaterial, List<int>>();        
-
-        #endregion
-
-        #region API
-
-        public void AddPolygon(TMaterial material, params (float,float,float)[] points)
-        {
-            var vertices = points.Select(item => new Vector3(item.Item1, item.Item2, item.Item3)).ToArray();
-
-            AddPolygon(material, vertices);
-        }
-
-        public void AddPolygon(TMaterial material, params Vector3[] points)
-        {
-            for (int i = 2; i < points.Length; ++i)
-            {
-                AddTriangle(material, points[0], points[i - 1], points[i]);
-            }
-        }
-
-        public void AddTriangle(TMaterial material, Vector3 a, Vector3 b, Vector3 c)
-        {
-            var aa = _Positions.Use(a);
-            var bb = _Positions.Use(b);
-            var cc = _Positions.Use(c);
-
-            // check for degenerated triangles:
-            if (aa == bb) return;
-            if (aa == cc) return;
-            if (bb == cc) return;
-
-            if (!_Indices.TryGetValue(material, out List<int> indices))
-            {
-                indices = new List<int>();
-                _Indices[material] = indices;
-            }
-
-            indices.Add(aa);
-            indices.Add(bb);
-            indices.Add(cc);
-        }             
-
-        public void CopyToNode(Node dstNode, Func<TMaterial, Material> materialEvaluator)
-        {
-            dstNode.Mesh = dstNode.LogicalParent.CreateMesh();
-            CopyToMesh(dstNode.Mesh, materialEvaluator);
-        }        
-
-        public void CopyToMesh(Mesh dstMesh, Func<TMaterial,Material> materialEvaluator)
-        {
-            var root = dstMesh.LogicalParent;
-
-            foreach (var kvp in _Indices)
-            {
-                var prim = dstMesh.CreatePrimitive()
-                    .WithVertexAccessor("POSITION", _Positions)
-                    .WithVertexAccessor("NORMAL", _CalculateNormals())
-                    .WithIndicesAccessor(PrimitiveType.TRIANGLES,kvp.Value);                
-
-                prim.Material = materialEvaluator(kvp.Key);
-            }
-        }
-
-        private Vector3[] _CalculateNormals()
-        {
-            var normals = new Vector3[_Positions.Count];
-
-            foreach(var prim in _Indices.Values)
-            {
-                for(int i=0; i < prim.Count; i+=3)
-                {
-                    var a = prim[i + 0];
-                    var b = prim[i + 1];
-                    var c = prim[i + 2];
-
-                    var aa = _Positions[a];
-                    var bb = _Positions[b];
-                    var cc = _Positions[c];
-
-                    var n = Vector3.Cross(bb - aa, cc - aa);
-
-                    normals[a] += n;
-                    normals[b] += n;
-                    normals[c] += n;
-                }
-            }
-
-            for(int i=0; i < normals.Length; ++i)
-            {
-                normals[i] = normals[i].Length() <= float.Epsilon ? Vector3.UnitX : Vector3.Normalize(normals[i]);
-            }
-
-            return normals;
-        }
-
-        #endregion
-    }
-
-    
-}

+ 50 - 0
tests/SharpGLTF.Tests/Schema2/Authoring/SolidMeshUtils.cs

@@ -7,6 +7,9 @@ namespace SharpGLTF.Schema2.Authoring
 {
 {
     using Geometry;    
     using Geometry;    
     
     
+    using VPOS = Geometry.VertexTypes.VertexPosition;
+    using VTEX1 = Geometry.VertexTypes.VertexTexture1;
+
     using VPOSNRM = Geometry.VertexTypes.VertexPositionNormal;
     using VPOSNRM = Geometry.VertexTypes.VertexPositionNormal;
 
 
     static class SolidMeshUtils
     static class SolidMeshUtils
@@ -109,11 +112,58 @@ namespace SharpGLTF.Schema2.Authoring
             var bc = Vector3.Normalize(b + c) * b.Length();
             var bc = Vector3.Normalize(b + c) * b.Length();
             var ca = Vector3.Normalize(c + a) * c.Length();
             var ca = Vector3.Normalize(c + a) * c.Length();
 
 
+            // central triangle
             _AddSphereTriangle(meshBuilder, material, xform, ab, bc, ca, iterations);
             _AddSphereTriangle(meshBuilder, material, xform, ab, bc, ca, iterations);
 
 
+            // vertex triangles
             _AddSphereTriangle(meshBuilder, material, xform, a, ab, ca, iterations);
             _AddSphereTriangle(meshBuilder, material, xform, a, ab, ca, iterations);
             _AddSphereTriangle(meshBuilder, material, xform, b, bc, ab, iterations);
             _AddSphereTriangle(meshBuilder, material, xform, b, bc, ab, iterations);
             _AddSphereTriangle(meshBuilder, material, xform, c, ca, bc, iterations);
             _AddSphereTriangle(meshBuilder, material, xform, c, ca, bc, iterations);
         }
         }
+
+        public static MeshBuilder<Materials.MaterialBuilder, VPOS, VTEX1> CreateTerrainMesh(int width, int length, Func<int,int,float> heightFunction, string terrainColorImagePath)
+        {
+            // we create a new material to use with the terrain mesh
+            var material = new Materials.MaterialBuilder("TerrainMaterial")
+                .WithChannelTexture("BaseColor", 0, terrainColorImagePath);
+
+            // we create a MeshBuilder
+            var terrainMesh = new MeshBuilder<Materials.MaterialBuilder, VPOS, VTEX1>("terrain");
+
+            var texScale = new Vector2(width, length);
+
+            // fill the MeshBuilder with quads using the heightFunction.
+            for (int y = 1; y < length; ++y)
+            {
+                for (int x = 1; x < width; ++x)
+                {
+                    // quad vertex positions
+
+                    var a = new Vector3(x - 1, heightFunction(x - 1, y + 0), y + 0);
+                    var b = new Vector3(x + 0, heightFunction(x + 0, y + 0), y + 0);
+                    var c = new Vector3(x + 0, heightFunction(x + 0, y - 1), y - 1);
+                    var d = new Vector3(x - 1, heightFunction(x - 1, y - 1), y - 1);
+
+                    // quad UV coordinates
+
+                    var at = new Vector2(a.X, a.Z) / texScale;
+                    var bt = new Vector2(b.X, b.Z) / texScale;
+                    var ct = new Vector2(c.X, c.Z) / texScale;
+                    var dt = new Vector2(d.X, d.Z) / texScale;
+
+                    terrainMesh
+                        .UsePrimitive(material)
+                        .AddPolygon
+                        (
+                            (a, at),
+                            (b, bt),
+                            (c, ct),
+                            (d, dt)
+                        );
+                }
+            }
+
+            return terrainMesh;
+        }
     }
     }
 }
 }

+ 6 - 0
tests/SharpGLTF.Tests/SharpGLTF.Tests.csproj

@@ -21,4 +21,10 @@
     <ProjectReference Include="..\..\src\SharpGLTF.Core\SharpGLTF.Core.csproj" />
     <ProjectReference Include="..\..\src\SharpGLTF.Core\SharpGLTF.Core.csproj" />
   </ItemGroup>
   </ItemGroup>
 
 
+  <ItemGroup>
+    <None Update="Assets\Texture1.jpg">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
+
 </Project>
 </Project>