Saturday, June 1, 2019

Procedural City: Doors, New Building Types, and Infinite Buildings

This is another one of those update posts where I show various procedural city changes I've made in the past few weeks. I haven't done any one particular thing that's interesting and complex enough to deserve its own blog post.


I've finally added doors to houses. Each house has exactly one simple white door. If the house has a porch, the door is placed under the porch. Otherwise, it's placed on a random wall with at least some distance between the door and the other walls and edge of the house. Here is my first pass at adding doors to houses. (I've disabled the grass blades because they're too large relative to these houses and obscure the doors and first floor windows.)

Houses now have white doors under their porches or along a random side (if there's no porch).

At this point, the doors and windows ignore each other's placements, so they often overlap. That's a bit difficult to solve given how doors are generated on the CPU and windows are added in a shader during drawing. I suppose the easiest fix is to remove the bottom row of windows on the wall of the house that has the door. This can be done by clipping the wall geometry in Z (vertical) for the windows pass. However, it was actually more difficult than I expected to get everything to line up again. There was a lot of work with integer rounding and choosing correct scaling parameters to get exact multiples of windows in various places.

That change fixes the overlap case, but now leaves some empty wall space with no first floor windows. I guess it's an improvement. Maybe sometime later I can go back and add some extra wall sections to the left and right of the door. I expect it could be a lot of work to make the windows line up with the row of second floor windows above them. Anyway, it's good enough for now.

Same view with the bottom row of windows removed from the sides of houses that have doors.

New Building Types

I've added more building types. Non-rectangular buildings (cylindrical, triangular, 5-8 sided, etc.) can now have multiple levels and multiple overlapping sections. In addition, multi-section buildings can have roof details and antennas on their tallest section. This adds in some more complex buildings and introduces new variety to the building architecture. Here are some screenshots showing the new building types.

New multipart non-rectangular building types, from left to right: elliptical, cylinder with flat edge, and hexagonal.

New building types with overlapping elliptical/cylindrical sections.

One of my favorite buildings, composed of multiple tall elliptical cylinders with flat sides. There's another interesting building with a large curved face behind and to the left.

Infinite Buildings

I did some experiments with my older city system where buildings are placed within a large region. This is similar to the secondary buildings where there are no roads, but in this case buildings can also be rotated. I was able to increase the building count up to 270K for that scene. That's a lot of buildings! It took over a minute to generate them and used about 600MB of building data. However, once the buildings were generated, 3DWorld was able to draw them just fine. They're not all visible at the same time, but it still looks impressive to see them drawn out to the horizon. Here I've removed the terrain and fog so that you can get a better idea of just how many buildings this is. Larger buildings are in the center, and smaller buildings and houses are further out in the background.

~70K visible buildings from the set of 270K drawn at around 100FPS. The terrain and fog have been disabled to get a better view of the buildings. The gaps are areas of water where there are no buildings.

That's a lot of buildings, but we can have more. How many more? How about an infinite number. The next thing I'm working on is "infinite buildings" mode. In this mode, buildings are generated incrementally in tiles that become visible to the player as the player moves around in the world. That way, cities can be unlimited in size rather than limited to the initial building placement area. This won't work for city grids with roads, only the unconstrained buildings shown in older images and the secondary buildings shown in the previous post.

This mode is intended for use with procedural terrain rather than fixed heightmap terrain. Building generation is fast enough that I can probably pre-generate all of the buildings for any size heightmap that can fit in memory. I've verified that I can generate 40K buildings in a few seconds to fill the 7Kx7K = 50M pixel island heightmap. My 270K buildings can probably fill a 16K x 16K = 256M pixel  heightmap.

Now, that does mean I can't flatten terrain under buildings because that only works with terrain read from heightmaps, not procedurally generated terrain. The underlying heightmap values can be edited and updated in realtime. That doesn't work the same way with procedural functions. I'll have to solve that problem later.

I decided to start with generating buildings for each terrain tile at the same time the tile was generated, and also deleting them when the tile was deleted. That saved a lot of effort duplicating all of the distance update and visibility logic. I was able to get infinite buildings to work in a few hours. At first they were too slow to generate and draw, and it took me much longer to fix that problem. It's a lot harder generating and drawing buildings quickly when they're spread across 300 individual tiles. Here's what I have at this point:

Buildings generated for each visible tile, for any tile the player can walk or fly to.

Yeah, it doesn't look any different from "regular" buildings mode. That's the point. You get the same quality, but you can walk forever in any direction and never reach the end of the buildings. Collision detection, shadows - everything just works. This was one of my long-term goals for 3DWorld. Now I just need to figure out how to add roads, cars, people, etc. Should be trivial, right? I don't know. One step at a time.

Here are some stats. There are between 298 and 316 tiles at any given time, and around 75-80 of them are visible for a 60 degree field of view. Each tile contains an average of 57 buildings for the parameters I've chosen; that's 17K-18K active buildings. There are about 20 different building materials, each with different sets of textures, and about 50 total textures including normal maps. Building tiles take an average of 2.7ms to generate and the entire thing takes 2.3ms to draw. The generation time is acceptable because it's rare to generate more than one tile per frame, even when moving at max speed.

Drawing requires 5 passes:
  1. Draw distant buildings with a simpler shader.
  2. Draw distant windows on top of distant buildings using different shader parameters.
  3. Draw nearby buildings with a more complex shader that includes shadow maps.
  4. Draw nearby windows.
  5. If night time, draw window lights using a different shader.
In some cases the same building may be drawn in both steps 1+2 and 3+4. It took quite a bit of effort to organize all of the drawing passes, view frustum culling, sorting by material, combining vertex buffers, etc. to get the draw time down to 2.3ms. I'm sure there's still some room for improvement, but at this time building generation and drawing are fast enough.

3DWorld is open source on GitHub.