Thursday, June 12, 2025

Parking Structures

I'm continuing to work on my series of special procedural building types. Previously I added malls, factories, warehouses, hospitals, and schools. I did go back and add a number of features to those other types. For example, I added locker rooms to schools, hospitals, and industrial buildings. I created coffee machines that dispense coffee and hot chocolate for break rooms. I placed shoes on the floor next to front doors. I added hard hats to factories and warehouses. Computers and mice on desks. Test tubes of blood, valves, and gauges in hospitals. Apples on school desks. Shader-based abstract art pictures in houses. (I might write a separate short blog post on these later.) Those additions aren't the subject of this post though. Maybe I'll show some screenshots of these changes in the next summary/progress post.

This time I'm working on something a bit different that has a new style of building compared to the other types. I've added parking structures, which are above ground variants of my underground parking garages. The biggest difference is that these buildings have open gaps between walls rather than individual windows. This may seem like a minor difference, but making the generation and drawing work in this case was quite difficult. Previous buildings always had the interior window surfaces drawn as opaque when the player looked in from outside. This was due to a technical limitation of how windows were cut out of exterior walls using textures and stencil masks rather than actual geometry. While this did make drawing more complex, it also meant that buildings functioned as occluders for objects behind them. This is not the case for parking structures. The player can see completely through them and out the other side. They're more open air than concrete walls, which means that some of my earlier optimizations are no longer valid.

I did have some trouble with lighting due to the way interior room lights and exterior sun/moon light is handled in two separate draw passes. All building objects are labeled as interior vs. exterior and drawn in exactly one of those passes. The problem with parking structures is that they have walls and pillars along the exterior edges that are visible from both inside and outside the building. I can't use trickery with the depth and stencil masks to draw walls with window cutouts here. What I ended up doing was drawing some of these objects in both interior and exterior passes, with different faces enabled for each pass depending on which way they were oriented. Those vertical pillars along the outside edges are effectively half inside and half outside the building. 

While the exterior is different, the interior is mostly a combination of existing components. I reused most of the existing underground parking garage code to create the above ground variants. This includes all of the logic to insert pillars, walls, ramps, parking spaces, cars, and sprinkler pipes in the ceiling. The only differences were related to anything placed on walls, and the logic to avoid obstacles. I did have to make some changes to object drawing to increase the draw distance of pillars and walls so that they wouldn't disappear when the player walks away from the building like the underground versions of these objects do.

Those exterior pillars I mentioned above are special cases. They're not added to underground parking garages because the outside walls provide support. However, parking structures don't have full height walls, so they need more structural support. I wrote code that attempts to move pillars to the sides in multiple iterative steps if they intersect another object or block the stairs, elevator, or ramp. I also added a type of narrower non-square pillar that is easier to fit into tight spaces. The placement algorithm should ensure there's enough clearance for cars to move around and between fixed structural objects like this. 

I reused the code that attached fire extinguishers to mall pillars to add them to pillars in both underground and above ground parking areas. I also repurposed the code that adds stains to walls and floors so that it places oil stains in some parking spaces.

Below are some screenshots of the new parking structures with indirect lighting enabled.

Parking structure interior with cars, a person, handicap spots, stairs, elevator, red sprinkler pipes, oil stains, and a fire extinguisher in the back center.

Parking structure with walls between rows of parking spaces.

All parking levels are connected by a set of vertically stacked ramps that extend from the basement up to the roof. These ramps run along one side of the building, are wide enough for two cars to fit side-by-side, and have enough clearance at the ends for cars to turn. I added railings on the inside edges and at the end of the upper (roof) level. These are wider versions of the original underground parking garage "ramps to nowhere". The player and building people/zombies can walk up parking ramps, and objects such as balls will roll down them.

Close view of a ramp connecting multiple parking levels.

 

A large and open parking area full of cars, with stains on the ground. The ramp is to the right.

I added the elevator and stairs next to each other on one of the building's sides. Handicap spaces are placed next to these and limited to 5% of total spaces. In my opinion, the U-shaped stairs work better than straight stairs for parking structures. They're more compact and leave more room for parking spaces, though they do look somewhat odd next to elevators due the the large difference in depth. Maybe elevators should be larger? I mixed the stairs up so that they face the same direction as the elevator 50% of the time and are perpendicular and facing away from the elevator the other 50%. Stairs extend from the basement all the way to the roof and are numbered on each floor. Elevators extend from the basement to the top floor. They don't reach the roof because I haven't implemented elevator entrances above building interiors. Technically, city malls have elevator levels above ground, but that's somewhat of a different case to handle.

A closeup of the stairs and elevator. Maybe the stairs need a light like I added to retail buildings?

Each parking structure has a small bathroom in a corner on the ground floor with a toilet and a sink. This uses the same room-inside-a-room logic as hospital patient rooms and industrial buildings. The entire remainder of the above ground building is a single room of type "parking" that covers all floors. Once again, it's much faster to reuse existing code. It's not entirely reused though because I had to add the side walls, as there are no full height exterior walls on the building itself that form the outside walls of the bathroom.

A parking structure bathroom. It's similar to bathrooms found in hospital rooms and smaller office buildings.

That's all for interiors. It's time to discuss the exterior design. Parking structures have a number of exterior doors with a bit of concrete wall to the left and right of them to give them a proper frame. There's also a cutout for the area where cars enter and exit. I added "enter" and "exit" signs, in addition to signs marking the clearance. A driveway extends from the entrance outward, but doesn't yet connect to a road.

Inside the entrance is a pair of ticket machines with gates/bars that can either be raised or lowered. The machines themselves use a texture I found online for the front panel and a yellow and black striped gate arm texture. The arm blocks the player but can easily be walked around. I haven't made these machines interactive yet. 

Now I'll show a few screenshots of parking structure exteriors. Note that the lighting looks different here. This is because 3DWorld has separate lighting environments for building exterior vs. interior light sources. The interior lights from the above screenshots have precomputed indirect volumetric lighting enabled. This only works for the building the player is currently inside (for performance reasons), so there is no indirect lighting visible in this outdoor screenshot. In addition, the custom ambient lighting for parking structures that I used to offset the lack of windows is disabled when the player is outside.

Parking structure entrance/exit viewed from the outside, with enter, exit, and clearance signs visible. Here the ramp is near the entrance.

Parking ticket machines with gates on both the entrance and exit sides. Some gates are open while others are closed.

 

The parking structure roof has a small enclosed area at the top of the stairs as well as the uppermost ramp segment. I haven't yet found a good way to add cars or parking spaces to the upper levels. This is technically the roof rather than an interior part of the building. The normal object placement system doesn't support objects added outside the interior bounds of the building. I have limited support for adding rooftop items, but this doesn't include 3D models. I also can't reuse the code to add cars to parking lots as that only works inside city bounds.

Since the rooftop is otherwise empty, I decided to add something similar to streetlights. This way there will at least be light sources if I do find a way to add cars. This doesn't actually use the same system as city streetlights because it wouldn't be efficient in this case, and again, this isn't inside a city. Instead I used the system I created to add porch lights to houses that collects together all light sources during the building generation step. The performance is acceptable because only around 50 out of 18,000 buildings are parking structures. It seems to work reasonably well, though it doesn't support shadows. I think that's okay because there aren't many shadow casting object on the roof yet anyway, other than the light poles themselves. These lights turn on at night time as the sun sets. They're not all that bright, which I suppose is to be expected for parking areas.

Parking structure viewed from the side and above. There are lights on the roof, but no cars. The big "Parking" sign is also visible.


Night time view of the roof with lights on. It's pretty empty up here.

Update:

Okay, now that I've written about it, I feel like I need to add rooftop cars. My hacky solution is to assign each parking structure a successive "city" index after all of the cities are added. Then I can add some special case logic to treat these roofs like a city with a single parking lot in a single block. It's messy and probably not very efficient, but it works. The existing lighting code happens to be correct as well, including the sun and moon shadows.

Parking structure, now with cars on the roof.

If you compare the images you can see that the lights have changed as well. They now have extra cubes at the bases. They're also now aligned to the rows of parking spaces, which means there are somewhat fewer lights than there used to be. I had to remove lights that were too close to the ramp to avoid blocking the paths of cars. I even modified the rooftop lights so that shadows are enabled and the cars now cast shadows. Of course it does hurt framerate somewhat to do this. At least they're static shadows.

Night time scene with shadow casting rooftop lights.

As usual, I'll probably improve and add to these building types after publishing this blog post. This happens with every new building type. There have been so many small additions that I'll never have the time to write about. Many of these changes are based on user feedback that I get from comments and emails.

Next on the list is factory conveyor belts. These are similar to escalators and moving walkways and should be a fun addition. It shouldn't take me very long to add them!

2 comments:

  1. I feel like it would be fun to have the procgen system occasionally put strange items in odd places. A factory locker room with top-hats instead of hard-hats. A coffee machine by the front door with the shoes. Just as a bit of spice for the world! I like the idea of shader-art. I was recently introduced to the Magic AI-Art: Dimensions world by Niko* in VRChat which has some very impressive shaders built in. You might want to check it out sometime for inspiration!

    Sounds like you need some kind of LOD system so you can instantiate simplified geometry initially and then ramify for resolution as necessary. You're already doing something like this for the rooftop cars being a nested city instance.

    Yeah, I feel you on the boundary cases. Very tricky to integrate the exterior and interior systems seamlessly. Impressive that you have it working at all, so good on you for that at least!

    Would it be feasible to do some sort of variable scale binary tree indirect lighting system that works across interior and exterior? That might help blend the transition between the two systems, even though shadows might not work.

    Two things jump out to me about the stairs. First is they are missing the inside rail (even re-using the one from the ramp would be a big improvement). The second is that the tread is way too long.

    I love the re-use of the interior sub-rooms to put bathrooms in the parking garages. For the basement areas there are almost always big blowers for air circulation, which you could put in a machine room.

    I'll say it again, I love the sub-city on the roof of the parking garage. Far from being "messy" I find it very elegant! I haven't looked at your implementation, so maybe inefficient and messy applies under the hood, but the principle of instantiating a smaller district with special weighting and rules for the lot population sounds like exactly the correct solution. That's what I was talking about with the "Complex" in this comment:
    https://3dworldgen.blogspot.com/2025/03/procedural-warehouses.html?showComment=1742665056825#c206534390212841082

    I'm glad you've arrived at this solution as well! Keep it up!

    ReplyDelete
    Replies
    1. Thanks for the feedback. I can add strange items, but each one has to be custom coded. I'm not sure if it's a good use of time to add something that will only be seen rarely. For example, if I create a top hat and make it have a small chance of appearing, who will see it? Maybe I can go back and add this later.

      For shader art I started with some fractal rendering (Mandelbrot, Julia Set, Burning Ship) that I already had the code for. Then I added some of the simpler shadertoy.com "abstract art" shaders. The input parameters such as offset and zoom are prodcedurally generated so that every one is unique. And it's nice because there are no textures to load. I'll probably add more of these later.

      There are already many LOD systems in 3D World. The problem is with objects such as the car 3D models. Many of these are created with topology that mesh simplification doesn't work well on. I can skip drawing some of the smaller detail at distances (if they're composed of separate objects). But other than that, all I can do is replace them with bounding cubes or not draw them at all.

      Rooftop cities aren't really stored as cities. I'm reusing some of the internal variables. City_id=building_id, road_type=building, etc. It's messy because I have to case split on road_type==building in the city code and treat it differently. I was expecting it to be slow, but in fact it was not. Rooftop cars had little effect on framerate even when I added them to every parking spot. I think it's because they're stationary, grouped compactly and easy to cull, and have no headlights enabled.

      The difficulty with interior vs. exterior environments is that they use two different lighting systems and different shaders. Buildings have 3D lighting due to the stacked nature of their floors, where each room light has a computed bounding cube. City lights (streetlights, car headlights, etc.) are stored in a 2D plane for each city and represented in a uniform grid. There is no indirect lighting for the exterior scene. I do have ambient occlusion for the sun, but it's part of the terrain system rather than the city system. It's not clear how to apply both lighting solutions at the same time, or blend between them.

      For stairs: Yes, I should add the interior railing. The downside is that it prevents the player from getting down stairs quickly by falling between the two halves. The length of the stairs was set large so that building people work. I don't remember if it was related to collisions or animations. I only remember that I had to keep increasing the length until the problems went away.

      I have elevator machines rooms in basements. I can add fans in the basement. I already added fans to factories, so I can probably reuse that code.

      Delete