Monday, January 8, 2024

Retail Spaces

It's time to add something other than offices to 3DWorld's commercial buildings. Someone suggested adding stores, so I'll start there. The easiest way to begin is by adding a retail area to the ground floor of some office buildings with rows of racks/shelves filled with items. This will be a good test of how well my building object management system scales to thousands of individual items in the same room. I've only attempted to add retail areas to rectangular buildings since that case seems much easier than buildings with angled or curved sides.

Rows of racks are first added along the longer dimension of the ground floor rectangle, with aisles in between them. These are broken into shorter segments to allow people (and possibly shopping carts?) to pass between the aisles. Any rack that's too close to an exterior door, stairs, or an elevator is either clipped smaller at one end or removed entirely. In addition, the area between the front door and central stairs/elevator is left clear of racks. Rectangular ceiling lights are placed in the space between the racks above the aisles. I may add some sort of checkout area later.

Each rack contains between 3 and 5 shelves, with optional top and side panels. I made the number of shelves and rack style consistent per-building, though that's not really required. Shelves are white or gray metal, with wooden pegboards separating the front and back sides that face each aisle. Each shelf is then filled with smaller objects that the player can take down and interact with.

I started by adding boxes of various sizes and shades of color, since there was already a function to add boxes to shelves. The player can open these boxes and remove the items.

Racks of shelves filled with boxes of various sizes and shades of color. This looks similar to storage rooms.

This worked without too much trouble. Now it looks like a FedEx warehouse or something. That's a good start, but there's not too much variety. It's time to divide the racks into categories and fill them with some of the other ~140 types of objects that are currently in 3DWorld's buildings. The categories I used are:

  • Boxes/boxed items
  • Food and Drink
  • Household Goods
  • Kitchen Items
  • Electronics

I originally had a few other categories such as clothing, but they only had a single item type and got merged into one of the other categories.

All shelves in a particular rack are assigned to the same category. However, some items have preferred shelves. For example, boxes of pizza are on the bottom shelf, while food boxes such as crackers and cereal are always on the top shelf. Some of the larger items such as computer monitors, lamps, and fire extinguishers can only be placed on taller shelves (racks with fewer shelves stacked vertically). Items are arranged either in rows, rows + columns, vertical stacks, or randomly.

Retail areas didn't look quite right, so I had to adjust the lighting and materials used. I also added vertical support pillars from floor to ceiling at the end of some racks. These next screenshots show retail areas after the carpet was replaced with tile floor and indirect lighting was enabled.

Retail area with a variety of object types on the shelves, in a brightly lit area with tile floor.


Another view of racks of items, looking down an aisle between two rows of shelves.


The electronics and kitchen sections, with stairs at the far end that lead up to offices and down to the parking garage below.


Another view of the retail area, with household items on shelves.

So how many objects are there? The building from the first few screenshots had 16,599 total objects on the shelves! I'm sure there are buildings with more. Maybe 20,000 objects?

Optimizations

One goal of this addition was to test the performance and scalability of adding a large number of objects in one place. Initially, the performance wasn't all that bad. Of course it really helped that I had bought a new, high end gaming PC while I was in the process of working on this. The CPU and GPU were both around 3x faster than my previous 9 year old PC. I went back and tested this on the old PC and the performance wasn't great. Creating the triangle geometry for this many objects was slow. Updating it when the player took an object (or even approached a shelf) was slow. Even drawing and creating the shadow map was slow.

But the biggest problem was with security monitors. See, I made sure to place cameras at the ends of the retail rooms as well, because why not? We have to catch the shoplifters, right? The problem was that trying to update these cameras with this many objects every frame, on top of everything else, reduced the framerate below 20 FPS. That's terrible. I put some effort into optimizing this. The biggest improvement came from skipping the shadows of all of these shelf objects, since they're difficult to see in the security monitors anyway. Most of the visible shadows are from the simple cube geometry of the shelves themselves.

There were many other optimizations. Profiling the code told me that the majority of the time was spent generating and drawing two of the 32 types of shelf objects: bottles and vases. Bottles were a problem because they have high vertex count smooth surfaces, and there are many of them. So I reduced both their detail and count, limiting the number of rows of bottles per shelf to only 2 rather than 3.

Vases were slow because they're formed from high vertex count, custom procedural curves. (You can see some of these in the third screenshot from the top, second rack back on the right, bottom two shelves.) Simply reducing their vertex density vertically and horizontally from 32x32 to 16x16 made them nearly 4s faster to generate and draw. Visually, they look nearly identical.

The next optimization I made was to improve occlusion culling for 3D models placed on shelves. The occlusion system currently only considered walls, ceilings, and floors as occluders. Since the entire ground floor retail area was one room, there was nothing to occlude the models. This was even worse than the problems I had with occlusion culling in the backrooms rooms. I decided to add the pegboard backs of the shelves as additional occluders. This dramatically improved the culling rate because most objects were completely blocked by the back of at least one rack between the player and the object.

At this point the drawing and shadowing runtime was acceptable, and the framerate was at least 50 FPS even on the old computer. The only major remaining problem was the ~100ms lag encountered the first time the player approached each individual rack of shelves. This is because these 16,599 objects aren't physical objects in the building, they're simply drawn as around 100 batches of triangles that have been merged across many objects. When the player approaches a shelf, the system needs to have a list of the individual objects so that the cursor can turn green to indicate that the player can pick up or interact with an item. This involves "expanding" the particular rack into its component objects, which requires updating various data structures that span the entire building. The worst case is when the player walks down each aisle and forces the racks to be expanded one-by-one. Racks are only expanded once, but there are many of them.

The only fix I was able to come up with was to disable the cursor and interactive content for shelf racks. It's not a big deal. I think the only iterative objects were microwave doors, boxes, and books that the player can open. Most of the electronics are unpowered and can't be interacted with. None of this is required for gameplay anyway. The player can still take an item from a shelf, and this will expand the rack. After that the cursor and interactive content will work on this rack since the objects were expanded. Now the lag only occurs when the player is actively stealing from the shelves, rather than simply browsing them. This seems fine because the player must pause to press the "take item" key, which does a good job hiding the lag. This is far less noticeable then the lag that occurred when simply walking down the aisle as it interrupts the smooth walking animation.

The code for all of this can be found in my GitHub repo. Shelf rack placement code is in the building_t::add_retail_room_objs() function in this file. Shelf expansion/population code is in the building_room_geom_t::get_shelfrack_objects() function in this file.


3 comments:

  1. Ceilings are still way too low.

    I love the procedural vases! This whole system is a great showcase of the item variety in the game. Could you procedurally add texture color variation?

    ReplyDelete
    Replies
    1. I have a way to make rooms two floors tall now, so maybe I can use that. It should be easy because the retail room is the entire floor and a separate part. Or would two floors be too tall? I'll have to see. I'm not quite sure how to handle the U-shaped stairs or elevators though.

      I have quite a few procedural items. There are over 100 items so far, but I only have about 30 on the shelves.

      The terrain already uses procedural textures. The downside is that procedural texturing either takes significant graphics memory (if pre-rendered), or has a high runtime draw cost when done directly in the shader. It would be difficult to have a large number of them or something unique per object. Something like a marble pattern might be possible.

      Delete
  2. These eclectic store shelves resemble the bargain stores in Japan. Sleeping mats are a logical addition, as they are bulky, and could also allow new gameplay.

    ReplyDelete