Procedura: Agentic 3D Modeling with Procedural Control

Procedura

Agentic 3D Modeling with Procedural Control

Youtian Lin1, Yikang Yang1, Zhanpeng Hu1, Mengqi Zhou1, Feihu Zhang2, Xun Cao1,
Jiaheng Liu1,†, Yao Yao1,† 1Nanjing University 2Envision Corresponding authors

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.

Procedura pipeline overview: planning, procedural modeling, refine, then materials and articulation
Planning authors parts and mates, procedural modeling builds the program one part at a time against a dynamic context, and a refine loop art‑directs the result. Compilation yields the mesh, the native part decomposition, and the painted, articulated asset.

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.

Motorcycle build after part 1: trellis_frame
+ trellis_frame · 1/19
Motorcycle build after part 2: v_twin_engine
+ v_twin_engine · 2/19
Motorcycle build after part 3: subframe
+ subframe · 3/19
Motorcycle build after part 4: fuel_tank
+ fuel_tank · 4/19
Motorcycle build after part 5: seat
+ seat · 5/19
Motorcycle build after part 6: radiator
+ radiator · 6/19
Motorcycle build after part 7: exhaust_system
+ exhaust_system · 7/19
Motorcycle build after part 8: rear_swingarm
+ rear_swingarm · 8/19
Motorcycle build after part 9: rear_shock
+ rear_shock · 9/19
Motorcycle build after part 10: rear_wheel
+ rear_wheel · 10/19
Motorcycle build after part 11: rear_brake
+ rear_brake · 11/19
Motorcycle build after part 12: drive_chain
+ drive_chain · 12/19
Motorcycle build after part 13: steering_assembly
+ steering_assembly · 13/19
Motorcycle build after part 14: front_fork
+ front_fork · 14/19
Motorcycle build after part 15: handlebars
+ handlebars · 15/19
Motorcycle build after part 16: headlight
+ headlight · 16/19
Motorcycle build after part 17: front_fender
+ front_fender · 17/19
Motorcycle build after part 18: front_wheel
+ front_wheel · 18/19
Motorcycle build after part 19: front_brakes
+ front_brakes · 19/19
assembly.scad
// 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);
    ...
plan.json
{ "name": "front_bumper_lightbar",
  "assembly": { "partner": "lower_chassis_frame",
                "mate": "bolt_pattern",
                "fit": "clearance", "count": 4 } }

The verification stack

Compile gate

The spliced program must compile. On failure the compiler's error text goes back into the regeneration prompt.

Mate gate

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.

Connectivity gate

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

Painted motorcycle rolling chassis, Blender Cycles PBR render Reference image of the motorcycle
The painted motorcycle beside its reference. Every material stays a per‑part attribute of the program, exported as multi‑group OBJ + MTL.

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.

3.1×

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.

0.590

P3D‑Bench assembly composite, first place across every published entry and both of our base models prompted directly.

MechBench‑36

MethodGeoAesSemOverallCLIPShrp60Dih95
Procedura (Gemini 3.7 Flash)0.7990.8270.8580.82882.67158.0897.97
Procedura (GPT‑5.6‑sol)0.7560.7930.8430.79781.76185.18105.28
Agentic 3D‑code
Adam CAD (GPT‑5.6‑sol)0.7780.7590.8610.79981.2657.3590.41
ArtiCraft (GPT‑5.6‑sol)0.6820.5960.8270.70279.1241.3194.74
CAD‑Coder0.0990.2100.0960.13560.627.6392.91
cadrille0.0860.1050.0830.09250.6519.5897.18
Single‑shot LLM
GPT‑5.6‑sol0.7900.7350.8520.79280.9160.3989.76
Gemini 3.7 Flash0.7190.7220.8490.76380.8663.5491.35
Gemini 3.1 Pro0.6980.6730.8240.73179.94
Native 3D generation
TRELLIS.20.7810.7870.8610.81077.51134.0273.11
UltraShape0.7310.6940.8180.74875.048.5833.06
Hunyuan3D0.6850.5900.8150.69774.626.4135.12
Direct3D‑S20.5830.5590.7750.63974.9035.1752.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

MethodSemanticGeometryAestheticsOverall
Procedura (Gemini 3.7 Flash)0.6660.4900.6160.590 ±0.009
Procedura (GPT‑5.6‑sol)0.6610.4600.6040.575 ±0.010
Gemini 3.7 Flash0.6620.4580.5770.566 ±0.016
GPT‑5.6‑sol0.6620.4440.5840.563 ±0.015
GPT‑5.50.6430.3920.5370.524 ±0.012
Gemini 3.1 Pro0.6390.3900.5080.513 ±0.012
Opus 4.60.5720.3270.4460.448 ±0.016
Kimi K2.60.5230.2940.3720.396 ±0.014
Qwen 3.60.4510.2640.3220.346 ±0.014
Doubao0.4080.2530.3020.321 ±0.014
GLM‑5V Turbo0.4020.2490.3000.317 ±0.013
MiMo Omni0.3790.2420.2940.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.

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},
}