Skip to content

Instantly share code, notes, and snippets.

@uyjulian
Created October 25, 2025 04:41
Show Gist options
  • Select an option

  • Save uyjulian/e6e347ec5a1c0446811fbdd9fbbede46 to your computer and use it in GitHub Desktop.

Select an option

Save uyjulian/e6e347ec5a1c0446811fbdd9fbbede46 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
# Program to copy images referenced by glTF files.
# Run the program with --help argument for arguments.
import sys
import shutil
import os
import json
def gltf_copy_image(in_gltf_path, in_img_path, out_img_path):
dic = None
embedded_buffer = None
with open(in_gltf_path, "rb") as f:
h = f.read(12)
if h[0:1] == b"\x7B":
f.seek(0)
dic = json.loads(f.read().decode("utf-8"))
elif h[0:4] == b"glTF" and h[4:8] == b"\x02\x00\x00\x00":
total_size = int.from_bytes(h[8:12], byteorder="little")
while True:
c = f.read(8)
if len(c) != 8:
break
c_size = int.from_bytes(c[4:8], byteorder=little)
if c == b"JSON":
dic = json.loads(f.read(c_size).decode("utf-8"))
elif c == b"BIN\x00":
embedded_buffer = f.read(c_size)
if dic == None:
raise Exception("invalid glTF file")
if "images" not in dic or not dic["images"]:
print("Warning: no images are attached to the glTF file")
return
image_fn_list = []
images = dic["images"]
for image in images:
if "uri" not in image:
print("Warning: detected image with no URI")
continue
uri = image["uri"]
if uri[0:5] == "data:":
print("Warning: detected image with data URI")
continue
image_fn_list.append(uri.rsplit("/", maxsplit=2)[-1])
image_fn_list = sorted(list(set(image_fn_list)))
if image_fn_list:
if not os.path.isdir(out_img_path):
os.makedirs(name=out_img_path)
for fn in image_fn_list:
in_img_path_full = in_img_path + "/" + fn
out_img_path_full = out_img_path + "/" + fn
if not os.path.isfile(in_img_path_full):
print("Warning: file " + in_img_path_full + " does not exist")
continue
if os.path.isfile(out_img_path_full):
print("Warning: file " + out_img_path_full + " does exist")
continue
shutil.copy2(in_img_path_full, out_img_path_full, follow_symlinks=True)
else:
print("Warning: no images to copy")
if __name__ == "__main__":
import argparse
import textwrap
parser = argparse.ArgumentParser(
description='Copies images referenced in glTF 2.0 files.',
usage='Use "%(prog)s --help" for more information.',
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument("input_file",
type=str,
help="The input glTF 2.0 file.")
parser.add_argument("--input-texture-path",
type=str,
default="",
help=textwrap.dedent('''\
The path to the input directory containing texture files.
If the path name is empty, it will default to the directory containing the input pathname.
''')
)
parser.add_argument("--output-texture-path",
type=str,
default="",
help=textwrap.dedent('''\
The path to the output directory containing texture files.
If it does not already exist, it will be created.
If the path name is empty, it will default to the input pathname with no extension with "__textures" appended to it.
''')
)
args = parser.parse_args()
input_file = args.input_file
if not os.path.isfile(input_file):
raise Exception("invalid input file")
input_file_noext = input_file.rsplit(".", maxsplit=2)[0]
input_texture_path = args.input_texture_path
if input_texture_path == "":
input_texture_path = os.path.dirname(input_file)
if input_texture_path == "":
input_texture_path = "."
output_texture_path = args.output_texture_path
if output_texture_path == "":
output_texture_path = input_file_noext + "__textures"
gltf_copy_image(input_file, input_texture_path, output_texture_path)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment