Merge pull request #692 from zeux/gltf-bas

gltfpack: Implement support for KHR_texture_basisu inputs
This commit is contained in:
Arseny Kapoulkine
2024-05-23 00:13:43 -05:00
committed by GitHub
7 changed files with 102 additions and 22 deletions
+1
View File
@@ -81,6 +81,7 @@ gltfpack supports most Khronos extensions and some multi-vendor extensions in th
- KHR_materials_variants
- KHR_materials_volume
- KHR_mesh_quantization
- KHR_texture_basisu
- KHR_texture_transform
- EXT_meshopt_compression
+3 -1
View File
@@ -98,11 +98,13 @@ static const char* prepareEncode(basisu::basis_compressor_params& params, const
{
std::string img_data;
std::string mime_type;
std::string result;
if (!readImage(image, input_path, img_data, mime_type))
return "error reading source file";
if (mime_type != "image/png" && mime_type != "image/jpeg")
return NULL;
int width = 0, height = 0;
if (!getDimensions(img_data, mime_type.c_str(), width, height))
return "error parsing image header";
+3 -1
View File
@@ -452,6 +452,7 @@ static void process(cgltf_data* data, const char* input_path, const char* output
bool ext_unlit = false;
bool ext_instancing = false;
bool ext_texture_transform = false;
bool ext_texture_basisu = false;
size_t accr_offset = 0;
size_t node_offset = 0;
@@ -509,6 +510,7 @@ static void process(cgltf_data* data, const char* input_path, const char* output
assert(textures[i].remap == int(texture_offset));
texture_offset++;
ext_texture_basisu = ext_texture_basisu || texture.has_basisu;
}
for (size_t i = 0; i < data->materials_count; ++i)
@@ -834,7 +836,7 @@ static void process(cgltf_data* data, const char* input_path, const char* output
{"KHR_materials_unlit", ext_unlit, false},
{"KHR_materials_variants", data->variants_count > 0, false},
{"KHR_lights_punctual", data->lights_count > 0, false},
{"KHR_texture_basisu", !json_textures.empty() && settings.texture_ktx2, true},
{"KHR_texture_basisu", (!json_textures.empty() && settings.texture_ktx2) || ext_texture_basisu, true},
{"EXT_mesh_gpu_instancing", ext_instancing, true},
};
+55 -8
View File
@@ -107,12 +107,12 @@ bool readImage(const cgltf_image& image, const char* input_path, std::string& da
}
}
static int readInt16(const std::string& data, size_t offset)
static int readInt16BE(const std::string& data, size_t offset)
{
return (unsigned char)data[offset] * 256 + (unsigned char)data[offset + 1];
}
static int readInt32(const std::string& data, size_t offset)
static int readInt32BE(const std::string& data, size_t offset)
{
return (unsigned((unsigned char)data[offset]) << 24) |
(unsigned((unsigned char)data[offset + 1]) << 16) |
@@ -120,6 +120,14 @@ static int readInt32(const std::string& data, size_t offset)
unsigned((unsigned char)data[offset + 3]);
}
static int readInt32LE(const std::string& data, size_t offset)
{
return unsigned((unsigned char)data[offset]) |
(unsigned((unsigned char)data[offset + 1]) << 8) |
(unsigned((unsigned char)data[offset + 2]) << 16) |
(unsigned((unsigned char)data[offset + 3]) << 24);
}
static bool getDimensionsPng(const std::string& data, int& width, int& height)
{
if (data.size() < 8 + 8 + 13 + 4)
@@ -132,8 +140,8 @@ static bool getDimensionsPng(const std::string& data, int& width, int& height)
if (data.compare(12, 4, "IHDR") != 0)
return false;
width = readInt32(data, 16);
height = readInt32(data, 20);
width = readInt32BE(data, 16);
height = readInt32BE(data, 20);
return true;
}
@@ -169,13 +177,13 @@ static bool getDimensionsJpeg(const std::string& data, int& width, int& height)
if (offset + 10 > data.size())
return false;
width = readInt16(data, offset + 7);
height = readInt16(data, offset + 5);
width = readInt16BE(data, offset + 7);
height = readInt16BE(data, offset + 5);
return true;
}
offset += 2 + readInt16(data, offset + 2);
offset += 2 + readInt16BE(data, offset + 2);
}
return false;
@@ -202,7 +210,7 @@ static bool hasTransparencyPng(const std::string& data)
while (offset + 12 <= data.size())
{
int length = readInt32(data, offset);
int length = readInt32BE(data, offset);
if (length < 0)
return false;
@@ -216,10 +224,49 @@ static bool hasTransparencyPng(const std::string& data)
return false;
}
static bool hasTransparencyKtx2(const std::string& data)
{
if (data.size() < 12 + 17 * 4)
return false;
const char* signature = "\xabKTX 20\xbb\r\n\x1a\n";
if (data.compare(0, 12, signature) != 0)
return false;
int dfdOffset = readInt32LE(data, 48);
int dfdLength = readInt32LE(data, 52);
if (dfdLength < 4 + 24 + 16 || unsigned(dfdOffset) > data.size() || unsigned(dfdLength) > data.size() - dfdOffset)
return false;
const int KDF_DF_MODEL_ETC1S = 163;
const int KDF_DF_MODEL_UASTC = 166;
const int KHR_DF_CHANNEL_UASTC_RGBA = 3;
const int KHR_DF_CHANNEL_ETC1S_RGB = 0;
const int KHR_DF_CHANNEL_ETC1S_AAA = 15;
int colorModel = readInt32LE(data, dfdOffset + 12) & 0xff;
int channelType1 = (readInt32LE(data, dfdOffset + 28) >> 24) & 0xf;
int channelType2 = dfdLength >= 4 + 24 + 16 * 2 ? (readInt32LE(data, dfdOffset + 44) >> 24) & 0xf : channelType1;
if (colorModel == KDF_DF_MODEL_ETC1S)
{
return channelType1 == KHR_DF_CHANNEL_ETC1S_RGB && channelType2 == KHR_DF_CHANNEL_ETC1S_AAA;
}
else if (colorModel == KDF_DF_MODEL_UASTC)
{
return channelType1 == KHR_DF_CHANNEL_UASTC_RGBA;
}
return false;
}
bool hasAlpha(const std::string& data, const char* mime_type)
{
if (strcmp(mime_type, "image/png") == 0)
return hasTransparencyPng(data);
else if (strcmp(mime_type, "image/ktx2") == 0)
return hasTransparencyKtx2(data);
else
return false;
}
+22 -3
View File
@@ -11,6 +11,12 @@ static bool areTexturesEqual(const cgltf_texture& lhs, const cgltf_texture& rhs)
if (lhs.sampler != rhs.sampler)
return false;
if (lhs.has_basisu != rhs.has_basisu)
return false;
if (lhs.basisu_image != rhs.basisu_image)
return false;
return true;
}
@@ -435,6 +441,17 @@ bool hasValidTransform(const cgltf_texture_view& view)
return false;
}
static const cgltf_image* getTextureImage(const cgltf_texture* texture)
{
if (texture && texture->image)
return texture->image;
if (texture && texture->has_basisu && texture->basisu_image)
return texture->basisu_image;
return NULL;
}
static void analyzeMaterialTexture(const cgltf_texture_view& view, TextureKind kind, MaterialInfo& mi, cgltf_data* data, std::vector<TextureInfo>& textures, std::vector<ImageInfo>& images)
{
mi.usesTextureTransform |= hasValidTransform(view);
@@ -442,9 +459,9 @@ static void analyzeMaterialTexture(const cgltf_texture_view& view, TextureKind k
if (view.texture)
textures[view.texture - data->textures].keep = true;
if (view.texture && view.texture->image)
if (const cgltf_image* image = getTextureImage(view.texture))
{
ImageInfo& info = images[view.texture->image - data->images];
ImageInfo& info = images[image - data->images];
mi.textureSetMask |= 1u << view.texcoord;
mi.needsTangents |= (kind == TextureKind_Normal);
@@ -549,7 +566,9 @@ static bool shouldKeepAlpha(const cgltf_texture_view& color, float alpha, cgltf_
if (alpha != 1.f)
return true;
return color.texture && color.texture->image && getChannels(*color.texture->image, images[color.texture->image - data->images], input_path) == 4;
const cgltf_image* image = getTextureImage(color.texture);
return image && getChannels(*image, images[image - data->images], input_path) == 4;
}
void optimizeMaterials(cgltf_data* data, const char* input_path, std::vector<ImageInfo>& images)
-2
View File
@@ -530,8 +530,6 @@ static cgltf_data* parseGltf(cgltf_data* data, cgltf_result result, std::vector<
*error = getError(result, data);
else if (requiresExtension(data, "KHR_draco_mesh_compression"))
*error = "file requires Draco mesh compression support";
else if (requiresExtension(data, "KHR_texture_basisu"))
*error = "file requires BasisU texture support";
else if (requiresExtension(data, "EXT_mesh_gpu_instancing"))
*error = "file requires mesh instancing support";
else if (needsDummyBuffers(data))
+18 -7
View File
@@ -952,27 +952,38 @@ void writeImage(std::string& json, std::vector<BufferView>& views, const cgltf_i
void writeTexture(std::string& json, const cgltf_texture& texture, const ImageInfo* info, cgltf_data* data, const Settings& settings)
{
if (texture.sampler)
{
append(json, "\"sampler\":");
append(json, size_t(texture.sampler - data->samplers));
}
if (texture.image)
{
if (texture.sampler)
{
append(json, "\"sampler\":");
append(json, size_t(texture.sampler - data->samplers));
append(json, ",");
}
if (info && settings.texture_mode[info->kind] != TextureMode_Raw)
{
comma(json);
append(json, "\"extensions\":{\"KHR_texture_basisu\":{\"source\":");
append(json, size_t(texture.image - data->images));
append(json, "}}");
return; // skip input basisu image if present, as we replace it with the one we encoded
}
else
{
comma(json);
append(json, "\"source\":");
append(json, size_t(texture.image - data->images));
}
}
if (texture.has_basisu && texture.basisu_image)
{
comma(json);
append(json, "\"extensions\":{\"KHR_texture_basisu\":{\"source\":");
append(json, size_t(texture.basisu_image - data->images));
append(json, "}}");
}
}
void writeMeshAttributes(std::string& json, std::vector<BufferView>& views, std::string& json_accessors, size_t& accr_offset, const Mesh& mesh, int target, const QuantizationPosition& qp, const QuantizationTexture& qt, const Settings& settings)