Wednesday, December 18, 2024

Procedural Malls

I started working on adding procedural malls to 3DWorld about two months ago. I actually had the item on my todo list for a while longer, but I didn't feel like I had all of the components for malls until recently. I finally got those last features from retail areas (escalators and glass floors), conference rooms (glass walls/interior windows), and exterior city elevators.

I can tell already that this will be a very long post. It should be, considering I haven't posted anything on Blogger in two months. I'm actually not completely finished with mall interiors either. I do feel like I have to post something now before my trip to PA for Christmas and New Years, because I may be too busy in January to have much time to work on this. I'm going to do things a bit different in this post by writing the text before adding the images. I'll be sure to scatter images around to break up the big blocks of text, once I'm done with the drawing code.

Has anyone attempted to generate procedural malls before? I can't find any online projects or articles on that topic. When I say "mall," what I really mean here is indoor malls rather than strip malls. And specifically in my case they're underground malls. Putting them under the ground makes for an easier starting point for both generation and rendering. I don't need to worry about windows, which means the mall interior can be drawn without having to display anything other than the mall, and only when the player is inside the mall. This allows me to pack more items in before I need to worry about memory usage and draw performance.

That doesn't mean the task was easy. These malls are massive, at least compared to to the rest of the buildings and underground areas. They extend far outside the terrain tile the building was placed on, far enough that I had to reduce the fog effect of the scene to make the interiors look brighter. The largest mall is a significant fraction of the length of a city center.

 

Mall Room Layout

The base of the mall is the concourse, which is a long rectangular room that extends from the parent building's connection as far out as there's space. I did add a length cap (of around 1000 feet) to keep it reasonable, though there's no hard limit based on the buildings or terrain systems. The connecting point is to the building's parking garage, which is the only other underground room/part. This isn't ideal, but at least it means visitors can find parking. I added an additional elevator that reaches the ground above the mall for buildings placed in cities. I'll describe this in more detail below.

All parts of the mall must fit into unoccupied underground area. The concourse is incrementally extended first, and then slightly widened. At each point the terrain above the ceiling is tested to make sure it completely covers the new section. Then I check that the room doesn't intersect any previously placed building basements, extended basements, in-ground pools, bodies of water, or sewage tunnels. Since the room is incrementally expanded on three sides, it may not remain centered on the parent building. Once the base floorplan has been set, the mall concourse is extended downward one floor at a time until either the selected number of floors is reached or an obstacle is encountered.


A large mall stretching out about a thousand feet into the distance with some 50 stores on two levels. I just had to add a palm tree.
The mall concourse is multiple stories tall with vertical pillars supporting the upper levels. I finally had to make the floor to ceiling distance higher than normal buildings. I used a value of around 2x the normal floor spacing, or 16 feet. The max number of floors is technically limited to 255, but I start running into performance problems at more than 5 floors. I set the max to 3 for now, though I've tested up to 10 floors without crashing. I think 3 floors is plenty as it adds space for up to about 100 stores. The upper floors have open cutouts so that players can look down at lower floors, and walkways with railings along the open areas. Each adjacent level is connected with several sets of stairs and up/down escalator pairs.

I placed a single elevator near the center that spans all floors. Then I added a digital or analog clock to the front and back of the elevator on each floor so that patrons would know the time. These are the same clocks I added a year ago, and they dynamically update to show the current real time. I often looked at these clocks myself while writing the code and wondered why I was up so late working on these malls!

A 7 level mall, just to show that it's possible. Something this large may not run well on low end or old computers, but it's functional.

Stairs and escalators reaching the lowest level can end on the large open surface of the floor. Those ending at middle levels would be floating in the air, so I added small landings connecting these exit points to the upper floor walkways at the sides of the concourse. Metal and glass railings are placed around all openings to keep the player and AI people from falling. The mix of low railings and transparent glass is mean to avoid obstructing the player's view.

Stores are placed to either side of the mall concourse on each floor, but only if there's space. Stores have random sizes so that there's a good mix of large and small spaces in each mall. Any store that clips through the terrain or intersects another building or tunnel is excluded, and the space is left empty. I also added stores to the two ends of the concourse if they fit, except for the place where the mall connects to the parking garage. All stores connect to the mall through an opening that may be gated, plus transparent glass interior windows between the store and the mall. In the future I may give the player control over store gates during gameplay mode.

Some floors have a pair of men's and women's restrooms near the center on one side and a water fountain between them. If there is a back hallway, it also connects to the other ends of the restrooms for employee access. These are the same restrooms that appear in office buildings with sinks, stalls with toilets, and a row of urinals in the men's room. I finally added support for door signs that rotate with doors, and used this to add male and female icons to the doors. I may go back and use this feature to add signs on office doors rather than next to them.

A pair of mall restrooms with a water fountain in between. The doors automatically close and the restroom lights are motion activated with a timer.

I also added back hallways connecting the sides of stores opposite their public entrances. These may eventually be expanded into storage rooms and other utility areas. The hallways wrap all the way around the mall if there's space, connecting into a ring. Lights are placed sparsely on the ceilings, which gives these areas a darker appearance with more shadows. Each completed end has an elevator next to a U-shaped stairwell that connects all floors that have hallways, assuming there's more than one. These hallways allow access to some stores even when the entrance gates are closed. Building AI people can use the elevator, but I haven't yet added AI logic for these new style multi-level wraparound stairs.

Back hallway stairs and elevator for the above 7 floor mall. I had to add a new type of wraparound stairwell that has a railing at the top floor.

Mall floors select a random marble or granite tile pattern, and ceilings use the same ceiling tile texture as office buildings. I chose brighter colors for mall interior walls to provide more of a contrast from the whites and grays used in office buildings and parking garages. This includes light blue, light green, and various shades of light yellow-orange-brown. Back hallways were textured with gray concrete similar to most of the other basement rooms.


Object Placement

The mall concourse is filled with a variety of objects. I added two types of trashcans along the walls. One is light brown and rectangular with a lid and opening, while the other is a metal cylinder with an open top formed from a black torus. I placed benches randomly along the walls, added pictures between stores, and put a water fountain between the bathrooms. These are a new style of mall bench with a molded colored plastic back + seat and black metal legs. I added plants to the sides of the stairs, escalators, and support pillars. Some pillars also have fire extinguishers on them.

The lowest level has extra space for placing objects because there are no open cutouts. I filled the open spaces under the cutouts with one of several large items or item groups. First, there is a fountain near the center of the mall with benches placed to either side. Next, I assigned an area as a food court and filled it with tables of various sizes and chairs. Some of these tables have additional small items placed on them, in particular food-related objects such as pizza boxes, bananas, water bottles, and cups. I haven't yet added actual restaurants, but they will likely be included sometime in the future.

The remaining open spaces are filled with either large potted palm trees or groups of vases/sculptures. Palm trees may look odd inside a mall, but I did want to try and use every type of object I had available. In fact I even tried to add a pine tree that I could maybe turn into a Christmas tree, but I couldn't get the alpha blending of the leaves to look good when mixed with the transparent store windows. It's nice to have some green spaces indoors, and the trees fit well with the potted plants. These sculptures use the same shapes formed from procedurally generated rotated profiles as the smaller vases and urns that can be found in houses.

Mall food court with tables and chairs, but no actual restaurants (yet).

Finally, it was time to populate the stores with items. Since malls can have as many as a hundred stores, I had to take extra care not to place too many individual objects in total. Grouped objects such as shelf racks, bookcases, shelves, etc. work much better because they can expand to their individual contained objects dynamically as the player interacts with them. This is a huge advantage of my virtual object system, which I feel is unique to my specific game engine.

Some objects are common to most of the store types. These include checkout counters with cash registers, security gates created with a grid of metal bars, tall support pillars, ceiling ducts with vents, and anti-theft sensors. The theft sensors in particular were a fun addition because they're interactive. The player inventory system now keeps track of which room/store objects are taken from, and the sensors can detect that the player is leaving the store with stolen items. I added an alarm sound and flashing light to these devices. The easiest way to shut them up? Steal the sensors as well!


 

Store Names

Before I describe how I've generated store interiors, I need to mention store names. What's one thing that malls are full of? Signs, right? Big, bright signs enticing visitors to enter the stores. Now I'm pretty limited here, considering I only have one font to work with at the moment. I can't add fancy signs - yet.

At least I can try to come up with decent store names. My first attempt was to use the Markov chain based name generator I used for planets, street names, last/family names of people, etc. I found that some of these names work, while others are difficult to even pronounce. I could do something closer to my company name generator that works be selecting random permutations of words from a list. The problem is that this tends to create names for a particular type of business chosen randomly, while I want the name to match a specific type of store. I needed the classification before the name, compared to how I handled buildings here. I also don't want to run into registered trademark trouble by selecting names randomly from a list of existing, real world store names.

After that I turned to searching for store names online. The search turned up mostly AI-generated name lists divided into specific categories. In the end I found a free online AI name generator and gave it prompts for the various types of stores I wanted, then manually reviewed, copied, and pasted the names I liked into a ~300 line text file. Here is it, complete with the sources I used. 3DWorld parses this line-based text file and creates a map from keyword to an array of names. Take a look at all the signs in the screenshots of this post for examples of what these names look like.

 

Store Types

Next, I'll go over the various types of stores currently found in 3DWorld's malls. There are retail stores, clothing stores, bookstores, furniture stores, food stores, and pet stores. I have appliance and plumbing stores in the list as well, but they're not yet selected/enabled because I haven't added any objects to them. Retail stores are further broken into a number of categories: food, electronics, household goods, kitchen, and boxed goods. Each type and category of store has a unique set of objects that it can randomly choose from per-shelf. Most of these are one of the existing ~180 room objects I've added to buildings so far. Some of these stores are more complete than others at the time of writing this post. I don't want to get into all of the details as this would make my post a huge wall of text and a beast to load with so many images. I'll add some brief descriptions of each store category that I've added below.

Retail Stores

Retail stores consist mostly of rows of what I call "shelf racks", which are the same objects I added to the ground floor retail areas of some office buildings almost a year ago. The only changes I had to make to shelf racks was to add control over object types so that they would match the theme of the store. These objects are covered pretty well in last year's post, so I won't go into the details again here.

Retail food store with rows of shelves containing food and drink items. Retail kitchen, electronics, and boxed goods have the same shelves but with different types of items.

Clothing Stores

To me, clothing stores look like huge closets. Therefore, I started by filling them with the contents of closets. In other words, clothes on clothes hangers that hang from horizontal bars. This isn't the first time I've reused closets. I actually used them to add clothes lines to residential yards, but I'm not going to get into that topic right now. The point is, this was fresh in my mind from the clothes lines. The more ways I can find to abuse and reuse code, the less new code I have to write, and the faster I can make progress. This is the key to implementing malls in a few months rather than a year. Ah, I'm getting distracted.

That handles the center areas of the store, but I still need to place objects along the walls. Maybe shelves with folded shirts and anything else similar that I happen to have? I would add shoes if I could find suitable low poly models of them. I definitely need to be careful with the model count though if I expect people to play this game without a fancy gaming PC. With this in mind, I mixed some flat rectangular images of shirts and pants in with the folded T-shirt models on wall shelves.

Clothing store containing rows of metal racks with shirts and pants on hangers. Shelves on the sides of the room hold folded shirt models and images of T-shirts and jeans.

Bookstores

Bookstores are pretty simple. They mostly consist of bookcases stretched out short and very wide and placed back-to-back in rows, with additional bookcases along the walls. It's easy enough to hack the bookcase creation code to form these. The only problem is that the performance is terrible. I attempted to create an entire large mall full of bookstores, and it takes nearly a second to generate and 600MB of additional GPU memory. There are something like 200K total books with millions of characters of text in all the author and title strings. Hopefully the performance is acceptable if I limit bookstores to only 10% of the total. I'll have to go back and optimize this eventually.

An example bookstore with dozens of bookcases arranged in rows containing thousands of individual unique books. Each book is unique and can be taken by the player.

Furniture Stores

Furniture stores are full of ... furniture, which I already have quite a bit of. I can reuse the various objects currently placed in houses and office buildings: tables, chairs, beds, dressers, nightstands, couches, and desks. I divided the store into a 2D grid of square sub-rooms which are classified as bedrooms, living rooms, dining rooms, kitchens, bathrooms, or offices. I added enough empty space between these sub-rooms for people to walk. The difficult part is organizing these into a coherent placement where rooms of the same types are grouped together. I'm not quite sure how to do this incrementally (one sub-room at a time), so I'll leave it for future work. I then added walls next to beds in interior sub-rooms with pictures on them that simulate background room walls. There are many areas for future improvement.

Furniture store with a mix of bedroom, living room, dining room, and office furniture. This is one of the larger stores at the end of the mall concourse.

Food Stores

Food stores are difficult. I already have retail areas with shelves full of food products. However, with this category I was thinking more about restaurants, coffee shops, and other places you would find around the food court. The problem is that I don't have many food related objects for buildings. All I really have are pizzas and bananas, and nothing really store related. Stoves and pans are about all there is. Food items, or rather anything organic, are quite difficult to generate procedurally. I can't get away with making them from cubes the same way I created much of my furniture. I suppose some fruits aren't that hard. After all, an orange is really just an orange sphere, right? For now I guess these stores will be mostly empty. Well, I suppose I can at least add some wine racks.

Sorry, food stores don't exist yet, so there's no screenshot for me to share.

Pet Stores

I have a limited selection for animals that I can add to pet stores. At this point I don't have 3D models of the usual pets such as dogs and cats. What I do have are rats, snakes, spiders, fish, birds, butterflies, cockroaches, and flies. I guess I'm going to stick to the first four or five though, since most pet stores don't carry insects.

I already have fish tanks that I can add - one down. I can reuse the tanks without water for snakes and maybe spiders. It would be pretty awesome if I could make spiders climb the inside glass of the terrariums! I'm sure I can either find or generate some sort of cage models for rats and birds. But in the meantime, I'll put rats in glass tanks as well. I know that's not good practice as the lack of ventilation is bad for their health, but I do see this done at some pet stores. Don't expect me to be filling these with toys for the animals any time soon though. It might be interesting to set some animals free and allow them to wander around the store since I already have the logic for this. I can possibly allow the player to pick animals up and put them back down.

An example pet store, with a closed gate. Currently pet stores contain only fish in fish tanks and empty glass tanks that will hold rats, snakes, and spiders in the future.


City Elevator Access

I wanted to add mall entrances other than that single door from the parking garage. What if pedestrians who aren't in cars want street access to these new malls? The problem is that the mall is underground and adjacent to the parent building, which means that there's no other place where I can add a door between them. The building system doesn't support secondary buildings connecting across terrain tile boundaries, while malls generally span multiple tiles. Cities have flat terrain above underground rooms, but areas outside the city have procedurally generated terrain with erosion that I can't easily add a building entrance to. This leaves only street level city building entrances to work with.

The mall entrance from the underground parking garage has fan-shaped stairs that descend to the floor of the upper level of the mall. I haven't added support for non-axis aligned railings, so I can only add railings along the back wall.

The obvious solution is to extend the existing elevator in the center of the mall up another floor to ground level. The placement system needs to check for intersections with above ground city objects such as buildings, roads, parking lots, trees, etc. If an intersection is found, either the intersecting object or the mall entrance must be moved. I chose to move the mall entrance since there are typically multiple candidates for mall elevator locations. They can be connected to either side of any available opening in the upper floor walkways. One limitation is that single floor malls have no need for an interior elevator, so there's nothing to extend up to street level. However, these rarely occur in cities because the flat terrain doesn't allow for the basement rooms of buildings at different elevations to block the area under mall concourses.

Malls are always connected to two floor parking garages since the upper mall concourse level is two floors tall and parking garages are at most two floors. This means that street level is always exactly one floor up. The difficulty here is that the elevator may still be far from the parent building, so the entrance can't be made part of the building. It must be a separate city structure. Fortunately, I can borrow parts of the earlier walkway and city elevator system to create a new city object that sits on top of the elevator shaft to form the exterior surface.

An above ground mall entrance elevator inside a city next to a building. Lighting is not working properly yet. People can walk by but not enter these elevators.

This gives me a drawn elevator, but it's not yet functional. The elevator buttons can't be pressed by the player because this logic is only run when the player is inside buildings. The best fix I could find was to add an invisible rectangular area over and in front of the elevator's entrance that's considered to be part of the building so that the player can call the elevator to that ground floor. It was quite messy to implement, though I did eventually get it working. I slapped a "Mall" sign above the entrance, added a "G" button label for "ground floor", and considered this part done.


Zombie/People AI

I didn't originally plan to write the AI logic for mall navigation because I assumed it would be a huge time sink to handle all of these stairs and escalators, gated doorways, and larger spacing between floors. In the end I did put some effort into this, scattered between the various other additions. Regular building people weren't that difficult to handle. I had to change some 20-30 places in the AI navigation code to support stairs, escalators, and elevators of variable heights. I added logic to the navigation graph to include non-gated doorways between rooms. And I had to put extra effort into handling those mall back hallway elevators that were outside of any interior room bounds.

A three level mall where the levels are connected by stairs, escalators, and an elevator. The stairs and escalators of the middle level connect to small landings. Building AI people can use all three of these.

I didn't complete everything. For example, people don't currently walk on those fan-shaped stairs that connect the parking garage to the mall concourse because they're not in a standard shape and have an odd height difference between the two floor levels. People don't use back hallway stairs either as I haven't written the AI logic to use stairs that wrap around in a cube-shaped spiral. Zombies sort of work, though they don't follow the player in all areas of the mall. In particular, places around stairs and escalators give them trouble since the collision boxes that cover those objects are too conservative. I'm sure I can fix all of these in the future with more effort.

Debug view showing the exterior of a mall that's under the terrain. The gray strips along the outside are the back hallways that wrap around the mall stores. There's a 7 level mall in the back right.


Summary and Future Work

I've added quite a bit of new content for malls. Some of these objects have been reused in other building and room types. For example, the plastic tables and chairs I used in mall food courts are now also used in offices. The mall style trashcans are used in office building lobbies and hallways. I have some ideas on other objects to add as well, but I can't add too many. I'm already at 185 unique room object types. The current limit is 256 unique types because the type field is stored in an 8-bit integer, which only has 256 possible values. I put a lot of effort into packing the room object class into 64 bytes, which should be a multiple or exact fraction of the cache line size.

Many of the store types are incomplete. Notably, this includes food stores, appliance stores, and pet store animal containers. This leaves plenty of room for later additions and improvements. Expect to see more mall-related posts in the future as I continue to add content. In particular, there may be an update post on pet stores once I have all of the animals added to their containers.

As always, the source code for all of this is publicly available on GitHub. The mall code in specifically can be found here.

Friday, October 25, 2024

Procedural Buildings: Sewer Tunnels

I was watching some YouTube videos of urban exploration where people were exploring storm drains under a city, and this gave me something else to add to 3DWorld's procedural cities. I've improved basements in the past few posts by adding interrogation rooms and machine rooms. Now it's time to add underground tunnels. I originally thought of connecting these to the water (ocean), but none of the cities are close to water. Also, the water is far from the player spawn point near the center of the island. So instead I decided to connect tunnels to basement rooms instead.

I started by iterating over all of the locked ("false") doors I had added at the end of extended basement hallways and long rooms to make them appear larger. Any door that was far enough underground and had empty space surrounding it on three sides was a candidate for placing a sewer tunnel. I added a vault-style heavy metal door with a wheel handle connecting the room to the tunnel that was open to the player. Tunnels run perpendicular to the room and door in both directions and may have right angle bends in their paths. I added metal grates in the form of vertical bars near the ends of the tunnels and made the very ends black. This, combined with dark fog, made the tunnels appear to continue past the grates.

Here is a view of a sewer tunnel system under the ground with the terrain and grass drawing disabled. This network runs around the perimeter of the extended basement of an office building, but in general it can go anywhere there's available space. I may even connect tunnels between multiple buildings in the future like I already did with extended basement hallways.

View from outside a tunnel system that runs under the ground. The terrain and grass drawing has been disabled. The dark rectangle on the bottom right is the sewage quad drawn under the bend.

Tunnels have dark brown sewage flowing inside them with a randomly chosen level and flow rate. They also have smaller pipes connecting to them on the sides and thin metal pipes passing through them along the top. I added rats on the ground in some segments that squeak and run to the nearest grate when the player approaches. I also added spiders that walk back and forth upside down along tunnel ceilings. Spiders don't currently interact with the player.

Tunnels are drawn as cylinders, with quarter torus sections for bends. It actually took me quite a bit of experimentation to figure out what the correct bend geometry was. Fortunately, I had already added half torus drawing for bucket handles, so a quarter torus was simple to add. I used a concrete texture for the walls and applied the wet effect to these to give them streaks of darker color. Speaking of darkness, there's no light inside these tunnels, so the player must bring their own flashlight or candle to see anything. See how dark this is? It's completely disorienting to walk through here without a light.

Flashlight view of a sewer tunnel near the place where it connects to a basement room, with a spider visible on the ceiling.

I did add logic for zombies to follow the player into the tunnels with a recursive path finding algorithm that forms a path between the connected cylinder and quarter torus sections. These are currently dead-end death traps for the player. Maybe I should add some reward at the end of the tunnel? I'm not sure what to place here. A vertical shaft leading back up to the surface that serves as an escape path? That could work in cities where the ground above is flat.

I'm considering adding T-junctions to sewer systems. I haven't yet figured out the mathematical function needed to join cylinders into a T-junction in a smooth and clean way. I do have a T-junction where the tunnel meets the room doorway, but this is square on the doorway side. It's easier to cut a square hole into a cylinder (remove half/180 degrees of the cylinder) than a circular hole. 3DWorld has CSG support for cubes, some types of cube + axis aligned cylinder operations, and a few others, but no cylinder vs. cylinder operations. This would be a problem for vertical escape shafts as well. It would be so much easier if I used rectangular tunnels. The problem is that I already have extended basements, which aren't much different from rectangular tunnels. The main difference is the lack of sewage on the floor!

Here is an earlier video where I explore a sewer tunnel with a flashlight and candle as light sources.

The player can find both light sources in buildings. Flashlights shine further with a narrow beam. Candles light only nearby objects, but in all directions. There are no ceiling lights, shadows, or indirect lighting.

Here's another video where I added spiders to the ceilings.

I think I'm done with basements for now. I may add underground shopping malls sometime in the future if I can figure out how to do it efficiently. The next item on my task list is writing the logic for cars to navigate city parking lots. I'm sure that will be challenging, considering how difficult cars have been to work with in the past.

Friday, October 11, 2024

Procedural Buildings: Machine Rooms

I've been adding new room types to 3DWorld's procedural buildings lately. I wrote a blog post on conference rooms, then a short post on interrogation rooms. The topic of this post is machine rooms. I've been wanting to add procedurally generated industrial type machines/devices to basements for a while, ever since I revisited basement pipes. I've also wanted to add more variety to extended basement rooms.

I've been working on adding code to route basement and parking garage pipes down the extended basement hallways and into leaf rooms. But it only makes sense if there are objects in these rooms to connect the pipes to. I do have bathrooms with sinks and toilets down in the basement, though not every building has these. Another option is to add more water heaters and furnaces similar to the ones placed in house basements and office building utility rooms. The appliances I've created are more residential sized and look out of place in a large open basement room though.

I added a new type of building room object called a "machine" that's represented as a bounding box placed on the floor and against a wall of the room. All placement and collision detection handles this as a box. However, the drawing code is free to add any details it wants when the vertex data is generated and sent to the GPU. None of this geometry is stored CPU side, it's all created as temporary data that's drawn later. An added benefit of machines being placed in basements is that the vertex data doesn't need to be generated until the player enters the building. This allows me to create very detailed geometry without the need to worry about optimizing for memory or generation time, since these are only found in one building at a time. I do technically create machines for multiple buildings ahead of time to help with visualization and debugging, but I don't think it makes much of a performance difference.

Each machine consists of a concrete slab base and one or two main components, which can either be rectangular/cube shaped or vertical cylinders (such as storage tanks). These larger components have smaller boxes and cylinders attached to their exterior surfaces with different colors and textures. I then added some special decoration objects that are already used in other places in buildings. These include AC units, vents, valve/wheel handles, and breaker boxes. Some breaker boxes are open to expose the interior. To finish things off, I added a variety of pipes and ducts connecting the components together and going into the walls, ceilings, and floors around the machines. These use multiple materials including plastics and metals (copper, brass, bronze, steel, and rusted). I used special checks to make sure these added detail objects don't intersect each other or clip through adjacent walls or machines in incorrect ways.

I found ten free textures online to use with these machines. Most are some type of metal panel or plate texture, some of them with rust and scratches. Only one of them has a matching normal map. I may look for better textures with normal maps later, but these are fine for now. I haven't put any effort into aligning textures to geometry because it's too difficult to automate this for this many different textures.

Here is an example of a long and narrow machine room.

Machine room with random machines lined up against the walls. Each one is unique.

 

Note that these rooms are somewhat brighter than they normally appear to make them easier to see. I prefer to have these deep basement rooms be darker. Here I have indirect lighting enabled, though it doesn't work very well due to the way rooms are randomly and sparsely placed, compared to the dense rectangular floorplan of above-ground building interiors. At least the shadows on the lower walls and floors are correct.

Machines are placed along the sides in a way that allows space for the player and building AI people to cross the room between them. I added extra clearance for doors so that they're not blocked. The rooms in these screenshots have up to 10 machines for testing purposes, though for normal buildings I'll probably set the max count to 4.

A different machine room with different machines and many cracks on the walls, ceilings, and floors.

I definitely like the way the cracks and water damage fit with the scratched and rusty metal textures I'm using with machines. The crack effect only applies to the walls, ceilings and floors though. I don't think cracks go well with metal surfaces. I did apply the wet/water damage texture effect to machines. This give the surfaces a darker but more shiny appearance. Here's another machine room with water damage and stains but no cracks.

A machine room in the basement of a house, with plaster walls.

I think I'm done with new room types for now. I plan to continue to work on extended basements in the near future. Next up is underground tunnels/pipes that the player can walk through. I'll likely have a blog post on that topic within the next few weeks.

Tuesday, October 1, 2024

Procedural Buildings: Interrogation Rooms

The interrogation room can be found deep inside the extended basement, after a long walk down dark and narrow passageways. A single empty chair sits in the center of the room under the light, facing the door.  Buckets of unknown colored liquids are placed around the chair. Bottles and bits of trash are strewn across the dirty floor. The walls, ceiling, and floor are wet, stained, and cracked. A ladder with an unclear purpose leans against a wall.

An interrogation room with cracked walls. It's not very bright in here, so I lit it up with my flashlight.

Interrogation rooms are relatively rare and are only placed in extended basements of buildings with water damage. They can appear in both houses and office buildings. Sometimes the chair has fallen over backwards, and sometimes the light doesn't work. Broken lights shower sparks over the chair. The ladder is optional. This is the only room that currently contains the new bucket-with-liquid object.

These rooms don't serve a purpose yet. They're mostly just there to be creepy, though I didn't go so far as to add blood, tools of torture, or anything like that. I might not add this at all, since it's not supposed to be that type of game.

Also, take a look at my new basement cracked surfaces effect in the screenshot above. I replaced the old shader code with new code that uses a Voronoi grid and an intensity mask to apply cracks to selected areas. It's based off of this Shadertoy shader. Cracks are added as a layer that modifies the material properties of the surface it's applied to.


Monday, September 30, 2024

Procedural Building Conference Rooms

I'm pretty happy with the look of glass upper retail floors added in my earlier post. Maybe they're not very practical for public spaces, but they make the room look larger while also allowing for increased usable space. I need more glass in these buildings. It would be interesting to have glass walls as well. Maybe for conference rooms? We have some real conference rooms with glass walls/windows at my work office.

The first step is to choose which rooms will be conference rooms. Since most of the buildings in 3DWorld share the same vertical floorplan across many floors, it makes the most sense to have one or more dedicated conference room on each floor. The ground floors of office buildings do have some special rooms such as storage rooms, security rooms, and utility rooms, so I can have the floorplanner overwrite the conference room with a special room type on the first floor.

Since I want to add glass walls to conference rooms, it makes the most sense to place them adjacent to hallways. A glass wall between two conference rooms, or a conference room and an office, would be odd. And I don't even want to think about what a glass wall shared with a bathroom would be like! I also have a constraint that the conference room can't have an exterior wall, since the interior window won't alpha blend properly with the exterior windows. (I spent a long time trying to solve this with glass floors and never found a solution I was happy with.) Fortunately, this means I don't need to worry about walkways connecting between buildings that end at conference rooms. The candidates are all rooms next to an interior hallway that have doors that either open into a secondary hallway that meets the other hallway at a 90 degree angle, or to another parallel hallway one row over. Another requirement is that conference rooms don't have any overlapping stairs or elevators. This rules out some rooms that have notches cut out where elevators are placed along the sides of the main hallway.

There is also a size preference. I want to add large conference room tables and many chairs to these rooms, with space around the sides for people to walk. The conference room should be the largest room that meets the placement requirements. I gather a list of all legal room candidates, find the largest one, and select all rooms within 90% of the largest room's area as possible conference rooms. Then one or more of these rooms are selected. Any room that has a conflict (such as intersecting stairs) on one floor is changed to a different room type such as an office for that floor. This sometimes happens with conference rooms at the top or bottom of a building stacked partition where stairs extend above or below.

Each conference room has a single large table that runs most of the length of the room, with as many rolling office chairs as will fit along each side. One of the walls at an end of the table has a large TV hanging from it, and another wall has a large whiteboard. Neither of these can be placed over the window or door(s). I found a conference room phone 3D model that I placed on most of the tables in a random position and orientation. Then I added some random items on top of the tables such as laptops, pizza boxes, books, cups, and water bottles. Some rooms have trash cans, recycling bins, and/or potted plants. Maybe I should add pens, pencils, and papers as well.

Here is what an example conference room looks like. There are actually two adjacent conference rooms with glass windows on this hallway. The doors open into the next hallway over, toward the back.

Two conference rooms in an office building, viewed from the hallway through glass windows.

I added proper collision detection for these glass panels that works with the player, building people, zombies, rats, snakes, insects, thrown objects, etc. Spiders can climb on the surface. I had to fix a number of problems related to lighting and occlusion culling so that objects on the other side of the glass were lit and visible even though there was no doorway between the rooms. I was originally worried that the AI would have path finding problems if a zombie saw the player on the other side of the glass but couldn't pass through it. But no, the existing system worked fine and the zombie walked out the door and around the corner to reach me. They target the last known location of the player when the player isn't visible.

Lighting isn't perfect for these rooms. Light will pass through the glass from the adjacent room, but the sides of the glass are only lit by lights on the same side. This means that the glass on the inside of a room will appear dark when the room lights are off but the adjacent room lights are on. This is one reason why I turned all the lights on for these screenshots. I did make sure to flag conference room lights with motion detection logic so that they automatically come on when the player enters, which helps to hide this problem. The player can manually switch them off though.

Conference room in an office building, viewed from inside.

Sometimes the window is on the shorter wall of the room. The conference room below has a long whiteboard and short glass window. This room is large enough that it needs two ceiling lights, rather than the more common (and efficient) one light per room. I believe there were 7 chairs on each side of the table.

Larger conference room with a long table that has a phone and a laptop on it. There are 14 chairs.

I haven't made these windows reflective yet. That's a complex process that reduces framerate significantly as some parts of the building interior must be drawn twice. Most indoor windows are designed to be anti-reflective anyway, so the reflections are only visible at sharp angles. I don't feel that the benefit of adding reflections justifies their cost.

While writing this post I though that it would be interesting if the player could break, or at least crack, these windows. Maybe throwing a large enough object at the window will create a crack, similar to throwing a ball at a mirror. The problem is that the largest object the player can actually throw is a ball. I don't think a basketball or soccer ball would normally crack a window. Maybe I should add some new object that can be thrown, where the only purpose is to crack windows. Should the player be able to throw a fire extinguisher? That would do it. Currently fire extinguishers can only be dropped. Or maybe the player can throw a laptop left on the conference room table, or the phone? Should I allow zombies to break through windows to get to the player?

Thursday, September 19, 2024

City Walkway Elevators

I added elevated walkways and skyways to 3DWorld's procedural cities earlier this year. The skyway with its moving belts on the floor provides the player with a transportation system that's several times faster than walking, assuming they're not setting the movement speed higher like I often do when debugging. The problem is that the skyway is very high up in the air, and it takes a long time to get to it by taking the stairs or elevator from inside a building. This means that it's not a very effective transportation system, considering the skyway is also limited to a single city.

I need to add a faster way for the player to gain elevation without passing through a building. One idea I had was to add elevators that start at ground level in an open area of the city near the sidewalk and extend upward to connect to the side of a walkway. I had this idea around the same time as my escalators, which I showed in the previous post. I decided to make these elevators have transparent glass walls similar to the upper glass floors I added to retail areas. However, I haven't made these glass elevators reflective, so the process has been easier. People don't use these elevators (yet) either. All they have to do is avoid walking into the elevator shaft.

Elevators are placed at the same position as the support pillar under the walkway, off to one side. This is typically around the midpoint of the walkway span. The pillar region has already been chosen to be in an open area free of other objects, which makes it likely that the elevator shaft can fit there as well. The elevator is omitted if there's a collision with another object such as a building. This includes objects that block the entrance to the elevator on the lower level.

Here is an example of an elevator connected to a relatively low walkway between two buildings that's two floors tall. There are doors at both the top and bottom that open and close to prevent the player from falling into the elevator shaft or being crushed by the elevator at the bottom. The upper floor doors open to the sides like normal elevator doors. I couldn't find a good way to add side opening doors at the bottom because of the way the elevator shaft is narrow and vertical, so I added a door that slides upward instead. It's really more of a gate - a grid of black metal bars that doesn't block the player's view through the glass walls of the elevator.

City walkway elevator viewed from the outside. Elevators have glass walls, a metal frame and platform, and a gate with bars guarding the bottom entrance.

These elevators have no player controls. They're fully automatic and will anticipate the player's movement so that they're open and waiting on whatever floor the player is currently on. They will change floors as the player changes elevation inside buildings or by flying in debug/noclip mode. They're also pretty safe as the glass walls and doors prevent the player from falling into or out of the elevator shaft. I did add gameplay fall damage as a "reward" just in case the player can find a way around this system.

I cut a hole in the exterior wall on one side of the walkway to add the elevator entrance. This had to be done for both sides of the walkway wall since the exterior wall is "owned" by the city while the interior is owned by one of the connected buildings. The entrance on each floor has typical dark window/door trim added around it to cover up the edges of the walls. I set up buildings so that the walkway interior is drawn when the player is in the elevator shaft. It's typically blocked by closed doors when the player is outside of the elevator. Elevators use outdoor city lighting with sun and moon light + shadows since the majority of their surfaces are outside the walkway.

City walkway elevator with doors open, seen from inside the walkway

These elevators use the same door opening/closing and bell sounds as interior building elevators. In fact a lot of the ideas and code were copied from indoor elevators. Once I've worked out the logic for these types of objects it's much easier to reuse in other places.

Here is a short video where I travel up and down an elevator leading to a high walkway that connects to the skyway.


Sunday, September 8, 2024

Escalators for Procedural Buildings

I added skyways to 3DWorld's procedural cities a few posts ago. These included horizontal moving walkways that the player could use to get around more quickly. This got me thinking that I should add escalators, since I already had stairs and elevators. At first I considered using escalators to replace the short sets of stairs that lead from walkways to skyways above them, but these are uncommon and don't have much space to work with.

Escalators

The first question was, where do I put escalators? I want them to be placed inside procedural buildings somewhere. I already have stairs and elevators connecting the floors of my buildings together. Escalators need more space, and are ideal for rooms more than one floor tall. How about placing them in tall retail areas? There's plenty of open space, both horizontally and vertically. I decided to add a pair of escalators going in opposite directions next to each other along the center line of the long dimension of the room. I start by placing them in the center of the room and then shift them alternately to either side until I find an open place between existing stairs, elevators, and exterior doors.

Originally, the escalator placement failed some of the time because the stairs and elevators were spaced out in the center of the primary hallway in the office building floors above the retail room. But now I've moved some of the elevators to the sides of the hallway, which frees up more space for escalators in the smaller buildings. You can see one of these side elevators in the screenshot below.

Here's an early version of a working escalator prototype. The bottom floor/steps part is animated in my attempt to recreate the movement pattern of escalator stairs. The player can ride it up and down, though there's no floor connected at the top. All you can do is fall off the high end. Walking up and down is much faster than standing and letting the escalator move you. Objects such as balls and animals collide with escalators as well, though they can't actually move with the escalator.


Glass Floors

The next question to decide was what to put at the top of escalators. I wanted to add another level to the retail section, something different than standard tile floor. I like working with reflections, so I added a transparent glass floor. Maybe this isn't very realistic for multi-level public interior spaces where one person can look up at someone above them. But I had already added this before someone pointed out this problem. Each floor is currently a single rectangle. In the future, I may add a second shape to form an "L". As I expected, the combination of transparency and reflectivity made this extra challenging.

First, the transparency: In OpenGL (and other graphics APIs), transparent objects must be drawn after the opaque objects for them to properly alpha blend with geometry drawn behind them. The problem is that the glass floor is next to windows that lead outside the building. This means that exterior objects such as the terrain, grass, trees, roads, etc. must be drawn before the glass floor. This is a problem because buildings are drawn first, and interiors use a different lighting system from exteriors. I had to draw the glass floors in a final pass after everything else in the scene was drawn. I didn't want the overhead of setting up the interior lighting pipeline a second time in one frame, so I changed the lighting system to use a custom ambient value for each face of the glass. This technically works because it allows the glass to be lit from both the interior lights and the outside sun, from either the top or the bottom side. It avoids the unrealistic dark surface on the bottom "unlit" side of the glass. The only downside is that the floor doesn't receive shadows from other objects. I think this looks acceptable, especially when reflections are added.

Reflections were the next challenge. I only needed to make the top surface reflective, so that the reflection was only enabled when the player was standing on top of the glass floor. 3DWorld doesn't write a buffer that contains normals that can be used for screen space reflections, so I had to render the scene a second time from a reflected camera. This works the same way as bathroom mirrors and swimming pool water surfaces, except it's an entire floor of a large room filled with tons of objects. Compare this to swimming pool rooms and flooded basements that have sparse object placement, and mirrors that have a small reflected region. On top of this, the exterior is visible through windows and must be drawn as well. It was quite difficult to get fast reflections that didn't halve the framerate. (Keep in mind that I'm targeting older computers for 3DWorld, so my goal is 120+ FPS on my gaming PC.) I had to rewrite parts of the object occlusion culling system and implement horizontal clipping planes for drawing.

Finally, it was time to add objects to the upper glass floor. I started by adding railings along the open end to keep the player and AI people from falling off. Then I added additional rows of "shelf rack" objects on the upper floor above the rows on the lower floor where there was space. Any shelf racks that partially fit were cut to shorter lengths as long as the clipped rack was above a certain minimum length. I made sure to add extra padding around the end of the escalator and any openings of stairs and elevators that reached the second floor. This works, though the spaces can be tight in some cases.

Here is what the escalators and glass floor looked like after these changes.

A pair of moving escalators in the retail area of a building leading up to a glass partial floor.

The escalator has upper and lower straight ends and a 45 degree polygon in the middle. I didn't have the patience to try and write the code to create proper curved sides like I see in most real world escalators. These shapes are much easier to write collision checks for anyway. Each escalator has a vertical pillar at the high end to help support it and the glass floor attached to it. I thought this would give them a more interesting look than filling in the area under the escalator with material.

Side view of the same pair of escalators. Maybe they need more depth to the sloped part? From this angle they look like a pair of high heeled shoes!

Here you can see some building AI people managed to make it up the escalator to this empty glass floor. It's easier to test reflections of dynamic objects without the shelf racks in the way. (Well, that's true, but in reality I've added these screenshots out of order.) I actually started by removing the transparency effect and setting the Fresnel term to 1.0 to turn these floors into a perfect mirror. That made it more obvious when something was missing or otherwise wrong with the reflection.

A glass floor at the top of the escalators with a railing and not much else to do. The reflections of the people and the interior of this room are visible in the glass.

It looks questionable when the escalator is in the center of a large building and half the building footprint is a single unsupported glass floor. That must be some pretty strong glass! So for the larger areas, I added vertical columns that pass from the floor below, through the glass, and into the ceiling. Technically these already existed on the floor below. I then added horizontal steel beams under the glass connecting the pillars with the exterior walls and each other. I suppose it looks more structurally stable this way. Here is what this looks like before objects were added to the upper floor.

A larger reflective glass floor area with an elevator through it, plus vertical support columns and horizontal metal support beams.

Note that this floor has an elevator going through it. Two elevators in fact, though the one on the left doesn't have enough clearance from the escalator to have a door on that side. Only the right side elevator can access the glass floor. The glass is clipped out in the elevator shaft so that you don't pass through it when riding in the elevator. It should be possible for enclosed stairs to pass through the glass floor, though I've never seen this happen in a building I've visited. I believe the reason is that stairs are placed in the center of the hallway, the escalator is placed next to them, and the glass floor is always the smaller half of the room area (which excludes the stairs).

And here is the same area with shelf racks added. You can see the reflections of the racks and their contained objects on the floor.

The same reflective glass floor as above, but with racks of shelves added on top to give people a reason to come up here.

AI People

Did I say the glass floor transparency and reflections were challenging? They were easy compared to updating the AI logic. It actually wasn't that bad to make the normal building people use escalators. They work like stairs, except they must select the correct "up" or "down" escalator based on the direction they're headed. People walk up and down escalators rather than standing and riding them because it was easier than trying to blend in an idle animation. I might attempt the standing/idle version of escalators sometime later.

Now that I have people getting to these floors, I have to give them a destination so that they don't simply stand there at the top of the escalator. Some random spot between the racks should work. I just need to make sure they stay over the glass area and don't walk out over the rest of the retail area where they float in the air. The problem with the existing logic is that it assumes each floor covers the entire horizontal extents of the room. I had to add logic to clip the walkable area to the bounds of the glass floor to many places. But then I realized the real bug was that people didn't consider the railing a collider, and making them avoid the railing fixed most of the remaining problems.

The final step was to get the zombie AI to behave itself while chasing the player around the escalators and glass floors. Two of the more difficult primary objectives were to (a) chase the player to any reachable area, and (b) not get stuck around the escalators. This took me many, many iterations over the course of more than a week. There were just so many special cases and rare failures to deal with:

  • What if the player goes up the down escalator? Do we follow them going the wrong direction, or take the correct escalator and try to catch them up top? And if the player stops on the escalator, do we go up to the glass floor on the up escalator, then go down on the down escalator after them? Or wait at the bottom for them instead?
  • What if another zombie is going the wrong way on the escalator we're on? What if a zombie is stopped at the top or bottom of our escalator? Do we stay (walk in place) on the escalator waiting for them to move? Push them to the side? Clip through them?
  • What if the player no-clips/flies (aka cheats) to a spot above the retail area that's not over the glass floor? Do we go to the lower floor and stand under them? Clip through the railing and walk on air to get to them? Go to the closest reachable point to them on the upper floor? Ignore them and go about our business of selecting random destinations?
  • What if there are stairs through or adjacent to the glass floor that don't connect to it, or we don't have a path to them, we're on the stairs, and the player is on the glass floor? Zombies really wanted to clip through the stairs walls in that case. (Technically, if the stairs pass through the floor, then they can be on the floor while also on the stairs.)
  • What if someone pushed a zombie onto an escalator that it wasn't planning to use? Or off of an escalator? For stairs I just snap the person to the nearest point outside the stairs, but for escalators that point may not be over the glass floor.

In the end I just had to keep adding special cases to handle situations like this, test, and iterate until everything was working. Every time I fixed one problem I encountered another. In the process I had to add missing error checks, convert fatal errors to debug printouts, add more debug visualizations, and things like that. I suppose those changes are generally useful. It mostly works now, though it's not perfect. There are two minor issues that I haven't solved and have flagged as accepted behavior.

Sometimes a zombie will get pushed by another zombie into that spot exactly between the two escalators. In this case the zombie effectively gets trapped because it intersects both escalators at the same time and can't leave that spot without clipping through at least one escalator. So it just stands there and looks at the player. I think this is acceptable though because it can still attack the player when using either escalator. They're effectively camping the escalators. It's actually worse for the player because it makes the escalators unusable and may trap the player on the upper floor if there is no other exit. On the plus side, I don't believe it's possible for more than one zombie to be stuck in this location at the same time.

The second problem is when a zombie exits the stairwell onto the ground floor of the retail room, sees the player on the glass floor, and immediately re-enters the stairwell. It's nonfatal because they'll eventually reach the floor above or below, then make their way back to the retail room to chase the player. This behavior seems to be random and somewhat rare. I'm not sure what causes it. It's difficult to debug because I don't know until after I see them enter the stairs, so I can't query the system for their state before they made that decision. I attempted to fix this multiple times. Each time I thought the fix worked because I didn't catch a zombie doing this, but I saw it happen again later when I was trying to debug something else.

Here's a final video showing the player and AIs using escalators and walking on the reflective glass floor. Maybe later I'll record a video of zombies in the retail room.

At this point I'm calling escalators and glass floors "done enough". I may get back to them later. Next up: glass elevators to city elevated walkways. I already have elevators, walkways, and the drawing code for glass surfaces, so why not?