pipelines/PIPE_3D_2026-07-29.md
Research date 2026-07-29. Every model/tool below is VERIFIED (fetched live this pass, URL inline) or
marked CARRIED (verified 2026-07-15 in the repo brief, not re-fetched this pass) or
UNVERIFIED-EXCLUDED. Licence-read-before-first-GPU-hour is honoured: no tool enters the stack
without its licence named.
**SUPERSEDED IN PART — 2026-08-05, the material-property law. Josh ruled (verbatim): "...some
crush, some bend, some are flexible, some are fragile, some are hard, some are heavy, some are
light."** This dossier specifies how a mesh is GENERATED and says nothing about what the object
is MADE OF once it exists. The mesh lane's own subject-composition rule already reads a material
clause off the registry row; nothing downstream could read a physical PROPERTY off anything,
because no such column existed.
- Five columns now land on all seven prop-bearing registries (431 rows) — mass_class,
hardness,fragility,flexibilityandderived_from— emitted by
harness/asset_factory/emit_phys_profiles.py, with the citable default table in
build/3d/schema/material_defaults.json (48 keys; Ashby, Wegst and Ashby, Klein and Dutrow,
the USDA Wood Handbook, Liese, Currey).
- Three modes, and the third is the discipline. DERIVED where canon states the property,
DEFAULTED where canon states the material, UNMEASURABLE where neither — declared with a
reason and never guessed. 50 of 431 land UNMEASURABLE on mass, and that count is a deliverable:
27 environment features canon declares massless (effect volumes, permanently correct) and 23
equipment rows that state a FORM and no material (a canon debt made countable).
- THE TOOTH:harness/asset_factory/check_phys_profile.py, wired intoverify_landed.pyas
T8, mutation-proven both directions on every invocation (11 fixtures). A prop reaching a
staging manifest whose row leaves a column blank, carries an off-enum value, cites a canon cell
that no longer exists, or resolves mass_class=UNMEASURABLE while shipping mesh media is
REFUSED. The manifests this dossier's lane emits are exactly the surface it guards.
- A rigged creature answers to a second law. See the anatomy supersession on
PIPE_ANIMATION_2026-07-29.mdandbuild/3d/schema/anatomy_reference/: tooth T7 measures a
rigged import against its archetype's bone roster, segment count and per-joint ROM, and its
must-fire fixture is this lane's own CR_0002 straight-rod previz rig.
Nothing in the generation stack below is retracted. What is superseded is the assumption that a
mesh plus a texture is a finished object: an object that cannot say whether it crushes, bends or
shatters is not finished, it is only modelled.
Every 3D-gen repo in the ruled roster pins a pre-Blackwell CUDA. Verified live today:
TRELLIS.2 pytorch 2.6.0 with CUDA 12.4 (github.com/microsoft/TRELLIS.2, Linux-only, 24GB floor,
nvdiffrast+nvdiffrec still in setup.sh flags); Hunyuan3D-2.1 `Python 3.10 and PyTorch
2.5.1+cu124; Direct3D-S2 CUDA 12.1 / torch==2.5.1+cu121`; ComfyUI-3D-Pack Windows prebuilds
Python 3.12, CUDA 12.4, torch 2.5.1+cu124 with zero Blackwell/50-series note in its docs.
The RTX 5090 is sm_120 and requires ≥ PyTorch 2.7.0 + cu128 — 2.7.0 was the first stable release
shipping sm_120 kernels (verified: pytorch forums, SaladCloud RTX-5090 guide, vLLM issue #13306).
**Consequence: the install plan is not "follow the README." It is "rebuild every CUDA extension
against cu128."** This is the single biggest unbudgeted item in the lane and it lands on top of D-1,
not instead of it. The runbook's CUDA-12.4 pin for TRELLIS.2 is CONFIRMED as the repo's published
requirement and simultaneously CONFIRMED as unrunnable on sm_120 — both are true; the pin must be
read as "the version the authors tested," not "the version we install."
**SUPERSEDED IN PART — 2026-08-05, generation-pipeline-v2 multi-view install, measured on
Milks5090 (RTX 5090, sm_120, WSL2 Ubuntu-24.04, torch 2.9.0+cu128).** The blanket claim
"rebuild EVERY CUDA extension against cu128" is **REFUTED for the Hi3DGen route and UPHELD for
the Direct3D-S2 route.** The dossier was right that the READMEs do not install; it was wrong
that compilation is therefore unavoidable. What decides it is not the repo's pinned CUDA but
whether a prebuilt wheel exists for the exact (torch, python, cuda) triple — and for the
sparse-conv and attention extensions, two do.
- Hi3DGen: ZERO compilation.xformers==0.0.33.post1andspconv-cu126==2.3.8both run on
sm_120 on a box with no nvcc and no MSVC installed at all. Verified by EXECUTING kernels,
not by importing them:xops.memory_efficient_attentionreturned(1,64,8,64)and a
spconv.pytorch.SubMConv3dreturned(128,8), both on the 5090. Note the spconv wheel is a
cu126 build running against a cu128 runtime — the CUDA minor version in a wheel name is
not the constraint the dossier assumed it was.
- The pin that actually bites is torch-to-xformers, not CUDA. xformers==0.0.35 requires
torch>=2.10 and ships a py3.10 build; installing it silently upgraded torch to 2.11.0 and
then refused to load its own C++/CUDA extensions against py3.12. 0.0.33.post1 is the build
pinned totorch==2.9.0. Install it with--no-depsso it can never move torch off the
sm_120-proven pin.
- Direct3D-S2 still needs the compiler, and the reason is an import, not a CUDA pin.
torchsparseis avoidable (SPARSE_BACKEND=spconv), but
direct3d_s2/modules/sparse/attention/__init__.py unconditionally imports the
SpatialSparseAttention module, which does a module-level
from flash_attn import flash_attn_varlen_func.ATTN_BACKEND=xformersdoes not dodge
it — the import runs before any backend switch is read. Dao-AILab publishes no
torch2.9/cp312/cu12/linux wheel (three release pages checked, zero matches), so flash-attn
must be compiled, which is what forces the CUDA 12.8 toolkit (DL_0025).
- Two cheap ways out were tried and ruled out by measurement. nvidia-cuda-nvcc-cu12
12.8.93 from PyPI ships nvvm, libdevice, headers and ptxas but **no bin/nvcc compiler
driver** — it is for JIT consumers like Triton. Ubuntu's own nvidia-cuda-toolkit is
12.0-era and cannot target sm_120.
- Build-resource note for whoever runs it next: MAX_JOBS=12 on flash-attn is OOM-killed
on a 61 GB box —dmesgshowsciccinvoking the OOM killer. UseMAX_JOBS=4.
The practical rule this replaces the headline with: **probe for a prebuilt wheel at the exact
triple first, and treat "needs nvcc" as a per-repo property driven by unconditional imports —
not as a property of the whole lane.**
Licence law applied throughout: a licence that restricts output/use is a ship-blocker; a licence
that restricts the tool's own code (GPL/CGAL-class) is not, because we run those as external
executables and never redistribute them — Blender's output has never been GPL. That distinction is
what separates the trap set below from the finishing set.
| Job | PRIMARY | FALLBACK | Licence · source |
|---|---|---|---|
| Geometry (weapons/props/vril sites) | TRELLIS.2-4B | Direct3D-S2 | MIT / MIT. TRELLIS.2 · Direct3D-S2 — S2 verified today: MIT, no nvdiffrast, 10GB@512 / 24GB@1024, OBJ out |
| Sharp-geometry hero silhouettes | Hi3DGen (MIT) | TripoSG (MIT) | ~~CARRIED from repo brief; no new release found~~ SUPERSEDED 2026-08-05: INSTALLED AND PROVEN. Repo is Stable-X/Stable3DGen @ c29f668, MIT body re-fetched (copyright reads Bytedance Inc.). It is a TRELLIS derivative with the NVIDIA libraries stripped — the same lineage whose textured path this table bans for nvdiffrast taint, minus the taint. Throwaway: 67.3 s end to end, peak 14.1 GiB allocated / 17.0 GiB reserved, 1,053,224 faces, watertight. Rows DL_0019–DL_0023 |
| Finished textured PBR | Hunyuan3D-2.1 | Hunyuan paint stage + ArmorLab | Tencent Community Licence — 1M MAU cap, EU/UK/South Korea excluded, AI-label required. VERIFIED live: repo still tops out at 2.1; 2.5/3.x remain closed |
| TRELLIS.2 textured output | BANNED from any shipped path | — | nvdiffrast/nvdiffrec = NVIDIA 1-Way Commercial, non-commercial-only. Issue #22 re-fetched today: still open, still no maintainer response (opened 2025-12-18). Geometry path is MIT and clean; the bake path is previz-only |
| Quad retopology | AutoRemesher (MIT, CLI: --input --output --target-quads --edge-scaling, Win/Mac/Linux, OBJ-in/OBJ-out) | MeshAnything V2 (MIT, ~1600-face cap) | github.com/huxingyi/autoremesher · CG Channel 2026-07 coverage. Does NOT generate UVs — pair it with xatlas |
| UV unwrap/pack | xatlas (MIT, C++11, no external deps, Python bindings) | Blender Smart UV headless | github.com/jpcy/xatlas — actively maintained, 1223 commits |
| Finishing pipeline host | Blender 5.2 LTS (released 2026-07-14, supported to 2028, Python 3.13) | Blender 4.5 LTS | GPL tool, output unencumbered. --background --python |
| Auto-rigging (132 creatures / 22 familiars) | UniRig (VAST+Tsinghua, SIGGRAPH 2025) | Blender Rigify | MIT verified today, 8GB VRAM, Python 3.11 / torch ≥2.3.1, exports FBX. Caveat the repo itself raises: checkpoint trained on Articulation-XL2.0 — read that dataset's terms before ship. UniRig |
| Part decomposition | PartCrafter (ships inside ComfyUI-3D-Pack) | SimArt (Apache 2.0, URDF-targeted) | CARRIED + verified present in the 3D-Pack node list |
| Worldgen / scene-scale | HY-World 2.0 — NEW, not in any repo brief | HunyuanWorld-1.0 | Verified today: releases Apr–May 2026, outputs meshes / 3DGS / point clouds / depth / normals, "directly importable into Unity / Unreal Engine", CUDA 12.8 recommended, Python 3.11+ — the only item in this lane that ships Blackwell-native. License.txt present, name not readable via fetch → LICENCE READ REQUIRED before first GPU hour. HY-World-2.0 |
| 3DGS capture→mesh | gsplat / Nerfstudio (Apache 2.0, commercial-clean; 2DGS now integrated) | OpenSplat | TRAP: the original Inria graphdeco-inria/gaussian-splatting reference impl is NON-COMMERCIAL ("may be used non-commercially... research and/or evaluation purposes only"), and SuGaR derives from it. Route the whole 3DGS lane through gsplat, never the Inria tree |
| Terrain chain | OpenTopography Global DEM API → GDAL/rasterio → 16-bit PNG → UE Landscape | AWS Open Data bulk COGs | Chain CARRIED from GEODATA_TERRAIN_CHAIN.md. NEW CONSTRAINT VERIFIED TODAY: the free API key is rate-limited to 50 calls / 24h for non-academics (250 for academics). 69 regions × 3 datasets ≈ 207 calls = 4+ days of API budget, or an OpenTopography Plus subscription, or the AWS bulk mirror. The repo brief said "free, issued instantly" and missed the ceiling |
| Landcover / biomes | ESA WorldCover 10m → weightmaps → PCG Biome Core | Sentinel-2 L2A drape | CARRIED |
| Motion (creature/NPC locomotion) | ARDY (NVIDIA, SIGGRAPH 2026, released 2026-07-10) | Kimodo (NVIDIA, 2026-03-16) | Josh's leads CONFIRMED, both real. ARDY: code Apache-2.0, weights NVIDIA Open Model Licence (commercial permitted), tested on 4090, text encoder ~14GB VRAM (CPU-offloadable), outputs .npz joints/rotations + .csv MuJoCo qpos. nv-tlabs/ardy · nv-tlabs/kimodo. LICENCE FLAG: SMPL/SMPL-X variants — the Max Planck SMPL-X model licence is academic-only, "any use for commercial purposes is prohibited," commercial routes through Meshcapade. Use the non-SMPL skeleton variants, or retarget joint rotations onto our own rig and never ship SMPL-X mesh data. This is a licence read Josh's lane owner must make before ARDY touches a shipped animation |
KILLED / EXCLUDED this pass: Sparc3D (no weights, no licence — CARRIED). Ubisoft CHORD
(Research-Only Copyleft — previz only, CARRIED). Pixal3D (MIT code but installs TRELLIS.2 first,
inherits the nvdiffrast chain — bench it instrumented, never assume clean; CARRIED). SATO (code
still unreleased — CARRIED, re-check post-SIGGRAPH). Meshy/Tripo/Rodin (closed SaaS — reference
tier only, per the standing Josh ruling). **NVIDIA Cosmos 3 — real and verified (weights 2026-05-31)
but EXCLUDED from this lane**: it emits video/image/audio/action, not meshes or USD; both
NVIDIA_WORLDGEN.md and COSMOS3_DEEP_DIVE.md reached that independently and nothing today
overturns it. NVIDIA's SIGGRAPH-2026 Omniverse drop (ovrtx, ovphysx, CAD-to-SimReady) is real
but is sim/robotics asset prep, not game-mesh generation — no licence taint found, no adoption case.
Environment lane (per D-1). Confirm option (a) WSL2 Ubuntu 24.04 + the Puget Docker App Pack
as the generation host, with (b) ComfyUI-on-Windows demoted to the image lane only. The rider's
own evidence now carries decisive weight: the Puget comfy_ui image is
nvidia/cuda:12.8.0-runtime-ubuntu24.04 + cu128 torch — i.e. the vendor already assembled the exact
sm_120 stack that §0 says every repo lacks. Do not use ComfyUI-3D-Pack's Windows prebuilds: they
are cu124/torch-2.5.1 and will not load on sm_120. Fork the pack, pin torch==2.9.0+cu128.
Disk placement (per the RULED layout). Gen4 4TB slot-3 = the whole lane: WSL2 vhdx, model
weights, ComfyUI models/, Blender, the generated-asset staging cache. Gen5 4TB stays UE hot
path + DDC — nothing from this lane touches it. 8TB SATA = the raw-GLB archive + every DEM/WorldCover
GeoTIFF (those are re-downloadable but rate-limited, so archive them once).
Install ORDER (dependency-forced, not preference):
1. WSL2 Ubuntu 24.04 + NVIDIA Container Toolkit via the Puget pack's setup.sh (hand-finish the
driver step — the pack hard-fails its own driver install under WSL2's passthrough model).
2. Pin the base env: Python 3.11, torch==2.9.0+cu128. Verify sm_120 before anything else:
python -c "import torch;print(torch.cuda.get_device_capability())" must return (12, 0).
3. Build the CUDA extensions from source against cu128 — flash-attn, spconv, torch_scatter,
torch_cluster, torchsparse, and TRELLIS.2's cumesh/o-voxel/flexgemm. **This is the
half-day-to-two-day item.** UniRig's own README warns flash-attn install commonly fails.
4. TRELLIS.2 geometry-only: run setup.sh WITHOUT --nvdiffrast --nvdiffrec. If the shape path
won't import without them, install them into a separate trellis2-previz env that is firewalled
from the shipped-asset path by construction, not by discipline.
5. Direct3D-S2 (clean-geometry control, no nvdiffrast) — the A/B partner that tells us whether we
need TRELLIS.2 at all.
6. Hunyuan3D-2.1 (the textured-PBR lane).
7. CPU-only finishing set — needs no GPU, no CUDA, install anywhere: Blender 5.2 LTS, AutoRemesher,
xatlas (pip install xatlas), GDAL/rasterio.
8. UniRig, then HY-World 2.0 (already cu128-native), then ARDY/Kimodo last.
Thursday-night download list. Sizes marked [M] measured/quoted, [E] estimated from parameter
count — measure at download and correct the runbook, do not carry [E] figures forward as facts.
comfy_ui image — ~15 GB [E]BY MEASUREMENT 2026-08-05: peak 5.16–6.09 GiB allocated (n=13, median 5.19), read from each
cell's own .gen.json. See §10.** Hi3DGen's runtime peak likewise measures 10.6–13.8 GiB
allocated across nine cells, against this row's ~5 GB download-size estimate — the two figures
are different quantities and the [E] was never a VRAM claim; §10 carries the runtime numbers
[M, quoted]. The 80B panorama tower is far outside a 32GB card. Pull WorldMirror-2 only (~3 GB [E])
until the benchmark says otherwise.
re-downloadable, so a partial night is recoverable.
VRAM / RAM budget and scheduling class — the concurrency law applied. This lane splits cleanly
into two halves with different scheduling classes, and that split is the whole answer to GPU
contention:
Hunyuan3D-2.1 combined 29 GB of 32 GB [M, repo-quoted] is the ceiling case and admits nothing
else on the card — not a resident 14B, not a UE editor. TRELLIS.2 24 GB floor [M]. Direct3D-S2
10 GB@512 / 24 GB@1024 [M]. HY-World unmeasured. UniRig 8 GB [M]. ARDY ~14 GB [M] with a
CPU-offload switch. **Rule: one GPU job at a time, serialized by a queue, and the queue holds
while UE holds the GPU.** An OOM from our own co-residency sloppiness would be a false trigger on
HARDWARE_DECISION's upgrade trigger #1.
retopo/UV/bake/export, AutoRemesher, xatlas, GDAL/rasterio, the DEM chain, FBX/glTF conversion,
Nanite-prep validation. Budget ~8 threads of the 9950X3D's 32 and ~32 GB of the 128 GB, leaving
the rest to UE compiles and the DDC. This is the lane's real throughput win: the finishing
pass is the bottleneck (§2 of the repo brief: no open AI does retopo+UV+LOD+collision end-to-end),
and it never needs the contested resource.
Every generated asset enters canon through DR-2 (docs/ASSET_DROPIN_CONTRACT.md) and nowhere else.
mesh_id_ref and writes back TO that same id. Itnever chooses a path. Default drop is path takeover at the ratified convention
/Game/Meshes/<class>/SM_<mesh_id_ref> (item 3 RULED: path takeover is the default; manifest
re-point via ue_asset_path is reserved for licensed/MetaHuman off-convention assets).
pending → prompt composed, generation_prompt_hash set, in_progress → import + apply_*_writeback, ue_asset_path + last_generated_timestamp written, complete.
QA rejection writes rejected and the id stays on its placeholder — a failed generation never
produces a broken build, only an unchanged one. regeneration_required re-enters the queue.
generation_tier (fidelity: background · region_standard · region_hero · realm · apex),
presentation_tier_ref (mastery variant), decay_stage (place condition, two frozen endpoint
values). All three are folded into generation_prompt_hash, so a tier or stage change
self-triggers regeneration_required (clause 6.3). A bare tier column is banned.
convention for skeletal/rigged, FBX or glTF for static. Textures out as separate PBR channels
(basecolor / normal / ORM). UniRig emits FBX natively, which is the reason it beats hand-rigging
at 132-creature volume.
ENGINE_OPTIMIZATION_DOCTRINE.md **R-01 rules Nanite-first static world with NO authored LOD
chains for world geometry**; density is controlled by placement (PCG + HLOD), not by cull knobs.
So the finishing pass does not build _LOD0.._LOD3 for world/static meshes — it exports the
dense mesh and lets Nanite cluster it. What the finishing pass DOES owe Nanite: opaque/masked
materials only (no translucency, no two-sided), clean manifold topology, no single-plane cards, a
collision hull, and WPO Disable Distance metadata on any WPO material (R-11). **R-02 bans
alpha-card and WPO-driven foliage as INPUTS to the Nanite-foliage path, and R-02b** requires
every hero_vegetation asset to resolve a conventional instanced route as well — so the
vegetation branch of this lane exports two routes and the validator asserts both.
Nanite skeletal is beta as of 5.7 and reworked in 5.8 (Epic docs; 5.7 crash repros gone) — so the
older brief's "Nanite has no skeletal support" line is stale and should be corrected at source.
model gate): mesh_id_ref resolves through the resolver (QT-8); the asset loads and is
non-placeholder; manifold/watertight check; triangle and material-slot budget; UV coverage and
overlap from xatlas; opaque/masked-only material check; collision hull exists. **Only assets that
pass the deterministic teeth are shown to an image critic**, which reads captures against the
concept-art reference and the realm charter's art-hook family (palette_law · silhouette_law ·
impossible_architecture_budget · signature_phenomenon). The RB-RLM-PROVENANCE before-state
line applies to any decay-bearing surface: shown a frame, the critic must be able to say what the
place looked like BEFORE, or decay_law has failed on the art side even where the data passes.
registry rows where generation_status IN (pending, regeneration_required), ordered by
generation_tier, and the queue runner takes them one at a time. Work-lists are script-emitted,
never hand-typed (standing rule). The prompt composer reads the row's canon fields + tier axes;
it never invents subject matter.
§5.2 benchmark on that class and its licence clears for that class's intended commercial use.
It is DEMOTED automatically on: a licence change, a deterministic-tooth pass-rate below the class
floor over a rolling window, or an image-critic rejection rate above the class ceiling. The
per-class default lives in one config file, so a demote is a one-line change and every subsequent
dispatch follows it. TRELLIS.2's textured path is permanently demoted to previz by licence and
that entry is a constant, not a benchmark output.
deterministic teeth, the whole DEM/WorldCover/GDAL chain, registry writeback via the sanctioned
writer with fidelity_baseline.json refreshed in the same commit. Failures write rejected and
move on — the queue never blocks on one bad asset.
director_in_loop_required; everyfirst asset of a new class or a newly-promoted model; every §17.1 care-gated id — DR-2 item 4
RULED that the in_progress→complete transition for a care-gated id is blocked until the
capture-rights/authenticity audit signs off. Real living-heritage architecture is never
raw-generated, full stop; the generator's role there is massing blockout only.
model. Any run that would co-reside with UE on the GPU.
1. D-1 ratification — confirm WSL2+Puget-pack as the generation host and ComfyUI-Windows as the
image lane only. The evidence now points one way; it needs the word.
2. The Puget burn-in/benchmark sheet — his account, his rep, not auto-delivered. Only
pre-arrival thermal/power baseline for this exact machine.
3. OpenTopography — free non-academic key is 50 calls/24h. Either accept a ~4-day spread across
69 regions, or he buys OpenTopography Plus, or we take the AWS bulk-COG route (no account, no
limit, more engineering). His call because it is a spend/schedule fork, not a technical one.
4. Two licence reads that need a human decision, not an agent's: (a) Hunyuan3D-2.1's EU/UK/South
Korea exclusion — does it block *us* using the model, or block *distributing outputs into* those
territories, or both; this decides whether the textured-PBR lane can ship at all. (b) whether the
SMPL-X-derived ARDY/Kimodo variants are avoidable in favour of the non-SMPL skeletons, or whether
a Meshcapade licence is wanted.
5. Nothing else. No account creation, no purchase, no credential in this lane is agent-work.
1. Does the cu128 rebuild actually produce working TRELLIS.2 / Hunyuan3D-2.1 / Direct3D-S2 on
sm_120 — and how many hours did it cost? (This number decides whether Direct3D-S2 replaces
TRELLIS.2 outright as the geometry primary; S2 is MIT, nvdiffrast-free, and would collapse the
whole previz-firewall problem if its quality holds.)
2. Real 5090 wall-clock and observed peak VRAM per model per class — replacing every [E] in §2
and every H100/4090 figure in the repo briefs.
3. Minutes-of-manual-work-to-game-ready per class, per generator — the only number that actually
schedules the 72-weapon / 132-creature / region-architecture volume. Generation speed is noise
next to it.
4. Does AutoRemesher + xatlas + Blender-headless clear the deterministic teeth unattended, and at
what pass rate? If that rate is low, the whole volume thesis needs re-planning, because no AI
closes this gap today.
5. Does UniRig produce UE-importable FBX rigs on non-humanoid creature anatomy at acceptable
quality — or does the 132-creature roster need a paid point-solution after all?
6. HY-World 2.0: what does WorldMirror-2 actually emit at region scale, what does it cost in VRAM,
and what does its License.txt say — this is the one genuinely new, Blackwell-native,
mesh-emitting worldgen candidate and it is entirely unassessed.
7. Instrument Pixal3D's process at runtime: does its textured output route through nvdiffrec in
practice? Trust the syscalls, not the NOTICE file.
8. Nanite ingest: what dense-mesh triangle count does the finishing pass hand UE before Nanite
VisBuffer cost breaks the doctrine's 2.25 ms budget line?
APPEND-ONLY addendum under the pipe-dossiers-bind law, from the 2026-08-05 comparator-pipeline research
(Josh: "what about their pipelines?"). This section covers how Capcom actually BUILDS a creature — rig
order, tool chain, automation — against our 132-creature roster. The moveset/telegraph cadence lands on
PIPE_ANIMATION_2026-07-29.md §7; the ability-VFX authoring structure on PIPE_ART_2026-07-29.md §7.
Single primary source for this section, cited per fact:
https://blogs.autodesk.com/media-and-entertainment/2026/04/23/the-production-infrastructure-behind-capcoms-monster-hunter-wilds/
It is a vendor-published production feature, not a first-party conference talk — treat the tool list as
reliable and the framing as promotional. Nothing here is repo canon until the director lands it.
Monster Hunter Wilds ran a character section of roughly 60 people; the background section managed 4,500+
assets with external collaborators; hundreds of developers total. **NO-ADOPT — this is the number that
must NOT be transferred.** It is recorded here so that any future claim of "we do what Capcom does" is
read against it. What transfers from this source is automation and ORDER, never headcount.
Capcom's technical artists build the skeleton structure FIRST, then simulate muscle movement (Ziva VFX),
then output the simulation result INCLUDING skin and fat to RE ENGINE. Fictional creatures were designed
against real animal anatomy references.
ADOPTED: (a) skeleton-before-deformation as the ruled order for our creature rigs — our UniRig lane
already emits a skeleton, so the adoption is that the deformation/corrective pass is a DECLARED second
stage, not an afterthought; (b) the real-anatomy reference rule, which our creature briefs should carry
per row (a fictional creature still cites the animal its motion is built from).
NO-ADOPT: Ziva VFX itself — Maya-bound, paid, and outside this lane's ruled Blender-headless chain.
Our substitute for the muscle pass is a corrective-shape / secondary-motion pass declared per creature.
This is a real quality delta and it should be named as one, not papered over: their skin slides over
simulated muscle; ours will not, and the honest register for our creature deliverables says so.
| Capcom (Wilds) | Job | Our ruled counterpart (§1) | Verdict |
|---|---|---|---|
| Maya | authoring host | Blender 5.2 LTS headless | equivalent, ADOPTED already |
| Simplygon | LOD / decimation | (none declared) | GAP — see below |
| Quad Remesher | retopology | AutoRemesher (MIT) | equivalent, ADOPTED already |
| ngSkinTools | skin-weight authoring/repair | (none declared) | GAP — the real one |
| Ziva VFX | muscle/fat simulation | none (corrective pass) | NO-ADOPT, delta declared §7.2 |
| MotionBuilder | mocap data processing | ARDY/Kimodo NPZ → BVH/FBX chain | equivalent in role |
| Houdini Engine inside Maya | fur reduction, alpha optimisation | none | NO-ADOPT this cycle |
| In-house Anim Tool | Maya↔engine FBX exchange, rig/weapon switching | (ad hoc) | ADOPT the pattern — §7.4 |
| Flow Production Tracking (ShotGrid) | asset metadata/thumbnails | registries + generation_lifecycle.json | equivalent, ours is stronger |
THE GAP, named: our chain (UniRig → FBX) has NO declared skin-weight repair step, and ngSkinTools
exists in their chain precisely because auto-generated weights on non-humanoid anatomy are not shippable
raw. This dossier's own §6 benchmark question 5 already asks whether UniRig produces acceptable rigs on
non-humanoid creature anatomy — **the comparator answer is that even hand-built rigs need a dedicated
weight-painting tool, so the honest expectation is that UniRig output requires a weight pass regardless
of how question 5 resolves.** Route: add a weight-repair stage to the creature finishing chain (Blender's
own weight tools are the free counterpart) and make it a declared rung, not an implicit one. The LOD gap
is softer — UE Nanite plus our existing decimation covers most of Simplygon's role for our target.
Capcom built an in-house "Anim Tool" that automates FBX exchange between Maya and RE ENGINE and owns the
complex cases: weapon switching, transformation controls, rig switching. Background assets link to the
engine through custom tools that automatically transfer asset IDs, thumbnail images, polygon counts,
shader names and collision data. Secondary motion is handled by a custom Maya implementation using
Position-Based Dynamics, and a skirt-animation template prevents foot penetration using auxiliary bones
and leg movement vectors.
ADOPTED AND ROUTED: (a) ONE scripted Blender↔UE exchange tool owning rig-switch and attachment cases,
rather than per-asset manual export — this is the same discipline as our ASSET_DROPIN_CONTRACT id rule,
extended to the exchange itself. (b) The engine-side auto-transfer of asset ID, poly count, shader name
and collision is a comparator PRECEDENT for extending our drop-in manifest beyond the id: the metadata
that the audit tooth wants should arrive with the asset, not be re-derived later.
ADOPTED AS A CLASS, not as tools: appendage/cloth penetration is solved by a per-costume TEMPLATE
(their skirt template), never by per-shot fixes. Our creature roster has the same class problem in
tails, wings, mandibles and hanging cloth; a template per appendage class is the cheap answer.
Capcom runs nightly mayapy automation for texture baking, LOD creation and asset commits.
ADOPTED AND ROUTED — this is the highest-value, lowest-cost adoption in the whole researcher-B lane.
The 5090 box is live 24/7/365 and compute-clean; the identical loop in our chain is nightly
blender --background --python doing bake, LOD, and the deterministic finishing teeth, committing what
passes and emitting a worklist for what does not. It needs no new model, no new licence and no GPU
budget negotiation, and it converts the "minutes-of-manual-work-to-game-ready" question (§6 item 3) from
a scheduling risk into a measured nightly number.
documents this chain, and the GDC Vault MHW postmortem
(https://www.gdcvault.com/play/1024981/-Monster-Hunter-World-Postmortem) was NOT watched. What the
written coverage of that postmortem does add: monsters were placed inside a food-pyramid structure
chosen from the player experience and level curve wanted, and at least one monster (Lagiacrus) was
prototyped and cut — i.e. the roster itself was iterated, not fixed at design time
(https://www.gamedeveloper.com/design/how-capcom-designed-i-monster-hunter-world-i-to-feel-approachable-and-alive ·
https://www.siliconera.com/monster-hunter-world-developers-show-off-prototype-lagiacrus-gdc-2018/).
our own already-ruled tools, and none of them introduces a new dependency.
Charter: docs/FACTORY_CONTINUATION.md tail, "JOSH CALLS THE PIPELINE". This section is the
dossier half of pillars 2 and 3. Pillar 1 (concept-first) and pillar 4 (the routing table) are
policy and live in the charter; this is what was installed, measured, and built.
| Candidate | Licence | Status 2026-08-05 | Evidence |
|---|---|---|---|
| Hi3DGen (Stable3DGen) | MIT (body pinned, docs/licence_records/stable3dgen/LICENSE) | INSTALLED, PROVEN on the throwaway | 67.3 s · peak 14.1 GiB alloc / 17.0 GiB reserved · 526,630 v / 1,053,224 f · watertight · own venv /root/mv3d/venv |
| Direct3D-S2 | MIT (at LICENSE.txt; the conventional LICENSE path 404s) | INSTALL INCOMPLETE — blocked on the flash-attn compile, mechanism fully diagnosed in §0's supersession | repo a1cf235 cloned, own venv /root/mv3d/venv_d3ds2, torch/spconv/CUDA 12.8 in place, flash-attn wheel building at MAX_JOBS=4 after an OOM at 12 |
| MV-Adapter (the natural pillar-3 view generator) | Apache-2.0 adapter, openrail++ SDXL base | NOT INSTALLED — flagged for ruling (DL_0026) | see §8.4 |
VRAM is reported as torch.cuda.max_memory_allocated/reserved, a true running maximum held by the
allocator — not a post-hoc nvidia-smi sample. The card is shared with three other lanes, so an
nvidia-smi total would measure the neighbours as much as the model.
Two committed tools, slotting into the existing chain at exactly the seam bl_finish2.py occupies
(same xatlas-UV'd mesh in, same T_<id>_BC.png out):
harness/asset_factory/bl_mv_stage.py — Blender headless. Bakes world POSITION and NORMAL intothe UV atlas plus a coverage MASK, and renders per-view linear depth (and optional unlit albedo).
Writes views.json carrying the world-to-camera matrices actually used, so the merge stage
consumes matrices rather than re-deriving cameras from angles. A duplicated convention would fail
as a texture subtly wrong everywhere and obviously wrong nowhere.
harness/asset_factory/mv_texture_bake.py — the merge. Each (texel, view) pair earns a weight that is the product of frustum × visibility (depth test) × facing (n·v)^p. Colour is the
weight-normalised sum; texels no view can see are reported as HOLES, not invented.
What v1 was doing, now measured. Positive control on MN_0001 (a landed asset whose basecolor
is known, so reconstruction error has ground truth). The ablation runs the *same code* at N views,
so N=1 reproduces the v1 single-projection path and isolates the multi-view contribution from any
bug in the merge:
| views | texels observed | never observed | MAE (8-bit) | PSNR |
|---|---|---|---|---|
| 1 (= the v1 path) | 30.3 % | 583,695 | 30.33 | 13.50 dB |
| 2 | 55.5 % | 372,882 | 13.01 | 18.46 dB |
| 4 | 91.0 % | 75,272 | 4.25 | 26.83 dB |
| 8 | 92.8 % | 60,389 | 4.15 | 27.14 dB |
| 24 | 98.6 % | 11,787 | 3.24 | 29.92 dB |
So bl_finish2.py's own docstring concession — "the far side carries stretched front pixels" — is
now a number: **on this asset the single front projection genuinely observes 30 % of the surface and
invents the other 70 %.** That is the white-shell and blur class, quantified. Twenty-four views
recover it to 98.6 % observed at 29.9 dB.
Look at it, do not just read the table: build/3d/v2_mvtex/MV_TEXTURE_PROOF.png is the
reference atlas, the N=1/N=4/N=24 reconstructions and the N=1 vs N=24 coverage maps on one sheet.
N=1 is visibly blotched with flat mis-toned blocks where front pixels were stretched around the
object and its coverage map is mostly dark; N=24 is near-indistinguishable from the reference and
its coverage map is nearly solid white. The numbers and the picture agree.
original Principled BSDF into an unlit scene; a Principled BSDF with nothing lighting it is
black, so all 24 views were black, the atlas came back mean 0,0,0, and the control reported
MAE 49.7 / PSNR 11 dB — a number that reads as a broken projection and was a broken light rig.
Fix: clone each material to an Emission fed by its Base Color.
margin=0 while POSITION/NORMAL keep a gutter. Position and normal want the gutter for filtering;
the mask defines "this texel is really on the surface" and must not.
1. Direct3D-S2 is not yet proven, and the last blocker is PROCESS LIFETIME, not the build.
In place and verified: repo a1cf235 in its own venv /root/mv3d/venv_d3ds2, torch
2.9.0+cu128 on sm_120, spconv-cu126 (so SPARSE_BACKEND=spconv retires torchsparse), and
nvcc 12.8.93 at /usr/local/cuda-12.8. Missing: the flash-attn wheel, and after that
third_party/voxelize and pip install -e ..
The compile was attempted four times and died four times at exactly
Building wheel for flash-attn (pyproject.toml): started. **Two DIFFERENT causes, and only
the first is a real engineering constraint:** (a) at MAX_JOBS=12 it was genuinely
OOM-killed — dmesg shows cicc invoking the OOM killer on a 61 GB box; (b) at
MAX_JOBS=4 there was no new OOM entry, and the build died anyway when its invoking
wsl.exe wrapper ended — this agent session cannot hold a WSL process alive long enough
for a flash-attn compile, and anchoring the distro with a long sleep did not survive
either. This is the same class as the standing "agents background the final gate step"
lesson: the work is not hard, the process just has to outlive the agent.
Resume in one command from a session that owns a durable shell (expect tens of minutes):
MAX_JOBS=4 CUDA_HOME=/usr/local/cuda-12.8 PATH=$CUDA_HOME/bin:$PATH \
TORCH_CUDA_ARCH_LIST=12.0 FLASH_ATTENTION_FORCE_BUILD=TRUE \
/root/mv3d/venv_d3ds2/bin/pip install --no-build-isolation flash-attn==2.8.3
then pip install --no-build-isolation ./third_party/voxelize and pip install -e ., and
run with SPARSE_BACKEND=spconv.
A fifth attempt used a PERSISTENT source tree at /root/mv3d/flash-attention (v2.8.3) so
ninja objects survive between bounded runs — the right shape for a resumable build, and it
is left in place for the next session. **Measured rate: 1 object in the first ~8-minute
window**, so budget hours, not minutes, and run it somewhere it will not be cut off. Note
the trim env vars set on that attempt (FLASH_ATTENTION_DISABLE_FP8 /
_DISABLE_HDIM256 / _DISABLE_BACKWARD) are unverified for the 2.8.x line — they are
documented for FA3/hopper and may simply be ignored here, in which case the full kernel
matrix is being built and the estimate should assume that. Weights are wushuang98/Direct3D-S2 @
8b04a8eddb7a56a0f4e89fe5f5b840c7d5610c00 (MIT), 10 GB VRAM at 512 / ~24 GB at 1024 per the
repo — note the card is shared, so the 1024 path needs the free-VRAM backoff the standing
lane already uses.
2. The throwaway's coverage ceiling is NOT explained. The Hi3DGen dragon saturates at
52 % observed at 24 views and 54.7 % at 80 — resolution-invariant (512/1024/2048 all ~52 %)
and threshold-invariant (8× depth bias and zero facing floor buy 2.4 points). Three hypotheses
were tested and killed: interior shells (26 components, but the largest holds 99.2 % of volume
and 98 % of area), depth aliasing, and my own thresholds. What remains untested is the **UV
layout**: the throwaway used the Smart-UV-Project fallback, which produces thousands of
micro-islands, where MN_0001 used a production xatlas atlas and reached 98.6 %. **Do not quote
52 % as a fact about generated geometry** — re-run coverage on an xatlas layout of the same mesh
before drawing any conclusion. That is the next measurement this lane owes.
3. The turnaround view GENERATOR is unratified. The route was deliberately built to take views
as (image, camera-matrix) pairs from any source, so nothing is blocked; but the benchmark A/B
the charter asks for needs a ruled generator. MV-Adapter is the obvious candidate and its
adapter is Apache-2.0 — but it is inert without an SDXL base, and SDXL is openrail++.
**The V2 charter says "MIT/permissive weights only"; the standing licence law says face-value
reads and flag only explicit triggered prohibitions, and OpenRAIL++-M permits commercial use,
claims no rights over output, and restricts uses this game does not make.** Those two rules
genuinely disagree. That is a ruling, not an executor's call, so it is surfaced rather than
quietly taken either way (DL_0026).
4. The benchmark set is still blind. The throwaway is Stable3DGen/assets/example_image/0.png,
a coiled dragon figurine shipped with the MIT repo — deliberately NOT one of the charter's
benchmark items (macuahuitl / woomera / madu / nunchaku, the naga, a shelled fauna, the two prop
controls, the garmented body), so the blind A/B still has an unspent set to judge on. It was
chosen because a coil is structurally the same failure class as the documented naga.
---
**This section supersedes §8's four open items 2 and 3 in part, and answers §6's
benchmark-day questions with numbers.** Full record:
build/3d/V2_BENCHMARK_RESULT.json; work products in D:/assetgen/v2/bench/**;
tools in harness/asset_factory/v2_bench_*.py.
The set and the arms. Nine items, thirty-one item-arm cells, all drawn from
documented failures or documented controls: the four identity-failed weapons
(macuahuitl, nunchaku, madu, woomera), the Khmer Naga, the Sumatran orangutan, obsidian
and native sulfur as controls, and the garmented child body. Arms: (a) the current stack
as shipped, (b) current stack on a canon-keyed picked plate, (c) Hi3DGen on that same
plate with the same single-projection bake, (d) Hi3DGen with the multi-view texture
route. Arm b is deliberately NOT run on the five items whose existing picked plate
already passes an identity read — their arm-b input would be byte-identical to arm a's,
and a duplicate in a blind sheet inflates whichever label wins the coin flip.
Only one multi-view model could be benchmarked. Direct3D-S2's install is still
incomplete (flash-attn compile; no published wheel for torch 2.9 / cp312 / cu12 / linux),
and MV-Adapter remains uninstalled pending the §8 item-3 licence ruling.
THE HEADLINE: the plate is the identity fix; the geometry model is not. Across the
four identity-failed weapons the blind identity mean runs **2.00 (a) → 4.25 (b) → 4.00
(c)**. Arm b changes only the conditioning plate and the surface constants. Arm c changes
the geometry model on top of that plate and moves identity by −0.25. Per item: WPN_036
2→4, WPN_042 2→5, WPN_045 2→5, WPN_072 2→3. **Pillar 1 (concept-first) is the
load-bearing pillar for the identity class; pillar 2 buys geometry, not identity.**
§8 item 2 is CLOSED — the Smart-UV hypothesis is dead. Every cell here carries a
production xatlas atlas from finish_mesh2, and the Hi3DGen meshes still measure
0.517–0.826 observed at 24 views while TripoSG meshes through the identical chain
measure 0.957–1.000. The variable is not the UV layout and not the remesh path (three
TripoSG cells took the tri-fallback and still measure 0.95–0.99): it is RUGOSITY,
surface area over convex-hull area — TripoSG median 0.88, Hi3DGen median 1.47,
Pearson r = −0.7303 over all 31 cells. Hi3DGen models concave relief that TripoSG
smooths away, and an exterior camera ring cannot see into it. **A low coverage number on
a Hi3DGen mesh is evidence of detail, not of a defect** — and the multi-view texture route
will need more than an exterior ring for detailed geometry.
Coverage is not a quality proxy, and this section will not let it become one. The
judged surface score correlates only r = 0.29 with 1-view coverage and r = 0.16
with 24-view coverage. How bad a white shell LOOKS depends on the object: a uniform
wooden baton smears invisibly, a two-tone obsidian smears catastrophically. Report
coverage as coverage.
The instrument was positive-controlled before any arm was scored by it: the shipped,
already-landed MN_0001 atlas measures 0.99937 observed at 24 views through the
identical invocation (records/POSCTRL_MN_0001_shipped_cov24.json).
Arm d ran its geometric half on every cell and its colour half on none. The merge
needs one colour image per view and nothing installed produces a generated turnaround —
Hi3DGen's shipped weights carry only slat_decoder_mesh (read from
weights/trellis-normal-v0-1/pipeline.json, not assumed), so there is no radiance-field
decoder to render colour views from. Re-rendering the v1 atlas from 24 angles and merging
it back would have reconstructed the v1 texture without adding information — a circular
measurement that would have looked like a result — so it was not done. **Cost is not the
blocker: the 24-view stage runs in 2.3 s against the single-projection bake's 2.25 s.**
**Controls: no regression from the multi-view geometry — but the controls did not
reproduce themselves on the current stack.** Arm c improved both (MN_0001 identity 2→4,
surface 1→2; MN_0002 identity 3→4, surface 2→4). Arm a's own fresh re-runs came out worse
than the landed artefacts: MN_0002__a is a negative-volume mesh (inconsistent
winding, 41 raw bodies) whose 24-view coverage is 0.156, the worst cell in the benchmark,
and MN_0001__a lost the obsidian's conchoidal relief entirely. Both passed
finish_mesh2's watertight-and-manifold check. That is the withdrawn-identity lesson
repeating on a new axis: **a healthy mesh census says nothing about whether the object is
the thing it is named.** "The current stack aced the compact-prop class" is a statement
about two landed artefacts, not a reproducible property of the stack.
Silhouette differentiates nothing (every arm within 0.25 of every other, per class),
and the naga's documented silhouette failure did not reproduce on either model — both
built the coil correctly. That failure needs re-attribution.
ROUTING, as the evidence supports it today. Concept-first is ratified for the
identity-bearing classes: a judged, picked plate keyed to the row's own mesh_tags.
Multi-view geometry is a HOLD — on a single-projection bake its extra relief is a net
loss (weapons surface 3.25 → 2.25). Pillars 2 and 3 cannot be bought separately: Hi3DGen's
geometry win only becomes visible through a texture route that can see the geometry it
wins, and that route's gating item is a licensed multi-view colour generator — i.e.
§8 item 3's ruling.
---
**This section RATIFIES §9's result into law and supersedes §9's closing "ROUTING, as the evidence
supports it today" paragraph — that paragraph was a recommendation; this is the table the emitter
and the runner now read.** Machine-readable record: build/3d/V2_ROUTING_RATIFIED.json, emitted by
harness/asset_factory/v2_ratify.py, which DERIVES every arm mean from the blind join and refuses
to emit if a route's evidence condition stops holding. Independent cold verifies:
build/3d/v2_bench/RATIFY_COLD_VERIFY.json and build/3d/v2_mva/RATIFY_COLD_VERIFY_MVA.json.
WHAT CHANGED IN THE 2026-08-05b PASS. The TEXTURE column previously read "v1 single
projection" on four rows and BLOCKED on the fifth. The multi-view colour generator that row was
blocked on is now installed, benchmarked and ratified, so every texture cell below is superseded.
Geometry and conditioning are unchanged — the multi-view geometry arm was run separately and
LOST. Texture conditions are evaluated against the MVA blind join
(build/3d/v2_mva/MVA_UNBLINDED.json), never against the earlier session's numbers, because judge
calibration drifts between sessions by more than the differences at stake.
THE LICENCE BAR, CORRECTED. Josh ruled 2026-08-05: the bar is COMMERCIAL USE PERMITTED. The
V2 charter's "MIT-only" phrasing is STRUCK — it was a self-inflicted tightening of the standing
face-value licence law, and it blocked a working route on a bar nobody had set. Full chain in §11.1;
the one genuine exclusion is nvdiffrast, on its own body.
| Class (consumers) | Conditioning | Geometry | Texture | Status | Evidence |
|---|---|---|---|---|---|
| weapon, equipment | CONCEPT_FIRST — n≥4 plates keyed to the row's own mesh_tags, judged, picked seed lifted | TripoSG as shipped | MULTI-VIEW (arm e) — MV-Adapter ig2mv 6 views → mv_texture_bake + texel-coverage tooth | RATIFIED (texture RATIFIED) | identity 2.00 (a) → 4.25 (b), n=4; arm c moves identity −0.25 on the same plate. Texture: surface +2.25, n=4, no weapon regressed |
| creature (folklore/named) | CONCEPT_FIRST | TripoSG; Hi3DGen a lead, not a switch | MULTI-VIEW (arm e) | LEAD (texture RATIFIED, n=1) | n=1 (CR_0161): identity/silhouette tie 5/5. Texture: surface 3 → 5 — the far flank and coil interior now carry scale colour |
| creature (real fauna) | CONCEPT_FIRST | TripoSG — no measured delta | MULTI-VIEW (arm e) — *was BLOCKED, the block is LIFTED* | RATIFIED (texture RATIFIED, n=1) | n=1 (CR_0026): geometry a genuine tie on every axis. Texture: surface 2 → 5, identity 4 → 5 — the white shell was a texture-route defect and the texture route fixed it |
| mineral, compact env prop | plate already picked; no plate arm was run for this class | TripoSG — HOLD | MULTI-VIEW (arm e) | HOLD (texture RATIFIED) | arm c wins both cells but MN_0002's arm-a baseline is a negative-volume mesh. Texture: identity +1.00, surface +1.50 — these are the CONTROLS and they moved UP |
| character body | CONCEPT_FIRST | neither single-pass arm — the two-pass composed body remains the route | MULTI-VIEW (arm e) | HOLD (texture ADOPTED ON A TIE, n=1) | tie at 4/4/4/3 on both arms. Texture: +0 on both axes — the one class the texture route did not move, recorded as a tie rather than folded into the mean |
**The texture headline: surface +1.89 per item across nine items, wins or ties every class,
regresses none. Two teeth hold it there: T6** refuses the emit if any routed class regresses on
arm e, and T7 refuses it if the aggregate falls inside the measured judge-noise floor (1.0,
derived from a mean absolute axis-score change of 1.125 between the two independently shuffled
scoring sessions). A route may not be ratified on drift.
The geometry half was tested and REJECTED. Arm f (Hi3DGen conditioned on six generated views)
scored silhouette −0.89 and form_identity −1.00 across nine items — four ties, four
regressions, zero improvements — and it regressed a control. Mechanism in §11.3, corroborated
in §10.6 by an instrument with no eye in it. Multi-view is a texture win and a geometry loss, and
the two halves are not bought together.
**The headline in one line: pillar 1 is the load-bearing pillar, and pillar 2 is not the identity
fix. Multi-view geometry is a HOLD across every class** — on a single-projection bake its extra
relief is a net loss (weapons surface 3.25 → 2.25), because rugosity is exactly what an exterior
camera ring cannot see into (TripoSG median 0.88, Hi3DGen 1.47, r = −0.7303 over 31 cells). Pillars
2 and 3 cannot be bought separately.
The confound, named and answered. Arm b changed the plate *and* the surface constants together,
and identity was scored on the textured sheet — so the identity axis alone does not isolate the
plate. The clay sheet carries no material at all and still moves form_identity 2.00 → 4.00
on the same change. A shading constant cannot produce a clay-sheet delta, so the geometry changed
because the conditioning image changed. Ratified on that isolation, not on the identity axis alone.
harness/asset_factory/emit_standing_worklist.py refuses any identity-bearing row that carries no concept_ref — satisfied only by a judged pick
record on disk (<ID>__pick.json) or a declared mesh_pipeline_type = PLATE_CONDITIONED. The
refusal class is NO_CONCEPT_REF and there is deliberately no override flag; the way through
is the re-key worklist the emitter itself emits. Scope is READ from
V2_ROUTING_RATIFIED.json's per-class identity_bearing field so the table and the law cannot
drift — the first version of the law inferred scope from the table's consumer classes and so
gated minerals, a class for which no plate arm was ever run.
standing_batch.gpu_stage refuses CONCEPT_REF_MISSING foran identity-bearing item with no picked seed. Without this the emitter's mark was a promise: run
a batch without --picks and picks.get(cid) returns None, the runner falls through to a
fresh objref, and the id is lifted on an unpicked first sample — the measured 2.00 route, with no
flag needed to reach it. The refusal sits before the --resume reuse on purpose: a cached raw
GLB carries no record of whether its plate was judged.
standing_batch.texture_route() reads theratified record and refuses to bake if it is missing, and the route lands on every
generation.json. Shipped route is the v1 single projection; the reason it is not the multi-view
route is the colour source, not cost (24-view stage 2.30 s vs the v1 bake's 2.25 s).
Mutation proofs, all run: v2_ratify.py --mutate {T1,T2,T3,T4,T5,T6,T7} each exit 1 (T6/T7
added 2026-08-05b and re-proven alongside the original five);
emit_standing_worklist.py --mutate strip_route drops the weapons batch 22 → 0 while leaving the
43 non-identity props untouched; --mutate law_source exits 1 on the inert-law positive control;
gpu_stage returns CONCEPT_REF_MISSING for an identity-bearing id, stays silent for a mineral,
and returns the *different* class PICKED_REF_MISSING once a pick is supplied — proving the law
lets a compliant item through rather than refusing everything.
**The stack, per identity-bearing asset (SUPERSEDED IN PLACE 2026-08-05b — the texture stage
changed):** 4 canon-keyed plates → judged pick → TripoSG (shipped constants) → finish_mesh2
(xatlas atlas) → MV-Adapter ig2mv 6-view turnaround → mv_texture_bake merge → texel-coverage
tooth. Hi3DGen is not in batch 3, and neither is the multi-view *geometry* arm.
| Stage | Measured | n |
|---|---|---|
| Plate, per candidate | 5.44 s (5.31–5.64) | 16 |
| TripoSG geometry, end to end | 26.2 s (14.6–44.4) | 13 |
finish_mesh2 | 11.9 s (4.5–32.1) | 31 |
| MV-Adapter texture route, marginal | 26.0 s (23.97–29.15) | 9 |
| ~~v1 bake~~ *(superseded; still the fallback where no plate exists)* | 2.25 s (1.93–2.62) | 22 |
| Machine total per asset | ≈ 81 s, ≈ 103 s with one replate | |
| Peak VRAM (TripoSG) | 5.16–6.09 GiB, median 5.19 | 13 |
| Peak VRAM (MV-Adapter, RESERVED) | ≈ 29.1 GiB (29.01–29.10) | 9 |
| Peak VRAM (Hi3DGen, not shipping) | 10.6–13.8 GiB | 9 |
| Observed texel fraction (measured, not judged) | 0.61 mean (0.52–0.79) | 9 |
| Plate-generator VRAM | unmeasured, reported as null — it runs inside the shared ComfyUI process this lane submits to rather than owns, and an nvidia-smi sample on a card shared with three lanes would measure the neighbours | — |
The honest cost of the texture win: ≈ 62 s → ≈ 81 s per asset. A 2.25 s bake became a 26 s
generation, and that buys surface +1.89. It is reported rather than absorbed.
VRAM is now the scheduling constraint, and this is the operationally important line. TripoSG
peaks near 6 GiB and shares the card comfortably. The texture stage reserves ≈ 29 GiB of 32, so
it cannot run beside a second heavy lane and the driver waits for ~24 GiB free before it starts.
**Batch 3 must therefore be staged, not interleaved: run all geometry first (shareable), then the
texture pass alone.** On a box that yields the card to three other lanes, a texture pass scheduled
against a busy neighbour does not run slowly — it waits.
The 61 % coverage is a view-count limit, not a merge defect. The 24-view ablation on a
production atlas reached 98.6 % observed and 30.3 % at one view; six views land where six views
should. Raising the count is the next measurable lever, but the checkpoint is *trained* at six, so
it is an experiment with an answer rather than a free win.
The replate row is budgeted, not treated as an exception. WPN_072's defining peg appeared in
3 of 8 canon-keyed candidates, so n=4 has a real miss rate — and one-shot text conditioning had
roughly a coin-flip chance of shipping the wrong object on that item. **The judged pick is an
attended step and is not in the 62 s**, and neither is queue wait: this lane yields the card to
three others and the yield is unbounded by design.
Re-running the emitter under the law: weapons 70 → 22 kept (48 refused, all
mesh_pipeline_type = TEXT_OBJREF), slice creatures 106 → 0 kept (all refused, the column is
blank across T0_Creature_Roster), flores props 43 kept, 0 refused (correctly out of scope).
That is the law working, not the law misfiring: every refused row is an identity-bearing asset
whose registry still declares the one-shot route the benchmark measured at identity 2.00. The
emitter writes the re-key worklist into its own output (concept_first_law.rekey_worklist),
script-emitted and never hand-typed. **The re-key itself is a registry write and is deliberately
NOT taken here** — the weapon rows are owned by the in-flight remediation lane, and two lanes
writing one registry is the collision class this project retired by construction.
Scope honesty on the extension. Only the weapon class was measured against a one-shot arm.
Creature and character rows are gated by *extension* — the mechanism (thin text-to-image coverage
for a specific named form) is identical for a Khmer Naga, and the charter's pillar 1 is a law
rather than a weapons-only fix. Real fauna is gated deliberately over-wide: an orangutan has
broad coverage and scored identity 5, but the emitter's class token does not separate fauna from a
named folklore being, and of the two available errors — a 5.4 s plate that was not needed, or a
wrong-identity asset landed — the cheap one is chosen on purpose. Minerals and flora are **out of
scope**, because no plate arm was run for the prop class and a law with no measurement behind it is
not a law.
1. finish_mesh2 has no sign-of-volume or winding test. MN_0002__a declares
watertight: true, manifold: true in its own finish record while census_final.volume is
−1.367 and census_final.winding_consistent is false — an inside-out solid that every
downstream numeric tooth reads as healthy. Re-derived here with trimesh, outside the benchmark's
own tooling. Boarded, not fixed in this pass.
2. TripoSG's peak VRAM was measured 13 times and never reached the record. Every cell's
.gen.json carries peak_vram_gb; §9's cost block reported only Hi3DGen. §2's ~3 GB [E] is
superseded above by the measured 5.16–6.09 GiB band.
Record: build/3d/v2_mva/RATIFY_COLD_VERIFY_MVA.json. This pass re-derived the MV-Adapter result
off the shipped artifacts in a fresh context, with instruments the benchmark did not use, before
any of it became law.
Confirmed. The blind keys re-derive exactly from their seeds over the sorted cell list —
40/40 clay and 31/31 tex — which rules out a key rewritten after scoring to flatter a winner. The
headline deltas recompute exactly from the raw scores joined to the keys. Four arm-e assets
re-rendered from their shipped GLBs at azimuth 45/135/225/315 and elevation 0.75, plus two low
raking views at 0.10 — no angle shared with the benchmark's 0/90/180/270 at 0.42 — and CR_0026's
white shell is genuinely gone, MN_0001 the control shows no regression, WPN_045's surface 1 → 5 is
real.
Arm f's rejection is corroborated by a second, eyeless instrument. PCA principal extents on the
shipped meshes: WPN_045 arm f is a flat pancake (flatness 8.88, length/width 0.90) where arm c
is two slender batons (1.38, 3.38); WPN_072 arm f collapses to length/width 1.11 from arm
c's 8.39. The blind judge and the ruler agree.
THE FINDING THAT BOUNDS THIS TABLE — the flat-profile weapon class fails on every arm. The
macuahuitl question was put as a binary and the answer is *yes on the material, no on the
morphology*. WPN_036 ships as a hardwood body carrying a continuous row of black stone blades —
correct — but photographed down its own long axis it is a circle, and its blades sit in a
single median channel rather than along both long edges. The canon row says "a flat broad
paddle of hardwood … both long edges channelled". Measured flatness (width/thickness):
| Weapon | Canon cross-section | a | b | c/d | f |
|---|---|---|---|---|---|
| WPN_036 macuahuitl | flat (real ≈ 3–4) | 0.99 | 1.79 | 1.02 | 1.61 |
| WPN_072 woomera | flat (real ≈ 5–7) | 1.17 | 1.81 | 0.86 | 1.35 |
| WPN_042 madu | planar | 2.66 | 8.51 | 6.98 ✔ | 2.34 |
| WPN_045 nunchuks | round | 1.01 | 3.11 | 1.38 ✔ | 8.88 |
The madu reaches 6.98 correctly and the nunchuks sits at 1.38 correctly, so the instrument is not
calling everything round — the defect is specific. No arm in the programme fixes it. The blind
identity axis could not price it in because a textured rod shot at 23° from four cardinal azimuths
reads as a paddle.
This does not touch the texture ratification — arm e's geometry *is* arm c's, byte-identical,
so the defect sits on both sides of every texture delta and cancels. What it does is **bound the
claim**: conditioning fixed *which object* gets made; it did not fix cross-section fidelity to the
canon row. The table may not claim weapon identity is solved. Boarded, not fixed here.
A methodology defect, for the next benchmark. The blind sheet is arm-blind *and* target-blind,
so the judge must infer which roster item each cell was meant to be before scoring "is this the
NAMED object". Cell T17 (WPN_072 woomera, arm b) was read as "a broad wooden leaf-blade … the
defining inset teeth are absent" — inset teeth are the *macuahuitl's* criterion, so a woomera was
scored against the wrong canon row. Arm b's two objects are near-identical in proportion (1.79/4.50
against 1.81/4.85), which is how they became confusable. Impact on the conclusions: none — T17
is an arm-b cell, enters no headline, and correcting it would raise arm b rather than lower it. The
fix is to show the judge the canon row id and its morphology text per cell: that keeps the A/B
blind, which is the only blinding the comparison needs, while making the identity axis well-defined
instead of a guess.
One scoring disagreement, recorded because it moves the treatment arm DOWN. The cold verify
re-scored WPN_042's arm b at identity 4, not the recorded 5: the canon row's most specific
clause — two horns pointing in OPPOSITE directions — is not realised, the mesh puts both horns
in a V. Cold arm-b mean 4.00 against the recorded 4.25. Either figure clears the ratification
threshold by a wide margin and the winner is unchanged.
---
**This section closes §8.4 item 3 and supersedes §9's "only one multi-view model could be
benchmarked".** Full records: build/3d/v2_mva/INSTALL_RECORD.json (install, licence route,
throwaway) and build/3d/v2_mva/V2_MVA_RESULT.json (the two arms, blind-scored). Tools:
harness/asset_factory/mva_views.py, run_hi3dgen_mv.py, bl_apply_bc.py, v2_mva_arms.py,
v2_mva_blind.py, v2_mva_unblind.py, plus `bl_mv_stage.py --cameras --render-pos-normal
--upright --mva-normalize`.
Josh ruled 2026-08-05, verbatim: *"This doesnt matter to me.. why would it? If you need the
license that permits commercial use than get that one?"* The bar is COMMERCIAL USE PERMITTED.
The V2 charter's "MIT/permissive weights only" was a self-inflicted tightening of the standing
face-value licence law and is STRUCK. Every body below was fetched over the wire this pass and
is pinned by sha256, because a model-card summary is not a licence.
| Artifact | Licence | Class | Row |
|---|---|---|---|
| MV-Adapter (adapter code + weights) | Apache-2.0, body c71d239d… | shippable | DL_0026 (superseded in place from LICENCE-READ-ONLY) |
stabilityai/stable-diffusion-xl-base-1.0 @ 462165984030 | CreativeML Open RAIL++-M, body 19b6998b… | shippable | DL_0027 |
huanngzh/mv-adapter @ 6de4033df6b5 (both checkpoints) | Apache-2.0 | shippable | DL_0028 |
| nvdiffrast | Nvidia Source Code License (1-Way Commercial), body ce20436c… | EXCLUDED | DL_0029 |
madebyollin/sdxl-vae-fp16-fix @ 207b116dae70 | MIT | shippable | DL_0030 (upstream card: docs/licence_records/sdxl_base/sdxl_vae_fp16_fix_README.md) |
The OpenRAIL++-M body, read at face value: §III says the licensor "claims no rights in the Output
You generate", there is no non-commercial clause anywhere in it, and Attachment A restricts uses
this game does not make. It clears the bar. **nvdiffrast — pinned in MV-Adapter's own
requirements.txt — does not:** its body grants use only where the work "may be used or intended
for use non-commercially", defined as "research or evaluation purposes only". That is the same
taint §1 already banned TRELLIS.2's textured path for, and the ruling excludes it.
Excluding it costs nothing, and the diagnosis is precise. mvadapter.pipelines.* — the actual
diffusion pipelines — import CLEAN on a box with no nvdiffrast. What is tainted is the whole
mvadapter.utils package, because its __init__ imports mesh_utils.camera,
mesh_utils/__init__.py imports .mesh and .render unconditionally, and even
mesh_utils/utils.py — whose job is to hold the type alias LIST_TYPE — imports nvdiffrast at
module level. **Same failure class as Direct3D-S2's flash-attn blocker in §0: an unconditional
module-level import in a package __init__ makes an optional dependency mandatory for code that
never calls it.** mva_views.py therefore loads the two pure-math helpers from their own files by
path under a synthetic package supplying only LIST_TYPE — MV-Adapter's own Apache-2.0 code,
unmodified, nothing vendored, no stub impersonating nvdiffrast — and **refuses to run if
nvdiffrast ever appears in sys.modules** (mutation-proven both directions: clean interpreter
passes, an injected ModuleType makes it fire). The rasterization it would have done is done by
Blender headless, whose output is unencumbered.
Own venv /root/mv3d/venv_mva on the same sm_120-proven pin as Hi3DGen (torch 2.9.0+cu128,
diffusers 0.31.0) — deliberately, because an independent venv on a different torch would make every
cross-arm timing and VRAM comparison meaningless. The proven Hi3DGen venv was re-checked after the
install and is unchanged. Throwaway (i2mv on the MIT example plate, not a benchmark item):
46.3 s end to end, peak 21.0 GiB allocated / 29.6 GiB reserved by the allocator's own running
maximum, six views consistent in palette, horns, crest and base, **view 3 a real modelled BACK
rather than a smear of the front**. build/3d/v2_mva/THROWAWAY_i2mv_GRID.png.
29.6 GiB reserved on a 32 GiB card is the scheduling fact: this model needs most of the card
and cannot run beside a second heavy lane.
Two arms, each a clean single-variable pair against a cell that already existed, re-scored blind
alongside all 53 pre-existing cells in one session under a fresh shuffle (seeds 20260812/13 — not
the original, whose key is on disk).
| Arm | Against | What differs | silhouette | form_id | identity | surface | Verdict |
|---|---|---|---|---|---|---|---|
e — MV-Adapter ig2mv views merged by mv_texture_bake | arm c | the texture route ONLY (mesh byte-identical) | — | — | +0.44 | +1.89 | ARM E WINS |
f — Hi3DGen run_multi_image on i2mv views | arm c | the conditioning ONLY (1 plate vs 6 views) | −0.89 | −1.00 | — | — | ARM C WINS |
Arm e wins or ties every class and regresses nowhere. Per class: weapons +0.25/+2.25, fauna
+1/+3, naga 0/+2, controls +1/+1.5, child tie. The cleanest single result is CR_0026 — the
white-shell fauna item §9 named as the best evidence for pillar 3 and against expecting pillar 2 to
fix it: surface 2 → 5, identity 4 → 5. The white shell was a texture-route defect and the
texture route fixed it. Biggest mover overall is WPN_045, surface 1 → 5 ("a thin wood stripe on
one side and white everywhere else" → "full wood grain on all four views, dark end caps, the cord
itself textured"). Contamination sensitivity: the executor had seen a labelled a/c/e comparison of
WPN_036; dropping that item entirely leaves the verdict unchanged and the win slightly larger
(+0.5/+2.0).
Arm f loses, and the mechanism is diagnosed, not guessed.
build/3d/v2_mva/ARM_F_MECHANISM_i2mv_views.png shows WPN_045's six i2mv views (which collapsed to
silhouette 1 / form-identity 1 — read blind as "a scatter of loose disconnected sticks and a
separate blob; no single object at all", against arm c's 5/5) beside WPN_042's (which tied at
5/5). WPN_045's views are not a turnaround of one object: different arrangements, different
counts, different scales, lying on a surface with cast shadows, one view a spoon entirely.
WPN_042's are a clean consistent turnaround. **The rule: i2mv INHERITS THE PLATE'S STAGING. A plate
that stages the object in a scene produces views of a scene, and a scene cannot be reconstructed as
one object.** run_multi_image in stochastic mode hands each denoising step a different view, so
inconsistent views are conflicting evidence and the sampler resolves them into fused sheets or
loose fragments. Every bulky, single-piece, already-upright subject ties.
What is NOT claimed: that multi-view geometry conditioning is worthless. What is measured is
that *unregistered i2mv views feeding a stochastic multi-image sampler* are worse than one good
plate on thin multi-part subjects and neutral elsewhere. A geometry model that consumes views
with their cameras — which run_multi_image does not — is a different experiment.
four of six MV-Adapter views were a featureless dot or a hairline strip — most of the
conditioning budget spent on nothing. bl_mv_stage.py --upright RATIO stands the longest axis up.
The gate is a ratio, not a class list: at 1.6 it fires on exactly the three long thin weapons
(8.85 / 3.38 / 6.57) and leaves the naga and child (already Z-longest) and the
near-equidimensional minerals and orangutan (1.01–1.07) authored. **Found by LOOKING at control
images, not by a failing assert.**
views_used 0, observed_fraction 0.0, 673,692 texels hole-filled — and still wrote an atlas
and a GLB that looked fine at a glance. Cause: mva_views.py runs in WSL and records /mnt/d
paths the Windows merge cannot stat, so all six views were correctly refused as "no colour
image". It was caught only because mv_texture_bake.py REPORTS holes instead of hiding them.
Fixed by path translation plus a refusal: a cell whose bake observed nothing is marked FAILED and
never reaches a blind sheet.
1. The 60% coverage ceiling is a VIEW-COUNT limit, not a merge defect. Observed texel fraction
runs 0.516–0.794, mean 0.613, against the ablation's 30.3% at one view and 98.6% at 24 on a
production xatlas atlas. Six views land where six views should. MV-Adapter's adapter is
initialised with num_views, so more are constructible — but the checkpoint is **trained at
six**, so raising it is an experiment with a measurable answer, not a free win.
2. Judge noise is now quantified and it is not small. Re-scoring the 53 pre-existing cells under
an independent shuffle reproduced 74 axis-scores exactly and changed 32, mean |change| 1.125.
A one-point difference on a single item is inside the noise floor — which is why every result
above is a class mean over multiple items, and why the two headline effects (+1.89 surface,
−1.00 form identity) are reported as real: they are larger than the drift. The earlier session's
headline survives the re-score.
3. The staging finding generalises past this arm. A concept plate that stages its subject in a
SCENE rather than isolated degrades every downstream generator conditioned on it. That is the
objref doctrine with a second independent piece of evidence.
RULED by the director on the CHAR_0001 arrival-outfit rebuild, as a CLASS rule and not an exception.
Anything whose binding constraint is FIT AGAINST A SPECIFIC SOLVED BODY -- garments, armour worn over
a body, gloves, boots -- routes to FIT-FIRST AUTHORED GEOMETRY, with the picked concept plate
conditioning the REGISTER, the material and the ornament rather than the silhouette. The silhouette
belongs to the body.
The reason is evidential rather than architectural: image-to-3D produces a garment on its own body at
its own proportions, and no amount of conditioning makes that garment fit a mesh it has never seen.
Identity, which is what the concept-first law was ratified to protect, is not this class's hard
problem -- a fitted top and fitted trousers are trivially pinned by text.
Measured on the first asset through it, against the body the game boots, on the AUTO rule's own
instrument (char_cloth_thickness.signed_thickness):
| penetrating | worst | p01 | |
|---|---|---|---|
| face-extraction (the rejected layer) | 11.996 % | -5.876 mm | -3.460 mm |
| fit-first authored | 2.230 % | -7.822 mm | -1.544 mm |
5.4x fewer penetrating vertices and a p01 tail less than half as deep; the deeper single worst case
is a 0.017 % residue (23 vertices) at the collar.
THE FREE-STANDING IDENTITY CLASSES ARE UNTOUCHED -- weapons, props, creatures and minerals keep
TripoSG. Scope follows the evidence, exactly as the original ratification did. The machine-readable
row is routing.fitted_garment in build/3d/V2_ROUTING_RATIFIED.json.