Procedura: Agentic 3D Modeling with Procedural Control
Agentic 3D Modeling with Procedural Control
Not a mesh.
A program.
Procedura writes the object as a procedural assembly: a parametric program whose parts are named, editable modules. The motorcycle on screen is 19 of them.
- trellis_frame
- v_twin_engine
- fuel_tank
- exhaust_system
- rear_swingarm
- front_fork
- handlebars
- headlight
- + 11 more modules
Joined by mate.
Placed by solving.
Every joint is a typed, machine‑checkable mate, both halves cut from one shared nominal. The assembly transform is solved from the mated frames, never guessed from a render.
Tj = Fi · Δ(φ) · Fj−1
From one prompt to a production asset
A single frozen LLM plans the object as an assembly graph, writes the program part by part, and refines it under a decoupled vision critic. The same graph then carries per‑part materials and a simulator‑validated articulation. No 3D training anywhere.
The program grows one part at a time
Each generation call sees the reference, the full compiling program, and a render of the build so far, and emits exactly one new module. A deterministic splice inserts it; three gates decide whether it stays. The strip replays the motorcycle on this page, one committed part per frame, in its actual build order.



















// MALE peg, base at origin, growing +Z, chamfered self-centring tip.
// Pair with asm_socket(d, ...) cut from the SAME d.
module asm_peg(d, len) {
lead = asm_lead(d);
union() {
cylinder(h = max(0.01, len - lead), d = d);
translate([0, 0, len - lead])
cylinder(h = lead, d1 = d, d2 = max(0.2, d - 2 * lead));
}
}
// FEMALE socket NEGATIVE tool. Bore = d + fit; mouth flares for lead-in.
module asm_socket(d, depth, cls = "location") {
bore = d + 2 * asm_fit(cls);
...
{ "name": "front_bumper_lightbar",
"assembly": { "partner": "lower_chassis_frame",
"mate": "bolt_pattern",
"fit": "clearance", "count": 4 } }
The verification stack
The spliced program must compile. On failure the compiler's error text goes back into the regeneration prompt.
Each mate is measured on the compiled mesh: registration area must reach τa d², penetration must stay inside the fit tolerance. Hovering or gouging parts are rejected with the measured values.
A span‑based floater check rejects a part only if it increases the count of visibly detached components. What ships is the mesh the gate graded.
Paint from the reference, part by part
Four one‑shot vision calls: extract a compact PBR material library from the reference, assign one entry per named part, let a decoupled material critic correct the assignment, then split modules into sibling colour blocks where one material is too coarse. Every rewrite is recompiled and rejected if the triangle count moves, so colour can never alter the shape.
The motorcycle's extracted library
Joints that survive a physics simulator
The mate network is the kinematic graph: static mates group parts into rigid links, kinematic mates become joints with measured axes and anchors. The plan is exported as OpenUSD and URDF, then driven through a fixed battery in Isaac Sim; a failed verdict feeds rendered simulation frames back for one refine.
Every clip above is a real headless Isaac Sim capture in per‑link colours: each case's exported USD is loaded fresh and every driven joint sweeps at once along phase‑offset sinusoids, so the whole mechanism is alive in a single take.
State of the art on both benchmarks
Every method is re‑rendered from one shared rig and scored by the same blinded judge, so the only difference between two rows is the method.
sharper edge structure than the next code‑emitting method on MechBench‑36 (Shrp 185.2 vs 60.4), with the top judge composite at 0.828.
P3D‑Bench assembly composite, first place across every published entry and both of our base models prompted directly.
MechBench‑36
| Method | Geo | Aes | Sem | Overall | CLIP | Shrp60 | Dih95 |
|---|---|---|---|---|---|---|---|
| Procedura (Gemini 3.7 Flash) | 0.799 | 0.827 | 0.858 | 0.828 | 82.67 | 158.08 | 97.97 |
| Procedura (GPT‑5.6‑sol) | 0.756 | 0.793 | 0.843 | 0.797 | 81.76 | 185.18 | 105.28 |
| Agentic 3D‑code | |||||||
| Adam CAD (GPT‑5.6‑sol) | 0.778 | 0.759 | 0.861 | 0.799 | 81.26 | 57.35 | 90.41 |
| ArtiCraft (GPT‑5.6‑sol) | 0.682 | 0.596 | 0.827 | 0.702 | 79.12 | 41.31 | 94.74 |
| CAD‑Coder | 0.099 | 0.210 | 0.096 | 0.135 | 60.62 | 7.63 | 92.91 |
| cadrille | 0.086 | 0.105 | 0.083 | 0.092 | 50.65 | 19.58 | 97.18 |
| Single‑shot LLM | |||||||
| GPT‑5.6‑sol | 0.790 | 0.735 | 0.852 | 0.792 | 80.91 | 60.39 | 89.76 |
| Gemini 3.7 Flash | 0.719 | 0.722 | 0.849 | 0.763 | 80.86 | 63.54 | 91.35 |
| Gemini 3.1 Pro | 0.698 | 0.673 | 0.824 | 0.731 | 79.94 | ||
| Native 3D generation | |||||||
| TRELLIS.2 | 0.781 | 0.787 | 0.861 | 0.810 | 77.51 | 134.02 | 73.11 |
| UltraShape | 0.731 | 0.694 | 0.818 | 0.748 | 75.04 | 8.58 | 33.06 |
| Hunyuan3D | 0.685 | 0.590 | 0.815 | 0.697 | 74.62 | 6.41 | 35.12 |
| Direct3D‑S2 | 0.583 | 0.559 | 0.775 | 0.639 | 74.90 | 35.17 | 52.78 |
Geo, Aes, and Sem are the judge's geometry, aesthetics, and semantic axes, mapped per case to 0‑1; Overall is their mean, the judge composite. CLIP is prompt‑to‑render alignment on the shared renders. Shrp60 and Dih95 measure edge structure on the shipped mesh: total sharp‑edge length above a 60° dihedral, and the 95th‑percentile dihedral. A case with no shipped mesh scores zero and stays in the mean, which is why CAD‑Coder and cadrille sit low. Native generators reproduce the silhouette, but bolt heads, vents, and panel lines fuse into the surface; compiled CSG keeps them sharp.
P3D‑Bench assembly
| Method | Semantic | Geometry | Aesthetics | Overall |
|---|---|---|---|---|
| Procedura (Gemini 3.7 Flash) | 0.666 | 0.490 | 0.616 | 0.590 ±0.009 |
| Procedura (GPT‑5.6‑sol) | 0.661 | 0.460 | 0.604 | 0.575 ±0.010 |
| Gemini 3.7 Flash | 0.662 | 0.458 | 0.577 | 0.566 ±0.016 |
| GPT‑5.6‑sol | 0.662 | 0.444 | 0.584 | 0.563 ±0.015 |
| GPT‑5.5 | 0.643 | 0.392 | 0.537 | 0.524 ±0.012 |
| Gemini 3.1 Pro | 0.639 | 0.390 | 0.508 | 0.513 ±0.012 |
| Opus 4.6 | 0.572 | 0.327 | 0.446 | 0.448 ±0.016 |
| Kimi K2.6 | 0.523 | 0.294 | 0.372 | 0.396 ±0.014 |
| Qwen 3.6 | 0.451 | 0.264 | 0.322 | 0.346 ±0.014 |
| Doubao | 0.408 | 0.253 | 0.302 | 0.321 ±0.014 |
| GLM‑5V Turbo | 0.402 | 0.249 | 0.300 | 0.317 ±0.013 |
| MiMo Omni | 0.379 | 0.242 | 0.294 | 0.305 ±0.012 |
Scores are the benchmark's judge axes normalized to 0‑1; Overall is their mean, the benchmark's composite, with 95% intervals. Baselines are the benchmark's entries re‑run under the same harness, plus single‑shot prompting of the two base models Procedura uses.
30 objects, straight from the pipeline
Ambient‑occlusion renders from the benchmark run, untouched. Hover a card to paint it with its Materials‑stage output; the inset shows the reference view the agent worked from.
Citation
@article{lin2026procedura,
title = {Agentic 3D Modeling with Procedural Control},
author = {Lin, Youtian and Yang, Yikang and Hu, Zhanpeng and
Zhou, Mengqi and Zhang, Feihu and Cao, Xun and
Liu, Jiaheng and Yao, Yao},
journal = {arXiv preprint},
year = {2026},
}