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.

No comments:

Post a Comment