Monday, March 14, 2022

Procedural Buildings: Parking Garages

Parking Garages

The past few months I've been mostly working on improving the interiors of houses because I've gotten more feedback from others in this area. I haven't put as much effort into office buildings lately. It's time to go back and work on office buildings once again. I added basements to houses last year, and now I've added underground parking garages to some of the larger office buildings. Smaller buildings have basements similar to houses, with offices and storage rooms. I've even added support for multiple levels of basements and parking garages that extend down under the building.

Parking garages are implemented somewhat differently than above ground building interiors. They're only visible when the player is inside the building because they have no windows, which means I can defer their generation until that point. This means that it makes more sense to add parking garages as interior detail objects rather than regular interior objects generated by the floorplan phase. I added a new drawing system for "interior detail geometry." The benefit here is that I can have much more detailed interiors for parking garages without having to worry about allocating too much memory to store geometry when the buildings are initially generated. This is why I didn't have office building basements enabled by default before now. I also realized that I was accidentally generating garbage basement geometry for non-rectangular buildings, which the player can't enter anyway, so I fixed that as well.

The floorplanner treats the entire parking garage as a single big rectangular room, like this.

Early version of an empty parking garage with only ceiling lights, and elevator, and stairs.

Code to fill in the interior is triggered when the player is close to the building. The first step is to determine how many rows and columns of parking spaces there will be based on the size of the largest car that can be placed. The available space is partitioned into rows of parking spaces separated by walls and pillars, with aisles between them and along the sides for cars to drive in when entering or leaving their parking spaces. I added beams crossing the ceiling in both directions, connected through the support pillars, with lights hanging from them. Then I connected stairs from the ground floor down to the parking garage level and to lower levels, and extended elevators downward into the parking garage. Finally, a ramp is added connecting each level of parking garage to the floor above. The ramp is wide enough and has enough clearance around it for cars to use. Walls, pillars, beams, and parking spaces are clipped or removed to keep the stairs, elevators, and ramps clear for people and cars to use them.

I added new elevator buttons and stairs/elevator signs for these underground levels. Basement levels begin with a "B", while parking levels begin with a "P". I placed railings along stairs and ramps for safety. Lights are added in a 2D grid along the ceiling attached to beams. Any lights overlapping other objects are moved horizontally to nearby free positions, or removed if there's no space for them. Here is what this looks like with cars added.

Parking garage with parking spaces and parked cars, with stairs, and elevator, and a ramp in the back.

Cars are high polygon count objects with many materials, so I had to put extra work into optimizing their drawing. The parking garage walls, ceilings, and floors are merged into maximal occluders for use in occlusion culling. This way cars that are hidden from the player don't need to be drawn. I also skip drawing of the surrounding terrain, city, and buildings when the player is in the basement and away from stairs leading up to a room with a window. These optimizations allow me to to get 70-120 FPS even in a large room filled with cars.

Pipes

[Disclaimer: I know nothing about plumbing or what the correct terms for these things are.]

That looks pretty good, but something is missing. What do parking garages always seem to have? How about some pipes running across the ceiling. I already know where all of the toilets, sinks, urinals, bathtubs, and showers are because these have been placed in the building before the parking garages are filled with objects. I can find these object locations on the floors above, combine their flows vertically, and produce the ends of a drain pipe for each stacked plumbing fixture above the parking garage. I made pipes increase in radius as they're merged so that have mixed sizes. The idea is to then connect the ends of the drains protruding from the ceiling into a network flowing into one main sewer pipe that exits into the wall or floor.

The only problem: Connecting these pipes is extremely complex. I have pipe ends of various sizes randomly scattered all over the ceiling, with some of the large buildings having as many as 80 of them. Then I have all of these pillars, lights, elevators, stairs, and ramps everywhere that I have to route around. I believe what I want here is a rectilinear Steiner minimum tree, with the addition of obstacles. This is an NP-hard problem - meaning, an optimal solution likely requires exponential runtime. And that's not even taking into account the obstacles part! All I can find online that discuss handling obstacles are conference papers in the field of VLSI layout routing. None of these are algorithms that I want to sit down and write from scratch just to connect my pipes together.

Okay, maybe I can implement this with a simpler and less optimal single trunk Steiner tree. I can have the main sewer line running along the longer direction of the parking garage and then add secondary feeder lines branching out in the other direction to connect to the drains. Then I just need to translate and rotate that main line around, maybe add a bend in it, and I can fit that part in. The secondary lines are similar to the main line, but rotated 90 degrees. It would sure help if the pipes coming from the plumbing fixtures were in neat rows and columns so that I could connect them all for a given bathroom with a single feeder line. The toilets from the bathroom stalls are in a nice aligned row, but the sinks and urinals are a slightly different distance from the bathroom wall. I know, I can pick one representative and then try to align the nearby pipes to the same X or Y value as that one, as long as they still fit within the XY bounds of the plumbing fixture and the room itself. (In reality, the pipes would drop down through the walls behind the toilets anyway so that they can pass through multiple levels of floors.)

Armed with this plan, ... countless hours and a dozen special cases later I have it working. The exit point logic was the most difficult because I had to handle the case where the main line exits straight into the side of the building, at a right angle to avoid an obstacle, or drops down to the floor below to exit if there's no other option on the current floor. It's hundreds of lines of source code, but only takes an average of 120us per building to do the pipe routing. I was even able to add fittings around the joints and caps on the pipe ends. I've visited dozens of buildings in-game and I can't find any pipes clipping through geometry, and only minor instances of pipes clipping through each other when the exit pipe is too close to a feeder pipe.

Some of the smaller pipes still pass by or slightly under the lights. It might make sense to move the lights below the pipes, but then they come close to colliding with the player's head and don't provide enough clearance for the cars. Maybe the pipes should cut through holes in the beams rather than hanging under them? I don't know, I guess I don't want to spend hours changing it again.

I've disabled shadows for horizontal pipes because they don't look right when I treat light fixtures as point lights rather than area lights. A pipe that crosses the center of the rectangular light should produce a blurry shadow rather than blocking all of the light. Vertical pipes that go into the floor do cast shadows, and also collide with the player.

Here are some more screenshots showing the network of pipes in the ceiling. Some of the pipes continue through the walls into other areas. Sorry if pipes are difficult to see due to the poor ceiling lighting, but that's typical of parking garages.





Parking garage with pipes in the ceiling but no cars.

After looking at these screenshots, I decided the lights are too bright and blueish. I reduced their color temperature to something of a warm yellow color, which looks more like the lighting you typically see in an underground parking garage. Here are more parking garage examples.

Parking garage with a lower color temperature yellow light.




Note that people (zombies in gameplay mode) and rats are placed in and can navigate in parking garages. There's nothing of value to steal down here, so I'm not sure why the player could come down to this level. Maybe I should hide valuable items in cars? That's one idea I'll consider.

Future Work

One problem I haven't solved is how to connect the ramp from the upper level of the parking garage outside the building. Cars need to enter and exit somehow. I have no easy way to add geometry outside of the building's bounding cube or lower/remove the terrain around the ramp, so the only option is to have it exit somewhere on the ground floor of the building, preferably near a corner or exterior wall. I can cut a hole into the floor above the ramp, but there will be other objects already placed there such as walls, offices, and hallways. (Remember, the entire building interior is generated before the parking garage.) Maybe the ramp has to be placed first, then the rooms assigned, then the parking garage filled in? The problem here is that my office building floorplan templates don't have a way to mark some corner as off-limits for offices and hallways. This would make all buildings with parking garages have an L-shaped floorplan with the corner cut out, which is more difficult to divide into rooms. I could merge all rooms that overlap the ramp and convert them from offices to some other room type, but then how do I remove the walls? I don't have support for a different floorplan on the ground floor compared to the floors above.

On top of this, if I add an exit door/hole, then the parking garage contents will be visible from outside the building. That means I have to generate it before the player enters the building, which ruins all of my plans. Sigh. So far I haven't figured this out, so I let the ramp end at the concrete ceiling under the ground floor for now. I suppose the cars will be stuck down there until I fix this.

What's next, other than solving the ramp problem? I was thinking that maybe I can work on the lighting and electrical system. I can add a circuit breaker panel in the basement or parking garage that controls the room lights for the entire building. Currently I don't even have light switches down there. I think that would be an interesting project, though I'm not sure how it would contribute to gameplay. Another idea is to add some chance of lights flickering, having a dim glow, or being completely out. That's pretty typical of the lower level of maintenance seen in a parking garage. This way the player can at least steal the broken, low value lights.