DEV Community

Cover image for Procedural Massing in the Browser: CGA Rules, glTF Exports, and a Weekend of Coordinate Bugs
Shapezo
Shapezo

Posted on

Procedural Massing in the Browser: CGA Rules, glTF Exports, and a Weekend of Coordinate Bugs

I wanted a city block in a browser tab. Not a screenshot, not a video, something I could orbit and reuse across projects. What I built was a small pipeline from real context data to a glTF scene, and along the way I collected several mistakes worth writing down so you do not have to make them again.

The idea

Pull the real world around a site, generate building massing from parcel footprints with rules instead of by hand, keep the roads and ground from the engineering models, and serve the whole thing as one glb a viewer can load. Four tools sit in that chain, and only two of them are procedural.

Step one: context without drawing it

Shapezo is where I get the surrounding area. I outline a region on the map, and it hands back a generated 3D model of that area built from the imagery and elevation data behind it. For a browser demo this is perfect, because the background blocks only need to read correctly at a distance, and I would rather delete a bad roof than model forty of them.
The honest problem: nothing it returns has a documented tolerance. My first export dropped a mid rise in the middle of a surface lot, and the parcel grid was rotated a couple of degrees from the state plane coordinates I was working in. So now I treat that output as scenery only. The moment something needs a number attached, it comes from the authoritative model instead.

Step two: the parts that have to be right

Roads, ground, and the site itself come from InfraWorks and Civil 3D, which is where the alignment, the finished grades, and the surfaces live. I export those as they are, because the curb line is a fact and not a suggestion. Two things I got wrong at first:
Export the ground and the buildings in the same coordinate frame or your model ends up three blocks from its own foundation. Bake the vertical datum into the file name. I had one export on NAVD88 and one on local project elevation and spent an afternoon blaming the viewer.

Step three: rules instead of modeling

CityEngine is the interesting part for anyone who likes code. You write rules in CGA and it generates massing from parcel polygons: floor heights, setbacks, podium and tower breaks, parapets, the courtyard opening on the block interior. My first rule file was a mess of hardcoded numbers. The version I kept uses attributes for floor count and ground floor height, so changing the district's height limit is one slider and a regenerate instead of forty edits.
If you have never written CGA, expect the mental shift. It is declarative geometry, closer to CSS than to drawing. My mistake was over-generalizing early; I had a rule for a bay window variant that nothing used for two weeks.

Step four: getting it into the browser

glTF is the export target, and it is where the friction lives. Textures and material names get renamed on the way out, so I keep a lookup table instead of trusting the exporter. Large districts blow past a sane file size, and my first scene was two hundred megabytes of nothing interesting until I found that the rule generator had emitted interior floor slabs I could not see from outside. I now strip hidden geometry and decimate the background before it ever reaches the viewer.

Step five: the flat map layer

For the 2D basemap under the model I used Halfmaps, which styles OpenStreetMap data into a map you control. I turned down the building footprints and road labels so the massing reads, then used the same map extent as the model boundary so the two line up.

What I would automate next

One script that checks the coordinate frame of every export and fails the build on mismatch. A size budget on the glb with a hard error over a threshold. A test scene with a known parcel so a rule change that silently moves geometry gets caught. Version rule files the way you version code, because one nobody can diff is a liability.
It runs. It loads in about three seconds on my laptop, and the block in the browser is now the first thing I show.

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.