Browse Source

Added KHR_texture_basisu extension.

Vicente Penades 5 years ago
parent
commit
77e1069210

+ 16 - 0
build/SharpGLTF.CodeGen/Constants.cs

@@ -6,6 +6,8 @@ namespace SharpGLTF
 {
     static class Constants
     {
+        #region root paths
+
         public static string RemoteSchemaRepo = "https://github.com/KhronosGroup/glTF.git";
 
         /// <summary>
@@ -13,6 +15,10 @@ namespace SharpGLTF
         /// </summary>
         public static string LocalRepoDirectory => System.IO.Path.Combine(System.IO.Path.GetDirectoryName(typeof(Program).Assembly.Location), "glTF");
 
+        #endregion
+
+        #region main schema paths
+
         /// <summary>
         /// Directory of the main schema within the download repo directory
         /// </summary>
@@ -23,6 +29,10 @@ namespace SharpGLTF
         /// </summary>
         public static string MainSchemaFile => System.IO.Path.Combine(MainSchemaDir, "glTF.schema.json");
 
+        #endregion
+
+        #region extension paths
+
 
         public static string KhronosSchemaDir => System.IO.Path.Combine(Constants.LocalRepoDirectory, "extensions", "2.0", "Khronos");
         public static string VendorSchemaDir => System.IO.Path.Combine(Constants.LocalRepoDirectory, "extensions", "2.0", "Vendor");
@@ -44,6 +54,11 @@ namespace SharpGLTF
 
         public static string TextureWebpSchemaFile => System.IO.Path.Combine(VendorSchemaDir, "EXT_texture_webp", "schema", "glTF.EXT_texture_webp.schema.json");
 
+        public static string TextureKtx2SchemaFile => System.IO.Path.Combine(KhronosSchemaDir, "KHR_texture_basisu", "schema", "texture.KHR_texture_basisu.schema.json");
+
+        #endregion
+
+        #region code generation output paths
 
         /// <summary>
         /// directory within the solution where the generated code is emitted
@@ -55,5 +70,6 @@ namespace SharpGLTF
         /// </summary>
         public static string OutputNamespace => "SharpGLTF.Schema2";
 
+        #endregion
     }
 }

+ 12 - 2
build/SharpGLTF.CodeGen/Program.cs

@@ -40,6 +40,7 @@ namespace SharpGLTF
             _ProcessKhronosTextureTransformExtension();
             _ProcessMicrosoftTextureDDSExtension();
             _ProcessTextureWebpExtension();
+            _ProcessTextureKtx2Extension();
 
             // these extansions are not fully supported and temporarily removed:
             // _ProcessDracoExtension();
@@ -241,7 +242,7 @@ namespace SharpGLTF
             var ctx = LoadSchemaContext(Constants.MicrosoftTextureDDSSchemaFile);
             ctx.IgnoredByCodeEmitter("glTF Property");
 
-            ProcessSchema("ext.MSFT.textureDDS.g", ctx);
+            ProcessSchema("ext.MSFT.TextureDDS.g", ctx);
         }
 
         private static void _ProcessTextureWebpExtension()
@@ -249,7 +250,15 @@ namespace SharpGLTF
             var ctx = LoadSchemaContext(Constants.TextureWebpSchemaFile);
             ctx.IgnoredByCodeEmitter("glTF Property");
 
-            ProcessSchema("ext.textureWEBP.g", ctx);
+            ProcessSchema("ext.TextureWEBP.g", ctx);
+        }
+
+        private static void _ProcessTextureKtx2Extension()
+        {
+            var ctx = LoadSchemaContext(Constants.TextureKtx2SchemaFile);
+            ctx.IgnoredByCodeEmitter("glTF Property");
+
+            ProcessSchema("ext.TextureKTX2.g", ctx);
         }
 
         #endregion
@@ -315,6 +324,7 @@ namespace SharpGLTF
 
             newEmitter.SetRuntimeName("MSFT_texture_dds extension", "TextureDDS");
             newEmitter.SetRuntimeName("EXT_texture_webp glTF extension", "TextureWEBP");
+            newEmitter.SetRuntimeName("KHR_texture_basisu glTF extension", "TextureKTX2");
 
             var classes = ctx.Classes.ToArray();
             var fields = classes.SelectMany(item => item.Fields).ToArray();

+ 46 - 6
src/SharpGLTF.Core/Memory/MemoryImage.cs

@@ -21,11 +21,13 @@ namespace SharpGLTF.Memory
         const string EMBEDDED_PNG_BUFFER = "data:image/png";
         const string EMBEDDED_DDS_BUFFER = "data:image/vnd-ms.dds";
         const string EMBEDDED_WEBP_BUFFER = "data:image/webp";
+        const string EMBEDDED_KTX2_BUFFER = "data:image/ktx2";
 
         const string MIME_PNG = "image/png";
         const string MIME_JPG = "image/jpeg";
         const string MIME_DDS = "image/vnd-ms.dds";
         const string MIME_WEBP = "image/webp";
+        const string MIME_KTX2 = "image/ktx2";
 
         /// <summary>
         /// Represents a 4x4 white PNG image.
@@ -35,12 +37,13 @@ namespace SharpGLTF.Memory
         internal static Byte[] DefaultPngImage => Convert.FromBase64String(DEFAULT_PNG_IMAGE);
 
         internal static readonly string[] _EmbeddedHeaders =
-                { EMBEDDED_OCTET_STREAM,
-                EMBEDDED_GLTF_BUFFER,
-                EMBEDDED_JPEG_BUFFER,
-                EMBEDDED_PNG_BUFFER,
-                EMBEDDED_DDS_BUFFER,
-                EMBEDDED_WEBP_BUFFER
+                { EMBEDDED_OCTET_STREAM
+                , EMBEDDED_GLTF_BUFFER
+                , EMBEDDED_JPEG_BUFFER
+                , EMBEDDED_PNG_BUFFER
+                , EMBEDDED_DDS_BUFFER
+                , EMBEDDED_WEBP_BUFFER
+                , EMBEDDED_KTX2_BUFFER
                 };
 
         public static MemoryImage Empty => default;
@@ -182,6 +185,16 @@ namespace SharpGLTF.Memory
         /// </summary>
         public bool IsWebp => _IsWebpImage(_Image);
 
+        /// <summary>
+        /// Gets a value indicating whether this object represents a valid KTX2 image.
+        /// </summary>
+        public bool IsKtx2 => _IsKtx2Image(_Image);
+
+        /// <summary>
+        /// Gets a value indicating whether this object represents an image backed by a glTF extension.
+        /// </summary>
+        public bool IsExtendedFormat => IsDds || IsWebp || IsKtx2;
+
         /// <summary>
         /// Gets a value indicating whether this object represents a valid image.
         /// </summary>
@@ -199,6 +212,7 @@ namespace SharpGLTF.Memory
                 if (IsJpg) return "jpg";
                 if (IsDds) return "dds";
                 if (IsWebp) return "webp";
+                if (IsKtx2) return "ktx2";
                 throw new NotImplementedException();
             }
         }
@@ -215,6 +229,7 @@ namespace SharpGLTF.Memory
                 if (IsJpg) return MIME_JPG;
                 if (IsDds) return MIME_DDS;
                 if (IsWebp) return MIME_WEBP;
+                if (IsKtx2) return MIME_KTX2;
                 throw new NotImplementedException();
             }
         }
@@ -230,6 +245,7 @@ namespace SharpGLTF.Memory
                 if (IsPng) return $"PNG {_Image.Count}ᴮʸᵗᵉˢ";
                 if (IsDds) return $"DDS {_Image.Count}ᴮʸᵗᵉˢ";
                 if (IsWebp) return $"WEBP {_Image.Count}ᴮʸᵗᵉˢ";
+                if (IsKtx2) return $"KTX2 {_Image.Count}ᴮʸᵗᵉˢ";
                 return "Undefined";
             }
         }
@@ -288,6 +304,7 @@ namespace SharpGLTF.Memory
                 if (this.IsJpg) mimeContent = EMBEDDED_JPEG_BUFFER;
                 if (this.IsDds) mimeContent = EMBEDDED_DDS_BUFFER;
                 if (this.IsWebp) mimeContent = EMBEDDED_WEBP_BUFFER;
+                if (this.IsKtx2) mimeContent = EMBEDDED_KTX2_BUFFER;
 
                 mimeContent += ";base64,";
             }
@@ -312,6 +329,7 @@ namespace SharpGLTF.Memory
             if (mime64content.StartsWith(EMBEDDED_JPEG_BUFFER, StringComparison.Ordinal) && !_IsJpgImage(data)) throw new ArgumentException("Invalid JPG Content", nameof(mime64content));
             if (mime64content.StartsWith(EMBEDDED_DDS_BUFFER, StringComparison.Ordinal) && !_IsDdsImage(data)) throw new ArgumentException("Invalid DDS Content", nameof(mime64content));
             if (mime64content.StartsWith(EMBEDDED_WEBP_BUFFER, StringComparison.Ordinal) && !_IsWebpImage(data)) throw new ArgumentException("Invalid WEBP Content", nameof(mime64content));
+            if (mime64content.StartsWith(EMBEDDED_KTX2_BUFFER, StringComparison.Ordinal) && !_IsKtx2Image(data)) throw new ArgumentException("Invalid KTX2 Content", nameof(mime64content));
 
             return true;
         }
@@ -332,6 +350,7 @@ namespace SharpGLTF.Memory
             if (format.EndsWith("jpeg", StringComparison.OrdinalIgnoreCase)) return IsJpg;
             if (format.EndsWith("dds", StringComparison.OrdinalIgnoreCase)) return IsDds;
             if (format.EndsWith("webp", StringComparison.OrdinalIgnoreCase)) return IsWebp;
+            if (format.EndsWith("ktx2", StringComparison.OrdinalIgnoreCase)) return IsKtx2;
 
             return false;
         }
@@ -384,6 +403,26 @@ namespace SharpGLTF.Memory
             return true;
         }
 
+        private static bool _IsKtx2Image(IReadOnlyList<Byte> data)
+        {
+            if (data[0] != 0xAB) return false;
+            if (data[1] != 0x4B) return false;
+            if (data[2] != 0x54) return false;
+            if (data[3] != 0x58) return false;
+
+            if (data[4] != 0x20) return false;
+            if (data[5] != 0x32) return false;
+            if (data[6] != 0x30) return false;
+            if (data[7] != 0xBB) return false;
+
+            if (data[8] != 0x0D) return false;
+            if (data[9] != 0x0A) return false;
+            if (data[10] != 0x1A) return false;
+            if (data[11] != 0x0A) return false;
+
+            return true;
+        }
+
         private static bool _IsImage(IReadOnlyList<Byte> data)
         {
             if (data == null) return false;
@@ -393,6 +432,7 @@ namespace SharpGLTF.Memory
             if (_IsJpgImage(data)) return true;
             if (_IsPngImage(data)) return true;
             if (_IsWebpImage(data)) return true;
+            if (_IsKtx2Image(data)) return true;
 
             return false;
         }

+ 0 - 0
src/SharpGLTF.Core/Schema2/Generated/ext.MSFT.textureDDS.g.cs → src/SharpGLTF.Core/Schema2/Generated/ext.MSFT.TextureDDS.g.cs


+ 56 - 0
src/SharpGLTF.Core/Schema2/Generated/ext.TextureKTX2.g.cs

@@ -0,0 +1,56 @@
+// <auto-generated/>
+
+//------------------------------------------------------------------------------------------------
+//      This file has been programatically generated; DON´T EDIT!
+//------------------------------------------------------------------------------------------------
+
+#pragma warning disable SA1001
+#pragma warning disable SA1027
+#pragma warning disable SA1028
+#pragma warning disable SA1121
+#pragma warning disable SA1205
+#pragma warning disable SA1309
+#pragma warning disable SA1402
+#pragma warning disable SA1505
+#pragma warning disable SA1507
+#pragma warning disable SA1508
+#pragma warning disable SA1652
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Numerics;
+using System.Text.Json;
+
+namespace SharpGLTF.Schema2
+{
+	using Collections;
+
+	/// <summary>
+	/// glTF extension to specify textures using the KTX v2 images with Basis Universal supercompression.
+	/// </summary>
+	partial class TextureKTX2 : ExtraProperties
+	{
+	
+		private Int32? _source;
+		
+	
+		protected override void SerializeProperties(Utf8JsonWriter writer)
+		{
+			base.SerializeProperties(writer);
+			SerializeProperty(writer, "source", _source);
+		}
+	
+		protected override void DeserializeProperty(string jsonPropertyName, ref Utf8JsonReader reader)
+		{
+			switch (jsonPropertyName)
+			{
+				case "source": _source = DeserializePropertyValue<Int32?>(ref reader); break;
+				default: base.DeserializeProperty(jsonPropertyName,ref reader); break;
+			}
+		}
+	
+	}
+
+}

+ 0 - 0
src/SharpGLTF.Core/Schema2/Generated/ext.textureWEBP.g.cs → src/SharpGLTF.Core/Schema2/Generated/ext.TextureWEBP.g.cs


+ 1 - 0
src/SharpGLTF.Core/Schema2/gltf.ExtensionsFactory.cs

@@ -28,6 +28,7 @@ namespace SharpGLTF.Schema2
 
             RegisterExtension<Texture, TextureDDS>("MSFT_texture_dds");
             RegisterExtension<Texture, TextureWEBP>("EXT_texture_webp");
+            RegisterExtension<Texture, TextureKTX2>("KHR_texture_basisu");
         }
 
         #endregion

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

@@ -80,7 +80,7 @@ namespace SharpGLTF.Schema2
         /// <summary>
         /// Retrieves the image file as a segment of bytes.
         /// </summary>
-        /// <returns>A <see cref="BYTES"/> segment containing the image file, which can be a PNG, JPG, DDS or WEBP format.</returns>
+        /// <returns>A <see cref="BYTES"/> segment containing the image file, which can be a PNG, JPG, DDS, WEBP or KTX2 format.</returns>
         private Memory.MemoryImage GetSatelliteContent()
         {
             // the image is stored locally in a temporary buffer
@@ -107,7 +107,7 @@ namespace SharpGLTF.Schema2
         /// <param name="content">A <see cref="Byte"/> array containing a PNG or JPEG image.</param>
         private void SetSatelliteContent(Memory.MemoryImage content)
         {
-            if (!content.IsValid) throw new ArgumentException($"{nameof(content)} must be a PNG, JPG, DDS or WEBP image", nameof(content));
+            if (!content.IsValid) throw new ArgumentException($"{nameof(content)} must be a PNG, JPG, DDS, WEBP or KTX2 image", nameof(content));
 
             _DiscardContent();
 

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

@@ -59,6 +59,9 @@ namespace SharpGLTF.Schema2
             var wbpimg = this.GetExtension<TextureWEBP>()?.Image;
             if (wbpimg != null) return wbpimg;
 
+            var ktximg = this.GetExtension<TextureKTX2>()?.Image;
+            if (ktximg != null) return ktximg;
+
             return _source.HasValue ? LogicalParent.LogicalImages[_source.Value] : null;
         }
 
@@ -75,6 +78,7 @@ namespace SharpGLTF.Schema2
             _source = null;
             this.RemoveExtensions<TextureDDS>();
             this.RemoveExtensions<TextureWEBP>();
+            this.RemoveExtensions<TextureKTX2>();
         }
 
         public void SetImage(Image primaryImage)
@@ -82,7 +86,7 @@ namespace SharpGLTF.Schema2
             Guard.NotNull(primaryImage, nameof(primaryImage));
             Guard.MustShareLogicalParent(this, primaryImage, nameof(primaryImage));
 
-            if (primaryImage.Content.IsDds || primaryImage.Content.IsWebp)
+            if (primaryImage.Content.IsExtendedFormat)
             {
                 var fallback = LogicalParent.UseImage(Memory.MemoryImage.DefaultPngImage.Slice(0));
                 SetImages(primaryImage, fallback);
@@ -100,20 +104,14 @@ namespace SharpGLTF.Schema2
             Guard.NotNull(fallbackImage, nameof(fallbackImage));
             Guard.MustShareLogicalParent(this, primaryImage, nameof(primaryImage));
             Guard.MustShareLogicalParent(this, fallbackImage, nameof(fallbackImage));
-            Guard.IsTrue(primaryImage.Content.IsDds || primaryImage.Content.IsWebp, "Primary image must be DDS or WEBP");
+            Guard.IsTrue(primaryImage.Content.IsExtendedFormat, "Primary image must be DDS, WEBP or KTX2");
             Guard.IsTrue(fallbackImage.Content.IsJpg || fallbackImage.Content.IsPng, nameof(fallbackImage), "Fallback image must be PNG or JPEG");
 
             ClearImages();
 
-            if (primaryImage.Content.IsDds)
-            {
-                _UseDDSTexture().Image = primaryImage;
-            }
-
-            if (primaryImage.Content.IsWebp)
-            {
-                _UseWEBPTexture().Image = primaryImage;
-            }
+            if (primaryImage.Content.IsDds) { _UseDDSTexture().Image = primaryImage; }
+            if (primaryImage.Content.IsWebp) { _UseWEBPTexture().Image = primaryImage; }
+            if (primaryImage.Content.IsKtx2) { _UseKTX2Texture().Image = primaryImage; }
 
             _source = fallbackImage.LogicalIndex;
         }
@@ -134,6 +132,14 @@ namespace SharpGLTF.Schema2
             return primary;
         }
 
+        private TextureKTX2 _UseKTX2Texture()
+        {
+            var primary = this.GetExtension<TextureKTX2>();
+            if (primary == null) { primary = new TextureKTX2(this); this.SetExtension(primary); }
+
+            return primary;
+        }
+
         internal bool _IsEqualentTo(Image primary, Image fallback, TextureSampler sampler)
         {
             if (primary != this.PrimaryImage) return false;
@@ -209,6 +215,31 @@ namespace SharpGLTF.Schema2
         }
     }
 
+    partial class TextureKTX2
+    {
+        internal TextureKTX2(Texture parent)
+        {
+            _Parent = parent;
+        }
+
+        private readonly Texture _Parent;
+
+        public Image Image
+        {
+            get => _source.HasValue ? _Parent.LogicalParent.LogicalImages[_source.Value] : null;
+            set
+            {
+                if (value != null)
+                {
+                    Guard.MustShareLogicalParent(_Parent, value, nameof(value));
+                    Guard.IsTrue(value.Content.IsKtx2, nameof(value));
+                }
+
+                _source = value?.LogicalIndex;
+            }
+        }
+    }
+
     [System.Diagnostics.DebuggerDisplay("TextureSampler[{LogicalIndex}] {Name}")]
     public sealed partial class TextureSampler
     {

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

@@ -116,7 +116,7 @@ namespace SharpGLTF.Materials
 
         /// <summary>
         /// Gets or sets the default image bytes to use by this <see cref="TextureBuilder"/>,
-        /// Supported formats are: PNG, JPG, DDS and WEBP
+        /// Supported formats are: PNG, JPG, DDS, WEBP and KTX2
         /// </summary>
         public IMAGEFILE PrimaryImage
         {
@@ -164,7 +164,7 @@ namespace SharpGLTF.Materials
         {
             if (!image.IsEmpty)
             {
-                Guard.IsTrue(image.IsValid, nameof(image), "Must be JPG, PNG, DDS or WEBP");
+                Guard.IsTrue(image.IsValid, nameof(image), "Must be JPG, PNG, DDS, WEBP or KTX2");
             }
             else
             {

BIN
tests/Assets/CesiumLogoFlat.ktx2


+ 1 - 0
tests/SharpGLTF.Tests/Schema2/Authoring/ExtensionsCreationTests.cs

@@ -164,6 +164,7 @@ namespace SharpGLTF.Schema2.Authoring
 
         [TestCase("shannon-dxt5.dds")]
         [TestCase("shannon.webp")]
+        [TestCase("CesiumLogoFlat.ktx2")]
         public void CreateSceneWithTextureImageExtension(string textureFileName)
         {
             TestContext.CurrentContext.AttachShowDirLink();