Explorar el Código

Updated meshoptimizer.

Бранимир Караџић hace 6 años
padre
commit
ea6153796c

+ 1 - 1
3rdparty/meshoptimizer/CMakeLists.txt

@@ -58,7 +58,7 @@ if(BUILD_DEMO)
 endif()
 endif()
 
 
 if(BUILD_TOOLS)
 if(BUILD_TOOLS)
-    add_executable(gltfpack tools/gltfpack.cpp tools/meshloader.cpp)
+    add_executable(gltfpack tools/gltfpack.cpp tools/meshloader.cpp tools/basistoktx.cpp)
     target_link_libraries(gltfpack meshoptimizer)
     target_link_libraries(gltfpack meshoptimizer)
     list(APPEND TARGETS gltfpack)
     list(APPEND TARGETS gltfpack)
 
 

+ 1 - 1
3rdparty/meshoptimizer/Makefile

@@ -12,7 +12,7 @@ LIBRARY_OBJECTS=$(LIBRARY_SOURCES:%=$(BUILD)/%.o)
 DEMO_SOURCES=$(wildcard demo/*.c demo/*.cpp) tools/meshloader.cpp
 DEMO_SOURCES=$(wildcard demo/*.c demo/*.cpp) tools/meshloader.cpp
 DEMO_OBJECTS=$(DEMO_SOURCES:%=$(BUILD)/%.o)
 DEMO_OBJECTS=$(DEMO_SOURCES:%=$(BUILD)/%.o)
 
 
-GLTFPACK_SOURCES=tools/gltfpack.cpp tools/meshloader.cpp
+GLTFPACK_SOURCES=tools/gltfpack.cpp tools/meshloader.cpp tools/basistoktx.cpp
 GLTFPACK_OBJECTS=$(GLTFPACK_SOURCES:%=$(BUILD)/%.o)
 GLTFPACK_OBJECTS=$(GLTFPACK_SOURCES:%=$(BUILD)/%.o)
 
 
 OBJECTS=$(LIBRARY_OBJECTS) $(DEMO_OBJECTS) $(GLTFPACK_OBJECTS)
 OBJECTS=$(LIBRARY_OBJECTS) $(DEMO_OBJECTS) $(GLTFPACK_OBJECTS)

+ 2 - 1
3rdparty/meshoptimizer/src/vertexcodec.cpp

@@ -420,7 +420,8 @@ static unsigned char kDecodeBytesGroupCount[256];
 #ifdef EMSCRIPTEN
 #ifdef EMSCRIPTEN
 __attribute__((cold)) // this saves 500 bytes in the output binary - we don't need to vectorize this loop!
 __attribute__((cold)) // this saves 500 bytes in the output binary - we don't need to vectorize this loop!
 #endif
 #endif
-static bool decodeBytesGroupBuildTables()
+static bool
+decodeBytesGroupBuildTables()
 {
 {
 	for (int mask = 0; mask < 256; ++mask)
 	for (int mask = 0; mask < 256; ++mask)
 	{
 	{

+ 294 - 0
3rdparty/meshoptimizer/tools/basistoktx.cpp

@@ -0,0 +1,294 @@
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "basisu_format.h"
+#include "khr_df.h"
+#include "ktx2_format.h"
+
+template <typename T>
+static void read(const std::string& data, size_t offset, T& result)
+{
+	if (offset + sizeof(T) > data.size())
+		throw std::out_of_range("read");
+
+	memcpy(&result, &data[offset], sizeof(T));
+}
+
+template <typename T>
+static void write(std::string& data, const T& value)
+{
+	data.append(reinterpret_cast<const char*>(&value), sizeof(value));
+}
+
+template <typename T>
+static void write(std::string& data, size_t offset, const T& value)
+{
+	if (offset + sizeof(T) > data.size())
+		throw std::out_of_range("write");
+
+	memcpy(&data[offset], &value, sizeof(T));
+}
+
+static void createDfd(std::vector<uint32_t>& result, int channels, bool srgb)
+{
+	assert(channels <= 4);
+
+	int descriptor_size = KHR_DF_WORD_SAMPLESTART + channels * KHR_DF_WORD_SAMPLEWORDS;
+
+	result.clear();
+	result.resize(1 + descriptor_size);
+
+	result[0] = (1 + descriptor_size) * sizeof(uint32_t);
+
+	uint32_t* dfd = &result[1];
+
+	KHR_DFDSETVAL(dfd, VENDORID, KHR_DF_VENDORID_KHRONOS);
+	KHR_DFDSETVAL(dfd, DESCRIPTORTYPE, KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT);
+	KHR_DFDSETVAL(dfd, VERSIONNUMBER, KHR_DF_VERSIONNUMBER_1_3);
+	KHR_DFDSETVAL(dfd, DESCRIPTORBLOCKSIZE, descriptor_size * sizeof(uint32_t));
+	KHR_DFDSETVAL(dfd, MODEL, KHR_DF_MODEL_RGBSDA);
+	KHR_DFDSETVAL(dfd, PRIMARIES, KHR_DF_PRIMARIES_BT709);
+	KHR_DFDSETVAL(dfd, TRANSFER, srgb ? KHR_DF_TRANSFER_SRGB : KHR_DF_TRANSFER_LINEAR);
+	KHR_DFDSETVAL(dfd, FLAGS, KHR_DF_FLAG_ALPHA_STRAIGHT);
+
+	static const khr_df_model_channels_e channel_enums[] = {
+	    KHR_DF_CHANNEL_RGBSDA_R,
+	    KHR_DF_CHANNEL_RGBSDA_G,
+	    KHR_DF_CHANNEL_RGBSDA_B,
+	    KHR_DF_CHANNEL_RGBSDA_A,
+	};
+
+	for (int i = 0; i < channels; ++i)
+	{
+		KHR_DFDSETSVAL(dfd, i, CHANNELID, channel_enums[i]);
+	}
+}
+
+std::string basisToKtx(const std::string& basis, bool srgb)
+{
+	std::string ktx;
+
+	basist::basis_file_header basis_header;
+	read(basis, 0, basis_header);
+
+	assert(basis_header.m_sig == basist::basis_file_header::cBASISSigValue);
+
+	assert(basis_header.m_total_slices > 0);
+	assert(basis_header.m_total_images == 1);
+
+	assert(basis_header.m_format == 0);
+	assert(basis_header.m_flags & basist::cBASISHeaderFlagETC1S);
+	assert(!(basis_header.m_flags & basist::cBASISHeaderFlagYFlipped));
+	assert(basis_header.m_tex_type == basist::cBASISTexType2D);
+
+	bool has_alpha = (basis_header.m_flags & basist::cBASISHeaderFlagHasAlphaSlices) != 0;
+
+	std::vector<basist::basis_slice_desc> slices(basis_header.m_total_slices);
+
+	for (size_t i = 0; i < basis_header.m_total_slices; ++i)
+		read(basis, basis_header.m_slice_desc_file_ofs + i * sizeof(basist::basis_slice_desc), slices[i]);
+
+	assert(slices[0].m_level_index == 0);
+	uint32_t width = slices[0].m_orig_width;
+	uint32_t height = slices[0].m_orig_height;
+	uint32_t levels = has_alpha ? uint32_t(slices.size()) / 2 : uint32_t(slices.size());
+
+	KTX_header2 ktx_header = {KTX2_IDENTIFIER_REF};
+	ktx_header.typeSize = 1;
+	ktx_header.pixelWidth = width;
+	ktx_header.pixelHeight = height;
+	ktx_header.layerCount = 0;
+	ktx_header.faceCount = 1;
+	ktx_header.levelCount = levels;
+	ktx_header.supercompressionScheme = KTX_SUPERCOMPRESSION_BASIS;
+
+	size_t header_size = sizeof(KTX_header2) + levels * sizeof(ktxLevelIndexEntry);
+
+	std::vector<uint32_t> dfd;
+	createDfd(dfd, has_alpha ? 4 : 3, srgb);
+
+	const char* kvp_data[][2] = {
+	    {"KTXwriter", "gltfpack"},
+	};
+
+	std::string kvp;
+
+	for (size_t i = 0; i < sizeof(kvp_data) / sizeof(kvp_data[0]); ++i)
+	{
+		const char* key = kvp_data[i][0];
+		const char* value = kvp_data[i][1];
+
+		write(kvp, uint32_t(strlen(key) + strlen(value) + 2));
+		kvp += key;
+		kvp += '\0';
+		kvp += value;
+		kvp += '\0';
+
+		if (i + 1 != kvp.size())
+			kvp.resize((kvp.size() + 3) & ~3);
+	}
+
+	size_t kvp_size = kvp.size();
+	size_t dfd_size = dfd.size() * sizeof(uint32_t);
+
+	size_t bgd_size =
+	    sizeof(ktxBasisGlobalHeader) + sizeof(ktxBasisSliceDesc) * levels +
+	    basis_header.m_endpoint_cb_file_size + basis_header.m_selector_cb_file_size + basis_header.m_tables_file_size;
+
+	ktx_header.dataFormatDescriptor.byteOffset = uint32_t(header_size);
+	ktx_header.dataFormatDescriptor.byteLength = uint32_t(dfd_size);
+
+	ktx_header.keyValueData.byteOffset = uint32_t(header_size + dfd_size);
+	ktx_header.keyValueData.byteLength = uint32_t(kvp_size);
+
+	ktx_header.supercompressionGlobalData.byteOffset = (header_size + dfd_size + kvp_size + 7) & ~7;
+	ktx_header.supercompressionGlobalData.byteLength = bgd_size;
+
+	// KTX2 header
+	write(ktx, ktx_header);
+
+	size_t ktx_level_offset = ktx.size();
+
+	for (size_t i = 0; i < levels; ++i)
+	{
+		ktxLevelIndexEntry le = {}; // This will be patched later
+		write(ktx, le);
+	}
+
+	// data format descriptor
+	for (size_t i = 0; i < dfd.size(); ++i)
+		write(ktx, dfd[i]);
+
+	// key/value pair data
+	ktx += kvp;
+	ktx.resize((ktx.size() + 7) & ~7);
+
+	// supercompression global data
+	ktxBasisGlobalHeader sgd_header = {};
+	sgd_header.globalFlags = basis_header.m_flags;
+	sgd_header.endpointCount = basis_header.m_total_endpoints;
+	sgd_header.selectorCount = basis_header.m_total_selectors;
+	sgd_header.endpointsByteLength = basis_header.m_endpoint_cb_file_size;
+	sgd_header.selectorsByteLength = basis_header.m_selector_cb_file_size;
+	sgd_header.tablesByteLength = basis_header.m_tables_file_size;
+	sgd_header.extendedByteLength = basis_header.m_extended_file_size;
+
+	write(ktx, sgd_header);
+
+	size_t sgd_level_offset = ktx.size();
+
+	for (size_t i = 0; i < levels; ++i)
+	{
+		ktxBasisSliceDesc sgd_slice = {}; // This will be patched later
+		write(ktx, sgd_slice);
+	}
+
+	ktx.append(basis.substr(basis_header.m_endpoint_cb_file_ofs, basis_header.m_endpoint_cb_file_size));
+	ktx.append(basis.substr(basis_header.m_selector_cb_file_ofs, basis_header.m_selector_cb_file_size));
+	ktx.append(basis.substr(basis_header.m_tables_file_ofs, basis_header.m_tables_file_size));
+	ktx.append(basis.substr(basis_header.m_extended_file_ofs, basis_header.m_extended_file_size));
+
+	ktx.resize((ktx.size() + 7) & ~7);
+
+	// mip levels
+	for (size_t i = 0; i < levels; ++i)
+	{
+		size_t slice_index = (levels - i - 1) * (has_alpha + 1);
+		const basist::basis_slice_desc& slice = slices[slice_index];
+		const basist::basis_slice_desc* slice_alpha = has_alpha ? &slices[slice_index + 1] : 0;
+
+		assert(slice.m_image_index == 0);
+		assert(slice.m_level_index == levels - i - 1);
+
+		size_t file_offset = ktx.size();
+
+		ktx.append(basis.substr(slice.m_file_ofs, slice.m_file_size));
+
+		if (slice_alpha)
+			ktx.append(basis.substr(slice_alpha->m_file_ofs, slice_alpha->m_file_size));
+
+		ktxLevelIndexEntry le = {};
+		le.byteOffset = file_offset;
+		le.byteLength = ktx.size() - file_offset;
+		le.uncompressedByteLength = 0;
+
+		write(ktx, ktx_level_offset + i * sizeof(ktxLevelIndexEntry), le);
+
+		ktxBasisSliceDesc sgd_slice = {};
+		sgd_slice.sliceByteOffset = 0;
+		sgd_slice.sliceByteLength = slice.m_file_size;
+
+		if (slice_alpha)
+		{
+			sgd_slice.alphaSliceByteOffset = slice.m_file_size;
+			sgd_slice.alphaSliceByteLength = slice_alpha->m_file_size;
+		}
+
+		write(ktx, sgd_level_offset + i * sizeof(ktxBasisSliceDesc), sgd_slice);
+
+		if (i + 1 != levels)
+			ktx.resize((ktx.size() + 7) & ~7);
+	}
+
+	return ktx;
+}
+
+#ifdef STANDALONE
+bool readFile(const char* path, std::string& data)
+{
+	FILE* file = fopen(path, "rb");
+	if (!file)
+		return false;
+
+	fseek(file, 0, SEEK_END);
+	long length = ftell(file);
+	fseek(file, 0, SEEK_SET);
+
+	if (length <= 0)
+	{
+		fclose(file);
+		return false;
+	}
+
+	data.resize(length);
+	size_t result = fread(&data[0], 1, data.size(), file);
+	fclose(file);
+
+	return result == data.size();
+}
+
+bool writeFile(const char* path, const std::string& data)
+{
+	FILE* file = fopen(path, "wb");
+	if (!file)
+		return false;
+
+	size_t result = fwrite(&data[0], 1, data.size(), file);
+	fclose(file);
+
+	return result == data.size();
+}
+
+int main(int argc, const char** argv)
+{
+	if (argc < 2)
+		return 1;
+
+	std::string basis;
+	if (!readFile(argv[1], basis))
+		return 1;
+
+	std::string ktx = basisToKtx(basis, true);
+
+	if (!writeFile(argv[2], ktx))
+		return 1;
+
+	return 0;
+}
+#endif

+ 138 - 0
3rdparty/meshoptimizer/tools/basisu_format.h

@@ -0,0 +1,138 @@
+// basis_file_headers.h + basisu.h
+// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#pragma once
+
+namespace basisu
+{
+	// Always little endian 2-4 byte unsigned int
+	template<uint32_t NumBytes>
+	struct packed_uint
+	{
+		uint8_t m_bytes[NumBytes];
+
+		operator uint32_t() const
+		{
+			uint32_t result = 0;
+			for (uint32_t i = 0; i < NumBytes; i++)
+				result |= m_bytes[i] << (8 * i);
+			return result;
+		}
+	};
+}
+
+namespace basist
+{
+	// Slice desc header flags
+	enum basis_slice_desc_flags
+	{
+		cSliceDescFlagsIsAlphaData = 1,
+		cSliceDescFlagsFrameIsIFrame = 2			// Video only: Frame doesn't refer to previous frame (no usage of conditional replenishment pred symbols)
+	};
+
+#pragma pack(push)
+#pragma pack(1)
+	struct basis_slice_desc
+	{
+		basisu::packed_uint<3> m_image_index;  // The index of the source image provided to the encoder (will always appear in order from first to last, first image index is 0, no skipping allowed)
+		basisu::packed_uint<1> m_level_index;	// The mipmap level index (mipmaps will always appear from largest to smallest)
+		basisu::packed_uint<1> m_flags;			// enum basis_slice_desc_flags
+
+		basisu::packed_uint<2> m_orig_width;	// The original image width (may not be a multiple of 4 pixels)
+		basisu::packed_uint<2> m_orig_height;  // The original image height (may not be a multiple of 4 pixels)
+
+		basisu::packed_uint<2> m_num_blocks_x;	// The slice's block X dimensions. Each block is 4x4 pixels. The slice's pixel resolution may or may not be a power of 2.
+		basisu::packed_uint<2> m_num_blocks_y;	// The slice's block Y dimensions. 
+
+		basisu::packed_uint<4> m_file_ofs;		// Offset from the header to the start of the slice's data
+		basisu::packed_uint<4> m_file_size;		// The size of the compressed slice data in bytes
+
+		basisu::packed_uint<2> m_slice_data_crc16; // The CRC16 of the compressed slice data, for extra-paranoid use cases
+	};
+
+	// File header files
+	enum basis_header_flags
+	{
+		cBASISHeaderFlagETC1S = 1,					// Always set for basis universal files
+		cBASISHeaderFlagYFlipped = 2,				// Set if the texture had to be Y flipped before encoding
+		cBASISHeaderFlagHasAlphaSlices = 4		// True if the odd slices contain alpha data
+	};
+
+	// The image type field attempts to describe how to interpret the image data in a Basis file.
+	// The encoder library doesn't really do anything special or different with these texture types, this is mostly here for the benefit of the user. 
+	// We do make sure the various constraints are followed (2DArray/cubemap/videoframes/volume implies that each image has the same resolution and # of mipmap levels, etc., cubemap implies that the # of image slices is a multiple of 6)
+	enum basis_texture_type
+	{
+		cBASISTexType2D = 0,					// An arbitrary array of 2D RGB or RGBA images with optional mipmaps, array size = # images, each image may have a different resolution and # of mipmap levels
+		cBASISTexType2DArray = 1,			// An array of 2D RGB or RGBA images with optional mipmaps, array size = # images, each image has the same resolution and mipmap levels
+		cBASISTexTypeCubemapArray = 2,	// an array of cubemap levels, total # of images must be divisable by 6, in X+, X-, Y+, Y-, Z+, Z- order, with optional mipmaps
+		cBASISTexTypeVideoFrames = 3,		// An array of 2D video frames, with optional mipmaps, # frames = # images, each image has the same resolution and # of mipmap levels
+		cBASISTexTypeVolume = 4,			// A 3D texture with optional mipmaps, Z dimension = # images, each image has the same resolution and # of mipmap levels
+
+		cBASISTexTypeTotal
+	};
+
+	enum
+	{
+		cBASISMaxUSPerFrame = 0xFFFFFF
+	};
+
+	struct basis_file_header
+	{
+		enum
+		{
+			cBASISSigValue = ('B' << 8) | 's',
+			cBASISFirstVersion = 0x10
+		};
+
+		basisu::packed_uint<2>      m_sig;				// 2 byte file signature
+		basisu::packed_uint<2>      m_ver;				// Baseline file version
+		basisu::packed_uint<2>      m_header_size;	// Header size in bytes, sizeof(basis_file_header)
+		basisu::packed_uint<2>      m_header_crc16;	// crc16 of the remaining header data
+
+		basisu::packed_uint<4>      m_data_size;		// The total size of all data after the header
+		basisu::packed_uint<2>      m_data_crc16;		// The CRC16 of all data after the header
+
+		basisu::packed_uint<3>      m_total_slices;	// The total # of compressed slices (1 slice per image, or 2 for alpha basis files)
+
+		basisu::packed_uint<3>      m_total_images;	// The total # of images
+				
+		basisu::packed_uint<1>      m_format;			// enum basist::block_format
+		basisu::packed_uint<2>      m_flags;			// enum basist::header_flags
+		basisu::packed_uint<1>      m_tex_type;		// enum basist::basis_texture_type
+		basisu::packed_uint<3>      m_us_per_frame;	// Framerate of video, in microseconds per frame
+
+		basisu::packed_uint<4>      m_reserved;		// For future use
+		basisu::packed_uint<4>      m_userdata0;		// For client use
+		basisu::packed_uint<4>      m_userdata1;		// For client use
+
+		basisu::packed_uint<2>      m_total_endpoints;			// The number of endpoints in the endpoint codebook 
+		basisu::packed_uint<4>      m_endpoint_cb_file_ofs;	// The compressed endpoint codebook's file offset relative to the header
+		basisu::packed_uint<3>      m_endpoint_cb_file_size;	// The compressed endpoint codebook's size in bytes
+
+		basisu::packed_uint<2>      m_total_selectors;			// The number of selectors in the endpoint codebook 
+		basisu::packed_uint<4>      m_selector_cb_file_ofs;	// The compressed selectors codebook's file offset relative to the header
+		basisu::packed_uint<3>      m_selector_cb_file_size;	// The compressed selector codebook's size in bytes
+
+		basisu::packed_uint<4>      m_tables_file_ofs;			// The file offset of the compressed Huffman codelength tables, for decompressing slices
+		basisu::packed_uint<4>      m_tables_file_size;			// The file size in bytes of the compressed huffman codelength tables
+
+		basisu::packed_uint<4>      m_slice_desc_file_ofs;		// The file offset to the slice description array, usually follows the header
+		
+		basisu::packed_uint<4>      m_extended_file_ofs;		// The file offset of the "extended" header and compressed data, for future use
+		basisu::packed_uint<4>      m_extended_file_size;		// The file size in bytes of the "extended" header and compressed data, for future use
+	};
+#pragma pack (pop)
+
+} // namespace basist

+ 16 - 16
3rdparty/meshoptimizer/tools/cgltf.h

@@ -1,7 +1,7 @@
 /**
 /**
  * cgltf - a single-file glTF 2.0 parser written in C99.
  * cgltf - a single-file glTF 2.0 parser written in C99.
  *
  *
- * Version: 1.3
+ * Version: 1.4
  *
  *
  * Website: https://github.com/jkuhlmann/cgltf
  * Website: https://github.com/jkuhlmann/cgltf
  *
  *
@@ -420,7 +420,7 @@ typedef struct cgltf_camera {
 	union {
 	union {
 		cgltf_camera_perspective perspective;
 		cgltf_camera_perspective perspective;
 		cgltf_camera_orthographic orthographic;
 		cgltf_camera_orthographic orthographic;
-	};
+	} data;
 	cgltf_extras extras;
 	cgltf_extras extras;
 } cgltf_camera;
 } cgltf_camera;
 
 
@@ -1711,7 +1711,7 @@ cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size
 #define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return CGLTF_ERROR_JSON; }
 #define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return CGLTF_ERROR_JSON; }
 #define CGLTF_CHECK_KEY(tok_) if ((tok_).type != JSMN_STRING || (tok_).size == 0) { return CGLTF_ERROR_JSON; } /* checking size for 0 verifies that a value follows the key */
 #define CGLTF_CHECK_KEY(tok_) if ((tok_).type != JSMN_STRING || (tok_).size == 0) { return CGLTF_ERROR_JSON; } /* checking size for 0 verifies that a value follows the key */
 
 
-#define CGLTF_PTRINDEX(type, idx) (type*)(cgltf_size)(idx + 1)
+#define CGLTF_PTRINDEX(type, idx) (type*)((cgltf_size)idx + 1)
 #define CGLTF_PTRFIXUP(var, data, size) if (var) { if ((cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1]; }
 #define CGLTF_PTRFIXUP(var, data, size) if (var) { if ((cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1]; }
 #define CGLTF_PTRFIXUP_REQ(var, data, size) if (!var || (cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1];
 #define CGLTF_PTRFIXUP_REQ(var, data, size) if (!var || (cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1];
 
 
@@ -1727,7 +1727,7 @@ static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk)
 {
 {
 	CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE);
 	CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE);
 	char tmp[128];
 	char tmp[128];
-	int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : sizeof(tmp) - 1;
+	int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1);
 	strncpy(tmp, (const char*)json_chunk + tok->start, size);
 	strncpy(tmp, (const char*)json_chunk + tok->start, size);
 	tmp[size] = 0;
 	tmp[size] = 0;
 	return atoi(tmp);
 	return atoi(tmp);
@@ -1737,7 +1737,7 @@ static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json
 {
 {
 	CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE);
 	CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE);
 	char tmp[128];
 	char tmp[128];
-	int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : sizeof(tmp) - 1;
+	int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1);
 	strncpy(tmp, (const char*)json_chunk + tok->start, size);
 	strncpy(tmp, (const char*)json_chunk + tok->start, size);
 	tmp[size] = 0;
 	tmp[size] = 0;
 	return (cgltf_float)atof(tmp);
 	return (cgltf_float)atof(tmp);
@@ -1865,7 +1865,7 @@ static int cgltf_parse_json_string_array(cgltf_options* options, jsmntok_t const
 static void cgltf_parse_attribute_type(const char* name, cgltf_attribute_type* out_type, int* out_index)
 static void cgltf_parse_attribute_type(const char* name, cgltf_attribute_type* out_type, int* out_index)
 {
 {
 	const char* us = strchr(name, '_');
 	const char* us = strchr(name, '_');
-	size_t len = us ? us - name : strlen(name);
+	size_t len = us ? (size_t)(us - name) : strlen(name);
 
 
 	if (len == 8 && strncmp(name, "POSITION", 8) == 0)
 	if (len == 8 && strncmp(name, "POSITION", 8) == 0)
 	{
 	{
@@ -3284,30 +3284,30 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
 				if (cgltf_json_strcmp(tokens+i, json_chunk, "aspectRatio") == 0)
 				if (cgltf_json_strcmp(tokens+i, json_chunk, "aspectRatio") == 0)
 				{
 				{
 					++i;
 					++i;
-					out_camera->perspective.aspect_ratio = cgltf_json_to_float(tokens + i, json_chunk);
+					out_camera->data.perspective.aspect_ratio = cgltf_json_to_float(tokens + i, json_chunk);
 					++i;
 					++i;
 				}
 				}
 				else if (cgltf_json_strcmp(tokens+i, json_chunk, "yfov") == 0)
 				else if (cgltf_json_strcmp(tokens+i, json_chunk, "yfov") == 0)
 				{
 				{
 					++i;
 					++i;
-					out_camera->perspective.yfov = cgltf_json_to_float(tokens + i, json_chunk);
+					out_camera->data.perspective.yfov = cgltf_json_to_float(tokens + i, json_chunk);
 					++i;
 					++i;
 				}
 				}
 				else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0)
 				else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0)
 				{
 				{
 					++i;
 					++i;
-					out_camera->perspective.zfar = cgltf_json_to_float(tokens + i, json_chunk);
+					out_camera->data.perspective.zfar = cgltf_json_to_float(tokens + i, json_chunk);
 					++i;
 					++i;
 				}
 				}
 				else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0)
 				else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0)
 				{
 				{
 					++i;
 					++i;
-					out_camera->perspective.znear = cgltf_json_to_float(tokens + i, json_chunk);
+					out_camera->data.perspective.znear = cgltf_json_to_float(tokens + i, json_chunk);
 					++i;
 					++i;
 				}
 				}
 				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 				{
 				{
-					i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->perspective.extras);
+					i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.perspective.extras);
 				}
 				}
 				else
 				else
 				{
 				{
@@ -3338,30 +3338,30 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
 				if (cgltf_json_strcmp(tokens+i, json_chunk, "xmag") == 0)
 				if (cgltf_json_strcmp(tokens+i, json_chunk, "xmag") == 0)
 				{
 				{
 					++i;
 					++i;
-					out_camera->orthographic.xmag = cgltf_json_to_float(tokens + i, json_chunk);
+					out_camera->data.orthographic.xmag = cgltf_json_to_float(tokens + i, json_chunk);
 					++i;
 					++i;
 				}
 				}
 				else if (cgltf_json_strcmp(tokens+i, json_chunk, "ymag") == 0)
 				else if (cgltf_json_strcmp(tokens+i, json_chunk, "ymag") == 0)
 				{
 				{
 					++i;
 					++i;
-					out_camera->orthographic.ymag = cgltf_json_to_float(tokens + i, json_chunk);
+					out_camera->data.orthographic.ymag = cgltf_json_to_float(tokens + i, json_chunk);
 					++i;
 					++i;
 				}
 				}
 				else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0)
 				else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0)
 				{
 				{
 					++i;
 					++i;
-					out_camera->orthographic.zfar = cgltf_json_to_float(tokens + i, json_chunk);
+					out_camera->data.orthographic.zfar = cgltf_json_to_float(tokens + i, json_chunk);
 					++i;
 					++i;
 				}
 				}
 				else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0)
 				else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0)
 				{
 				{
 					++i;
 					++i;
-					out_camera->orthographic.znear = cgltf_json_to_float(tokens + i, json_chunk);
+					out_camera->data.orthographic.znear = cgltf_json_to_float(tokens + i, json_chunk);
 					++i;
 					++i;
 				}
 				}
 				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 				else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
 				{
 				{
-					i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->orthographic.extras);
+					i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras);
 				}
 				}
 				else
 				else
 				{
 				{

+ 200 - 150
3rdparty/meshoptimizer/tools/gltfpack.cpp

@@ -91,6 +91,7 @@ struct Settings
 
 
 	bool texture_embed;
 	bool texture_embed;
 	bool texture_basis;
 	bool texture_basis;
+	bool texture_ktx2;
 
 
 	int texture_quality;
 	int texture_quality;
 
 
@@ -140,6 +141,15 @@ struct MaterialInfo
 struct ImageInfo
 struct ImageInfo
 {
 {
 	bool normal_map;
 	bool normal_map;
+	bool srgb;
+};
+
+struct ExtensionInfo
+{
+	const char* name;
+
+	bool used;
+	bool required;
 };
 };
 
 
 struct BufferView
 struct BufferView
@@ -2612,6 +2622,36 @@ void remapNodes(cgltf_data* data, std::vector<NodeInfo>& nodes, size_t& node_off
 	}
 	}
 }
 }
 
 
+void analyzeImages(cgltf_data* data, std::vector<ImageInfo>& images)
+{
+	for (size_t i = 0; i < data->materials_count; ++i)
+	{
+		const cgltf_material& material = data->materials[i];
+
+		if (material.has_pbr_metallic_roughness)
+		{
+			const cgltf_pbr_metallic_roughness& pbr = material.pbr_metallic_roughness;
+
+			if (pbr.base_color_texture.texture && pbr.base_color_texture.texture->image)
+				images[pbr.base_color_texture.texture->image - data->images].srgb = true;
+		}
+
+		if (material.has_pbr_specular_glossiness)
+		{
+			const cgltf_pbr_specular_glossiness& pbr = material.pbr_specular_glossiness;
+
+			if (pbr.diffuse_texture.texture && pbr.diffuse_texture.texture->image)
+				images[pbr.diffuse_texture.texture->image - data->images].srgb = true;
+		}
+
+		if (material.emissive_texture.texture && material.emissive_texture.texture->image)
+			images[material.emissive_texture.texture->image - data->images].srgb = true;
+
+		if (material.normal_texture.texture && material.normal_texture.texture->image)
+			images[material.normal_texture.texture->image - data->images].normal_map = true;
+	}
+}
+
 bool parseDataUri(const char* uri, std::string& mime_type, std::string& result)
 bool parseDataUri(const char* uri, std::string& mime_type, std::string& result)
 {
 {
 	if (strncmp(uri, "data:", 5) == 0)
 	if (strncmp(uri, "data:", 5) == 0)
@@ -2772,7 +2812,7 @@ struct TempFile
 	}
 	}
 };
 };
 
 
-bool encodeBasis(const std::string& data, std::string& result, bool normal_map, int quality)
+bool encodeBasis(const std::string& data, std::string& result, bool normal_map, bool srgb, int quality)
 {
 {
 	TempFile temp_input(".raw");
 	TempFile temp_input(".raw");
 	TempFile temp_output(".basis");
 	TempFile temp_output(".basis");
@@ -2790,11 +2830,16 @@ bool encodeBasis(const std::string& data, std::string& result, bool normal_map,
 	cmd += ql;
 	cmd += ql;
 
 
 	cmd += " -mipmap";
 	cmd += " -mipmap";
+
 	if (normal_map)
 	if (normal_map)
 	{
 	{
 		cmd += " -normal_map";
 		cmd += " -normal_map";
 		// for optimal quality we should specify seperate_rg_to_color_alpha but this requires renderer awareness
 		// for optimal quality we should specify seperate_rg_to_color_alpha but this requires renderer awareness
 	}
 	}
+	else if (!srgb)
+	{
+		cmd += " -linear";
+	}
 
 
 	cmd += " -file ";
 	cmd += " -file ";
 	cmd += temp_input.path;
 	cmd += temp_input.path;
@@ -2812,6 +2857,9 @@ bool encodeBasis(const std::string& data, std::string& result, bool normal_map,
 	return rc == 0 && readFile(temp_output.path.c_str(), result);
 	return rc == 0 && readFile(temp_output.path.c_str(), result);
 }
 }
 
 
+// basistoktx.cpp
+extern std::string basisToKtx(const std::string& basis, bool srgb);
+
 void writeImage(std::string& json, std::vector<BufferView>& views, const cgltf_image& image, const ImageInfo& info, size_t index, const char* input_path, const char* output_path, const Settings& settings)
 void writeImage(std::string& json, std::vector<BufferView>& views, const cgltf_image& image, const ImageInfo& info, size_t index, const char* input_path, const char* output_path, const Settings& settings)
 {
 {
 	std::string img_data;
 	std::string img_data;
@@ -2846,9 +2894,12 @@ void writeImage(std::string& json, std::vector<BufferView>& views, const cgltf_i
 		{
 		{
 			std::string encoded;
 			std::string encoded;
 
 
-			if (encodeBasis(img_data, encoded, info.normal_map, settings.texture_quality))
+			if (encodeBasis(img_data, encoded, info.normal_map, info.srgb, settings.texture_quality))
 			{
 			{
-				writeEmbeddedImage(json, views, encoded.c_str(), encoded.size(), "image/basis");
+				if (settings.texture_ktx2)
+					encoded = basisToKtx(encoded, info.srgb);
+
+				writeEmbeddedImage(json, views, encoded.c_str(), encoded.size(), settings.texture_ktx2 ? "image/ktx2" : "image/basis");
 			}
 			}
 			else
 			else
 			{
 			{
@@ -2865,32 +2916,38 @@ void writeImage(std::string& json, std::vector<BufferView>& views, const cgltf_i
 		if (settings.texture_basis)
 		if (settings.texture_basis)
 		{
 		{
 			std::string full_path = getFullPath(image.uri, input_path);
 			std::string full_path = getFullPath(image.uri, input_path);
-			std::string basis_path = getFileName(image.uri) + ".basis";
+			std::string basis_path = getFileName(image.uri) + (settings.texture_ktx2 ? ".ktx" : ".basis");
 			std::string basis_full_path = getFullPath(basis_path.c_str(), output_path);
 			std::string basis_full_path = getFullPath(basis_path.c_str(), output_path);
 
 
-			if (!readFile(full_path.c_str(), img_data))
-			{
-				fprintf(stderr, "Warning: unable to read image %s, skipping\n", image.uri);
-			}
-			else
+			if (readFile(full_path.c_str(), img_data))
 			{
 			{
 				std::string encoded;
 				std::string encoded;
 
 
-				if (!encodeBasis(img_data, encoded, info.normal_map, settings.texture_quality))
-				{
-					fprintf(stderr, "Warning: unable to encode image %s with Basis, skipping\n", image.uri);
-				}
-				else if (!writeFile(basis_full_path.c_str(), encoded))
+				if (encodeBasis(img_data, encoded, info.normal_map, info.srgb, settings.texture_quality))
 				{
 				{
-					fprintf(stderr, "Warning: unable to save Basis image %s, skipping\n", image.uri);
+					if (settings.texture_ktx2)
+						encoded = basisToKtx(encoded, info.srgb);
+
+					if (writeFile(basis_full_path.c_str(), encoded))
+					{
+						append(json, "\"uri\":\"");
+						append(json, basis_path);
+						append(json, "\"");
+					}
+					else
+					{
+						fprintf(stderr, "Warning: unable to save Basis image %s, skipping\n", image.uri);
+					}
 				}
 				}
 				else
 				else
 				{
 				{
-					append(json, "\"uri\":\"");
-					append(json, basis_path);
-					append(json, "\"");
+					fprintf(stderr, "Warning: unable to encode image %s with Basis, skipping\n", image.uri);
 				}
 				}
 			}
 			}
+			else
+			{
+				fprintf(stderr, "Warning: unable to read image %s, skipping\n", image.uri);
+			}
 		}
 		}
 		else
 		else
 		{
 		{
@@ -2905,6 +2962,24 @@ void writeImage(std::string& json, std::vector<BufferView>& views, const cgltf_i
 	}
 	}
 }
 }
 
 
+void writeTexture(std::string& json, const cgltf_texture& texture, cgltf_data* data, const Settings& settings)
+{
+	if (texture.image)
+	{
+		if (settings.texture_ktx2)
+		{
+			append(json, "\"extensions\":{\"KHR_texture_basisu\":{\"source\":");
+			append(json, size_t(texture.image - data->images));
+			append(json, "}}");
+		}
+		else
+		{
+			append(json, "\"source\":");
+			append(json, size_t(texture.image - data->images));
+		}
+	}
+}
+
 void writeMeshAttributes(std::string& json, std::vector<BufferView>& views, std::string& json_accessors, size_t& accr_offset, const Mesh& mesh, int target, const QuantizationParams& qp, const Settings& settings)
 void writeMeshAttributes(std::string& json, std::vector<BufferView>& views, std::string& json_accessors, size_t& accr_offset, const Mesh& mesh, int target, const QuantizationParams& qp, const Settings& settings)
 {
 {
 	std::string scratch;
 	std::string scratch;
@@ -3317,18 +3392,18 @@ void writeCamera(std::string& json, const cgltf_camera& camera)
 	case cgltf_camera_type_perspective:
 	case cgltf_camera_type_perspective:
 		append(json, "\"type\":\"perspective\",\"perspective\":{");
 		append(json, "\"type\":\"perspective\",\"perspective\":{");
 		append(json, "\"yfov\":");
 		append(json, "\"yfov\":");
-		append(json, camera.perspective.yfov);
+		append(json, camera.data.perspective.yfov);
 		append(json, ",\"znear\":");
 		append(json, ",\"znear\":");
-		append(json, camera.perspective.znear);
-		if (camera.perspective.aspect_ratio != 0.f)
+		append(json, camera.data.perspective.znear);
+		if (camera.data.perspective.aspect_ratio != 0.f)
 		{
 		{
 			append(json, ",\"aspectRatio\":");
 			append(json, ",\"aspectRatio\":");
-			append(json, camera.perspective.aspect_ratio);
+			append(json, camera.data.perspective.aspect_ratio);
 		}
 		}
-		if (camera.perspective.zfar != 0.f)
+		if (camera.data.perspective.zfar != 0.f)
 		{
 		{
 			append(json, ",\"zfar\":");
 			append(json, ",\"zfar\":");
-			append(json, camera.perspective.zfar);
+			append(json, camera.data.perspective.zfar);
 		}
 		}
 		append(json, "}");
 		append(json, "}");
 		break;
 		break;
@@ -3336,13 +3411,13 @@ void writeCamera(std::string& json, const cgltf_camera& camera)
 	case cgltf_camera_type_orthographic:
 	case cgltf_camera_type_orthographic:
 		append(json, "\"type\":\"orthographic\",\"orthographic\":{");
 		append(json, "\"type\":\"orthographic\",\"orthographic\":{");
 		append(json, "\"xmag\":");
 		append(json, "\"xmag\":");
-		append(json, camera.orthographic.xmag);
+		append(json, camera.data.orthographic.xmag);
 		append(json, ",\"ymag\":");
 		append(json, ",\"ymag\":");
-		append(json, camera.orthographic.ymag);
+		append(json, camera.data.orthographic.ymag);
 		append(json, ",\"znear\":");
 		append(json, ",\"znear\":");
-		append(json, camera.orthographic.znear);
+		append(json, camera.data.orthographic.znear);
 		append(json, ",\"zfar\":");
 		append(json, ",\"zfar\":");
-		append(json, camera.orthographic.zfar);
+		append(json, camera.data.orthographic.zfar);
 		append(json, "}");
 		append(json, "}");
 		break;
 		break;
 
 
@@ -3397,6 +3472,46 @@ void writeLight(std::string& json, const cgltf_light& light)
 	append(json, "}");
 	append(json, "}");
 }
 }
 
 
+void writeArray(std::string& json, const char* name, const std::string& contents)
+{
+	if (contents.empty())
+		return;
+
+	comma(json);
+	append(json, "\"");
+	append(json, name);
+	append(json, "\":[");
+	append(json, contents);
+	append(json, "]");
+}
+
+void writeExtensions(std::string& json, const ExtensionInfo* extensions, size_t count)
+{
+	comma(json);
+	append(json, "\"extensionsUsed\":[");
+	for (size_t i = 0; i < count; ++i)
+		if (extensions[i].used)
+		{
+			comma(json);
+			append(json, "\"");
+			append(json, extensions[i].name);
+			append(json, "\"");
+		}
+	append(json, "]");
+
+	comma(json);
+	append(json, "\"extensionsRequired\":[");
+	for (size_t i = 0; i < count; ++i)
+		if (extensions[i].used && extensions[i].required)
+		{
+			comma(json);
+			append(json, "\"");
+			append(json, extensions[i].name);
+			append(json, "\"");
+		}
+	append(json, "]");
+}
+
 void finalizeBufferViews(std::string& json, std::vector<BufferView>& views, std::string& bin, std::string& fallback)
 void finalizeBufferViews(std::string& json, std::vector<BufferView>& views, std::string& bin, std::string& fallback)
 {
 {
 	for (size_t i = 0; i < views.size(); ++i)
 	for (size_t i = 0; i < views.size(); ++i)
@@ -3460,6 +3575,23 @@ void printMeshStats(const std::vector<Mesh>& meshes, const char* name)
 	printf("%s: %d triangles, %d vertices\n", name, int(triangles), int(vertices));
 	printf("%s: %d triangles, %d vertices\n", name, int(triangles), int(vertices));
 }
 }
 
 
+void printSceneStats(const std::vector<BufferView>& views, const std::vector<Mesh>& meshes, size_t node_offset, size_t mesh_offset, size_t material_offset, size_t json_size, size_t bin_size)
+{
+	size_t bytes[BufferView::Kind_Count] = {};
+
+	for (size_t i = 0; i < views.size(); ++i)
+	{
+		const BufferView& view = views[i];
+		bytes[view.kind] += view.bytes;
+	}
+
+	printf("output: %d nodes, %d meshes (%d primitives), %d materials\n", int(node_offset), int(mesh_offset), int(meshes.size()), int(material_offset));
+	printf("output: JSON %d bytes, buffers %d bytes\n", int(json_size), int(bin_size));
+	printf("output: buffers: vertex %d bytes, index %d bytes, skin %d bytes, time %d bytes, keyframe %d bytes, image %d bytes\n",
+	       int(bytes[BufferView::Kind_Vertex]), int(bytes[BufferView::Kind_Index]), int(bytes[BufferView::Kind_Skin]),
+	       int(bytes[BufferView::Kind_Time]), int(bytes[BufferView::Kind_Keyframe]), int(bytes[BufferView::Kind_Image]));
+}
+
 void printAttributeStats(const std::vector<BufferView>& views, BufferView::Kind kind, const char* name)
 void printAttributeStats(const std::vector<BufferView>& views, BufferView::Kind kind, const char* name)
 {
 {
 	for (size_t i = 0; i < views.size(); ++i)
 	for (size_t i = 0; i < views.size(); ++i)
@@ -3561,13 +3693,7 @@ void process(cgltf_data* data, const char* input_path, const char* output_path,
 
 
 	std::vector<ImageInfo> images(data->images_count);
 	std::vector<ImageInfo> images(data->images_count);
 
 
-	for (size_t i = 0; i < data->materials_count; ++i)
-	{
-		const cgltf_material& material = data->materials[i];
-
-		if (material.normal_texture.texture && material.normal_texture.texture->image)
-			images[material.normal_texture.texture->image - data->images].normal_map = true;
-	}
+	analyzeImages(data, images);
 
 
 	QuantizationParams qp = prepareQuantization(meshes, settings);
 	QuantizationParams qp = prepareQuantization(meshes, settings);
 
 
@@ -3595,9 +3721,11 @@ void process(cgltf_data* data, const char* input_path, const char* output_path,
 
 
 	for (size_t i = 0; i < data->images_count; ++i)
 	for (size_t i = 0; i < data->images_count; ++i)
 	{
 	{
+		const cgltf_image& image = data->images[i];
+
 		if (settings.verbose && settings.texture_basis)
 		if (settings.verbose && settings.texture_basis)
 		{
 		{
-			const char* uri = data->images[i].uri;
+			const char* uri = image.uri;
 			bool embedded = !uri || strncmp(uri, "data:", 5) == 0;
 			bool embedded = !uri || strncmp(uri, "data:", 5) == 0;
 
 
 			printf("image %d (%s) is being encoded with Basis\n", int(i), embedded ? "embedded" : uri);
 			printf("image %d (%s) is being encoded with Basis\n", int(i), embedded ? "embedded" : uri);
@@ -3605,7 +3733,7 @@ void process(cgltf_data* data, const char* input_path, const char* output_path,
 
 
 		comma(json_images);
 		comma(json_images);
 		append(json_images, "{");
 		append(json_images, "{");
-		writeImage(json_images, views, data->images[i], images[i], i, input_path, output_path, settings);
+		writeImage(json_images, views, image, images[i], i, input_path, output_path, settings);
 		append(json_images, "}");
 		append(json_images, "}");
 	}
 	}
 
 
@@ -3615,11 +3743,7 @@ void process(cgltf_data* data, const char* input_path, const char* output_path,
 
 
 		comma(json_textures);
 		comma(json_textures);
 		append(json_textures, "{");
 		append(json_textures, "{");
-		if (texture.image)
-		{
-			append(json_textures, "\"source\":");
-			append(json_textures, size_t(texture.image - data->images));
-		}
+		writeTexture(json_textures, texture, data, settings);
 		append(json_textures, "}");
 		append(json_textures, "}");
 	}
 	}
 
 
@@ -3834,110 +3958,42 @@ void process(cgltf_data* data, const char* input_path, const char* output_path,
 	}
 	}
 	append(json, "}");
 	append(json, "}");
 
 
-	append(json, ",\"extensionsUsed\":[");
-	append(json, "\"KHR_mesh_quantization\"");
-	if (settings.compress)
-	{
-		comma(json);
-		append(json, "\"MESHOPT_compression\"");
-	}
-	if (!json_textures.empty())
-	{
-		comma(json);
-		append(json, "\"KHR_texture_transform\"");
-	}
-	if (ext_pbr_specular_glossiness)
-	{
-		comma(json);
-		append(json, "\"KHR_materials_pbrSpecularGlossiness\"");
-	}
-	if (ext_unlit)
-	{
-		comma(json);
-		append(json, "\"KHR_materials_unlit\"");
-	}
-	if (data->lights_count)
-	{
-		comma(json);
-		append(json, "\"KHR_lights_punctual\"");
-	}
-	append(json, "]");
+	const ExtensionInfo extensions[] = {
+	    {"KHR_mesh_quantization", true, true},
+	    {"MESHOPT_compression", settings.compress, !settings.fallback},
+	    {"KHR_texture_transform", !json_textures.empty(), false},
+	    {"KHR_materials_pbrSpecularGlossiness", ext_pbr_specular_glossiness, false},
+	    {"KHR_materials_unlit", ext_unlit, false},
+	    {"KHR_lights_punctual", data->lights_count > 0, false},
+	    {"KHR_image_ktx2", !json_textures.empty() && settings.texture_ktx2, true},
+	    {"KHR_texture_basisu", !json_textures.empty() && settings.texture_ktx2, true},
+	};
 
 
-	append(json, ",\"extensionsRequired\":[");
-	append(json, "\"KHR_mesh_quantization\"");
-	if (settings.compress && !settings.fallback)
-	{
-		comma(json);
-		append(json, "\"MESHOPT_compression\"");
-	}
-	append(json, "]");
+	writeExtensions(json, extensions, sizeof(extensions) / sizeof(extensions[0]));
 
 
-	if (!views.empty())
-	{
-		std::string json_views;
-		finalizeBufferViews(json_views, views, bin, fallback);
+	std::string json_views;
+	finalizeBufferViews(json_views, views, bin, fallback);
+
+	writeArray(json, "bufferViews", json_views);
+	writeArray(json, "accessors", json_accessors);
+	writeArray(json, "images", json_images);
+	writeArray(json, "textures", json_textures);
+	writeArray(json, "materials", json_materials);
+	writeArray(json, "meshes", json_meshes);
+	writeArray(json, "skins", json_skins);
+	writeArray(json, "animations", json_animations);
+	writeArray(json, "nodes", json_nodes);
 
 
-		append(json, ",\"bufferViews\":[");
-		append(json, json_views);
-		append(json, "]");
-	}
-	if (!json_accessors.empty())
-	{
-		append(json, ",\"accessors\":[");
-		append(json, json_accessors);
-		append(json, "]");
-	}
-	if (!json_images.empty())
-	{
-		append(json, ",\"images\":[");
-		append(json, json_images);
-		append(json, "]");
-	}
-	if (!json_textures.empty())
-	{
-		append(json, ",\"textures\":[");
-		append(json, json_textures);
-		append(json, "]");
-	}
-	if (!json_materials.empty())
-	{
-		append(json, ",\"materials\":[");
-		append(json, json_materials);
-		append(json, "]");
-	}
-	if (!json_meshes.empty())
-	{
-		append(json, ",\"meshes\":[");
-		append(json, json_meshes);
-		append(json, "]");
-	}
-	if (!json_skins.empty())
-	{
-		append(json, ",\"skins\":[");
-		append(json, json_skins);
-		append(json, "]");
-	}
-	if (!json_animations.empty())
-	{
-		append(json, ",\"animations\":[");
-		append(json, json_animations);
-		append(json, "]");
-	}
 	if (!json_roots.empty())
 	if (!json_roots.empty())
 	{
 	{
-		append(json, ",\"nodes\":[");
-		append(json, json_nodes);
-		append(json, "],\"scenes\":[");
+		append(json, ",\"scenes\":[");
 		append(json, "{\"nodes\":[");
 		append(json, "{\"nodes\":[");
 		append(json, json_roots);
 		append(json, json_roots);
 		append(json, "]}]");
 		append(json, "]}]");
 	}
 	}
-	if (!json_cameras.empty())
-	{
-		append(json, ",\"cameras\":[");
-		append(json, json_cameras);
-		append(json, "]");
-	}
+
+	writeArray(json, "cameras", json_cameras);
+
 	if (!json_lights.empty())
 	if (!json_lights.empty())
 	{
 	{
 		append(json, ",\"extensions\":{\"KHR_lights_punctual\":{\"lights\":[");
 		append(json, ",\"extensions\":{\"KHR_lights_punctual\":{\"lights\":[");
@@ -3951,19 +4007,7 @@ void process(cgltf_data* data, const char* input_path, const char* output_path,
 
 
 	if (settings.verbose)
 	if (settings.verbose)
 	{
 	{
-		size_t bytes[BufferView::Kind_Count] = {};
-
-		for (size_t i = 0; i < views.size(); ++i)
-		{
-			BufferView& view = views[i];
-			bytes[view.kind] += view.bytes;
-		}
-
-		printf("output: %d nodes, %d meshes (%d primitives), %d materials\n", int(node_offset), int(mesh_offset), int(meshes.size()), int(material_offset));
-		printf("output: JSON %d bytes, buffers %d bytes\n", int(json.size()), int(bin.size()));
-		printf("output: buffers: vertex %d bytes, index %d bytes, skin %d bytes, time %d bytes, keyframe %d bytes, image %d bytes\n",
-		       int(bytes[BufferView::Kind_Vertex]), int(bytes[BufferView::Kind_Index]), int(bytes[BufferView::Kind_Skin]),
-		       int(bytes[BufferView::Kind_Time]), int(bytes[BufferView::Kind_Keyframe]), int(bytes[BufferView::Kind_Image]));
+		printSceneStats(views, meshes, node_offset, mesh_offset, material_offset, json.size(), bin.size());
 	}
 	}
 
 
 	if (settings.verbose > 1)
 	if (settings.verbose > 1)
@@ -4254,6 +4298,11 @@ int main(int argc, char** argv)
 		{
 		{
 			settings.texture_basis = true;
 			settings.texture_basis = true;
 		}
 		}
+		else if (strcmp(arg, "-tc") == 0)
+		{
+			settings.texture_basis = true;
+			settings.texture_ktx2 = true;
+		}
 		else if (strcmp(arg, "-tq") == 0 && i + 1 < argc && isdigit(argv[i + 1][0]))
 		else if (strcmp(arg, "-tq") == 0 && i + 1 < argc && isdigit(argv[i + 1][0]))
 		{
 		{
 			settings.texture_quality = atoi(argv[++i]);
 			settings.texture_quality = atoi(argv[++i]);
@@ -4328,6 +4377,7 @@ int main(int argc, char** argv)
 		fprintf(stderr, "-sa: aggressively simplify to the target ratio disregarding quality\n");
 		fprintf(stderr, "-sa: aggressively simplify to the target ratio disregarding quality\n");
 		fprintf(stderr, "-te: embed all textures into main buffer\n");
 		fprintf(stderr, "-te: embed all textures into main buffer\n");
 		fprintf(stderr, "-tb: convert all textures to Basis Universal format (with basisu executable)\n");
 		fprintf(stderr, "-tb: convert all textures to Basis Universal format (with basisu executable)\n");
+		fprintf(stderr, "-tc: convert all textures to KTX2 with BasisU supercompression (using basisu executable)\n");
 		fprintf(stderr, "-tq N: set texture encoding quality (default: 50; N should be between 1 and 100\n");
 		fprintf(stderr, "-tq N: set texture encoding quality (default: 50; N should be between 1 and 100\n");
 		fprintf(stderr, "-c: produce compressed gltf/glb files\n");
 		fprintf(stderr, "-c: produce compressed gltf/glb files\n");
 		fprintf(stderr, "-cf: produce compressed gltf/glb files with fallback for loaders that don't support compression\n");
 		fprintf(stderr, "-cf: produce compressed gltf/glb files with fallback for loaders that don't support compression\n");

+ 627 - 0
3rdparty/meshoptimizer/tools/khr_df.h

@@ -0,0 +1,627 @@
+/* The Khronos Data Format Specification (version 1.3) */
+/*
+** Copyright (c) 2015-19 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+/* This header defines a structure that can describe the layout of image
+   formats in memory. This means that the data format is transparent to
+   the application, and the expectation is that this should be used when
+   the layout is defined external to the API. Many Khronos APIs deliberately
+   keep the internal layout of images opaque, to allow proprietary layouts
+   and optimisations. This structure is not appropriate for describing
+   opaque layouts. */
+
+/* We stick to standard C89 constructs for simplicity and portability. */
+
+#ifndef _KHR_DATA_FORMAT_H_
+#define _KHR_DATA_FORMAT_H_
+
+/* Accessors */
+typedef enum _khr_word_e {
+    KHR_DF_WORD_VENDORID = 0U,
+    KHR_DF_WORD_DESCRIPTORTYPE = 0U,
+    KHR_DF_WORD_VERSIONNUMBER = 1U,
+    KHR_DF_WORD_DESCRIPTORBLOCKSIZE = 1U,
+    KHR_DF_WORD_MODEL = 2U,
+    KHR_DF_WORD_PRIMARIES = 2U,
+    KHR_DF_WORD_TRANSFER = 2U,
+    KHR_DF_WORD_FLAGS = 2U,
+    KHR_DF_WORD_TEXELBLOCKDIMENSION0 = 3U,
+    KHR_DF_WORD_TEXELBLOCKDIMENSION1 = 3U,
+    KHR_DF_WORD_TEXELBLOCKDIMENSION2 = 3U,
+    KHR_DF_WORD_TEXELBLOCKDIMENSION3 = 3U,
+    KHR_DF_WORD_BYTESPLANE0 = 4U,
+    KHR_DF_WORD_BYTESPLANE1 = 4U,
+    KHR_DF_WORD_BYTESPLANE2 = 4U,
+    KHR_DF_WORD_BYTESPLANE3 = 4U,
+    KHR_DF_WORD_BYTESPLANE4 = 5U,
+    KHR_DF_WORD_BYTESPLANE5 = 5U,
+    KHR_DF_WORD_BYTESPLANE6 = 5U,
+    KHR_DF_WORD_BYTESPLANE7 = 5U,
+    KHR_DF_WORD_SAMPLESTART = 6U,
+    KHR_DF_WORD_SAMPLEWORDS = 4U
+} khr_df_word_e;
+
+typedef enum _khr_df_shift_e {
+    KHR_DF_SHIFT_VENDORID = 0U,
+    KHR_DF_SHIFT_DESCRIPTORTYPE = 17U,
+    KHR_DF_SHIFT_VERSIONNUMBER = 0U,
+    KHR_DF_SHIFT_DESCRIPTORBLOCKSIZE = 16U,
+    KHR_DF_SHIFT_MODEL = 0U,
+    KHR_DF_SHIFT_PRIMARIES = 8U,
+    KHR_DF_SHIFT_TRANSFER = 16U,
+    KHR_DF_SHIFT_FLAGS = 24U,
+    KHR_DF_SHIFT_TEXELBLOCKDIMENSION0 = 0U,
+    KHR_DF_SHIFT_TEXELBLOCKDIMENSION1 = 8U,
+    KHR_DF_SHIFT_TEXELBLOCKDIMENSION2 = 16U,
+    KHR_DF_SHIFT_TEXELBLOCKDIMENSION3 = 24U,
+    KHR_DF_SHIFT_BYTESPLANE0 = 0U,
+    KHR_DF_SHIFT_BYTESPLANE1 = 8U,
+    KHR_DF_SHIFT_BYTESPLANE2 = 16U,
+    KHR_DF_SHIFT_BYTESPLANE3 = 24U,
+    KHR_DF_SHIFT_BYTESPLANE4 = 0U,
+    KHR_DF_SHIFT_BYTESPLANE5 = 8U,
+    KHR_DF_SHIFT_BYTESPLANE6 = 16U,
+    KHR_DF_SHIFT_BYTESPLANE7 = 24U
+} khr_df_shift_e;
+
+typedef enum _khr_df_mask_e {
+    KHR_DF_MASK_VENDORID = 0x1FFFFU,
+    KHR_DF_MASK_DESCRIPTORTYPE = 0x7FFFU,
+    KHR_DF_MASK_VERSIONNUMBER = 0xFFFFU,
+    KHR_DF_MASK_DESCRIPTORBLOCKSIZE = 0xFFFFU,
+    KHR_DF_MASK_MODEL = 0xFFU,
+    KHR_DF_MASK_PRIMARIES = 0xFFU,
+    KHR_DF_MASK_TRANSFER = 0xFFU,
+    KHR_DF_MASK_FLAGS = 0xFFU,
+    KHR_DF_MASK_TEXELBLOCKDIMENSION0 = 0xFFU,
+    KHR_DF_MASK_TEXELBLOCKDIMENSION1 = 0xFFU,
+    KHR_DF_MASK_TEXELBLOCKDIMENSION2 = 0xFFU,
+    KHR_DF_MASK_TEXELBLOCKDIMENSION3 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE0 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE1 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE2 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE3 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE4 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE5 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE6 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE7 = 0xFFU
+} khr_df_mask_e;
+
+/* Helper macro:
+   Extract field X from basic descriptor block BDB */
+#define KHR_DFDVAL(BDB, X) \
+    (((BDB)[KHR_DF_WORD_ ## X] >> (KHR_DF_SHIFT_ ## X)) \
+     & (KHR_DF_MASK_ ## X))
+
+/* Helper macro:
+   Set field X of basic descriptor block BDB */
+#define KHR_DFDSETVAL(BDB, X, val) \
+    ((BDB)[KHR_DF_WORD_ ## X] = \
+     ((BDB)[KHR_DF_WORD_ ## X] & \
+      ~((KHR_DF_MASK_ ## X) << (KHR_DF_SHIFT_ ## X))) | \
+     (((val) & (KHR_DF_MASK_ ## X)) << (KHR_DF_SHIFT_ ## X)))
+
+/* Offsets relative to the start of a sample */
+typedef enum _khr_df_sampleword_e {
+    KHR_DF_SAMPLEWORD_BITOFFSET = 0U,
+    KHR_DF_SAMPLEWORD_BITLENGTH = 0U,
+    KHR_DF_SAMPLEWORD_CHANNELID = 0U,
+    KHR_DF_SAMPLEWORD_QUALIFIERS = 0U,
+    KHR_DF_SAMPLEWORD_SAMPLEPOSITION0 = 1U,
+    KHR_DF_SAMPLEWORD_SAMPLEPOSITION1 = 1U,
+    KHR_DF_SAMPLEWORD_SAMPLEPOSITION2 = 1U,
+    KHR_DF_SAMPLEWORD_SAMPLEPOSITION3 = 1U,
+    KHR_DF_SAMPLEWORD_SAMPLEPOSITION_ALL = 1U,
+    KHR_DF_SAMPLEWORD_SAMPLELOWER = 2U,
+    KHR_DF_SAMPLEWORD_SAMPLEUPPER = 3U
+} khr_df_sampleword_e;
+
+typedef enum _khr_df_sampleshift_e {
+    KHR_DF_SAMPLESHIFT_BITOFFSET = 0U,
+    KHR_DF_SAMPLESHIFT_BITLENGTH = 16U,
+    KHR_DF_SAMPLESHIFT_CHANNELID = 24U,
+    /* N.B. Qualifiers are defined as an offset into a byte */
+    KHR_DF_SAMPLESHIFT_QUALIFIERS = 24U,
+    KHR_DF_SAMPLESHIFT_SAMPLEPOSITION0 = 0U,
+    KHR_DF_SAMPLESHIFT_SAMPLEPOSITION1 = 8U,
+    KHR_DF_SAMPLESHIFT_SAMPLEPOSITION2 = 16U,
+    KHR_DF_SAMPLESHIFT_SAMPLEPOSITION3 = 24U,
+    KHR_DF_SAMPLESHIFT_SAMPLEPOSITION_ALL = 0U,
+    KHR_DF_SAMPLESHIFT_SAMPLELOWER = 0U,
+    KHR_DF_SAMPLESHIFT_SAMPLEUPPER = 0U
+} khr_df_sampleshift_e;
+
+typedef enum _khr_df_samplemask_e {
+    KHR_DF_SAMPLEMASK_BITOFFSET = 0xFFFFU,
+    KHR_DF_SAMPLEMASK_BITLENGTH = 0xFF,
+    KHR_DF_SAMPLEMASK_CHANNELID = 0xF,
+    /* N.B. Qualifiers are defined as an offset into a byte */
+    KHR_DF_SAMPLEMASK_QUALIFIERS = 0xF0,
+    KHR_DF_SAMPLEMASK_SAMPLEPOSITION0 = 0xFF,
+    KHR_DF_SAMPLEMASK_SAMPLEPOSITION1 = 0xFF,
+    KHR_DF_SAMPLEMASK_SAMPLEPOSITION2 = 0xFF,
+    KHR_DF_SAMPLEMASK_SAMPLEPOSITION3 = 0xFF,
+    /* ISO C restricts enum values to range of int hence the
+       cast. We do it verbosely instead of using -1 to ensure
+       it is a 32-bit value even if int is 64 bits. */
+    KHR_DF_SAMPLEMASK_SAMPLEPOSITION_ALL = (int) 0xFFFFFFFFU,
+    KHR_DF_SAMPLEMASK_SAMPLELOWER = (int) 0xFFFFFFFFU,
+    KHR_DF_SAMPLEMASK_SAMPLEUPPER = (int) 0xFFFFFFFFU
+} khr_df_samplemask_e;
+
+/* Helper macro:
+   Extract field X of sample S from basic descriptor block BDB */
+#define KHR_DFDSVAL(BDB, S, X) \
+    (((BDB)[KHR_DF_WORD_SAMPLESTART + \
+            ((S) * KHR_DF_WORD_SAMPLEWORDS) + \
+            KHR_DF_SAMPLEWORD_ ## X] >> (KHR_DF_SAMPLESHIFT_ ## X)) \
+     & (KHR_DF_SAMPLEMASK_ ## X))
+
+/* Helper macro:
+   Set field X of sample S of basic descriptor block BDB */
+#define KHR_DFDSETSVAL(BDB, S, X, val) \
+    ((BDB)[KHR_DF_WORD_SAMPLESTART + \
+           ((S) * KHR_DF_WORD_SAMPLEWORDS) + \
+           KHR_DF_SAMPLEWORD_ ## X] = \
+     ((BDB)[KHR_DF_WORD_SAMPLESTART + \
+            ((S) * KHR_DF_WORD_SAMPLEWORDS) + \
+            KHR_DF_SAMPLEWORD_ ## X] & \
+      ~((uint32_t)(KHR_DF_SAMPLEMASK_ ## X) << (KHR_DF_SAMPLESHIFT_ ## X))) | \
+     (((val) & (uint32_t)(KHR_DF_SAMPLEMASK_ ## X)) << (KHR_DF_SAMPLESHIFT_ ## X)))
+
+/* Helper macro:
+   Number of samples in basic descriptor block BDB */
+#define KHR_DFDSAMPLECOUNT(BDB) \
+    (((KHR_DFDVAL(BDB, DESCRIPTORBLOCKSIZE) >> 2) - \
+      KHR_DF_WORD_SAMPLESTART) \
+     / KHR_DF_WORD_SAMPLEWORDS)
+
+/* Helper macro:
+   Size in words of basic descriptor block for S samples */
+#define KHR_DFDSIZEWORDS(S) \
+    (KHR_DF_WORD_SAMPLESTART + \
+     (S) * KHR_DF_WORD_SAMPLEWORDS)
+
+/* Vendor ids */
+typedef enum _khr_df_vendorid_e {
+    /* Standard Khronos descriptor */
+    KHR_DF_VENDORID_KHRONOS = 0U,
+    KHR_DF_VENDORID_MAX     = 0x1FFFFU
+} khr_df_vendorid_e;
+
+/* Descriptor types */
+typedef enum _khr_df_khr_descriptortype_e {
+    /* Default Khronos basic descriptor block */
+    KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT = 0U,
+    /* Extension descriptor block for additional planes */
+    KHR_DF_KHR_DESCRIPTORTYPE_ADDITIONAL_PLANES = 0x6001U,
+    /* Extension descriptor block for additional dimensions */
+    KHR_DF_KHR_DESCRIPTORTYPE_ADDITIONAL_DIMENSIONS = 0x6002U,
+    /* Bit indicates modifying requires understanding this extension */
+    KHR_DF_KHR_DESCRIPTORTYPE_NEEDED_FOR_WRITE_BIT = 0x2000U,
+    /* Bit indicates processing requires understanding this extension */
+    KHR_DF_KHR_DESCRIPTORTYPE_NEEDED_FOR_DECODE_BIT = 0x4000U,
+    KHR_DF_KHR_DESCRIPTORTYPE_MAX         = 0x7FFFU
+} khr_df_khr_descriptortype_e;
+
+/* Descriptor block version */
+typedef enum _khr_df_versionnumber_e {
+    /* Standard Khronos descriptor */
+    KHR_DF_VERSIONNUMBER_1_0 = 0U, /* Version 1.0 of the specification */
+    KHR_DF_VERSIONNUMBER_1_1 = 0U, /* Version 1.1 did not bump the version number */
+    KHR_DF_VERSIONNUMBER_1_2 = 1U, /* Version 1.2 increased the version number */
+    KHR_DF_VERSIONNUMBER_1_3 = 2U, /* Version 1.3 increased the version number */
+    KHR_DF_VERSIONNUMBER_LATEST = KHR_DF_VERSIONNUMBER_1_3,
+    KHR_DF_VERSIONNUMBER_MAX = 0xFFFFU
+} khr_df_versionnumber_e;
+
+/* Model in which the color coordinate space is defined.
+   There is no requirement that a color format use all the
+   channel types that are defined in the color model. */
+typedef enum _khr_df_model_e {
+    /* No interpretation of color channels defined */
+    KHR_DF_MODEL_UNSPECIFIED  = 0U,
+    /* Color primaries (red, green, blue) + alpha, depth and stencil */
+    KHR_DF_MODEL_RGBSDA       = 1U,
+    /* Color differences (Y', Cb, Cr) + alpha, depth and stencil */
+    KHR_DF_MODEL_YUVSDA       = 2U,
+    /* Color differences (Y', I, Q) + alpha, depth and stencil */
+    KHR_DF_MODEL_YIQSDA       = 3U,
+    /* Perceptual color (CIE L*a*b*) + alpha, depth and stencil */
+    KHR_DF_MODEL_LABSDA       = 4U,
+    /* Subtractive colors (cyan, magenta, yellow, black) + alpha */
+    KHR_DF_MODEL_CMYKA        = 5U,
+    /* Non-color coordinate data (X, Y, Z, W) */
+    KHR_DF_MODEL_XYZW         = 6U,
+    /* Hue, saturation, value, hue angle on color circle, plus alpha */
+    KHR_DF_MODEL_HSVA_ANG     = 7U,
+    /* Hue, saturation, lightness, hue angle on color circle, plus alpha */
+    KHR_DF_MODEL_HSLA_ANG     = 8U,
+    /* Hue, saturation, value, hue on color hexagon, plus alpha */
+    KHR_DF_MODEL_HSVA_HEX     = 9U,
+    /* Hue, saturation, lightness, hue on color hexagon, plus alpha */
+    KHR_DF_MODEL_HSLA_HEX     = 10U,
+    /* Lightweight approximate color difference (luma, orange, green) */
+    KHR_DF_MODEL_YCGCOA       = 11U,
+    /* ITU BT.2020 constant luminance YcCbcCrc */
+    KHR_DF_MODEL_YCCBCCRC     = 12U,
+    /* ITU BT.2100 constant intensity ICtCp */
+    KHR_DF_MODEL_ICTCP        = 13U,
+    /* CIE 1931 XYZ color coordinates (X, Y, Z) */
+    KHR_DF_MODEL_CIEXYZ       = 14U,
+    /* CIE 1931 xyY color coordinates (X, Y, Y) */
+    KHR_DF_MODEL_CIEXYY       = 15U,
+
+    /* Compressed formats start at 128. */
+    /* These compressed formats should generally have a single sample,
+       sited at the 0,0 position of the texel block. Where multiple
+       channels are used to distinguish formats, these should be cosited. */
+    /* Direct3D (and S3) compressed formats */
+    /* Note that premultiplied status is recorded separately */
+    /* DXT1 "channels" are RGB (0), Alpha (1) */
+    /* DXT1/BC1 with one channel is opaque */
+    /* DXT1/BC1 with a cosited alpha sample is transparent */
+    KHR_DF_MODEL_DXT1A         = 128U,
+    KHR_DF_MODEL_BC1A          = 128U,
+    /* DXT2/DXT3/BC2, with explicit 4-bit alpha */
+    KHR_DF_MODEL_DXT2          = 129U,
+    KHR_DF_MODEL_DXT3          = 129U,
+    KHR_DF_MODEL_BC2           = 129U,
+    /* DXT4/DXT5/BC3, with interpolated alpha */
+    KHR_DF_MODEL_DXT4          = 130U,
+    KHR_DF_MODEL_DXT5          = 130U,
+    KHR_DF_MODEL_BC3           = 130U,
+    /* BC4 - single channel interpolated 8-bit data */
+    /* (The UNORM/SNORM variation is recorded in the channel data) */
+    KHR_DF_MODEL_BC4           = 131U,
+    /* BC5 - two channel interpolated 8-bit data */
+    /* (The UNORM/SNORM variation is recorded in the channel data) */
+    KHR_DF_MODEL_BC5           = 132U,
+    /* BC6H - DX11 format for 16-bit float channels */
+    KHR_DF_MODEL_BC6H          = 133U,
+    /* BC7 - DX11 format */
+    KHR_DF_MODEL_BC7           = 134U,
+    /* Gap left for future desktop expansion */
+
+    /* Mobile compressed formats follow */
+    /* A format of ETC1 indicates that the format shall be decodable
+       by an ETC1-compliant decoder and not rely on ETC2 features */
+    KHR_DF_MODEL_ETC1          = 160U,
+    /* A format of ETC2 is permitted to use ETC2 encodings on top of
+       the baseline ETC1 specification */
+    /* The ETC2 format has channels "red", "green", "RGB" and "alpha",
+       which should be cosited samples */
+    /* Punch-through alpha can be distinguished from full alpha by
+       the plane size in bytes required for the texel block */
+    KHR_DF_MODEL_ETC2          = 161U,
+    /* Adaptive Scalable Texture Compression */
+    /* ASTC HDR vs LDR is determined by the float flag in the channel */
+    /* ASTC block size can be distinguished by texel block size */ 
+    KHR_DF_MODEL_ASTC          = 162U,
+    /* ETC1S is a simplified subset of ETC1 */
+    KHR_DF_MODEL_ETC1S         = 163U,
+    /* PowerVR Texture Compression */
+    KHR_DF_MODEL_PVRTC         = 164U,
+    KHR_DF_MODEL_PVRTC2        = 165U,
+    /* Proprietary formats (ATITC, etc.) should follow */
+    KHR_DF_MODEL_MAX = 0xFFU
+} khr_df_model_e;
+
+/* Definition of channel names for each color model */
+typedef enum _khr_df_model_channels_e {
+    /* Unspecified format with nominal channel numbering */
+    KHR_DF_CHANNEL_UNSPECIFIED_0  = 0U,
+    KHR_DF_CHANNEL_UNSPECIFIED_1  = 1U,
+    KHR_DF_CHANNEL_UNSPECIFIED_2  = 2U,
+    KHR_DF_CHANNEL_UNSPECIFIED_3  = 3U,
+    KHR_DF_CHANNEL_UNSPECIFIED_4  = 4U,
+    KHR_DF_CHANNEL_UNSPECIFIED_5  = 5U,
+    KHR_DF_CHANNEL_UNSPECIFIED_6  = 6U,
+    KHR_DF_CHANNEL_UNSPECIFIED_7  = 7U,
+    KHR_DF_CHANNEL_UNSPECIFIED_8  = 8U,
+    KHR_DF_CHANNEL_UNSPECIFIED_9  = 9U,
+    KHR_DF_CHANNEL_UNSPECIFIED_10 = 10U,
+    KHR_DF_CHANNEL_UNSPECIFIED_11 = 11U,
+    KHR_DF_CHANNEL_UNSPECIFIED_12 = 12U,
+    KHR_DF_CHANNEL_UNSPECIFIED_13 = 13U,
+    KHR_DF_CHANNEL_UNSPECIFIED_14 = 14U,
+    KHR_DF_CHANNEL_UNSPECIFIED_15 = 15U,
+    /* MODEL_RGBSDA - red, green, blue, stencil, depth, alpha */
+    KHR_DF_CHANNEL_RGBSDA_RED     =  0U,
+    KHR_DF_CHANNEL_RGBSDA_R       =  0U,
+    KHR_DF_CHANNEL_RGBSDA_GREEN   =  1U,
+    KHR_DF_CHANNEL_RGBSDA_G       =  1U,
+    KHR_DF_CHANNEL_RGBSDA_BLUE    =  2U,
+    KHR_DF_CHANNEL_RGBSDA_B       =  2U,
+    KHR_DF_CHANNEL_RGBSDA_STENCIL = 13U,
+    KHR_DF_CHANNEL_RGBSDA_S       = 13U,
+    KHR_DF_CHANNEL_RGBSDA_DEPTH   = 14U,
+    KHR_DF_CHANNEL_RGBSDA_D       = 14U,
+    KHR_DF_CHANNEL_RGBSDA_ALPHA   = 15U,
+    KHR_DF_CHANNEL_RGBSDA_A       = 15U,
+    /* MODEL_YUVSDA - luma, Cb, Cr, stencil, depth, alpha */
+    KHR_DF_CHANNEL_YUVSDA_Y       =  0U,
+    KHR_DF_CHANNEL_YUVSDA_CB      =  1U,
+    KHR_DF_CHANNEL_YUVSDA_U       =  1U,
+    KHR_DF_CHANNEL_YUVSDA_CR      =  2U,
+    KHR_DF_CHANNEL_YUVSDA_V       =  2U,
+    KHR_DF_CHANNEL_YUVSDA_STENCIL = 13U,
+    KHR_DF_CHANNEL_YUVSDA_S       = 13U,
+    KHR_DF_CHANNEL_YUVSDA_DEPTH   = 14U,
+    KHR_DF_CHANNEL_YUVSDA_D       = 14U,
+    KHR_DF_CHANNEL_YUVSDA_ALPHA   = 15U,
+    KHR_DF_CHANNEL_YUVSDA_A       = 15U,
+    /* MODEL_YIQSDA - luma, in-phase, quadrature, stencil, depth, alpha */
+    KHR_DF_CHANNEL_YIQSDA_Y       =  0U,
+    KHR_DF_CHANNEL_YIQSDA_I       =  1U,
+    KHR_DF_CHANNEL_YIQSDA_Q       =  2U,
+    KHR_DF_CHANNEL_YIQSDA_STENCIL = 13U,
+    KHR_DF_CHANNEL_YIQSDA_S       = 13U,
+    KHR_DF_CHANNEL_YIQSDA_DEPTH   = 14U,
+    KHR_DF_CHANNEL_YIQSDA_D       = 14U,
+    KHR_DF_CHANNEL_YIQSDA_ALPHA   = 15U,
+    KHR_DF_CHANNEL_YIQSDA_A       = 15U,
+    /* MODEL_LABSDA - CIELAB/L*a*b* luma, red-green, blue-yellow, stencil, depth, alpha */
+    KHR_DF_CHANNEL_LABSDA_L       =  0U,
+    KHR_DF_CHANNEL_LABSDA_A       =  1U,
+    KHR_DF_CHANNEL_LABSDA_B       =  2U,
+    KHR_DF_CHANNEL_LABSDA_STENCIL = 13U,
+    KHR_DF_CHANNEL_LABSDA_S       = 13U,
+    KHR_DF_CHANNEL_LABSDA_DEPTH   = 14U,
+    KHR_DF_CHANNEL_LABSDA_D       = 14U,
+    KHR_DF_CHANNEL_LABSDA_ALPHA   = 15U,
+    /* NOTE: KHR_DF_CHANNEL_LABSDA_A is not a synonym for alpha! */
+    /* MODEL_CMYKA - cyan, magenta, yellow, key/blacK, alpha */
+    KHR_DF_CHANNEL_CMYKSDA_CYAN    =  0U,
+    KHR_DF_CHANNEL_CMYKSDA_C       =  0U,
+    KHR_DF_CHANNEL_CMYKSDA_MAGENTA =  1U,
+    KHR_DF_CHANNEL_CMYKSDA_M       =  1U,
+    KHR_DF_CHANNEL_CMYKSDA_YELLOW  =  2U,
+    KHR_DF_CHANNEL_CMYKSDA_Y       =  2U,
+    KHR_DF_CHANNEL_CMYKSDA_KEY     =  3U,
+    KHR_DF_CHANNEL_CMYKSDA_BLACK   =  3U,
+    KHR_DF_CHANNEL_CMYKSDA_K       =  3U,
+    KHR_DF_CHANNEL_CMYKSDA_ALPHA   = 15U,
+    KHR_DF_CHANNEL_CMYKSDA_A       = 15U,
+    /* MODEL_XYZW - coordinates x, y, z, w */
+    KHR_DF_CHANNEL_XYZW_X = 0U,
+    KHR_DF_CHANNEL_XYZW_Y = 1U,
+    KHR_DF_CHANNEL_XYZW_Z = 2U,
+    KHR_DF_CHANNEL_XYZW_W = 3U,
+    /* MODEL_HSVA_ANG - value (luma), saturation, hue, alpha, angular projection, conical space */
+    KHR_DF_CHANNEL_HSVA_ANG_VALUE      = 0U,
+    KHR_DF_CHANNEL_HSVA_ANG_V          = 0U,
+    KHR_DF_CHANNEL_HSVA_ANG_SATURATION = 1U,
+    KHR_DF_CHANNEL_HSVA_ANG_S          = 1U,
+    KHR_DF_CHANNEL_HSVA_ANG_HUE        = 2U,
+    KHR_DF_CHANNEL_HSVA_ANG_H          = 2U,
+    KHR_DF_CHANNEL_HSVA_ANG_ALPHA      = 15U,
+    KHR_DF_CHANNEL_HSVA_ANG_A          = 15U,
+    /* MODEL_HSLA_ANG - lightness (luma), saturation, hue, alpha, angular projection, double conical space */
+    KHR_DF_CHANNEL_HSLA_ANG_LIGHTNESS  = 0U,
+    KHR_DF_CHANNEL_HSLA_ANG_L          = 0U,
+    KHR_DF_CHANNEL_HSLA_ANG_SATURATION = 1U,
+    KHR_DF_CHANNEL_HSLA_ANG_S          = 1U,
+    KHR_DF_CHANNEL_HSLA_ANG_HUE        = 2U,
+    KHR_DF_CHANNEL_HSLA_ANG_H          = 2U,
+    KHR_DF_CHANNEL_HSLA_ANG_ALPHA      = 15U,
+    KHR_DF_CHANNEL_HSLA_ANG_A          = 15U,
+    /* MODEL_HSVA_HEX - value (luma), saturation, hue, alpha, hexagonal projection, conical space */
+    KHR_DF_CHANNEL_HSVA_HEX_VALUE      = 0U,
+    KHR_DF_CHANNEL_HSVA_HEX_V          = 0U,
+    KHR_DF_CHANNEL_HSVA_HEX_SATURATION = 1U,
+    KHR_DF_CHANNEL_HSVA_HEX_S          = 1U,
+    KHR_DF_CHANNEL_HSVA_HEX_HUE        = 2U,
+    KHR_DF_CHANNEL_HSVA_HEX_H          = 2U,
+    KHR_DF_CHANNEL_HSVA_HEX_ALPHA      = 15U,
+    KHR_DF_CHANNEL_HSVA_HEX_A          = 15U,
+    /* MODEL_HSLA_HEX - lightness (luma), saturation, hue, alpha, hexagonal projection, double conical space */
+    KHR_DF_CHANNEL_HSLA_HEX_LIGHTNESS  = 0U,
+    KHR_DF_CHANNEL_HSLA_HEX_L          = 0U,
+    KHR_DF_CHANNEL_HSLA_HEX_SATURATION = 1U,
+    KHR_DF_CHANNEL_HSLA_HEX_S          = 1U,
+    KHR_DF_CHANNEL_HSLA_HEX_HUE        = 2U,
+    KHR_DF_CHANNEL_HSLA_HEX_H          = 2U,
+    KHR_DF_CHANNEL_HSLA_HEX_ALPHA      = 15U,
+    KHR_DF_CHANNEL_HSLA_HEX_A          = 15U,
+    /* MODEL_YCGCOA - luma, green delta, orange delta, alpha */
+    KHR_DF_CHANNEL_YCGCOA_Y       =  0U,
+    KHR_DF_CHANNEL_YCGCOA_CG      =  1U,
+    KHR_DF_CHANNEL_YCGCOA_CO      =  2U,
+    KHR_DF_CHANNEL_YCGCOA_ALPHA   = 15U,
+    KHR_DF_CHANNEL_YCGCOA_A       = 15U,
+    /* MODEL_CIEXYZ - CIE 1931 X, Y, Z */
+    KHR_DF_CHANNEL_CIEXYZ_X = 0U,
+    KHR_DF_CHANNEL_CIEXYZ_Y = 1U,
+    KHR_DF_CHANNEL_CIEXYZ_Z = 2U,
+    /* MODEL_CIEXYY - CIE 1931 x, y, Y */
+    KHR_DF_CHANNEL_CIEXYY_X        = 0U,
+    KHR_DF_CHANNEL_CIEXYY_YCHROMA  = 1U,
+    KHR_DF_CHANNEL_CIEXYY_YLUMA    = 2U,
+
+    /* Compressed formats */
+    /* MODEL_DXT1A/MODEL_BC1A */
+    KHR_DF_CHANNEL_DXT1A_COLOR = 0U,
+    KHR_DF_CHANNEL_BC1A_COLOR  = 0U,
+    KHR_DF_CHANNEL_DXT1A_ALPHAPRESENT = 1U,
+    KHR_DF_CHANNEL_DXT1A_ALPHA = 1U,
+    KHR_DF_CHANNEL_BC1A_ALPHAPRESENT  = 1U,
+    KHR_DF_CHANNEL_BC1A_ALPHA  = 1U,
+    /* MODEL_DXT2/3/MODEL_BC2 */
+    KHR_DF_CHANNEL_DXT2_COLOR =  0U,
+    KHR_DF_CHANNEL_DXT3_COLOR =  0U,
+    KHR_DF_CHANNEL_BC2_COLOR  =  0U,
+    KHR_DF_CHANNEL_DXT2_ALPHA = 15U,
+    KHR_DF_CHANNEL_DXT3_ALPHA = 15U,
+    KHR_DF_CHANNEL_BC2_ALPHA  = 15U,
+    /* MODEL_DXT4/5/MODEL_BC3 */
+    KHR_DF_CHANNEL_DXT4_COLOR =  0U,
+    KHR_DF_CHANNEL_DXT5_COLOR =  0U,
+    KHR_DF_CHANNEL_BC3_COLOR  =  0U,
+    KHR_DF_CHANNEL_DXT4_ALPHA = 15U,
+    KHR_DF_CHANNEL_DXT5_ALPHA = 15U,
+    KHR_DF_CHANNEL_BC3_ALPHA  = 15U,
+    /* MODEL_BC4 */
+    KHR_DF_CHANNEL_BC4_DATA = 0U,
+    /* MODEL_BC5 */
+    KHR_DF_CHANNEL_BC5_RED   = 0U,
+    KHR_DF_CHANNEL_BC5_R     = 0U,
+    KHR_DF_CHANNEL_BC5_GREEN = 1U,
+    KHR_DF_CHANNEL_BC5_G     = 1U,
+    /* MODEL_BC6H */
+    KHR_DF_CHANNEL_BC6H_COLOR = 0U,
+    KHR_DF_CHANNEL_BC6H_DATA = 0U,
+    /* MODEL_BC7 */
+    KHR_DF_CHANNEL_BC7_DATA = 0U,
+    KHR_DF_CHANNEL_BC7_COLOR = 0U,
+    /* MODEL_ETC1 */
+    KHR_DF_CHANNEL_ETC1_DATA  = 0U,
+    KHR_DF_CHANNEL_ETC1_COLOR = 0U,
+    /* MODEL_ETC2 */
+    KHR_DF_CHANNEL_ETC2_RED   = 0U,
+    KHR_DF_CHANNEL_ETC2_R     = 0U,
+    KHR_DF_CHANNEL_ETC2_GREEN = 1U,
+    KHR_DF_CHANNEL_ETC2_G     = 1U,
+    KHR_DF_CHANNEL_ETC2_COLOR = 2U,
+    KHR_DF_CHANNEL_ETC2_ALPHA = 15U,
+    KHR_DF_CHANNEL_ETC2_A     = 15U,
+    /* MODEL_ASTC */
+    KHR_DF_CHANNEL_ASTC_DATA  = 0U,
+    /* MODEL_ETC1S */
+    KHR_DF_CHANNEL_ETC1S_DATA  = 0U,
+    KHR_DF_CHANNEL_ETC1S_COLOR = 0U,
+    /* MODEL_PVRTC */
+    KHR_DF_CHANNEL_PVRTC_DATA  = 0U,
+    KHR_DF_CHANNEL_PVRTC_COLOR = 0U,
+    /* MODEL_PVRTC2 */
+    KHR_DF_CHANNEL_PVRTC2_DATA  = 0U,
+    KHR_DF_CHANNEL_PVRTC2_COLOR = 0U,
+
+    /* Common channel names shared by multiple formats */
+    KHR_DF_CHANNEL_COMMON_LUMA    =  0U,
+    KHR_DF_CHANNEL_COMMON_L       =  0U,
+    KHR_DF_CHANNEL_COMMON_STENCIL = 13U,
+    KHR_DF_CHANNEL_COMMON_S       = 13U,
+    KHR_DF_CHANNEL_COMMON_DEPTH   = 14U,
+    KHR_DF_CHANNEL_COMMON_D       = 14U,
+    KHR_DF_CHANNEL_COMMON_ALPHA   = 15U,
+    KHR_DF_CHANNEL_COMMON_A       = 15U
+} khr_df_model_channels_e;
+
+/* Definition of the primary colors in color coordinates.
+   This is implicitly responsible for defining the conversion
+   between RGB an YUV color spaces.
+   LAB and related absolute color models should use
+   KHR_DF_PRIMARIES_CIEXYZ. */
+typedef enum _khr_df_primaries_e {
+    /* No color primaries defined */
+    KHR_DF_PRIMARIES_UNSPECIFIED = 0U,
+    /* Color primaries of ITU-R BT.709 and sRGB */
+    KHR_DF_PRIMARIES_BT709       = 1U,
+    /* Synonym for KHR_DF_PRIMARIES_BT709 */
+    KHR_DF_PRIMARIES_SRGB        = 1U,
+    /* Color primaries of ITU-R BT.601 (625-line EBU variant) */
+    KHR_DF_PRIMARIES_BT601_EBU   = 2U,
+    /* Color primaries of ITU-R BT.601 (525-line SMPTE C variant) */
+    KHR_DF_PRIMARIES_BT601_SMPTE = 3U,
+    /* Color primaries of ITU-R BT.2020 */
+    KHR_DF_PRIMARIES_BT2020      = 4U,
+    /* CIE theoretical color coordinate space */
+    KHR_DF_PRIMARIES_CIEXYZ      = 5U,
+    /* Academy Color Encoding System primaries */
+    KHR_DF_PRIMARIES_ACES        = 6U,
+    /* Color primaries of ACEScc */
+    KHR_DF_PRIMARIES_ACESCC      = 7U,
+    /* Legacy NTSC 1953 primaries */
+    KHR_DF_PRIMARIES_NTSC1953    = 8U,
+    /* Legacy PAL 525-line primaries */
+    KHR_DF_PRIMARIES_PAL525      = 9U,
+    /* Color primaries of Display P3 */
+    KHR_DF_PRIMARIES_DISPLAYP3   = 10U,
+    /* Color primaries of Adobe RGB (1998) */
+    KHR_DF_PRIMARIES_ADOBERGB    = 11U,
+    KHR_DF_PRIMARIES_MAX         = 0xFFU
+} khr_df_primaries_e;
+
+/* Definition of the optical to digital transfer function
+   ("gamma correction"). Most transfer functions are not a pure
+   power function and also include a linear element.
+   LAB and related absolute color representations should use
+   KHR_DF_TRANSFER_UNSPECIFIED. */
+typedef enum _khr_df_transfer_e {
+    /* No transfer function defined */
+    KHR_DF_TRANSFER_UNSPECIFIED = 0U,
+    /* Linear transfer function (value proportional to intensity) */
+    KHR_DF_TRANSFER_LINEAR      = 1U,
+    /* Perceptually-linear transfer function of sRGH (~2.4) */
+    KHR_DF_TRANSFER_SRGB        = 2U,
+    /* Perceptually-linear transfer function of ITU non-HDR specifications (~1/.45) */
+    KHR_DF_TRANSFER_ITU         = 3U,
+    /* SMTPE170M (digital NTSC) defines an alias for the ITU transfer function (~1/.45) */
+    KHR_DF_TRANSFER_SMTPE170M   = 3U,
+    /* Perceptually-linear gamma function of original NTSC (simple 2.2 gamma) */
+    KHR_DF_TRANSFER_NTSC        = 4U,
+    /* Sony S-log used by Sony video cameras */
+    KHR_DF_TRANSFER_SLOG        = 5U,
+    /* Sony S-log 2 used by Sony video cameras */
+    KHR_DF_TRANSFER_SLOG2       = 6U,
+    /* ITU BT.1886 EOTF */
+    KHR_DF_TRANSFER_BT1886      = 7U,
+    /* ITU BT.2100 HLG OETF */
+    KHR_DF_TRANSFER_HLG_OETF    = 8U,
+    /* ITU BT.2100 HLG EOTF */
+    KHR_DF_TRANSFER_HLG_EOTF    = 9U,
+    /* ITU BT.2100 PQ EOTF */
+    KHR_DF_TRANSFER_PQ_EOTF     = 10U,
+    /* ITU BT.2100 PQ OETF */
+    KHR_DF_TRANSFER_PQ_OETF     = 11U,
+    /* DCI P3 transfer function */
+    KHR_DF_TRANSFER_DCIP3       = 12U,
+    /* Legacy PAL OETF */
+    KHR_DF_TRANSFER_PAL_OETF    = 13U,
+    /* Legacy PAL 625-line EOTF */
+    KHR_DF_TRANSFER_PAL625_EOTF = 14U,
+    /* Legacy ST240 transfer function */
+    KHR_DF_TRANSFER_ST240       = 15U,
+    /* ACEScc transfer function */
+    KHR_DF_TRANSFER_ACESCC      = 16U,
+    /* ACEScct transfer function */
+    KHR_DF_TRANSFER_ACESCCT     = 17U,
+    /* Adobe RGB (1998) transfer function */
+    KHR_DF_TRANSFER_ADOBERGB    = 18U,
+    KHR_DF_TRANSFER_MAX         = 0xFFU
+} khr_df_transfer_e;
+
+typedef enum _khr_df_flags_e {
+    KHR_DF_FLAG_ALPHA_STRAIGHT      = 0U,
+    KHR_DF_FLAG_ALPHA_PREMULTIPLIED = 1U
+} khr_df_flags_e;
+
+typedef enum _khr_df_sample_datatype_qualifiers_e {
+    KHR_DF_SAMPLE_DATATYPE_LINEAR = 1U << 4U,
+    KHR_DF_SAMPLE_DATATYPE_EXPONENT = 1U << 5U,
+    KHR_DF_SAMPLE_DATATYPE_SIGNED = 1U << 6U,
+    KHR_DF_SAMPLE_DATATYPE_FLOAT = 1U << 7U
+} khr_df_sample_datatype_qualifiers_e;
+
+#endif

+ 125 - 0
3rdparty/meshoptimizer/tools/ktx2_format.h

@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2010-2018 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+ * Author: Mark Callow from original code by Georg Kolling
+ */
+
+/*
+ * Converted from ktxint.h + basis_sgd.h by extracting meaningful structures for gltfpack
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#define KTX2_IDENTIFIER_REF  { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }
+#define KTX2_HEADER_SIZE     (80)
+
+typedef enum ktxSupercmpScheme {
+    KTX_SUPERCOMPRESSION_NONE = 0,  /*!< No supercompression. */
+    KTX_SUPERCOMPRESSION_BASIS = 1, /*!< Basis Universal supercompression. */
+    KTX_SUPERCOMPRESSION_LZMA = 2,  /*!< LZMA supercompression. */
+    KTX_SUPERCOMPRESSION_ZLIB = 3,  /*!< Zlib supercompression. */
+    KTX_SUPERCOMPRESSION_ZSTD = 4,  /*!< ZStd supercompression. */
+    KTX_SUPERCOMPRESSION_BEGIN_RANGE = KTX_SUPERCOMPRESSION_NONE,
+    KTX_SUPERCOMPRESSION_END_RANGE = KTX_SUPERCOMPRESSION_ZSTD,
+    KTX_SUPERCOMPRESSION_BEGIN_VENDOR_RANGE = 0x10000,
+    KTX_SUPERCOMPRESSION_END_VENDOR_RANGE = 0x1ffff,
+    KTX_SUPERCOMPRESSION_BEGIN_RESERVED = 0x20000,
+} ktxSupercmpScheme;
+
+/**
+ * @internal
+ * @~English
+ * @brief 32-bit KTX 2 index entry.
+ */
+typedef struct ktxIndexEntry32 {
+    uint32_t byteOffset; /*!< Offset of item from start of file. */
+    uint32_t byteLength; /*!< Number of bytes of data in the item. */
+} ktxIndexEntry32;
+/**
+ * @internal
+ * @~English
+ * @brief 64-bit KTX 2 index entry.
+ */
+typedef struct ktxIndexEntry64 {
+    uint64_t byteOffset; /*!< Offset of item from start of file. */
+    uint64_t byteLength; /*!< Number of bytes of data in the item. */
+} ktxIndexEntry64;
+
+/**
+ * @internal
+ * @~English
+ * @brief KTX 2 file header.
+ *
+ * See the KTX 2 specification for descriptions.
+ */
+typedef struct KTX_header2 {
+    uint8_t  identifier[12];
+    uint32_t vkFormat;
+    uint32_t typeSize;
+    uint32_t pixelWidth;
+    uint32_t pixelHeight;
+    uint32_t pixelDepth;
+    uint32_t layerCount;
+    uint32_t faceCount;
+    uint32_t levelCount;
+    uint32_t supercompressionScheme;
+    ktxIndexEntry32 dataFormatDescriptor;
+    ktxIndexEntry32 keyValueData;
+    ktxIndexEntry64 supercompressionGlobalData;
+} KTX_header2;
+
+/* This will cause compilation to fail if the struct size doesn't match */
+typedef int KTX_header2_SIZE_ASSERT [sizeof(KTX_header2) == KTX2_HEADER_SIZE];
+
+/**
+ * @internal
+ * @~English
+ * @brief KTX 2 level index entry.
+ */
+typedef struct ktxLevelIndexEntry {
+    uint64_t byteOffset; /*!< Offset of level from start of file. */
+    uint64_t byteLength;
+                /*!< Number of bytes of compressed image data in the level. */
+    uint64_t uncompressedByteLength;
+                /*!< Number of bytes of uncompressed image data in the level. */
+} ktxLevelIndexEntry;
+
+typedef struct ktxBasisGlobalHeader {
+    uint32_t globalFlags;
+    uint16_t endpointCount;
+    uint16_t selectorCount;
+    uint32_t endpointsByteLength;
+    uint32_t selectorsByteLength;
+    uint32_t tablesByteLength;
+    uint32_t extendedByteLength;
+} ktxBasisGlobalHeader;
+
+// This header is followed by imageCount "slice" descriptions.
+
+// 1, or 2 slices per image (i.e. layer, face & slice).
+// These offsets are relative to start of a mip level as given by the
+// main levelIndex.
+typedef struct ktxBasisSliceDesc {
+    uint32_t sliceFlags;
+    uint32_t sliceByteOffset;
+    uint32_t sliceByteLength;
+    uint32_t alphaSliceByteOffset;
+    uint32_t alphaSliceByteLength;
+} ktxBasisSliceDesc;