Skip to content

Generated props (TRELLIS)

The settlement furniture and loose items under public/props/ were generated from reference images with TRELLIS.2, then prepared in the prop workshop. The shipped packages do not need anything on this page: you only need it to generate new props or regenerate existing ones.

Everything here runs in an isolated Python environment under .gear-work/, which is a local workbench directory and not part of the repository. It uses the Windows community port pinned to revision d5d38f1e033a881e2c00e3137822ef8aa4e193de; the official project targets Linux.

Install uv, then run:

Terminal window
./scripts/setup-trellis.ps1

The script:

  • clones the port into .gear-work/trellis2-source at the pinned revision (and refuses an existing checkout at a different revision);
  • applies scripts/trellis2-sdpa.patch, which switches the port from FlashAttention (no supported Windows wheel) to PyTorch SDPA for sparse windowed and variable-length attention;
  • creates a Python 3.11 environment in .gear-work/trellis2-env with PyTorch 2.8 for CUDA 12.8;
  • installs the port’s cumesh, flex_gemm and o_voxel wheels and utils3d at a pinned commit, but not the port’s nvdiffrast or nvdiffrec_render wheels (see Licensing);
  • runs scripts/trellis/patch_o_voxel_import.py to remove the o_voxel initializer’s eager import of its unused upstream baker (it keeps a backup, refuses an unexpected initializer, and is safe to rerun);
  • finishes with scripts/trellis-assets.py --check.

It downloads code and Python packages only, never model weights (those come from Hugging Face the first time you generate), and it does not run the port’s own installer.

The image encoder is Meta’s gated DINOv3 ViT-L checkpoint. Request access with your own Hugging Face account, then log the isolated environment in once:

Terminal window
& .gear-work/trellis2-env/Scripts/hf.exe auth login

Later downloads reuse that credential. Never put a token in source files. Use the ViT-L checkpoint above even if the access email links a larger one: TRELLIS was trained against it.

Two checks are available:

Terminal window
# CUDA, extensions and gated model access, without loading weights:
& .gear-work/trellis2-env/Scripts/python.exe scripts/trellis-assets.py --check
# Extension and bake imports only; no CUDA, no model access. Fails if nvdiffrast is imported:
& .gear-work/trellis2-env/Scripts/python.exe scripts/trellis-assets.py --check-imports --offline

The input must be an RGBA PNG with real transparency around the object; the script rejects an opaque or fully transparent image. No background-removal model is used.

Terminal window
& .gear-work/trellis2-env/Scripts/python.exe scripts/trellis-assets.py `
--image path/to/my-prop.png `
--out .gear-work/trellis2-output/my-prop-01.glb `
--seed 1337 --triangles 5000 --texture 1024
FlagMeaning
--pipeline 512 (default) or 1024_cascadeThe cascade also loads the 1024 shape and texture models for more detail.
--steps NOverrides the step count of all three samplers (4 to 50).
--triangles NTarget of the exported, remeshed GLB (100 to 50,000).
--texture 512|1024|2048Size of the baked texture maps.
--keep-sourceAlso saves the native reconstruction as <id>.source.pt (see below).
--offlineUses the cached checkpoints only and fails if a file is missing.
--seed NSampler seed (default 1337).

The script always loads models in low-VRAM mode with SDPA attention. The output must be a new .glb: existing files are never overwritten. Geometry is checked for finite, non-empty data before export, which is not a substitute for looking at it. A <id>.recipe.json beside the GLB records the input hash, source revision, seed, pipeline, sampler parameters, triangle and texture settings, timings, peak PyTorch CUDA allocation (not total board VRAM), the bake used and the licensing toolchain block. Close the game while generating to leave VRAM free.

With --keep-source, the archive holds the native mesh and material voxels before remeshing (vertices, faces, attrs, coords, layout, voxel_size, origin, voxel_shape, plus input hash, seed, pipeline, model and revision). It lets textures be re-baked later without running inference again, and is large (hundreds of megabytes for a chair). It is a PyTorch pickle: only load archives you made.

A larger output texture does not raise the reconstruction resolution. For best results generate a high-budget raw mesh first (the settlement batch used 50,000 triangles and 2048 px textures), inspect it from every side, and build LODs in the workshop. That separates reconstruction defects from simplification damage.

For thin objects such as folded cloth, make a low-triangle LOD by exporting again from the native archive instead of decimating the review export:

Terminal window
& .gear-work/trellis2-env/Scripts/python.exe scripts/trellis/rebake-from-source.py `
--source .gear-work/trellis2-output/<batch>/<id>.source.pt `
--out .gear-work/trellis2-output/<batch>/<id>.t4800.glb --triangles 4800 --texture 1024

It runs the generator’s own export (remesh, decimation inside the remesher, texture bake) without inference, in under a minute. TRELLIS reconstructs thin cloth as a closed thin shell, and collapse decimation of the exported mesh folds the two walls through each other: a folded shirt comes out as crumpled paper at any budget, while the re-export keeps its collar and seams. It needs CUDA, so do not run it beside a batch job that is using most of the card: on Windows the two then share memory through the system and the job slows several times over.

Ask for one physically plausible object in a clear three-quarter view, with soft neutral lighting, a genuinely transparent background including the negative spaces, substantial structural members and restrained surface detail. Include the whole silhouette. Avoid painted perspective, thin floating details, several views in one image, floor shadows and dramatic baked lighting. Leave out liquids, flames, foliage, light and text: those belong to the engine’s runtime systems. The result is a reconstruction, not an exact copy: small details such as carvings will differ from the reference.

A generator without transparency (Midjourney, for example) shoots the object on a flat backdrop, and scripts/trellis/matte-reference.ts cuts it out:

Terminal window
npx tsx scripts/trellis/matte-reference.ts downloads/tunic.png `
art-input/trellis/wardrobe-drops-v2/tunic/reference.png

It grows the backdrop inward from the image border, taking in the soft contact shadow that generators add even when told not to (a shadow is the backdrop’s hue made darker), drops small specks, estimates each edge pixel’s coverage against its own neighbouring backdrop and frees its colour of it, then centres the object on a transparent 1024 px square. It refuses an object that touches the image border. Name a backdrop colour far from the object’s in the prompt: pale or sky blue behind browns and creams, warm yellow behind blues, deep blue behind gold. A grey or steel object on a pale backdrop can be taken for shadow. Look at every result before generating from it, because whatever stays opaque becomes geometry.

Two options take a hard shadow’s core, too dark to carry the backdrop’s hue: --shadow-min 0.12 accepts darker shadow of the same hue, and --dark 40 accepts any near-black pixel the backdrop reaches. --dark is only for a light object: on dark leather it eats the gaps between plates. --debug mask.png writes the decision itself, which is also how to find where a refused image touches its border.

The shipped props came from two manifests:

  • docs/settlement-props-generation.json (batch settlement-props-v1): 84 jobs. Six styles (medieval, Grey, Skell, Nacre, Vesper and tree-bough) each have a chair, bench, bed, table, counter, workbench, shelf, storage barrel, planter, lamp, vessel and hearth, and twelve extra medieval village and farm props complete the set.
  • docs/settlement-prop-variants-generation.json (batch settlement-props-v2): 96 jobs, 84 alternate forms (variantOf links each to its original) and 12 loose items such as books, bread, cutlery and plates.

Wearable items have their own manifest, docs/wardrobe-drops-v2-generation.json (batch wardrobe-drops-v2): the dropped form of each garment, matching how it is worn, a gold nugget, and the worn hood and pauldron. Its jobs also record imageSource (openai or midjourney), which the recipe’s toolchain block carries so the notices name the right source; a single --image run takes --image-source instead. Their drops are re-exported at 4800 triangles, prepared by scripts/blender/prepare-wardrobe-drop.py and promoted by scripts/promote-wardrobe-drops.ts, which moves each texture onto the colour the item is worn in (scripts/trellis/recolour-glb.ts). The worn pieces (hood, pauldron, helm, cuirass, belt, plate legs and sabatons) are worn on every body instead: scripts/build-character-worn-gear.mjs fits each into each body’s publication from assets/character/worn-gear.json. A generated model that holds two worn pieces is cut first: scripts/trellis/split-glb.ts cuts along a plane (the plate legs came with their sabatons, cut at the ankle), and scripts/trellis/mirror-half-glb.ts rebuilds a half TRELLIS never made. Legs drawn pressed together are reconstructed as their union, so one leg has no inner wall from the hip to the knee; the tool clips the leg exactly at its middle plane and mirrors the outer half into its place. Before that, scripts/trellis/fill-texture-holes.ts fills the black texels a bake leaves inside the UV charts of a thin closed shell, from the colour around them, and for a single colour fabric scripts/trellis/even-texture.ts removes the stains TRELLIS paints on the sides the reference never showed (the hood uses --radius 8 --strength 1 --max-gain 10 --fill-below 0.45 --chroma 0.8 at 2048 px), keeping only the fabric’s grain. Do not use it on leather or plate, whose tonal variation is part of the look. Worn pieces are re-exported from their native source at around ten thousand triangles with 2048 px textures: the fitter handles that density, and it holds folds and rivets the 4800 triangle cut lost. The finished source then goes to 1024 px JPEG with scripts/trellis/texture-size-glb.ts: the fitter copies a source’s texture into the part it writes for every body, and packing only compresses a part’s file (the 1024 px WebP pass covers a body’s own textures), so a 2048 px PNG shipped at about 4.5 MB per part per body.

Plate is fitted differently from cloth. A TRELLIS plate is a closed shell a few millimetres thick, and pushing it out of the body moves both walls onto the same surface, where the lining shows through the steel in patches; a recipe’s wall moves every vertex by the deepest push within that distance, so the shell keeps its thickness. The stride sweep and the lift that let cloth give way to a moving limb shred a rigid shell, so plate turns them off (sweep: false, lift: false) and follows its carrier bones. A cuirass leaves the arms out of its limit (exclude), or an idle arm resting against the torso pushes the plate’s side out past it, and names the garment drawn beneath it (beneath: a gambeson under the cuirass, padded hose under the plate legs), whose cut it is fitted over.

A TRELLIS shell’s outer wall and lining also pinch together along seams and share vertices there, and an exported or averaged normal at such a vertex points the wrong way: the model shows spots lit from behind, lit patches on a dark side and black ones on a lit side. The winding is consistent, so normals are rebuilt from the faces (scripts/lib/shell-normals.mjs): a pinched vertex gets one copy per side, and a normal averages only faces on its own side. The fitter does this for every worn piece, and scripts/trellis/fix-shell-normals.ts does it for a drop model.

Each job records its id, style, type, intended height, LOD0 target, seed, reference image path, output path and the prompt used for the reference. scripts/plan-settlement-props.mjs and scripts/plan-settlement-prop-variants.mjs wrote these once; the variants planner refuses to overwrite its manifest. Do not regenerate them over existing work, because job ids tie the outputs, packages and workshop together.

Run a batch with:

Terminal window
& .gear-work/trellis2-env/Scripts/python.exe -u scripts/trellis-assets.py `
--batch docs/settlement-props-generation.json `
--pipeline 1024_cascade --steps 20 --triangles 50000 --texture 2048 `
--keep-source --offline

The generation settings come from these flags, not from the manifest. One worker keeps the models loaded, generates one job at a time, and polls every 15 seconds for reference images that have not appeared yet. Only run one worker per GPU. Its state is in .gear-work/trellis2-output/<batch>/status.json: per-job status (waiting-image, running, complete, failed), counts and the worker’s PID.

  • A job whose GLB and recipe already exist is marked complete and skipped, so a stopped batch resumes with the same command.
  • A job left running by an interrupted worker goes back to waiting-image on restart.
  • waiting-image also covers queued jobs whose image already exists.
  • Failed jobs keep their error and are skipped. Fix the cause, then reset only those entries to waiting-image.
  • The worker exits when every job is complete or failed, or when nothing is left to do and an inputs-complete.json marker exists in the batch directory. Write that marker once all reference images are in place.
  • Write reference PNGs atomically (write elsewhere, then move), so the worker never reads a half-written file.

These Blender scripts work on any GLB, outside the workshop:

  • scripts/blender/preview-glb.py renders clay and texture contact sheets.
  • scripts/blender/render-prop-review.py renders four PBR material views under neutral lighting without changing materials or geometry, so reconstruction defects stay visible (--source, --out, --resolution, and --node for one LOD of a packed model).
  • scripts/blender/optimize-settlement-prop.py is a standalone review preparation: metre scale, a base-centred Y-up origin, three triangle-budgeted LODs, a convex hull of at most 128 vertices and an asset.json with provenance. It refuses an existing output directory. The workshop uses its own preparation instead.
Terminal window
blender -b -t 2 --python-exit-code 1 --python scripts/blender/render-prop-review.py -- `
--source .gear-work/props/review/my-prop-01/lod0.glb `
--out .gear-work/props/review/my-prop-01-renders --resolution 900

A whole-object convex hull is only a first collision proxy: it fills the space above a chair seat. Usable furniture needs separate colliders and measured sockets, which the workshop provides.

Every stage that touches a shipped prop is permissively licensed or never redistributed:

StageToolLicenceRedistributed
GeneratorTRELLIS.2 code and microsoft/TRELLIS.2-4B weightsMITno (weights)
Image encoderfacebook/dinov3-vitl16-pretrain-lvd1689mDINOv3 License (royalty-free, with use restrictions and a “Built with DINOv3” attribution for redistribution), gatednever; you need your own Hugging Face grant
Mesh extractioncumesh, FlexGEMM, utils3dMITno
Texture bakeMicrosoft-derived scripts/trellis/o_voxel_bake.py with the original Pinionfall scripts/trellis/uv_rasterize.pyMIT; see the adjacent LICENSE-o-voxel.txt and LICENSE-uv-rasterize.txt noticesyes
Reference imagesOpenAI gpt-image (API)output owned by the author under OpenAI’s Services Agreementno
Reference imagesMidjourney (paid plan)output owned by the author under the Midjourney Terms of Serviceno

The port’s own texture export (o_voxel.postprocess.to_glb) rasterises the UV layout through nvdiffrast, and the port also ships nvdiffrec_render. Both are under NVIDIA’s Source Code License, which is non-commercial. Neither is installed or needed. scripts/trellis/o_voxel_bake.py is a vendored copy of to_glb whose rasteriser calls go through scripts/trellis/uv_rasterize.py (pure PyTorch; uv_rasterize_test.py checks it against a naive reference). Do not install the port’s nvdiffrast wheels.

Every shipped package’s textures come from this bake. Assets generated before the change were re-baked from their native archives and their LOD maps rebuilt; the one asset that had no archive, medieval-chair-01, was regenerated from its recorded reference image and seed and prepared again.

Each package’s preparation.json carries the toolchain block that the third-party notices summarise.

Written by release:site-sync from docs/trellis-assets.md at v2026.10.04. Edit the engine document, not this page.