Saturday, August 3, 2024

Procedural Buildings: Interior Dirt, Damage, and Messiness

I'm going back to work on building interior details. Up until now building interiors have been mostly clean and in perfect condition, except for the occasional flickering or sparking ceiling light. The only mess I've added since I started on buildings is clothes lying on the floor. I did also add trash in the form of paper balls in some trash cans, but that doesn't make the room look very messy. I'm going to show the various "imperfections" I've added to building basements, in approximately the order that I added them. This will be a mostly show-and-tell post with screenshots and very little technical detail, since most of these were relatively simple building improvements.

My first non-clean addition was empty swimming pools with deflated pool floats such as the one shown in the screenshot below. Deflated pool floats have been scaled in height by one tenth and use a darker opaque material. There is no water and there are no fish in these pools. Since the edge of an empty pool represents a significant drop onto a hard surface, this is one of the rare locations where the player can take fall damage in gameplay mode.

Empty basement swimming pool with sad, deflated pool floats. Empty pools have player fall damage.

It's time to add some dirt and damage effects. I'm starting with building basements and parking garages, since these tend to be messier in general. These areas are used for storage and are less likely be seen by visitors. Dark and dirty basements with their creepy sounds provide a good contrast to the clean and bright rooms found above ground.

The first damage effect I added was water damage, which reused the existing rain puddle shader effect to add random patches of dark/wet areas to surfaces using procedural noise. Material colors are gradually shifted toward moldy black and the specular lighting component is increased to produce a reflective, wet effect. Each building has a random threshold selected to control the frequency of water damage spots so that the strength of the effect varies across buildings. This applies to every fixed building surface in the basement. It doesn't apply to objects such as people, cars, and animals. The effect uses 3D noise so that it properly wraps around surfaces and works even on objects with complex curved shapes such as the sprinkler pipes.

Parking garage with water damage on the walls, ceilings, and floors.

The second effect I added was cracks to walls and floors. It's not as simple as drawing a crack texture as a decal over the existing geometry. This would be complex and slow because it requires drawing all crack-enabled geometry twice, the second time with a custom shader that applies an alpha mask so that it only applies to certain pixels. A texture-based solution would need to tile and could have obvious repetition in the crack pattern. There are also potential problems with blending and Z-fighting of the fragments.

My solution was to use another shader effect that applies the same 3D noise as water damage, except using a threshold and large exponent to create dark lines along the surface contours. Any noise value within a narrow band is shifted toward a matte black color. The approach is similar to how marble patterns and mineral veins are created in some shaders. These aren't quite the same as real cracks because they can form loops and don't branch very frequently. However, the technique is quite simple and efficient.

I only apply cracks to basement interior walls and floors. Ceilings of some houses are wood, and cracks don't look good on wood, so I disabled the crack effect on ceilings entirely. Walls are always plaster/stucco or concrete. The floor is usually concrete or tile, but can occasionally be carpet. Neither the drawing code nor the shader actually track which sections are carpet, so it's not easy to avoid carpet cracks. Objects on walls and floors such as pictures and rugs don't have cracks enabled. 40% of buildings have basement cracks present.

Basement with cracked walls and floors, and a bit of water damage near the ceiling.

The third effect I added was stains on the floor. I had an existing system to draw blood and bug guts as decals using a custom colored alpha masked texture atlas. This can be modified to add random stains or puddles of liquid with various colors such as red (for blood), green, and black. Since stains are small and locally applied, it's okay to draw them over the existing geometry. These stains work equally well with all floor coverings including concrete, tile, carpet, and rugs. Here's a screenshot with an excessive number of floor stains.

Extended basement rooms with stains on the floor in a variety of shapes and colors. The number of stains was set larger than normal for testing purposes.

What about adding stains to walls? That should work as well, though it requires more effort to check for blocking objects and set the orientation of stains to align to arbitrary surfaces. I also added more random size and orientation variations for stains to make them less repetitive. Here is what this looks like in a different building's basement pool room.

Room with both wall and floor stains. (The top surface of the pool table reflects a green color from the ceiling light.)

Now it's time to make a mess of some of the room objects. One idea is to scatter items such as books across the floor in random locations and orientations. Another idea is to rotate objects to non-orthogonal angles so that they're no longer parallel to walls and other objects. Maybe some of the larger items such as bookcases can be pushed over and/or broken in some way. I would apply the broken glass texture to TVs, computer monitors, and mirrors. The problem is that these generally aren't placed in basements.

I just need to be careful not to block the path of the player and building AI people with these "clutter" objects. The placement system will add most of the larger items along walls in a way that they can't block the center area of a room. But tall and narrow items such as bookcases could extend further into a room when pushed over. Fortunately, the existing collision detection logic should allow the player to walk over and stand on low objects, which means they don't truly block the player.

Books from the basement library are scattered around randomly on the cracked floor.

We have books on the floor. What about adding furniture that's fallen over? I can see that working with objects such as bookcases, chairs, tables, desks, trashcans, etc. I just need to rotate these objects around a pivot point such as the back legs of a chair, make sure there's space for the fallen over object to not collide with anything, and draw it in the new orientation. Of course it's not enough to simply update the drawing code, I need to also change the detailed collision detection logic for chair legs vs. balls, animals, etc. Here's what this looks like for chairs.

Basement room with fallen over chairs and books scattered on the floor. Here you can see what cracks look like on carpet.

That cracks effect looks pretty bad on carpets. I mentioned this potential problem above, but it actually looks worse than I imagined. I'll have to fix this with some nasty special cases that test if the filename contains the string "carpet" and set the crack effect scale to zero for those materials. Here, this looks better. The cracks are only applied to the walls, not the carpeted floors.

Office building basement with cracks effect applied to the walls but not the carpet.

Fallen over bookcases can be added as well. In this case they can only fall away from the wall and toward the center of the room. I do worry that zombies could be blocked by fallen bookcases and not be able to chase the player into a room. However, in this case they can probably walk around them.

An extended basement library where some of the bookcases have fallen over, spilling books onto the floor. There's also water damage in this room.

I can create broken objects as well. The player can currently break mirrors, TVs, and computer monitors by throwing objects such as balls at them. Broken objects use a crack texture as a decal drawn over their surfaces. I can apply this same crack texture to some of the basement glass table tops. The only difference is that I need a special transparent version of this texture that only has opaque pixels along the crack lines. These cracked tables, along with other broken items, have less value to the player when taken.

Basement room with cracked glass table and another fallen over chair.

How about more trash on the floor? One idea is to scatter empty bottles of random types and in random orientations across the floor. I had to add custom rotation logic for drawing bottles that aren't aligned to a coordinate axis. Here you see a mixture of water, coke, beer, wine, medicine, and poison bottles thrown everywhere in this house. Some are upright, but most are on their sides. I did make sure that spiders and rats can properly collide with floor clutter objects.

Empty bottles are randomly strewn across the floor in these basement rooms with random rotations.

It would be nice if there was an easy way to make some of these bottles broken. I can't think of a good way to do it, but I can at least add some piles of broken glass on the floor. This reuses the same broken glass pile drawing as cars where the player has broken a window to steal the items inside. This screenshot shows one of my favorite rooms with a bottle on top of a stain and broken glass on a rug nearby.

A pile of broken glass on the floor, next to my favorite wine bottle with a stain under it that looks like the wine was spilled.

Adding floor clutter definitely improves the feel of these underground rooms. I need to add trash as well, and for this I can place paper balls drawn with the same random distorted spheres that I use for trash in trashcans. These are distributed randomly on the floor with a variety of sizes and colors.

Office building basement rooms with randomly sized and colored balls of paper trash scattered across the floor. This is an excessive number of paper balls.

That's definitely too much trash for a single room. And the earlier screenshot probably has too many bottles on the floor as well. These tests were good for checking object distributions and overlaps with other room items, but I need to reduce their counts by an order of magnitude. I've set the max number of bottles to 10 and the max number of paper balls to 5. The screenshot below looks much more reasonable, and is better for performance. There's still a decent mess, but not an excessive amount.

A more reasonable number of objects placed on a basement room floor: several books, several bottles, and several pieces of trash.

That covers everything I've added so far. I'm moving on to other tasks now, though I may get back to this topic later. Leave a comment if you would like to suggest other items or effects that I can add.

3 comments:

  1. I think the water damage would look better if it was more on the floors than the ceilings? Or maybe if you applied a non-uniform scaling so the water streaks on the walls are tall and narrow?
    The "cracks" look kind of pretty like marble, so you might need a different technique if you want it to look damaged.
    Seeing the ceiling tiles reminded me of the break room at work, where a persistent leak resulted in a mold patch forming on the ceiling. Perhaps you could do this by blurring a squashed sphere?
    The clutter appears too regularly distributed. Could you correlate clutter density with the volume noise map? So you get more of those bottles lining up with the stains?

    ReplyDelete
    Replies
    1. Damage is a shader effect added with 3D noise and isn't aware of floors vs. walls vs. ceilings. I could maybe scale the noise based on the surface normal, though that wouldn't work on curved surfaces such as pipes and would be discontinuous at edges and corners. I'll have to experiment with this.

      Yes, cracks use the same approach as marble. I haven't found a way to add proper cracks that can tile across unbounded surfaces. If you know of an approach, please share!

      I could possibly add decals to the ceilings to show mold. This is more efficient than drawing a sphere. Thanks for the idea.

      Floor clutter is added by generating a random location in the room and checking for other objects. I used this approach because it was simple and efficient. I have better approaches for placing things like trees in the world, but it's not clear that this would be fast enough to run on the large number of building rooms that are generated as the player moves. The volume noise map is used on the GPU, while objects are placed on the CPU. Volume noise is probably too slow for this purpose.

      Bottles and stains are added in nearby blocks of code, so I can definitely make them more correlated. For example, I can have a probability for adding a stain under a bottle that has fallen over. I can even change the stain color to match the liquid in the bottle because I have control over all of this.

      Delete
    2. Okay, I made the easy changes. Water damage is now vertically stretched on walls. Tile and wood ceilings now have black mold decals. Bottles and paper balls on the floor now use a random walk algorithm that tends to place them in groups rather than uniformly random. And some fallen over bottles now have stains on the floor under their open ends in the color of the liquid contained in the bottle.

      Delete