Home / Docs / Python API & batch

Python API & batch

The packing engine is pure Python — no bpy, no scene, no UI — so you can pack from scripts and process entire .blend libraries headless.

The core call

Packer.pack_islands() takes a list of UVIsland and a PackConfig and returns a PackResult:

from src.core.packer import Packer
from src.core.types import AABB, PackConfig, UVIsland

def make_island(x, y, w, h, idx):
    uvs = [(x, y), (x + w, y), (x + w, y + h), (x, y + h)]
    return UVIsland(
        polygons=[idx], uvs=uvs,
        bbox=AABB(x, y, x + w, y + h),
        area=w * h, aspect_ratio=w / h,
        source_object=f"object_{idx}",
    )

islands = [make_island(0.0, 0.0, 0.3, 0.2, i) for i in range(6)]

config = PackConfig(
    atlas_size=(1024, 1024),
    margin=0.005,
    algorithm="maxrects",
    allow_rotation=True,
    selected_only=False,
    normalize=True,
)

result = Packer().pack_islands(islands, config)
print(f"Efficiency: {result.efficiency:.1%}, {result.islands_packed} islands")

Reading the result

Each IslandPlacement describes the final transform of one island:

p = result.placements[0]
p.position    # lower-left corner of the placed bbox, UV space
p.rotation    # degrees, counter-clockwise
p.scale       # global scale applied by the packer
p.udim_tile   # 0 = tile 1001, 1 = 1002, ...
p.placed_bbox # final axis-aligned bounding box

The transform mapping an original UV (u, v) to the atlas: rotate by rotation around the origin, scale by scale * texel_density_scale, then translate so the rotated bounding box's lower-left corner lands on position (plus the UDIM tile offset) — the same transform applied to the mesh inside Blender.

Advanced config fields

Stacking, locking, grouping and destination are plain PackConfig fields:

config = PackConfig(
    atlas_size=(2048, 2048), margin=0.002, algorithm="maxrects",
    allow_rotation=True, selected_only=False, normalize=True,
    stack_similar=True,        # identical islands share one slot
    lock_overlapping=True,     # intentional stacks move as one group
    group_by="material",       # one UDIM tile per material
    pack_to_tile=0,            # target UDIM tile (0 = 1001)
    pack_to_region=None,       # or AABB(0.5, 0.5, 1.0, 1.0) for a sub-region
)

Progress callbacks

def on_progress(step, total, message):
    print(f"{step}/{total} — {message}")

Packer().pack_islands(islands, config, progress_callback=on_progress)

Headless batch processing

Use scripts/batch_pack.py to pack whole libraries of .blend files from the command line:

blender --background --factory-startup \
        --python scripts/batch_pack.py -- \
        --input-dir ./assets --output-dir ./packed

The worker uses the repository via sys.path and does not require the add-on to be installed.

ℹ️

The full API reference (every field of PackConfig, UVIsland, IslandPlacement and PackResult) ships with the source in docs/api/. The code blocks above are executed as part of the test suite, so they stay correct release to release.