DEV Community

Cover image for CityEngine CGA to Unreal: The Files GenAnywhere Never Hands You
GenAnywhere
GenAnywhere

Posted on

CityEngine CGA to Unreal: The Files GenAnywhere Never Hands You

The district was 4,100 buildings on the east side of Tempe, Arizona. The deliverables were a walkable Unreal scene for a public meeting and a rule set the planning office could keep using after we left. I had ten days, one shapefile from the county, and a laptop that hated the shapefile. Here is the pipeline, the numbers I measured, and the exact point where a generated scene stops being a pipeline node.

Two Kinds of Output, One of Them Is a Dead End

CityEngine hands you geometry, attributes, and the rule that made both. GenAnywhere hands you a finished scene. For a mood board, the second one wins on time. For a project that will be revised four times, the second one is a dead end, because there is nothing inside it that you can change by name.

The Rule File

The rule was 180 lines of CGA, which is not a lot once you get the block structure. Lot becomes Building, Building splits into a ground floor and an upper stack, the upper stack repeats by floor height, the facade subdivides into bays and windows, the roof gets either a parapet or a mechanical screen. The attributes that mattered: floor height 3.2, ground floor 4.2, maximum floors by zone, lot coverage, facade variant, and a binary for rooftop units.

Every attribute has a unit and a range. heightMax is meters, not stories. Stories are derived. That one decision saved us from the classic mistake of a zoning table in feet feeding a model in meters.

Python for the Parts You Repeat

The county has 2,300 parcels in the boundary, grouped into six export tiles. Clicking through that by hand is how you introduce errors, so a small Python script does the assignment: read the parcel table, map zoning to attribute sets, apply the rule, export per tile, and write a log line with the triangle count and the building count. The whole batch runs in six minutes on the office machine, which is fast enough to run twice a day without thinking about it.

Export Settings That Actually Matter

glTF 2.0 out, meters, shared origin at the county grid intersection. Everything in one coordinate frame was the single biggest time saver later, because six tiles imported into Unreal land exactly where they belong with no manual nudging. Materials stay separate per rule material, with no texture atlas, so an override in the engine is a two-click job. Pivot at the world origin, not at the object center. If you get that wrong, every block rotates around a point in the river.

Import and the First Night

1.9 million triangles across 4,100 buildings. First import took fourteen minutes and the scene ran at 34 frames per second with everything visible. Merging static meshes per block brought it to 58 frames per second, with no visible difference at street level. Facade UVs survived because we exported them instead of letting the engine regenerate them. Collision is simple boxes generated in the engine, not the render mesh, because nobody needs window reveals to block a player.

What GenAnywhere Gave Us, Honestly

A single mesh, no names, no attributes, no hierarchy. In our test it produced 61 fewer buildings than the parcel table and invented three towers on lots capped at five floors, and the materials were baked into one image. When the zoning cap changed on day six, everything downstream would have had to be rebuilt. We used the image on a slide, credited it as an illustration, and moved on.

Iteration Cost, Measured

Parking ratio from 1.2 to 0.8: six minutes to regenerate, fourteen to reimport, zero minutes to remap materials because the material names did not change. GenAnywhere: ninety seconds to regenerate, then every arrow, light, and material group gone. Over ten days the rule path cost more per change and paid for itself by Wednesday.

What I Would Automate Next

Three checks in CI: a triangle budget per tile, a building count compared against the parcel table, and a checksum of the rule file written into the export name. The count check would have caught our one bad export before it reached the meeting, and the checksum tells you which version of the rules made the scene in front of you.

Top comments (0)