Step by Step Docker python pipeline
The rapid adoption of 3D technologies has driven the demand for efficient file formats that cater to various platforms and devices. glTF (GL Transmission Format) has emerged as the de facto standard for web and real-time 3D applications due to its compactness and ease of transmission. However, for augmented reality (AR) applications—especially on Apple devices—the USDZ format is indispensable. Introduced with iOS 12 and ARKit 2.0, USDZ enables seamless integration of 3D content into the Apple ecosystem.
Despite its importance, there is no standardized method for converting glTF files into USDZ. This gap poses significant challenges for developers who need to work across different ecosystems. In this guide, I detail my experience creating a Python-based conversion pipeline using a Docker container. This comprehensive article will cover:
By the end of this guide, you will have a solid understanding of the conversion process and be equipped to implement, modify, or extend the pipeline for your own projects.
glTF was introduced by the Khronos Group in 2015 as a file format designed to efficiently transmit 3D models and scenes. It was later updated to glTF 2.0 in 2017, which improved support for modern rendering techniques such as Physically-Based Rendering (PBR). Often described as the “JPEG of 3D files,” glTF is widely adopted due to its:
There are two common variants:
.gltf
Files: A JSON-based format that references external resources such as textures and binary data..glb
Files: A binary version that bundles all necessary components into a single file for ease of use.USDZ is a specialized version of the Universal Scene Description (USD) format, originally developed by Pixar and later adopted by Apple for AR applications. It is a compressed or zipped archive that encapsulates all necessary data—including geometry, textures, animations, and materials—into a single, portable file. Key features include:
Converting from glTF to USDZ is not a straightforward process due to several technical hurdles:
To address these challenges, I explored multiple approaches before settling on a Docker-based solution that encapsulates all dependencies and provides a consistent environment across operating systems.
My initial strategy involved using Google’s usd_from_gltf utility to generate an intermediate USD file. I then attempted to package this into a USDZ file using Pixar’s USD toolkit and its usdzip tool. However, this approach proved problematic because:
usd_from_gltf
tool itself has the capability to produce USDZ files directly, eliminating the need for a separate packaging step.After testing several libraries and online conversion tools, I discovered the Docker image plattar/python-xrutils
. This image bundles Google’s usd_from_gltf
along with other essential libraries, simplifying the conversion process by providing a ready-to-use environment. Docker allows the entire pipeline to run in a containerized setup, which ensures:
The solution comprises several modules, each responsible for a specific aspect of the conversion process. The key components include:
python -m venv .
. ./bin/activate
pip install docker
main.py
argparse
module. Three optional arguments are provided:
# main.py
import argparse
def main():
parser = argparse.ArgumentParser(description='Convert glTF files to USDZ.')
parser.add_argument('--input', type=str, default='input/', help='Input file or directory')
parser.add_argument('--output', type=str, default='output/', help='Output directory')
parser.add_argument('--read_dir_recursive', action='store_true', help='Read directories recursively')
args = parser.parse_args()
# Call the converter function here with the arguments
convert_gltf_to_usdz(args.input, args.output, args.read_dir_recursive)
if __name__ == '__main__':
main()
check_io
ensures that the input exists and creates the output directory if necessary.
# check_io.py
import os
def check_io(arguments):
input_path = arguments.input
output_path = arguments.output
if not os.path.exists(input_path):
raise ValueError(f"Input path {input_path} does not exist.")
if not os.path.exists(output_path):
os.makedirs(output_path)
return True
DockerUtil
class connects to the local Docker daemon, ensures the required image is available, and sets up the file exchange between the host and the container. It consists of several methods - Below is the full code for the Docker utility:
# docker_util.py
import docker
import os
import glob
from path_operations import get_absolute_path, new_file_path, change_extension, check_io
class DockerUtil:
def __init__(self, input, output, recursive):
self.image_name = 'plattar/python-xrutils'
self.docker_release = 'latest'
self.docker_input_path = '/input'
self.docker_output_path = '/output'
self.instance = docker.from_env()
self.input = input
self.output = output
self.recursive = recursive
def get_image(self):
images = self.instance.images.list(self.image_name)
if len(images) > 0:
return images[0]
return self.instance.images.pull(self.image_name, tag=self.docker_release)
def bind_volume(self):
input_path = get_absolute_path(self.input)
output_path = get_absolute_path(self.output)
volumes = {
input_path: {'bind': self.docker_input_path, 'mode': 'rw'},
output_path: {'bind': self.docker_output_path, 'mode': 'rw'}
}
self.container = self.instance.containers.run(
self.image_name,
volumes=volumes,
tty=True,
detach=True
)
def call_converter(self, input_file):
def create_args(input_file):
input_docker_path = new_file_path(self.docker_input_path, input_file)
output_docker_path = change_extension(new_file_path(self.docker_output_path, input_file), 'usdz')
return input_docker_path, output_docker_path
input_docker_path, output_docker_path = create_args(input_file)
command = f"usd_from_gltf {input_docker_path} {output_docker_path}"
self.container.exec_run(command)
def stop(self):
self.container.kill()
self.container.remove()
def start(self):
self.get_image()
self.bind_volume()
try:
input_files = list_files(self.input, self.recursive)
for input_file in input_files:
self.call_converter(input_file)
except Exception as e:
print(f"An error occurred: {e}")
finally:
self.stop()
path_operations.py
provides consistent file path management, ensuring that host paths are correctly mapped to container paths. These functions also help with operations such as changing file extensions, creating new file paths, and validating input/output directories.
# path_operations.py
import os
def get_absolute_path(path):
return os.path.abspath(path)
def new_file_path(new_dir, filename):
return os.path.join(new_dir, os.path.basename(filename))
def change_extension(filename, new_ext):
base = os.path.splitext(filename)[0]
return f"{base}.{new_ext}"
def check_io(arguments):
input_path = arguments.input
output_path = arguments.output
if not os.path.exists(input_path):
raise ValueError(f"Input path {input_path} does not exist.")
if not os.path.exists(output_path):
os.makedirs(output_path)
return True
main.py
, which imports the Docker utility class, validates the paths using check_io
, and then starts the conversion process.
# main.py
import argparse
from docker_util import DockerUtil
from check_io import check_io
def main():
parser = argparse.ArgumentParser(description='Convert glTF files to USDZ.')
parser.add_argument('--input', type=str, default='input/', help='Input file or directory')
parser.add_argument('--output', type=str, default='output/', help='Output directory')
parser.add_argument('--read_dir_recursive', action='store_true', help='Read directories recursively')
args = parser.parse_args()
check_io(args)
converter = DockerUtil(args.input, args.output, args.read_dir_recursive)
converter.start()
if __name__ == '__main__':
main()
This guide has provided an in-depth look at creating a robust, Docker-powered pipeline for converting glTF files to USDZ using Python. By leveraging Docker, the solution overcomes platform-specific challenges and simplifies dependency management, while the modular design allows for easy integration and future enhancements.
Through careful design and implementation, this pipeline offers a scalable solution that can be extended for batch processing, integrated into web services, or optimized further for specific workflows. I hope this guide serves as a valuable resource for anyone looking to bridge the gap between glTF and USDZ in their 3D and AR projects.
Feel free to use, modify, and extend this solution to suit your needs. I welcome any feedback or suggestions for further improvements.