This tool is a part of Rév's UV suite and will likely merge into a single app.
Takes N input meshes, each with its own texture(s), and produces one square atlas texture plus N output meshes whose UV channel now points into the packed atlas.
The transfer is pixel-based, not bake-based, mitigating the problems associated with baking. Every output texel is sampled directly from one source texture via the barycentric coordinates of the triangle that covers it. What the artist painted is what lands in the atlas.
The usual atlas pipeline in Blender / Substance / Max rebakes via a renderer. Bakes can miss occluded geometry, introduce sampling noise, or need cage tuning. For the common case of "I already have good textures and I just want them repacked onto a shared atlas", a render pass is unnecessary and may cause artifacts. This tool does the repack by direct pixel copy.
Python 3.11.9 with a venv at multi_uv_repack/venv/.
pip install -r requirements.txt
fbxsdkpy (the optional FBX backend) is hosted on Inria's GitLab package registry, not PyPI. The --extra-index-url is already declared inside requirements.txt, so the line above installs everything in one shot. To install it manually:
pip install fbxsdkpy --extra-index-url https://gitlab.inria.fr/api/v4/projects/18692/packages/pypi/simple
Details
python -m multi_uv_repack.main \
--mesh path/to/mesh_a.obj --tex albedo=path/to/a_albedo.png \
--mesh path/to/mesh_b.fbx --tex albedo=path/to/b_albedo.png \
--tex normal=path/to/b_normal.png \
--out out/ \
--resolution 2048 \
--padding 16 \
--dilation 8 \
--filter bilinear \
--allow-rotation
Read More
Each --mesh starts a new asset record. --tex CHANNEL=PATH flags that follow attach to the most recent --mesh until the next one. Paths can be absolute and come from any folder - there is no project-root constraint. Channel names are free-form strings; albedo, normal, roughness, metallic, ao, emissive are conventional.
Optional flags:
--packer bitmap|xatlas- which packer to run.bitmap(default) uses the in-tree SimpleBitmapPacker port (numba-accelerated, matches brute-force utilization in ~1.5s at any resolution).xatlasdelegates to the xatlas C++ library.--brute-force- enable xatlasbruteForcepacking. xatlas-only; ignored by bitmap. Off by default because at 4k it raises pack time from ~2s to ~200s for a few percent utilization gain.--world-scale-priority- pre-scale each asset's UVs bysqrt(3D surface area / UV area)so physically larger meshes claim proportionally more atlas texels. Off by default (equal-priority packing).--allow-rotation- let the bitmap packer rotate shells 90 degrees CCW when it tightens the fit. Tangent-space normal maps are re-shaded automatically. Bitmap packer only.
python -m multi_uv_repack.ui_web
- Atlas resolution (512px to 32,768px). Output is forced square. The bitmap packer respects this exactly; xatlas treats it as a hint and may inflate the output 1.4-1.5x for better utilization.
- Padding (range 8-64 texels). Default scales with resolution: 64 at 8192, halving each step down with an 8-texel floor. Space reserved between islands by the packer.
- Dilation ring (range 4-32 texels, default 8). Post-process pixel bleed out of each island so bilinear filtering / mip generation does not pull in black edge.
- Filter (nearest / bilinear / bicubic, default bilinear). Resampling filter when the packer scales an island.
- glTF still goes through trimesh on read / write, which merges per-position and loses per-corner seams. Same limitation OBJ used to have. Swap in
pygltflibif / when a real asset exhibits the problem. - No live preview on field change. Pack on button press only (by spec).
- No GPU path. CPU numba throughput is sufficient for 4k / ~20 assets in seconds.
- Mixing authored unit systems across input meshes (one mesh in cm, another in m) will break
--world-scale-priority. Not fixable inside the tool.
The bitmap packer is a new integration of existing research into bin packing. Read the bitmap_uv_packing.pdf to learn more about it.
The CPU bound packer, can reach utilisation ratios upto ~94% and while offline, is still fast irrespective of output resolution.
- Packing by xatlas via xatlas-python, plus the in-tree bitmap packer ported from the author's Maya UV-packer project.