Saturday, September 20, 2025

Building Interior Cube Map Reflections

This post will be a bit different. I haven't added any new building, room, or object types. Well, technically I did add few new object types since the last post, but that's not what I'm going to talk about here. I've finally gotten around to adding cube map reflections for metal objects in 3DWorld's procedural buildings. I've been wanting to do this for a while now.

Reflective metal was one of those tasks that was easy to get the basics working but very difficult to make good looking and fast. I started from the existing cube map reflection support I had in my shaders and C++ code from all the way back in 2016: Part I, Part II, and Part III. This works by drawing the scene six times, once for each face of the cube. Each face uses a different view direction, and the total of all faces covers all 360 degrees of view. I limited drawing to the interior of the current building and the exterior of nearby buildings in the same terrain tile to save runtime. The initial reflection image is colored a light blue to match the day time sky, and will transition to a dark gray at night time. The reflection image does include dynamic objects such as people and animals in the current building.

One of the most difficult steps was determining where to put the origin of the reflection cube. It's not as simple as rendering an environment map (or skybox) where the background can be considered as infinitely far away. This had to work for building interiors, including high aspect ratio rooms and complex non-convex room shapes. I also had to use the same cube map for multiple reflecting objects at different angles and distances from the camera. I originally wanted to sample the surroundings from the center of each room and only update the cube map sparsely when the player moved to a new room to improve performance. However, this didn't work for big open areas such as malls where the center of the room was very far away from the camera and reflective objects. And in some cases the center of the room was inside an object such as an elevator.

I eventually gave up and used the camera position as the cube map origin. Since this is inside the player's head, I wasn't able to draw the reflection of the player model because it would have been inside-out. This also forced me to update all of the cube faces any time the player moved. One additional advantage of using the player position is that it should have never been inside a building object. Except, of course, for elevators and stairwells that the player is also inside and which have proper interiors to draw.

The fact that the reflection follows the camera makes it look more dynamic, but it's not physically correct. This is more obvious when the player is standing very close to an object such as a wall and that object takes up most of the reflected image. I did apply parallax correction to my cube maps in the fragment shader, but that's not perfect because it assumes the cube map extends equally in all directions. This simply isn't the case when the player is standing next to a wall and looking at a reflective surface on the other side of the room.

The next step was to determine what objects to make reflective. I started with shiny materials that I was already flagging as metal. This was then extended to include textured metal with paint, scratches, and rust. I later added other specular surfaces including dielectrics (insulators/non-metals) and panes of glass such as windows. The basic theory and equations work for all of these object types, which means I can use the same cube map and shader code for everything. I'll go over some of these object types below and show a number of screenshots.

First we have the objects that are already loaded as 3D models and have metal properties in their material files. This includes objects such as door handles. I made the brass and steel handles reflective and left the black painted handles with the original white specular light reflections only. The complex curved surface helps to hide the inaccuracy of the reflections.

Reflective brass door handle. Yes, it's a mixture of a mismatched decal on the door texture and a 3D handle model.

Railings along the sides of stairs are also reflective. These use the same material as door handles. They're composed of cylinders in various orientations, which also hide the imperfections of the reflections in their curved surfaces. I really like the way railings turned out.

Reflective brass railings to match the door handles. The metal colors don't always match, though they happen to match in this house.

Moving on to office buildings, we have reflective elevator doors. Actually, there are quite a few smaller reflective objects in office buildings, but the elevator doors stand out because they're almost like perfect mirrors. The reflection isn't as crisp because I'm only using a 512x512 texture for each cube face as another method of improving framerate. This lower resolution leads to a somewhat rougher look compared to a mirror's reflection rendered at full screen resolution.

Elevator doors with mirror-like surface. There are lockers visible in the reflection, so this must be a school rather than an office building.

Malls have the most reflective surfaces. (Factories are probably second.) This includes escalators, railings, floor trim, security gates, round trashcans, ducts, and vents. The sides of escalators are another example of near perfect mirrors. I didn't like the look of mirror reflections in the ducts and trim though, so I added support for roughness/shininess using my existing code. Rough material surfaces use a combination of texture mipmaps and 2D blur to average together adjacent pixels in the reflected image. This is another way to hide the visual artifacts in the captured reflection images.

Mall interior with reflective metal objects such as escalators, trash cans, and ducts along the ceiling.

It took quite a bit of effort to create these cube maps efficiently in large open spaces such as malls and factories. The brute force approach of drawing the building interior six times (once per cube face) cut the framerate to less than half. I was able to optimize this through a combination of reducing the draw distance of 3D models in the reflection pass and skipping materials associated with small objects that don't contribute much to reflections. These small objects are mostly from the shelf racks in retail stores and include bottles, cans, vases, balls, flashlights, candles, shoes, and various similar items. The small objects usually aren't very visible in reflections, especially with the blurring of rough materials. In addition, retail stores don't have any large reflective objects. It was time consuming to add no_reflect support to the geometry drawing system and flag dozens of individual items as non-reflecting.

While I was at this, I also marked many of these items as not shadow casters. It doesn't look quite as nice visually, but this does appear to completely fix the problem with lag when the player turns quickly. In this case the shadow manager needs to generate shadows for multiple light sources that come into view each frame. Malls are full of both lights and shadow casting objects, so this is doubly expensive. Since there are more lights than there are shadow map slots, my system that caches and reuses shadow maps across frames doesn't really help here.

After a lot of work, I was finally able to reduce the cube map generation overhead to something reasonable. My main test case was standing at the end of a very long mall. The original framerate was around 260 FPS. The initial implementation of reflection capture dropped this down to about 50 FPS, but I was able to get it all the way up to 180 FPS. Note that this only applies when the player is moving or a person is walking within the reflection distance. The worst case I was able to find was a looking out the window of the retail room of a large office building with an attached mall. This was around 110 FPS without reflections and a bit under 100 FPS with reflections. My performance goal is 150 FPS minimum on the new computer, which translates to roughly 60 FPS on my old computer that I haven't used in over a year. That old computer is what I would consider the minimum supported hardware for procedural city mode.

At this point I only had support for reflective colored metals with variable roughness. This worked for many objects, but not for something like water heaters. They're far too shiny. Making them rough doesn't look much better.

Shiny metal, mirror-like water heaters in an appliance/plumbing store.
 

The fix was to allow metalness values in the full range between 0.0 and 1.0. This allowed me to support materials such as painted metal that have a reflective layer on top of a diffuse/matte layer. My solution isn't a physically correct multi-layer material model, but I think it looks good enough. For example, a 40% metal material applied to a gray albedo (base color) on the water heaters leads to this much more reasonable result. Note that the pipes are still very reflective.

Water heaters with a more realistic surface that's darker and less reflective.

This extends to non-metal (dielectric) materials as well. These are flagged as specular with an index of refraction greater than one but metalness of 0. These objects have a white Fresnel reflection that varies based on the view angle. We can see this in the toilets that are on the other side of the same appliance/plumbing store as the water heaters shown above.

Rows of shiny toilets in the same appliance/plumbing store. Even dielectric materials can be reflective.

Some of my 3D models have the materials setup correctly, while others don't. I have to put some effort into fixing this at some point. I already had to fix some of the clothing models that had index of refraction set to 1.45 for some reason, which made them look like plastic. It would have worked if I had a raincoat model!

The same effect can be applied to glass surfaces such as TV and computer monitor screens. Here you can see that each of the TVs on the shelf of this retail area reflect the environment. The strength of the reflection is a function of the view angle. The reflection disappears when the TV is turned on as the emissive color drowns out the faint reflection.

TVs on shelves have reflective glass screens.

Here is an example of a whiteboard in an office conference room. The conference table and TV on the back wall can be seen in the whiteboard reflection. One limitation is that this system can't draw reflections inside reflections. [I do have recursive rendering set up for my older scene (in the blog posts from 2016), but that only works because each object has its own cube map. Here I only have one cube map that doesn't support reading from itself while writing to itself.] In most cases, reflective objects aren't really positioned in a way where this is obviously wrong, so I don't feel the need to fix it.

Reflective surface on an office building conference room whiteboard. The TV on the opposite wall is also reflective. So is the window on the side of the room.

Finally, we have glass found in windows, floors, and tables. Glass is technically both reflective and refractive, but the refraction term is only approximated with alpha blending. However, the reflected rays are in fact bent based on the index of refraction of the glass. This is basically the cube map equivalent of my planar reflections for retail glass floors. Sorry, I don't have a blog post to reference for that feature. The screenshot below shows a faint reflection of the mall lights and support pillars in the glass of this pet store. Some of the metal objects in the pet store are reflective as well.

This pet store glass window has a faint reflection. All glass surfaces have an index of refraction and proper Fresnel reflections. I turned the lights off in the pet store because it was easier to see the reflection in a dark window.

Here is a short YouTube video that shows off many of these reflective surfaces. Unfortunately, the YouTube compression makes it difficult to see many of the fine details. This is why I included so many high resolution screenshots above. I do need to add a video to show the way the reflections change as the player moves.


The logical next step is to make this same system work for exteriors as well as building interiors. In particular, I would like to make the exterior walls of metal and glass city office buildings reflective. That should be possible, since exteriors use the same base shader.

As a final note, I passed the 200K lines of C++ code milestone for the 3DWorld project! 

Sunday, August 24, 2025

Procedural Prisons

This post continues my work on special purpose procedural buildings. I added basement jail cells in the previous post. This time I'm expanding on that topic and generating entire prisons, where the jail cells form one of the basic building blocks.

Prisons have been more work than I originally expected, and I can tell from the start that this will be a long post. This also partially explains why I haven't posted anything else in the past two months. The other reason is that I'm typically busier with non-programming work in the summer. I need to get off the computer and outside for at least part of the year, and the best weather is in summer. My daughter is starting high school as well, so that's one more distraction from this project.

Prisons required quite a few new features:

  • A new building type (prison, obviously)
  • A new floorplan class (cell block - open area with cells cut out of it)
  • A new window type (barred)
  • A new interior door type (metal door with window)
  • A new flooring type (rubber gym mat) 
  • Three new room types (visitation, gym, shower)
  • Four new object types (gym weight, exercise machine, cafeteria tray, bar of soap)
  • Five new 3D models (dumbbell rack, Roman chair, exercise bike, horizontal bar, leg press)

I can tell just by looking at the line counts of the source files that prisons were more work than retail stores, factories, warehouses, hospitals, and schools. So far there's 1391 lines of code. Prisons weren't as much work as malls, but malls have multiple blog posts. If you want to take a look, the code can be found in my GitHub project page.

I'll cover all of these additions below. I think it makes the most sense to explain the details of these categories and then show interior screenshots of everything together near the end of the post.

Interior Floorplan

Most of the existing above ground special building types were assigned to the single rectangle building shape. This includes retail stores, factories, warehouses, hospitals, schools, and parking structures. These buildings have a simple rectangular floorplan that makes it easy to assign as open space or add hallways with rooms to the sides in regular patterns. The problem is that most of these buildings are already assigned to one of the previous functions/types and there aren't many unassigned buildings left to make into prisons.

So instead I'm going to select multi-rectangle building footprints for prisons. These are the ones shaped like the letters L, T, H, U, O, or +. (Well, I guess '+' isn't a letter.) I'm limiting this to non-stacked floorplans where all vertical floors have the same footprint. Even with that limitation, things get complex as I need to handle adjacency between these rectangles and ensure that everything is connected and all rooms are reachable. Note that this also includes those 'O' shaped buildings with interior courtyards, which of course caused me trouble.

The basic idea is to convert each rectangle into a separate cell block. I decided to place prison cells along windows such that each cell has exactly one full exterior window at the back, and possibly a window on the side for corner cells. Window spacing tends to be around 6-8 feet, which makes for a reasonable cell size that can fit a bed, sink, and toilet. Regularly spaced windows leads to uniform cell sizes. Aligning cells to windows also avoids the need to check for and avoid walls that intersect windows.

The cell block floorplan has cells along the windows on two opposite sides and a central hallway down the center. I chose the hallway direction (north-south vs. east-west) as the one that allows the largest number of cells to be placed. This is usually the longer side that has more windows, except for a few cases where cells can't be placed near interior corners. Large rectangular areas will have a very wide hallway that can be further partitioned into a cluster of central rooms, effectively breaking the area up into two separate cell blocks. I divided this central area into a row of adjacent rooms and a hallway connecting the opposite cell blocks if there's space. That way I can make some of these rooms private types such as bathrooms without requiring people to walk through them to get from one cell block to the other. The combination of two cell blocks and connecting hallway typically forms a H or S shape of public hallway space in each building part.

Adjacent rectangular parts are connected together with a wall and a metal door to provide access to all areas of the prison. Doors are required at each adjacency, except for the case of O-shaped ring buildings where one door can be omitted while still having a fully connected floorplan. The placer can remove cells or re-generate the floorplan so that required doors can be added. The most effective fix for missing doors is to remove cells from one side of the hallway (preferred on the interior side) to make more space for a door.

The next task was to add stairs and elevators to connect floors vertically. The existing placement logic for stairs and elevators doesn't work for this custom floorplan. There is no single central hallway, and most rooms are too small or too private to place stairs in the center. The best solution I could come up with was to place both the stairs and the elevator in a corner of a room if there's no other valid placement. This will sometimes create elevators adjacent to exterior walls that partially overlap windows. It doesn't look great, but it's more important to ensure buildings are connected for gameplay purposes.

Sometimes the stairs placement will still fail. This happens often in H and O shaped buildings with thin rectangles. The problem is that the placement areas have just enough room for cells on both sides and a hallway in the center, but the hallway isn't wide enough to fit stairs at any point along it. I decided on an iterative approach where I throw out and regenerate the floorplan if stairs placement fails. Each iteration I increase the minimum hallway width, and eventually the placer can only fit cells on one side of the hallway. This works for all buildings I've seen.

The second part of the problem is connecting basements with stairs. Prisons can have basements with either parking garages or more cells (dungeons). My solution to this problem was to extend the above ground stairs to the basement when possible. I blocked off the space below these stairs in the basement to make sure no cells or parking spaces are placed there. These stairs will also continue down to lower basement levels, connecting the entire building together vertically with a single set of stairs. This is the first time I've written code to create stairs that span the entire height like this. I went back and changed the stairs placement so that the other building types such as office buildings can use this approach as well.

The default flooring in prisons is concrete, and the default ceiling is acoustic tiles. Maybe the ceiling should be concrete? Or is that too much concrete? Some of the special room types have other flooring and ceilings. For example, offices and classrooms have carpeting, gyms have rubber or wood, and cafeterias, showers, and bathrooms have some type of tile. The various flooring types show up as different shades of gray in the floorplan image below. 

Prison L-shaped floorplan in top-down map view. A total of 20 Cells are along the top and bottom rows. Walls and ceiling lights are white while desks and tables are brown. Tile is light gray, concrete is mid gray, and carpet is dark gray.

Windows in rooms assigned as cells or cell blocks have thicker frames and bars installed on the inside of the glass. Doors connecting cell blocks to other rooms use a new type of heavy metal plate door with a barred window. This type of door required extra work so that the player and building AI/zombies can see through the window, rather than treating closed doors as sight occluders. Metal doors have a different opening and closing sound that's metallic.

Exterior doors posed a new challenge. If I have prison cells at all available windows, where can I find space for the entrance door? In some cases it can be placed on the side of the building part that doesn't have cells. But that isn't always possible with those thin rectangles of H and O shaped buildings as the cell doors may block the entrance door when they're both open. My solution was to choose a ground floor cell and change it to a small entrance room, with a door to the outside and a metal door leading to the cell block on the inside. I had to make a similar change to add a courtyard door to the O-shaped buildings. (I would like to make the courtyard into an exercise area or basketball court, but I haven't gotten to that yet.)

H-shaped prison exterior showing entrance door and bars in the windows. Ah, Mink Prison.

Prison entrance doorway area with its own metal security door. This is really a repurposed cell.

Path Finding 

This new floorplan broke building AI navigation. The problem was that the cell block "hallway" areas weren't simple rectangular hallways because they had missing cells where building rectangle parts abutted and there were no windows. This formed open areas with L, U, and T shapes. The existing AI path finding system only works on rectangular rooms. Now this isn't exactly new; I do have some odd room shapes in other buildings. For example, the factory floor has an office and bathroom cut into the corners, and hospital patient rooms have corner bathrooms. I had written special case code to handle these, but it was too difficult to extend that to work with arbitrary cell block shapes with any number of nested rooms.

The new system I wrote orders rooms inner to outer based on nesting: cells first, then the cell block/hallway. The room an object is assigned to is the first room that contains the center point of the object. This works with the player, building AI, and placed objects. Doors between sub-rooms work in a similar way: The first connection is to the parent room containing the door, and the second connection is to the child room adjacent to the door. Building AI navigation must also consider walls between nested rooms as blockers/colliders for the parent room.

This took quite a bit of experimenting to get correct. It involved some head scratching trying to figure out why people were stuck in place or walking through walls. I think I have it working correctly now. (Update: No, it's still broken, but I'll fix it.)

Room Assignment 

Okay, back to that cluster of rooms between the cell blocks. I decided on a list of eight possible room assignments for these, some of which are new room types:

  • Visitation Room (new)
  • Laundry Room (commercial type like the ones in hotels)
  • Office (generic room type that can always be added)
  • Cafeteria (same as school)
  • Gym (new)
  • Shower (new)
  • Classroom (same as school and hospital)
  • Bathroom (office style with stalls)

Some of these are reused, with minor changes to adjust for prisons. For example, there are no alcohol cans or bottles, and most of the clothing is orange. Larger bathrooms have one wall of cells replaced with enclosed/private showers. These are basically bathroom stalls with the toilet replaced by a shower and small bench.

Prison bathroom with shower stalls along one wall. These use the toilet stall geometry but have a shower instead. Out of order signs apply here as well.

I decided to create a new system for prison room assignment rather than writing a mess of special cases like I did for other building types. This system assigns weights to the various room types based on their constraints and preferences, and chooses the room type for each available "slot" as the one with the highest weight. I added a bit of randomness as well so that the same types aren't chosen for rooms stacked above each other.

This system has a number of scored categories and constraints:

  • Preferred min/max room size
  • Desired room count (per prison): one, multiple, etc.
  • Are stairs and elevators allowed in this room?
  • Are exterior doors allowed in this room?
  • Is room private? If so, it can't be on the only path between two other rooms. 
  • Preference for ground floor vs. upper floors vs. basement
  • Desire for windows vs. no windows vs. don't care

Each candidate room is scored across the categories and the room type with the highest score is chosen. This system isn't perfect as it tends to place most of the special (count=1) rooms on the ground floor as rooms are assigned bottom up. However, this does work out better for the player since they can see these rooms without taking the stairs or elevator to upper floors. There are also situations where the more constrained room types such as bathrooms and shower rooms are missing from the building entirely. I don't have a good fix for this, other than setting a max size for buildings selected for prisons to ensure there's space for more interior (non-cell block) rooms.

Laundry room visible through the barred window on a cell block metal door.

 

Rounded Rectangles

I wanted to add metal trays to prison cafeterias rather than the ceramic plates I had in malls. This is good for school cafeterias as well. I originally created rectangular shapes, but they didn't look very good. I wanted rounded corners. I discovered that I could create a rectangle with rounded corners by taking a cylinder, splitting it into four quadrants, and translating those quadrants away from each other in a way that stretches the edges connecting them. In addition, I can apply this to a truncated cone to get a rectangular shape with sloped sides.

Now "round rectangle" is an official object shape that can be used. This is the shape I used for cafeteria trays and bars of soap. I also changed the existing drawing of mattresses, trashcans, cell phones, and mall store theft sensors to have rounded corners. I attempted this with conference tables, but I was never able to get texturing to work with this shape. So I only apply this to solid color, untextured objects.

Prison cafeteria with rounded corner metal food trays. I should probably remove the plates.

Cell Blocks

Cell blocks are the areas that include rows of cells and the connecting hallway. They may have cells on one side, both sides, or a mix of the two for some of their length. Cells are separated from adjacent cells by plaster walls and separated from the hallway by metal bars and a barred door. This is similar to the basement "dungeon" prison rooms in the previous blog post.

I added guard desks with chairs and phones to hallways wide enough for them to fit. I also added flashing red warning lights between some of the cells by reusing those objects from factories. I was thinking of placing keys on either the guard desk or the wall somewhere, but I haven't gotten to that yet.

Cell block with cells, stairs, and open metal door.

 
Cell block with U-shaped hallway/open area (opens up behind the room on the back left) and guard desk. 

 

Visitation Rooms

Prison visitation rooms are divided into two halves for the inmates vs. visitors. There is a glass window separating the sides, with rows of small tables and chairs between them. I placed telephones on both sides of the desks. I think it would make sense to have only the receiver part of the phone, but I only have that one office phone 3D model with an included keypad. Separators are added between visit stations for privacy. The visitor side of the room has a row of additional chairs along the wall to serve as a waiting area.

The dividing line must be chosen so that both halves of the room have an exit door. Then the prisoner vs. visitor side is determined based on factors such as connected room types, location relative to building entrance, presence of windows, etc. If no valid dividing line is found, the room is reassigned to another type.

Prison visitation room with glass wall, tables, chairs, phones, and dividers. This is the visitor side with some waiting room chairs on the right. I'm not sure what's causing the light banding on the dividers; I'll fix this later.

This split room does cause problems with zombie path finding. They sometimes enter the side opposite the player and will wait there rather than going around to the player's side. I believe this is the first room type that can't be crossed. I'm sure I could fix it with enough special cases, but I don't know if it's worth the trouble.

Gyms

Prison gyms have custom flooring that's either rubber gym mat or hardwood boards. I think the rubber mat is more appropriate for weight lifting areas, so that type is more common. Gyms can either be assigned to interior rooms or exterior rooms that have windows.

Gyms are full of exercise equipment and multiple types of weights. I've added five different 3D models of equipment, plus multiple styles of procedurally generated lifting and hand weights, as well as individual metal weight rings. Gyms also have a row of lockers along one wall, assuming there's space. Benches are placed along walls in random places. Sometimes an orange shirt is added on the floor, and orange shirts are in the lockers. I didn't have a proper prison jumpsuit 3D model, so I reused the tee-shirt model. Some gyms have water fountains and others have wall mirrors. I didn't want to add too many objects and risk making these rooms too cluttered for the AI people to cross through, so there's quite a bit of open space.

I allowed stairs to be placed in gyms. This looks somewhat out of place, though it's not nearly as bad as stairs in bathrooms or shower rooms. Stairs don't fit in cafeterias, classrooms, or visitation rooms either. I had to allow them in gyms to avoid being too restrictive and seeing buildings with no stairs.

Prison gym with rubber floor mat. This one has exercise machines, weights, lockers on the back left, and a water fountain in the back right.

 
Prison gym with the less common wood flooring (using a fence texture!) This one has metal benches on the sides and a mirror in the back. Lockers are on the back right.

Shower Rooms

And last we have prison shower rooms. We can't forget to add shower rooms! Thankfully I've never been in one of these, so I had to use reference images. The walls, floor, and ceiling use the same tile textures as underground swimming pool rooms. (That green tile wall texture looks exactly like the room I remember taking swimming lessons in when I was a kid.)

I decided on having an open area with shower stations along the walls everywhere that's not blocked by a door. Wider rooms have an additional central wall with more showers that creates a ring-like space. Each shower has single or dual handles, a shower head, and a drain. The player can turn these showers on, but this only plays a water sound. I placed some metal benches between the showers were there was space. And of course, you can also find bars of soap on the floor that can be picked up. I reused the rounded rectangle shape for soap.

Large prison shower room with a central wall and multiple benches. Look, a rat on the floor! There are also bars of soap on the floor if you look carefully. They blend in with the white tile.


Smaller and dirtier prison shower room with water damage, trash on the floor, and a rusty metal door.
 

Final Thoughts

Wow, this post was a lot of work to write! I hope people find all of this interesting. There are some minor fixes and improvements left to do for prisons. For example, I'm still not entirely happy with the room assignment. I would prefer that each prison has at least one room of each special type.

What's next? I finally have a plan for drawing those reflective metal objects in building interiors. I'm going to see if I can generate a cube map texture for the room the player is in to use for reflections in the fragment shader. I'm not sure how this will work for non-rectangular rooms like prison cell blocks. I guess we'll have to wait and see.

Sunday, June 22, 2025

Underground Jails

It's getting close to the 4th of July and summer time in general when I'll be busy with other things and won't have as much time to work on 3DWorld. I may not get a chance to write a blog post in July. I didn't want to start on a big project such as a new building type right now, so instead I added a new room type. I'm not exactly sure what made me think of this, but I decided to add basement jails. I was particularly interested to see how the lighting and shadow code performed on a room full of narrow horizontal and vertical bars. I suppose these fit with the interrogation rooms I added last October.

It's always easiest to place special room types underground as I don't need to worry about windows and other exterior elements. The existing code for extended basements creates a huge network of underground rooms that I can work with. The first step is to choose one that's both long enough and wide enough to create a hallway with rows of jail cells on either side. This isn't as common as you may think since the existing code tends to create rooms on the smaller side or rooms that are long and narrow. (Usually the long narrow rooms are the result of failed hallways that don't have a room to connect to and are converted to "leaf" rooms.) The second test is for a doorway near the center of either one or both of the long ends of the room, and no side doorways. Most rooms meet the door requirements.

Once the jail rooms are selected, they're divided in the short dimension into a central hallway with rows of cells to either side. Bars separating the cells and hallway use a mixture of rusted metal or scratched metal textures. The room doorway connects to this hallway. Cell size is uniform for each room and chosen to be deep enough to fit a bed's length and long enough to fit the bed width, cell door, and sink side-by-side. The cell door is in the center and has a set of bars to either side of it spanning the width of the cell. In addition, I added a toilet to the back wall opposite the door with a toilet paper holder next to it. It's not much privacy, but I suppose it's not much worse than the toilet placement in some of my generated houses.

Each cell is created in a similar way to bedroom closets in houses. I didn't make them sub-rooms because the sub-room logic really only works well for a single nested room, and it wasn't needed in this case. I added a small round light to the ceiling in each cell, in addition to the larger hallway light(s). At first I had the two groups independently controlled, but later added a single light switch next to the door that controls both sets of lights. I reduced the amount of indirect lighting for jails because there are so many lights, few occluders, and I feel it looks better when the jail area is somewhat darker than other rooms.

Jail bars are handled like interior windows used with conference rooms. They're not occluders since the player can see through them, but they do block object interactions. This means you can't take objects through the bars or interact with objects on the other side of the bars such as flushing the toilet. Zombies also can't attack through jail cell bars.

All cell doors swing outward into the hallway. In fact I reused the same logic as interior doors and only changed the graphics to replace white wood with rusty metal bars. I also replaced the wood door open/close sound with a metal door sound. Jail cell doors are narrower than standard building interior doors so that they don't block the narrow central hallway as much. I initially had problems with zombies passing through the openings because their collision bounds were expanded to include their animations and were wider than the doorway. After some experimenting, I went with shrinking the length of the bars to either side of the door when used with AI collisions. This effectively widens the opening slightly for the AI to pass through while following the player.

The first style of jail cell has walls separating adjacent cells. These use a plaster material in houses and a concrete material in office buildings to match the extended basement room walls of these two building types. I replaced the carpet on the floor with concrete in these rooms. The walls, ceilings, and floors pick up the cracks and water damage properties of the building's basement. Here is an example of this style of jail with indirect lighting enabled.

Jail with walls between cells. Some doors have been opened and some others are locked with brown padlocks.

Some of the cell doors are locked with a padlock, with a key to all cell locks hanging on the wall opposite the door. The screenshot above has brown padlocks and a brown key that can be seen in the very back if you click on the image to zoom in. In theory it should be possible to lock a zombie in a cell if I set the doors to be re-lockable, but I don't think this works in practice. They only enter the cell if the player is in there, and they tend to block the exit and corner the player in the cell. The only way I've been able to get by them without being killed is by pausing the AI logic or disabling collision checks. So instead I changed the padlocks to be permanently removed when the cells are unlocked so that it's not possible for the player to lock themself inside. The key remains in the player's inventory after use and can be used from either side of the door just in case. Maybe I'll eventually add a way for the player to place zombie bait in jail cells, and then allow the door to be re-locked.

The second style of jail cell uses another set of bars in the opposite direction to separate cells rather than adding walls. It forms cells surrounded by bars on up to three sides. It didn't look correct to place a sink next to these bars, so I moved the sink to the back wall between the bed and toilet. Only cells that are long enough to fit all three of {bed, sink, toilet} along the back wall can use this style. This produces a more open floorplan and more complex shadows.

Another jail with bars between cells rather than walls.

Jail beds are different from house beds. They have black metal rather than wood frames, and always have white sheets. The theme here is grayscale colors. Some of the cells have a single bed, while others have bunk beds. These are really just two beds stacked vertically with a short ladder added to the side.

Closeup of a jail cell bunk bed. A sink and toilet are also visible.

I actually added bunk beds to house bedrooms earlier, I just haven't shown that in my blog yet. I also added a small random chance of a person sleeping in the bed. This somewhat unintentionally applies to jail cell beds as well, but only for jails in house basements. Let me find someone sleeping ... there she is. That's not a very relaxed position, and her head is a bit too much sunk into the pillow. I didn't have a sleeping animation, so I used the first frame of the idle animation instead.

A jail cell bunk bed with a woman sleeping in it.

She's not wearing an orange or black and white jumpsuit though. And high heels in jail? I guess there's not much I can do about it because I can't find a 3D model of a person with a prison uniform on Mixamo, other than one of the zombie models I already have. Wait, I can make all zombies prisoners. That's appropriate for this post! They're not lying in beds though because I don't have any animation for this zombie model that looks correct when lying in a bed. Both the idle and walking animations have him hunched over with his arms out.

Zombie prisoners have cornered me at the end of the jail hallway. At least he only has one arm to hit me with.

I still feel like these jails are a bit too bright. I can turn off indirect lighting and it looks like the screenshot below. There are more shadows and light vs. dark color contrast. I kind of like this look. It reminds me of a black and white photograph. What do you think?

A jail with concrete rather than plaster walls and many cracks. This screenshot has indirect lighting disabled and has harsher lighting with more shadows.

That was a fun post to write, especially the part at the end with the woman in bed and the zombies. What's next? Now that I have jail cells, it should be possible to add full prisons. I would need to work out the window placement so that cells can be added to above ground rooms. It's likely significant effort to customize the interior enough for a prison, so I'm not sure if and when I'll attempt this.

The project with source code is available on GitHub. Some people have been asking me how they can view these buildings themselves. This requires changing default.txt to enable config_heightmap.txt, which is the base scene I've added cities and buildings to. It helps to download the extra 3D models and textures from my Google Drive link. You can find the instructions in my README file.

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!

Sunday, May 11, 2025

Procedural Schools

I've been working on adding schools to 3DWorld's procedural cities on and off for a few months now. I started schools shortly after hospitals, and switched back and forth between working on the two building types. Hospitals got to a semi-finished state first, so I wrote a blog post on them last month. This post will be on the topic of procedural schools. I'm not quite done with schools at this point, but I'm starting to work on other unrelated topics. Maybe reader comments/feedback will give me some ideas on how to finish schools. I definitely got a lot of good feedback on hospitals last month.

As with the hospitals post, I'll describe each new type of room and show at least one screenshot for each of them. Technically, none of the room types is really new, but they're all at least somewhat customized for schools.

School buildings are of the taller and blocky "city" form rather than the strip mall-like one or two floor schools that seem to be more common in areas with more land. I tried my best to fit them into my standard office building templates with a central hallway and optional secondary hallways, with possible vertical stacked parts of progressively smaller footprints. This leads to most of the rooms having similar sizes, which isn't ideal, but I'll try to work with that. I ran into that same limitation with hospitals. Maybe sometime I'll find a better system to create buildings with larger and more complex footprints. I could use that for malls and factories as well.

Classrooms

I actually added classrooms to both schools and hospitals, though they're much more common in schools. In fact most of the rooms in schools are assigned as classrooms. I feel like the classroom discussion belongs in the schools post rather than the hospitals post, so I held off on writing about them until now.

Classrooms have a few requirements. First, there is a minimum room size required to fit at least 3x3 student desks. In order to guarantee most rooms are large enough to be classrooms, I modified the hallway and room generation logic in the office building floor planning step to create fewer larger rooms. One component of this was to increase the threshold or building size needed for adding secondary hallways. Classrooms must also have at least one exterior wall with a window, and no stairs, elevators, or exterior doors.

Most of the space in classrooms is filled with a 2D grid of smaller student desks with chairs, placed in multiple rows and columns as space allows. I copied and customized the code that I used to place rows of shelves in retail rooms. Any desks that are too close to a door or other object are omitted. The placement logic is relatively simple compared to some other room types because I don't need to worry about things like exterior doors, stairs, and elevators. A random assortment of books, papers, pens, and pencils is placed on top of these desks.

A teacher desk is placed against a wall, with preference for the long side of the room and interior walls with no windows. This desk has drawers and the top surface can have papers and an optional cup, which contains coffee 50% of the time. If the wall is windowless, a chalkboard/blackboard is added behind the teacher's desk and covers most of the wall. I'm going to refer to it as a blackboard because I've made them all black rather than dark green. Plus it seems to fit better with "whiteboard" used in offices. I then added an American flag to one side of the blackboard and an analog clock to the other. To finish the room off, I placed a few other objects such as plants, paper and trash on the floor, and room number signs outside the door.

A school classroom with student desks and a teacher's desk and chalkboard in front.

Here's a larger classroom with darker chair colors. The screenshot was taken from inside the doorway to the room, which is why there are no desks or chairs in the foreground.


A second classroom with darker chairs.

Oh! I also changed the titles of most of the books found in schools to match a selection of real school subjects. The list I came up with is: Math, Mathematics, Algebra, Geometry, Trigonometry, Calculus, Science, Biology, Chemistry, Physics, Economics, Computer Science, English, Literature, Language Arts, Social Studies, History, Geography, Government, Sociology, and Visual Arts. Some of these have "I" or "II" suffixes. Unfortunately, I haven't yet found a way to make the subjects match the images placed on the book covers. The textbook subjects found in each classroom are random as well, rather than matching the subject taught in the room.

A student desk with a calculus book ... that has a picture of a museum dinosaur exhibit on it? What does that have to do with calculus?
 

Blackboards come with chalk and erasers that the player can use to draw on them. I was thinking of adding some existing text and drawings on the boards, but I don't know where to get the images from. Drawing text with normal font doesn't look correct; I need something handwritten. Most of the images I saw in Google image search are artistic drawings rather than something you would find in schools. I was only able to find one image of some math that was high resolution, free, not watermarked, and the correct color and aspect ratio. It's clipped at the edges, but otherwise looks okay. This image is added to the blackboards of 25% of classrooms. If I find more useful images I'll add them later. Sadly, erasers held by the player don't work on these images.

Blackboard with some math writing that's somewhat stretched and clipped.

Hallway Lockers

Most larger building types have hallways, but only schools have lockers placed in these hallways. I added rows of lockers to each side of the primary hallway on each floor, wherever there was space. There are gaps in the rows where doors or other objects such as water fountains and fire extinguishers block their placement. Each school building part chooses a random locker color from a selection of light/bright colors. Some lockers have padlocks added to them.

School hallways also have a random chance of papers and trash on the floor. Otherwise, they're similar to office buildings with restrooms, water fountains, fire extinguishers, and trashcans on each floor.

A shorter school hallway with blue lockers and stairs. There is some paper trash visible in the bottom center.

Some of the larger schools have hundreds of lockers lining the hallway. This is probably too many given the number of desks in the classrooms, but it's fine for now. I'm sure some schools have too few lockers as well.

A longer school hallway with red lockers, elevators, and intersecting hallways. There are a few papers on the floor in the back.

The player can open the lockers without padlocks, and of course they contain items. All locker interiors have a larger bottom section and a smaller top section. Both shelves can have stacks of textbooks. Smaller objects such as cell phones, paper balls, bottles, and cans are placed on these books or on the empty shelf. Some lockers have papers attached to the inside of their doors. The larger bottom shelf also has a chance of a hanging colored tee shirt (which is a bit undersized, so I had to add it at an angle).

A school building with light brown lockers. I've opened the ones that aren't locked with a padlock.

Cafeterias

I added a cafeteria to some schools, though it's generally not large enough to fit many students. The minimum size is set so that there are at least three rows of tables. Each row has either a single long table or two shorter tables with an aisle in between. Most building floorplans contain similar sized rooms, which is a limitation. Maybe I should remove walls and merge two or more adjacent rooms into a single larger room? That way it could be used for cafeterias, auditoriums, gyms, etc. Of course I would probably need to increase the ceiling height of these rooms as well. None of this is particularly easy given the way the floor planning code is written and the fact that room assignment is run at a later time when it's too late to change walls or doors.

Cafeterias are similar to mall food courts and use the same table and chair geometry. The main difference is that cafeteria tables tend to be longer than mall tables. Various food, drink, and school items are placed on these tables. This includes cans, bottles, cups, pizza boxes, bananas, plates, silverware, vases, books, and laptops. There's also a clock on the wall. Now that I'm writing this, I think I'll add larger cylindrical trashcans to the corners of the cafeteria.

Cafeterias always have some type of tile floor texture that's easier to clean than carpeting. The rest of the school has randomly either carpeted or tile floor.

A school cafeteria with tables and orange chairs. Yes, that's a can of beer on the left table. You shouldn't be surprised, considering I had beer in hospital rooms as well (see the previous blog post).

Teacher Lounges

Teacher lounges are basically the same as the lounges found in some office buildings. They have a central table with chairs, one or more couches, and optionally a refrigerator, a vending machine, and a wall mounted TV. Here we have the opposite problem to cafeterias: The rooms are too large. I should probably add more items to make use of the extra space. Or maybe place teacher lounges next to cafeterias and move the wall so that half the lounge size is added to the cafeteria? This won't work in general though because there are multiple teacher lounges and only one cafeteria.

Teacher lounge with couch, tables, chairs, bookcase, vending machine, refrigerator, and wall TV.

Libraries

The final room type I've added to schools is libraries. Well, that's not really true. Schools have restrooms, offices, utility rooms, and server rooms as well. What I really meant to say is the final school-specific room type is libraries. Except that some office buildings have libraries as well. Anyway, this is the final type I'm going to discuss in the current post.

Libraries have the expected stacks of books in the form of bookcases against the walls. They also have  normal office furniture such as desks, tables, chairs, and filing cabinets. Sometimes they're small, such as the library in the screenshot below. I'm adding libraries last since they're optional, so they only get assigned to rooms that haven't been set to some other purpose. This means that they don't have much selection based on room size, and some smaller schools have no libraries.

A small school library with bookcases, desks, a computer, and a filing cabinet.

That's as far as I've gotten with schools. There are still some room types that I want to add later, especially if I can get more control over room size and height. It would be nice if I could find a way to add procedural writing on blackboards as well.

The code can be found in my GitHub repo here.