Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gltfpack: Add support for EXT_texture_webp #709

Merged
merged 5 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions extern/cgltf.h
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,8 @@ typedef struct cgltf_texture
cgltf_sampler* sampler;
cgltf_bool has_basisu;
cgltf_image* basisu_image;
cgltf_bool has_webp;
cgltf_image* webp_image;
cgltf_extras extras;
cgltf_size extensions_count;
cgltf_extension* extensions;
Expand Down Expand Up @@ -4551,6 +4553,34 @@ static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tok
}
}
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "EXT_texture_webp") == 0)
{
out_texture->has_webp = 1;
++i;
CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int num_properties = tokens[i].size;
++i;

for (int t = 0; t < num_properties; ++t)
{
CGLTF_CHECK_KEY(tokens[i]);

if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0)
{
++i;
out_texture->webp_image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk));
++i;
}
else
{
i = cgltf_skip_json(tokens, i + 1);
}
if (i < 0)
{
return i;
}
}
}
else
{
i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture->extensions[out_texture->extensions_count++]));
Expand Down Expand Up @@ -6561,6 +6591,7 @@ static int cgltf_fixup_pointers(cgltf_data* data)
{
CGLTF_PTRFIXUP(data->textures[i].image, data->images, data->images_count);
CGLTF_PTRFIXUP(data->textures[i].basisu_image, data->images, data->images_count);
CGLTF_PTRFIXUP(data->textures[i].webp_image, data->images, data->images_count);
CGLTF_PTRFIXUP(data->textures[i].sampler, data->samplers, data->samplers_count);
}

Expand Down
1 change: 1 addition & 0 deletions gltf/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ gltfpack supports most Khronos extensions and some multi-vendor extensions in th
- KHR_texture_transform
- EXT_mesh_gpu_instancing
- EXT_meshopt_compression
- EXT_texture_webp

Even if the source file does not use extensions, gltfpack may use some extensions in the output file either by default or when certain options are used:

Expand Down
3 changes: 3 additions & 0 deletions gltf/gltfpack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@ static void process(cgltf_data* data, const char* input_path, const char* output
bool ext_instancing = false;
bool ext_texture_transform = false;
bool ext_texture_basisu = false;
bool ext_texture_webp = false;

size_t accr_offset = 0;
size_t node_offset = 0;
Expand Down Expand Up @@ -512,6 +513,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;
ext_texture_webp = ext_texture_webp || texture.has_webp;
}

for (size_t i = 0; i < data->materials_count; ++i)
Expand Down Expand Up @@ -838,6 +840,7 @@ static void process(cgltf_data* data, const char* input_path, const char* output
{"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) || ext_texture_basisu, true},
{"EXT_texture_webp", ext_texture_webp, true},
{"EXT_mesh_gpu_instancing", ext_instancing, true},
};

Expand Down
38 changes: 38 additions & 0 deletions gltf/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ static const char* kMimeTypes[][2] = {
{"image/jpeg", ".jpeg"},
{"image/png", ".png"},
{"image/ktx2", ".ktx2"},
{"image/webp", ".webp"},
};

static const char* inferMimeType(const char* path)
Expand Down Expand Up @@ -128,6 +129,7 @@ static int readInt32LE(const std::string& data, size_t offset)
(unsigned((unsigned char)data[offset + 3]) << 24);
}

// https://en.wikipedia.org/wiki/PNG#File_format
static bool getDimensionsPng(const std::string& data, int& width, int& height)
{
if (data.size() < 8 + 8 + 13 + 4)
Expand All @@ -146,6 +148,7 @@ static bool getDimensionsPng(const std::string& data, int& width, int& height)
return true;
}

// https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format#File_format_structure
static bool getDimensionsJpeg(const std::string& data, int& width, int& height)
{
size_t offset = 0;
Expand Down Expand Up @@ -189,6 +192,7 @@ static bool getDimensionsJpeg(const std::string& data, int& width, int& height)
return false;
}

// https://en.wikipedia.org/wiki/PNG#File_format
static bool hasTransparencyPng(const std::string& data)
{
if (data.size() < 8 + 8 + 13 + 4)
Expand Down Expand Up @@ -224,6 +228,7 @@ static bool hasTransparencyPng(const std::string& data)
return false;
}

// https://github.khronos.org/KTX-Specification/ktxspec.v2.html
static bool hasTransparencyKtx2(const std::string& data)
{
if (data.size() < 12 + 17 * 4)
Expand Down Expand Up @@ -261,12 +266,45 @@ static bool hasTransparencyKtx2(const std::string& data)
return false;
}

// https://developers.google.com/speed/webp/docs/riff_container
static bool hasTransparencyWebP(const std::string& data)
{
if (data.size() < 12 + 4 + 12)
return false;

if (data.compare(0, 4, "RIFF") != 0)
return false;
if (data.compare(8, 4, "WEBP") != 0)
return false;

// WebP data may use VP8L, VP8X or VP8 format, but VP8 does not support transparency
if (data.compare(12, 4, "VP8L") == 0)
{
if (unsigned(data[20]) != 0x2f)
return false;

// width (14) | height (14) | alpha_is_used (1) | version_number(3)
unsigned int header = readInt32LE(data, 21);
return (header & (1 << 28)) != 0;
}
else if (data.compare(12, 4, "VP8X") == 0)
{
// zero (2) | icc (1) | alpha (1) | exif (1) | xmp (1) | animation (1) | zero (1)
unsigned char header = data[20];
return (header & (1 << 4)) != 0;
}

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 if (strcmp(mime_type, "image/webp") == 0)
return hasTransparencyWebP(data);
else
return false;
}
Expand Down
9 changes: 6 additions & 3 deletions gltf/material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ 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)
if (lhs.basisu_image != rhs.basisu_image)
return false;

if (lhs.basisu_image != rhs.basisu_image)
if (lhs.webp_image != rhs.webp_image)
return false;

return true;
Expand Down Expand Up @@ -446,9 +446,12 @@ static const cgltf_image* getTextureImage(const cgltf_texture* texture)
if (texture && texture->image)
return texture->image;

if (texture && texture->has_basisu && texture->basisu_image)
if (texture && texture->basisu_image)
return texture->basisu_image;

if (texture && texture->webp_image)
return texture->webp_image;

return NULL;
}

Expand Down
9 changes: 8 additions & 1 deletion gltf/write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -975,13 +975,20 @@ void writeTexture(std::string& json, const cgltf_texture& texture, const ImageIn
}
}

if (texture.has_basisu && texture.basisu_image)
if (texture.basisu_image)
{
comma(json);
append(json, "\"extensions\":{\"KHR_texture_basisu\":{\"source\":");
append(json, size_t(texture.basisu_image - data->images));
append(json, "}}");
}
else if (texture.webp_image)
{
comma(json);
append(json, "\"extensions\":{\"EXT_texture_webp\":{\"source\":");
append(json, size_t(texture.webp_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)
Expand Down