Portable Gazebo SDF bundler. One file, zero setup.
Pack a Gazebo SDF world or model with all its assets into a single .sdfz file.
# Pack
python3 gz_bundle.py world.sdf -o forest3d.sdfz
python3 gz_bundle.py models/robot/model.sdf -o robot.sdfz
# Run
python3 forest3d.sdfzworld_trimmed.mp4
model_trimmed.mp4
- Python 3.6+ (stdlib only, no pip packages)
- Gazebo installed
fuse-zip(optional, enables zero-copy mount instead of /tmp extraction)
sudo apt install fuse-zip # optional# Pack a world
python3 gz_bundle.py worlds/my_world.sdf -o my_world.sdfz
# Pack a model
python3 gz_bundle.py models/tree/model.sdf -o tree.sdfz
# Run (self-executing, no gz_bundle.py needed)
python3 my_world.sdfz
# Verbose (shows every asset packed)
python3 gz_bundle.py worlds/my_world.sdf -o my_world.sdfz -v
# Pass extra args to gz sim
python3 my_world.sdfz -- -vmy_world.sdfz (ZIP_STORED, uncompressed)
├── world.sdf ← rewritten SDF (URIs → relative)
├── __main__.py ← self-run bootstrap
├── gz_bundle.py ← bundler embedded for repacking
├── manifest.json
├── models/
│ ├── ground/
│ │ ├── model.sdf
│ │ └── mesh/terrain.obj
│ └── Tree/
│ ├── model.sdf
│ └── mesh/Tree.glb
├── materials/textures/
│ ├── bark_albedo.png
│ └── bark_normal.png
└── plugins/
└── libgz_terramechanics.so
- Meshes (
.dae,.obj,.stl,.glb,.gltf) - PBR textures (albedo, normal, roughness, metalness, emissive)
- Nested models (full directory trees, recursive)
- Custom plugins (
.so, platform-specific) - Model bundles generate a preview world automatically when run
Pack
world.sdf ──→ gz_bundle ──→ forest3d.sdfz
│
├── Resolve model://, file://, relative URIs
├── Collect meshes, textures, PBR maps, plugins
├── Rewrite URIs to relative paths
└── Pack into ZIP (uncompressed for fuse-zip)
Run
forest3d.sdfz ──→ python3 ──→ gz sim
│
├── Mount (fuse-zip) or extract to /tmp
├── Set GZ_SIM_RESOURCE_PATH + plugin paths
└── Launch Gazebo, cleanup on exit
No export GZ_SIM_RESOURCE_PATH needed. The bundler finds models by walking up to the project root (.git, package.xml, CMakeLists.txt) and scanning the tree. Works with flat projects, ROS2 packages, and multi-package workspaces.
- Plugins are platform-specific, which means
.sowon't work elsewhere (to be arranged) - Fuel URIs (
https://fuel.gazebosim.org/...) are left as-is and fetched by gz-sim at runtime - Built-in plugins (
gz-sim-physics-system, etc.) ship with Gazebo
-
Rename.gzworldto.sdfz -
ZIP_STORED uncompressed; fuse-zip mount - World + model bundling
-
URI rewriting in root SDF - Parse
model.configfor non-standard SDF filenames -
package://URI support (ROS2) - Heightmap support (
<heightmap><uri>) - Actor animation support (
<actor><animation><filename>) - Refactor
_crawl_sdf: single-pass + decouple parse/resolve/collect - Native
gz sim world.sdfz(upstream PR)
Issues and PRs welcome.
MIT