Procedura OBJ Exports: Keep Editable Props at the Right Scale

A source audit and reproducible five-box fixture explain Procedura’s OBJ normalization, per-variant scale recovery, and the pivot data to preserve.

colinkoko6 min read

Key takeaways

  • Procedura offers editable procedural assemblies; preserving their intended dimensions requires an explicit export contract.
  • The inspected successful normalized final.obj path centers the mesh and makes its longest side 2 coordinate units, not 1.
  • Save each variant’s pre-normalization bounds, declared units, and intended pivot. A single fixed import scale cannot recover all size edits.

Procedura makes an appealing promise for prop variants: edit a procedural assembly instead of rebuilding a shape. But a dimension edit in the source is only half the handoff. At the revision inspected here, when final.obj normalization succeeds, publication centers the mesh and makes its longest bounding-box side 2 coordinate units. Preserve the original bounds and your intended units before treating that file as a correctly sized game asset.[2][3][4]

Lavender sci-fi crates of different proportions beside cyan bounding cubes and an arrow indicating a conversion.
Concept illustration of the scale-handoff problem. These crates are not Procedura outputs, measured geometry, or a software screenshot; use the numeric fixture below for exact results.

Why editable source is worth inspecting

The August 26, 2026 paper describes a 3D modeling agent that builds named parts into a parameterized assembly. That suggests a useful workflow for hard-surface variants: keep a source-level dimension or component editable, then recompile when the brief changes. The paper’s quality results are not reproduced in this article.[1]

As checked on October 4, public code is available under MIT. Running its generation pipeline still requires configured model access and dependencies; code availability is not a promise of free hosted generation. The software license is also not blanket permission for every third-party input or service a project might use.[2][5]

For a game team, the useful evaluation question is narrow: can an approved edit survive export with the intended dimensions and attachment point intact? A source file and a triangulated delivery file serve different jobs. Keep both, and make the relationship explicit.

The inspected export contract is two units

This audit pins commit fac191ed49f55fcc2e0f23897e986042249f59fe. The normalization routine subtracts the bounding-box midpoint and multiplies every coordinate by 2 divided by the longest side. The final OBJ call site uses that publisher. The README’s shorthand about a unit bounding box should therefore not be read as a one-unit-long result.[3][4][2]

L = largest pre-normalization bounding-box side
c = pre-normalization bounding-box midpoint
q = (p - c) * 2 / L
The successful path’s coordinate equation. p is a source vertex; q is its normalized coordinate. This is an explanation, not a pipeline invocation.

There are important exceptions. A normalization failure can fall back to raw OBJ geometry, so inspect the log and actual bounds. Requesting the optional STL does not avoid successful normalization: it copies the transformed geometry. Motion exports follow separate code paths: USD authors a metersPerUnit value, while the URDF mesh writer applies a units conversion.[3][6][7]

What five synthetic boxes reveal

We wrote a small, dependency-free Python fixture using eight corner vertices per box. It applies the inspected equation, writes and reparses OBJ-style vertex lines, measures the resulting bounds, and checks the inverse transform. We did not run Procedura, compile a generated assembly, or import a mesh into an engine. This is a reproducible arithmetic demonstration of the source contract.

For this example only, we define source dimensions in millimeters. The baseline is 100 × 50 × 25. A second variant changes only the first dimension to 200; a third doubles every dimension. These are author-defined test inputs, not a claim that Procedura enforces millimeters.

Fixture input (mm)Normalized boundsAt shared scale 0.05 (m)Intended bounds (m)
100 × 50 × 252 × 1 × 0.50.1 × 0.05 × 0.0250.1 × 0.05 × 0.025
200 × 50 × 252 × 0.5 × 0.250.1 × 0.025 × 0.01250.2 × 0.05 × 0.025
200 × 100 × 502 × 1 × 0.50.1 × 0.05 × 0.0250.2 × 0.1 × 0.05
Measured fixture bounds, in X × Y × Z order. The shared 0.05 scale is deliberately wrong for the variants.

The length-only edit changes the normalization multiplier for the whole box. If an importer keeps the baseline multiplier, the intended doubled length disappears and the unchanged width and height become half-sized. Doubling all dimensions is even less visible: the normalized box is identical to the baseline. Normalized shape alone cannot identify the original size.

The downloadable results also include a translated baseline and a box whose Y axis is longest. All five cases passed dimension and source-position recovery checks; three invalid fixture inputs were rejected by our script. A second run produced byte-identical JSON. Those checks establish our calculation’s behavior, not Procedura’s robustness on arbitrary meshes.

python scale-fixture.py > scale-results.json
Download the original script from the evidence links below. It uses only Python’s standard library and makes no network or generation calls.

Recover size per variant, then choose the pivot

Let u be your declared meters per source unit. Multiply normalized coordinates by L × u / 2 to recover the centered dimensions. In our millimeter fixture, u is 0.001. The baseline needs 0.05 meters per normalized unit; the length-only variant needs 0.1. Record L separately for each variant rather than copying yesterday’s import setting.

centered position in meters = q * (L * u / 2)
original-source position in meters = q * (L * u / 2) + c * u
Inverse calculation for the successful normalized path. Restore the midpoint only when you want the original source-origin placement; an intended game pivot can be different.

The translated fixture starts at [30, -10, 7] and still normalizes to the same centered shape. Restoring dimensions alone does not restore its original placement. For a crate, you may instead want the bottom center at the origin; for a hinged prop, you may want the hinge there. Record that decision rather than assuming a bounding-box center is the correct gameplay pivot.

A practical sidecar can store the source revision, variant parameters, pre-normalization bounds, declared unit conversion, intended pivot, and chosen axis convention. This is our handoff recommendation, not a claim that Procedura already emits such a game-ready manifest. Measure the compiled bounds rather than assuming a parameter equals the outer dimension; an added handle or foot can change the longest side.

Check the destination after every transform

If your delivery step converts OBJ to glTF, Khronos defines linear distances in meters. More precisely, mesh positions are meter-valued after applying the node’s global transform. A valid conversion can therefore put the correction in vertex data or in the appropriate transform; do not inspect only raw POSITION values and ignore the node hierarchy.[8]

Before accepting a variant, check three dimensions in the final scene, confirm the intended pivot against an attachment or ground plane, and inspect any parent transforms. Apply the unit correction once. If geometry was already converted, applying the same factor again creates another scale error. Axis conversion is a separate check from dimensions.

Use a deliberately asymmetric prop or test box: equal-sided cubes hide axis swaps, and similarly framed thumbnails hide absolute size. Keep a small known-size reference in the review scene. These are proposed acceptance checks, not tests completed in a particular editor.

What this audit does not establish

This article does not rate generated geometry, demonstrate a working game importer, or verify materials, UVs, collision, articulation, LODs, or runtime performance. It examines a pinned geometry-export path and an original synthetic fixture. Future revisions may change that path; recheck the code and a real exported file before adopting the calculation.

The broader lesson is useful beyond this project: editability is most valuable when the downstream asset keeps the meaning of the edit. Preserve a dimensional reference and an intentional pivot alongside the procedural source, then judge the final delivery file against that contract.

Evidence used

  • Original five-box scale and position results

    Deterministic synthetic vertex calculations with five box cases, normalized bounds, mistaken shared scale, per-variant recovery, hashes, and explicit method limits.

  • Reproduce the normalization fixture

    Original Python standard-library script; independently implements the inspected equation without downloading or executing Procedura. Includes vertex-text round trips and assertions.

Sources

  1. 1.Procedura paper and submission history — Procedura authors. Accessed 2026-10-04.
  2. 2.Procedura: editable assemblies and setup requirements — Procedura authors. Accessed 2026-10-04.
  3. 3.Procedura: normalized mesh publishing and raw fallback — Procedura authors. Accessed 2026-10-04.
  4. 4.Procedura: final OBJ publication call site — Procedura authors. Accessed 2026-10-04.
  5. 5.Procedura software license — Procedura authors. Accessed 2026-10-04.
  6. 6.Procedura: motion USD units — Procedura authors. Accessed 2026-10-04.
  7. 7.Procedura: motion URDF mesh conversion — Procedura authors. Accessed 2026-10-04.
  8. 8.glTF 2.0: units and mesh instantiation — Khronos Group. Accessed 2026-10-04.

Give your next prop a measurable brief

Explore Goblin3D, then keep intended dimensions and inspection criteria with your asset references.

Explore Goblin3D

Sources, product facts, and original evidence were checked before publication.

Share your feedback

Sign in to share feedback with the Goblin3D team.

Sign in

support@goblin3d.ai