Thursday, June 23, 2022

Attics for Houses

I was originally planning to work on indirect lighting for building interiors next. This turned out to be a very difficult and time consuming project, and I don't have too much to show for this yet because it's still incomplete. My next post will likely be on the topic of indirect lighting. Somewhere in the middle of that my development was interrupted by someone suggesting I add attics to houses. That's a great idea!

Problem Statement

Of course attics were also quite complex and time consuming to add. I would say about as much work as house basements or office building parking garages. This was primarily due to two reasons: First, attics aren't cube-shaped rooms, they have sloped roof polygons rather than vertical walls and horizontal/parallel ceilings. None of my existing code worked in this case, including object placement, wall decorations, lighting, player collision, and people/rat/spider AI updates. I basically had to special case all of this code to handle attics. Then I had to write custom sphere/ray/cube intersection code to handle roof polygons, which required many debug iterations.

Second, I wanted to add attics without modifying any of the existing interior or exterior geometry. I can't be going back and reworking every system each time I add a new building feature. This means that attics must connect to existing rooms without moving walls and light fixtures. They must handle things like chimneys crossing through them. They can't break the existing people, rat, or spider movement systems. And, by far the most difficult, they must work with multiple sections of roof intersecting at odd angles.

Adding attics introduced a lot of new complexity, but at least I didn't have to deal with windows and doors. I'm sure it's possible to go back and add skylights and ventilation to attics later if I'm up for the challenge.

Attic Roofs

This last case can come up in L-shaped houses that have both cube sections at the same height, such that the roofs of the two parts intersect each other. According to this diagram, these would be the "crossed gable" and "cross hipped" roof types. Up until now the player was not allowed inside the roof area, so all of the polygons clipping through each other weren't visible. But now that I want to make this part of the attic interior, this has to be dealt with somehow. At first I added special cases to skip adding attics to this type of house. Later I got back to working on this and wrote the code to clip roof polygons (triangles and convex quads) to the other existing roof polygons. It wasn't as bad as I thought, and worked pretty well. I still wasn't sure what to do with an L-shaped area and how exactly to add objects such as wood beams in the roof. In the end I added vertical walls to split the area into two separate cubes, and made the larger one the attic.

Now that I think of it, I can probably go back and simplify all of the rooftop solar panel placement logic. The existing system goes through a lot of trouble to detect if a roof polygon clips through another polygon to avoid placing a solar panel on it that intersects other geometry. Maybe this code is no longer needed since polygons no longer intersect? I may be able to simplify roof power line connection logic as well.

Another issue that came up was attics where the ceiling was too low for the player to stand. Depending on how I implemented the collision detection, either the player's head would stick out of the roof, or they couldn't enter the attic. Neither of these is acceptable. My initial thought was to skip adding an attic in this case, but this didn't work because attics were added before the roof. I didn't want to reorder the steps because this would break that "without modifying existing geometry" rule above. So instead I set a min roof height for the part containing the attic to ensure the player could stand inside it. This mostly worked, but generated comically tall and narrow roofs for some thin house sections. I had to go back and skip adding attics to house parts that were too narrow. I don't like this complex chain of logic that was needed, but I'm not sure how else it could have been done.

Small attic with two lights and an open door with a ladder extending down into a dark room below.

Attic Access Doors and Ladders

Attics are accessed by a door that pulls down from the ceiling with a folding wooden ladder. This is how the attic is accessed in most of the houses I've lived in. I placed the door in one of the larger rooms near the center of the house, in the ceiling of the topmost floor. If the house contained a hallway in the correct location then I used that as the access room. I put it off to the side of the center of the room to avoid blocking the room light and the path through the room (in the case of hallways), but not so far to the side that it blocks doors. There's an extra check for clearance in front of the door in both the floor below and the attic above to make sure the player has enough space to enter and leave at both ends.

The folding ladder activated with the player's interact key and only draw when extended down to the floor. It's very steep, near vertical, and doesn't work well as stairs. I made it function as a ramp instead, using the same code as parking garage ramps. I wanted to make the player climb the ladder slowly, so I decreased player movement speed to only 20% when on the ladder. This partially accounts for the fact that zombies can't yet climb attic ladders. This slow movement makes ladders more risky, because zombies can get you when you're climbing and will sometimes wait for you at the bottom of the ladder. The attic isn't supposed to be a safe hiding spot. Of course it's also possible to jump down from the attic on the opposite of the opening from the ladder and land in the room below quickly.

Attic access door and extended ladder in the hallway of a house shown with indirect lighting.

Attic Lighting

Lighting works differently in attics. I don't have a flat ceiling to add a standard rectangular or flat cylindrical light to, so instead I hung spherical lights from the center ceiling beam. Shorter/square attics have a single large light, while longer attics have two lights, one near each end. Attics will be full of random large objects, which creates many shadows. In addition, the light radius and field of view aren't large enough to reach into the corners. I increased the indirect lighting contribution in attics to make objects further from the lights actually visible. I still get nice dark areas in the occluded corners behind larger objects and brighter spots in front of objects, which can be seen in the various screenshots in this post. While this high level of indirect lighting is better for taking screenshots, I may reduce the indirect lighting later because I rather like the creepy dark corners of the attics.

I said earlier that I still haven't completed indirect lighting. At this point it's mostly a problem with performance vs. noise. I can't get low noise with good CPU compute time for volumetric indirect lighting. However, attics are a bit easier since they're one room with only 1-2 lights. I can increase the number of simulated rays and get results in reasonable runtime with acceptable noise. So I've enabled indirect lighting for attics, since I feel it improves the look quite a bit compared to using a constant ambient lighting term. I'll likely get into more detail on this topic in a future post.

Long attic under a gabled roof with a rug in the distance and many objects scattered about. A rat can be seen on the attic floor.

Object Placement

Next I'll discuss object placement. Most attics that I've seen are full of complex wood beams that hold up the roof. I'm not sure what the correct term for these different parts are, so I'll just refer to them as "woodwork". I suppose the beams that run along the bottom of the roof are rafters, but what are the beams that run along the roof line and the vertical posts called? And what about those horizontal support beams that form a sort of A-frame shape? Whatever these are called, I looked at some reference images of attic interiors and tried to add all of the woodwork. There are two code templates I used, one for peaked/gable roofs and the other for hipped roofs due to the difference in the way the roof polygons are placed. In some cases one or more sides has part of a vertical wall, which I also treat as a "roof polygon". All of these beams start as a cube that's rotated into the proper orientation using lots of complex math involving cross products and trigonometry. It took me many hours to get this right. Since the ends of the beams aren't properly mitered, they don't quite meet exactly and have small gaps and intersections. I did my best to try and hide this with small translates and other geometry. I'm not sure how practical it is to calculate the correct faces/angles for every beam, and if I'll ever have the time and patience for that. It works well enough for now. A lot of that will be hidden in shadow in the final attic anyway.

I would love to have spiders crawling around on the rafters and other beams. So far I haven't figured out quite how to make this happen, given how complex all of the surfaces are. My existing solution based on object building cubes won't work here. In fact the rafters aren't even real collision objects, they're drawn as part of the roof when the player is in the building. Only the vertical posts that the player can run into are real objects. At least I can put spiders and rats on the floor and have them run around among the boxes and other objects.

Attics work as storage rooms, but I can't quite reuse the existing storage room placement logic because of the various differences in the shape of the room and the types of objects to be placed. For sure there are tons of boxes and crates to add. Small furniture and appliances such as lamps, nightstands, and chairs will work for attics. In addition, I can scatter balls, paint cans, books, and various other smaller objects across the floor and on top of larger placed objects. (I was thinking of adding something larger such as a dresser, but that currently can't fit through the opening for the attic door. I'm not sure how someone would get it up the narrow attic ladder either.) The more cluttered the better, as long as the player is able to pick up and/or move these objects to clear a path to walk. It might be nice to add some unique items that are only found in attics such as kid's toys. It would be neat to have a rocking chair or horse that the player can push to make it rock back and forth.I'll have to see what free 3D models I can find online.

Attic with an open access door, a chimney passing through it in the back right, and indirect lighting casting soft shadows.

Furnaces

I've also added furnaces to some attics. Furnaces were another side project for me. I've had them on my to-do list for a while, ever since I added vents to office buildings. I started with adding furnaces to houses because at least I know what they should look like from experience. The houses I've lived in have mostly had their furnaces in the basements or attics. The house I currently live in has the furnace in a small room off the central hallway, but I don't think that's as common. For 3DWorld, I added the furnace in the same room as the water heater if the house has a basement. Otherwise, I add it in the attic if there is one. I couldn't find a good 3D model, so I took a picture of my furnace, did a poor job of editing it and trying to fix the slight rotation, and slapped that on as a texture. I couldn't get a picture from the optimal angle and the lighting wasn't great. The various pipes that come out the front aren't properly 3D, but it works well enough. It's obviously a furnace. I'll need to add all of the duct-work and actual pipe geometry later. That should add a lot of more clutter to the attic, though I need to be careful that it doesn't block the player's path too much since these objects won't be movable by the player.

House basements now have gas lines that connect to the stove, water heater, and fireplace. I've made gas lines connect to furnaces above the basement as well. However, the only case where a furnace is above the basement is when it's in the attic, and that only happens when there's no basement to put it in, so the pipes won't be visible anyway. I'm sure I'll get back to this at some point. One possibility is to make the furnace placement random when the house has both a basement and an attic.

Attic under a hipped roof with a furnace in the back left.

Friday, May 6, 2022

Procedural Buildings: Office Building Utilities

I spent a lot of time improving my procedural houses last year and in the beginning of this year. Now I'm getting back to putting more work into office buildings. Two posts ago I showed some parking garages with sewer pipes in the ceilings. I've been working on several different areas in the past month rather than a single big feature. I've added cold and hot water pipes, a vertical fire suppression system pipe, electrical panels, utility rooms, water heaters, and vents since the last post. The pipes are all connected to water heaters and other plumbing fixtures to form a complete system. Each of these additions increases draw time, so some of my effort has also gone into making optimizations to the code to get back to my framerate targets of 100 FPS outdoors and 60 FPS indoors.

Here are some more details on these additions to my procedural office buildings.

Cold and Hot Water Pipes

I definitely liked the way sewer pipes turned out in my earlier post. I want to add more pipes to parking garage ceilings, and water pipes seemed like the obvious next step. And why add just cold water when I can have hot water pipes as well?

I reused most of the sewer pipe code to add water pipes. These were placed above the sewer pipes, closer to the ceiling, using a smaller pipe diameter. I used copper material for the pipes and shiny brass for the fittings. I didn't have enough space near the ceiling to route hot water pipes above cold water pipes, so I put them at the same level and added cold water pipes as blockers going into the hot water pipe routing algorithm. This way cold water had a priority, and hot water was added where there was space between everything else. In many situations the hot and cold water mains ran parallel to either side of the risers feeding the main bathrooms.

Here's a screenshot. I don't particularly like how water pipes must pass through ceiling beams, but I guess it's good enough for now. I disabled cars to avoid distracting from and blocking the view of pipes in the ceiling. Note that parking garages now have yellow curbs at the ends of parking spaces and some handicap spots near the elevator(s). I also made the occasional parking garage ceiling light flicker, though you can't see that in screenshots.

Water pipes in the ceiling of a parking garage with no cars. I've also added handicap parking spots and yellow curbs.

Hot water pipes have an additional layer of white thermal insulation. I've seen buildings with this style of pipes and feel it has good contrast against the copper while also matching the ceiling so as not to stand out too much.

Hot water, cold water, and sewer pipes in the ceiling of the parking garage.

Fire Suppression

The fire suppression/sprinklers pipe is currently just a big red tube placed near an exterior wall that extends vertically from the lowest level of the parking garage up into the building. However, I did add support for extruded N-gon shapes, which I used to add hexagon bolts to connect the pipe sections together. Now I can use this system to add bolts and similar details to other building objects.

I haven't added any horizontal pipes or actual sprinklers yet. I might add these to parking garage levels at some time in the future if I can find a way to fit them. Maybe they can hang down lower than the other pipes. Or maybe I can get away with only having them along walls and away from other types of pipes. I'll have to look through some reference images to help me decide what approach would work the best and fit within the constraints of my buildings.

Fire suppression/sprinkler water pipe running vertically through the parking garage, with hex bolts.

Utility Rooms and Water Heaters

Each large office building now has a dedicated utility room on the ground floor. This is assigned to a windowless room that's as close as possible to a bathroom to minimize the average length of water pipes. So far utility rooms contain only a row of water heaters. I used the same style of vertical cylinder water heater that I used for houses, except the pipes bend down and pass into the floor below instead of extending to the ceiling as with houses. This is because the plumbing is in the basement below the water heater, while water heaters are placed in the basement of houses. At some point in the future I plan to add other objects such as HVAC components to utility rooms to use up all that wasted floor space.

Water heaters have their own hot and cold water lines that connect through the floor to the pipes in the parking garage below. The hot water main connects between these and plumbing fixtures that use hot water such as sinks. The number of water heaters is determined by the number of hot water fixtures, which generally scales with building size. Hot water flow is evenly distributed between heaters. If some of them can't be connected, a section of the pipe joining the row of water heaters is moved inside the floor itself, or somewhere else not visible by the player.

I've added signs to the doors of utility rooms as well as storage rooms and libraries using the same style I previously used with men's and women's restrooms. I plan to add more types of building signs in the future. For now they're placed on the wall next to the door rather than the door itself to avoid having to deal with rotating them when doors are opened and closed.

Office building utility room with a row of five water heaters with pipes reaching down to the floor. I even added a sign next to the door.


Electrical Panels

Once I had the plumbing done I started working on the electrical system. Houses have an electrical panel with circuit breakers placed on an exterior wall of the basement. Office buildings can have multiple electrical panels placed on the wall of the underground parking garage, if there is one. These boxes have metal conduits exiting from their tops and going into the ceiling above. I found a circuit breaker texture that I can tile to show vertical rows of breakers on the inside of the box when it's open. For some reason I was only able to find marine breaker pictures. Most of the normal circuit breaker images had poor lighting, hands or tools in the way, labels for breakers that I didn't want, etc. So I clipped off the picture of a boat and used the rest of the image as the texture.

Currently the player can only open and close the metal doors of each panel. I might add more functionality to these later. For example, I can make individual breakers clickable so that the player can disable lights on different floors and possibly other electrical items such as the elevators. I'm not sure how to implement this yet, or what gameplay purpose this would serve. I can pretty easily expand electrical panels into a series of breakers that the player can interact with to switch on and off. I would need a new system to handle tracking of which areas are powered, with some sensible default positions for breakers in panels that haven't yet been opened and generated. Maybe each breaker will need to search through the lights and other appliances in the building and disable the correct ones. That means I also have to figure out what "zone" each breaker controls. I feel that having one breaker per room is too many, and one breaker per floor is too few. For example, the building I took these screenshots from has 19 floors and 913 rooms.

This is what the breaker panels currently look like when open and closed.

Two breaker panels in an underground office building parking garage, one open and one closed.

Ah, wait. It's too hard to see the panel with the poor lighting of the parking garage ceiling. Here, the flashlight makes it all better:

An open breaker panel lit with a flashlight.

Vents

I added vents near the ceiling on walls of offices, storage rooms, and utility rooms. These are placed on interior walls to avoid having to deal with windows. If you look closely you can see one in a screenshot above. They don't connect to anything yet, they're simple wall decorations. Building walls and ceilings are too thin to add internal vents anyway. I think I would have to fix that before I can add a proper ventilation system. Sorry, I don't plan on adding gameplay where the player climbs through ceiling vents any time soon.

An air vent in the wall of a bathroom near the ceiling.

There are many possible ways to improve on these systems. Buildings have thousands of different types of objects and many different interacting systems. This is certainly an endless project!

Also, this is post #128. Yay, a power of two!

Sunday, April 3, 2022

Spiders

I've explained and shown how rats were added to 3DWorld's procedural buildings in a previous blog post. I had a lot of fun adding rats, so I felt it was time to add another type of animal. This time I wanted the movement and animation to be different. Maybe something that could climb walls, with more legs. How about spiders? I've never added spiders to anything in 3DWorld yet, so this sounded like an interesting challenge.

It seems that adding spiders was a similar amount of work compared to adding rats. It's hard to say for sure because I'm not quite done with spiders, but I'm almost there. I was able to use a lot of the existing code from rat placement, movement, drawing, and animation. However, writing an AI that can walk on walls, floors, ceilings, doors, and room objects was far more difficult than navigating around on the floor alone. I spent over half my time on this task. The details can be found below.

Animations

The first step was to add leg animations. No, wait - the first step was to generate the geometry of a spider that I can then draw. I don't want to repeat that process I used for butterflies where I had to split the model into multiple parts so that I could animate it. I can construct a spider from ellipsoids (squished spheres) for the body/abdomen/eyes/joints, and cylinders for the legs. Then I can assign my own custom vertex attributes for the joints and leg segments to indicate which segment of which leg of which side of the body it's on. I can then use this information for animation inside the vertex shader without having to split or otherwise modify the model. It would be nice to have a framework for this alternate movement and animation system.

I had to watch some videos on YouTube (such as this one) to figure out how spider legs move. It looks like I only need to create a single leg's motion, apply it to alternating pairs of legs from back to front, then mirror it to the other pairs of legs and the other side of the spider. The first and third leg pairs move together, and the second and fourth leg pairs move 180 degrees out of phase. Similarly, the left and right legs are 180 degrees shifted from each other. I created several joints which I call the "hip", the "knee", the "ankle", and the "foot." I have no idea what the correct terms for these are when applied to spiders, so I decided to map the three joints and corresponding three leg segments to the parts of a human leg. The hip moves with the body, the knee moves relative to the hip, the ankle moves relative to the knee, and the foot moves relative to the ankle. I initially created a huge house-sized spider to test animations on. All it took was several hours attempting to fit sine waves to the X, Y, and Z dimensions of these three joints to get them moving properly. It's not perfect, because spider legs don't move in a perfect elliptical path, but I think it looks good enough. Their legs tend to move so quickly that you can't easily tell what the motion patterns are anyway.

Here's an initial spider animation and floor/ceiling/wall walking test.


Movement

The task of having spiders walk on the floor was trivial. All I had to do was copy the code from rat movement (or simply draw spiders instead of rats!) Handling walls and ceilings was far more difficult for several reasons. First, movement isn't within a plane. There are turns that have to be made gradually, without clipping through objects in the process. Second, the up direction has to change based on the orientation of the surface the spider is walking on. I can't always use +Z (vertical) for up like I do with rats. Third, it's far too easy for a spider to get stuck in the spaces between multiple nearby objects.

I was about to list "path finding is more difficult," but I stopped myself because that's a lie. As far as I'm aware, spiders are pretty dumb. They don't make complex path finding decisions or otherwise think ahead very much. They simply walk in one direction until they find something. Or at least that's what they appear to be doing, so I'm sure I can get away with making my spiders act as dumb as real spiders appear to be. I don't think I even have to make them chase the player. They can instead just sit there on the floor or hanging from webs on the ceiling, waiting for the player to walk by and get bitten. Their natural defense mechanism is to bite whatever is about to step on them.

Right. I suppose now I should explain how I got spiders to walk on the walls and ceilings. I worked on that code in between implementing all the other features I discuss in this post, and then again after I was done with everything else. And then again later for good measure. I suppose I have to write enough about this to get across the idea of how long this task actually took to figure out. You can skip over this next part if you find my technical content hard to follow.

I started out by iterating over all of the surfaces and objects of the house that were near each spider. This includes walls, ceilings, floors, doors, furniture, appliances, stairs, etc. If the spider hit something that was round or otherwise not a cube shape, it would bounce back and pick a new direction. (Cubes are much easier to start with.) I then found the surface the spider was currently walking on from among all of the remaining cubes based on the spider's up vector. In addition, I found the nearest secondary cube face, which represents the surface the spider is most likely to encounter next and must adjust its position and direction to account for. I calculated the distance between the current and next surface and used the relative distances to interpolate a smooth path for the forward and up directions to transition from one surface to the next.

This worked well for most cases involving floors, ceilings, and walls. Unfortunately, it didn't work at all for outside edges of walls (such as around door frames) or corners where three different cubes/surfaces came together. Obviously, if I'm only tracking the two nearest surfaces, I can't properly handle three surfaces at once. Sometimes the collision system would switch between two of the three surfaces each frame and the spider would turn around constantly. In addition, spiders were always getting stuck between furniture and walls because that system couldn't handle a spider simultaneously colliding with two surfaces of the same orientation. So any time these situations came up, the spider would either get stuck forever, jitter/spin around randomly, or clip through an object. Clearly that's no good.

I couldn't find an incremental fix for any of these issues, so I threw out the code and rewrote the whole thing from scratch. I treated the spider as a sphere (well, technically an ellipsoid because it was shorter than it was long/wide). The goal was to always have the sphere touch one or more surfaces and never intersect an object or float in space. If a movement pushed the sphere into an object, I used collision detection/resolution to push it out. If the spider moved into empty space, I moved it back and selected a different direction or pushed it to touch the closest object. Rather than trying to special case all the different cube faces/orientations, I simply generated 50 random movement vectors in the roughly forward direction and picked the one that moved the spider the furthest without colliding or entering empty space. I also added a small preference for motion in the "up" direction to induce more frequent climbing behavior.

This new system solved all of the previous problems, but also introduced some newer, lesser issues. For example, I still didn't have a good solution for the outside cube edge case. What I mean by this is if a spider is walking along a wall and encounters a doorway, it should walk around the edge of the door frame and onto the opposite side of the wall. The reason this case isn't handled is because only the current surface is being tracked. If the spider walks in a straight line it will go off the end of the wall before it collides with the edge of the door frame since the edge isn't in its path. I eventually came up with the idea of searching for the orthogonal edge of the previous surface when the spider had run into empty space. This at least works with right angle outside corners from the same cube. Then I realized I could simply move the spider to the closest point on the cube and it would somewhat follow the wall.

At least in theory - when I implemented this the spider just clipped through the door frame. I tried several approaches and they all had the same outcome. I was determined to make it work and stayed up until past 2AM trying to get this right. (Yes, it was a Friday.) I eventually gave up and went to sleep, then figured it out in about 10 min. the next day. The spider logic was right all along, it was the door frame logic that was wrong. I thought the door frame was supposed to be added as a thin wrapper along the edges of the wall, but instead it was added as an extension to the wall and was in fact hollow inside. Spiders were following the wall itself, and this is what made them clip through the door frame and get stuck in the empty space where the wall should have been. That explains why I had a similar problem with rats clipping through the door frames that I was never able to solve!

This was an easy fix, and after that it ... almost worked. There's still some instability where spiders will randomly switch between walking up and down along the door frame. It doesn't happen too often, which makes it that much more difficult to debug. I *think* what's going on is that some of the larger spiders are wider than the wall and can't quite balance on the edge of the wall without falling off one side or the other. When they do this, it triggers that same edge-of-wall following behavior and they switch directions and try again. This only lasts a few seconds until they either finally align to the center line of the wall, or eventually reach the top or bottom of the door frame. So maybe this temporary, um, indecisiveness is acceptable. Or I could make the spiders smaller, but then I would have to more accurately handle things like wall and door trim because they would start to clip through these thin objects. I think at this point I've spend enough time on this task and can move on to something easier.

I placed all of the spiders in the basement and on the first floors of houses and office buildings, just like I did with rats. However, spiders don't always stay on the first floor. They can climb the stairs, and eventually some of them make it to the upper floors.

Here is the result of my movement work.


Scalability

The next big question is, how many spiders can I put in a single house? At first it was very slow because I had forgotten to add view frustum culling and occlusion culling. Fortunately, this works the same for spiders and rats, so I was able to reuse the code. With some minor amount of code optimizations I had the system scaling to 1000 spiders with a minimal drop from 103 FPS to 89 FPS, which is something like 100 spiders in each room. That's ... a lot of spiders, especially when they're this large in size. Good luck trying to run through even one room without getting bitten! (Yes, I've tried it, and I can tell you that my survival rate was very low.) Anyway, you end up with something like this screenshot.

This is the result of adding 1000 spiders to the ground floor of a single house. There are about 80 spiders in every room (12 rooms total), all over the walls, ceilings, and floors!

What's even scarier than a room with 100 spiders? A spider on a web by the light that casts a huge shadow on the floor below. And what's even scarier than that? I don't know, but I'll let you know when I come up with something else to add. In the meantime, I leave the answer up to the reader's imagination.

A spider hanging from the ceiling light casts an ominous shadow on the floor. The light is a point light source, even though it really should be a rectangular area light.

Webs

I've added visible white web strands since taking that screenshot above, so now you can tell the spider is hanging rather than floating in midair. They like to drop down from the ceiling when they collide with each other or reach an obstacle they can't easily climb on such as round lights and the railings of stairs. (I haven't yet figured out how the movement logic works on curved surfaces like this.) Note that the player can also collide with spiders and push them around somewhat when they're on ground, on the wall, or on a web. My daughter suggested adding spider webs in the corners of rooms. Maybe I can add that at some later time.

Spider dropping on a strand of spider web from the top of the stairs.

Gameplay

Next, I had to figure out how spiders interact with the player in zombie gameplay mode. They don't make any noise, so that interaction mechanic is out. As I mentioned earlier, I didn't want them to actively chase or follow the player. They simply ignore the player and do their thing, but they will bite the player if stepped on or bumped into. This does a small amount of damage, but more importantly it poisons the player so that health drops slowly over time until the player is "healed." This means I also had to add medicine, and the best place for that is inside bathroom medicine cabinets, which can now be opened by the player with the interact key.

Medicine cabinets with mirrors that are placed above sinks in most house bathrooms can now be opened by the player, revealing medicine that will restore full health and cure poisoning.

I haven't yet added logic to allow the player to pick up, carry, and drop spiders. I'm not sure what sane person would actually attempt that with spiders of this size. Besides, I've already implemented that mechanic for rats so it wouldn't really be classified as a new feature anyway.

I'm sure there are many ways to continue with this direction of my work. I could add other crawling bugs now that I have the "N-legged climbing bugs" code. Maybe beetles or ladybugs? I could spend another few weeks adding flying birds or insects *inside* buildings. Or I could have a spider squishing mini-game for the player.

Monday, March 14, 2022

Procedural Buildings: Parking Garages

Parking Garages

The past few months I've been mostly working on improving the interiors of houses because I've gotten more feedback from others in this area. I haven't put as much effort into office buildings lately. It's time to go back and work on office buildings once again. I added basements to houses last year, and now I've added underground parking garages to some of the larger office buildings. Smaller buildings have basements similar to houses, with offices and storage rooms. I've even added support for multiple levels of basements and parking garages that extend down under the building.

Parking garages are implemented somewhat differently than above ground building interiors. They're only visible when the player is inside the building because they have no windows, which means I can defer their generation until that point. This means that it makes more sense to add parking garages as interior detail objects rather than regular interior objects generated by the floorplan phase. I added a new drawing system for "interior detail geometry." The benefit here is that I can have much more detailed interiors for parking garages without having to worry about allocating too much memory to store geometry when the buildings are initially generated. This is why I didn't have office building basements enabled by default before now. I also realized that I was accidentally generating garbage basement geometry for non-rectangular buildings, which the player can't enter anyway, so I fixed that as well.

The floorplanner treats the entire parking garage as a single big rectangular room, like this.

Early version of an empty parking garage with only ceiling lights, and elevator, and stairs.

Code to fill in the interior is triggered when the player is close to the building. The first step is to determine how many rows and columns of parking spaces there will be based on the size of the largest car that can be placed. The available space is partitioned into rows of parking spaces separated by walls and pillars, with aisles between them and along the sides for cars to drive in when entering or leaving their parking spaces. I added beams crossing the ceiling in both directions, connected through the support pillars, with lights hanging from them. Then I connected stairs from the ground floor down to the parking garage level and to lower levels, and extended elevators downward into the parking garage. Finally, a ramp is added connecting each level of parking garage to the floor above. The ramp is wide enough and has enough clearance around it for cars to use. Walls, pillars, beams, and parking spaces are clipped or removed to keep the stairs, elevators, and ramps clear for people and cars to use them.

I added new elevator buttons and stairs/elevator signs for these underground levels. Basement levels begin with a "B", while parking levels begin with a "P". I placed railings along stairs and ramps for safety. Lights are added in a 2D grid along the ceiling attached to beams. Any lights overlapping other objects are moved horizontally to nearby free positions, or removed if there's no space for them. Here is what this looks like with cars added.

Parking garage with parking spaces and parked cars, with stairs, and elevator, and a ramp in the back.

Cars are high polygon count objects with many materials, so I had to put extra work into optimizing their drawing. The parking garage walls, ceilings, and floors are merged into maximal occluders for use in occlusion culling. This way cars that are hidden from the player don't need to be drawn. I also skip drawing of the surrounding terrain, city, and buildings when the player is in the basement and away from stairs leading up to a room with a window. These optimizations allow me to to get 70-120 FPS even in a large room filled with cars.

Pipes

[Disclaimer: I know nothing about plumbing or what the correct terms for these things are.]

That looks pretty good, but something is missing. What do parking garages always seem to have? How about some pipes running across the ceiling. I already know where all of the toilets, sinks, urinals, bathtubs, and showers are because these have been placed in the building before the parking garages are filled with objects. I can find these object locations on the floors above, combine their flows vertically, and produce the ends of a drain pipe for each stacked plumbing fixture above the parking garage. I made pipes increase in radius as they're merged so that have mixed sizes. The idea is to then connect the ends of the drains protruding from the ceiling into a network flowing into one main sewer pipe that exits into the wall or floor.

The only problem: Connecting these pipes is extremely complex. I have pipe ends of various sizes randomly scattered all over the ceiling, with some of the large buildings having as many as 80 of them. Then I have all of these pillars, lights, elevators, stairs, and ramps everywhere that I have to route around. I believe what I want here is a rectilinear Steiner minimum tree, with the addition of obstacles. This is an NP-hard problem - meaning, an optimal solution likely requires exponential runtime. And that's not even taking into account the obstacles part! All I can find online that discuss handling obstacles are conference papers in the field of VLSI layout routing. None of these are algorithms that I want to sit down and write from scratch just to connect my pipes together.

Okay, maybe I can implement this with a simpler and less optimal single trunk Steiner tree. I can have the main sewer line running along the longer direction of the parking garage and then add secondary feeder lines branching out in the other direction to connect to the drains. Then I just need to translate and rotate that main line around, maybe add a bend in it, and I can fit that part in. The secondary lines are similar to the main line, but rotated 90 degrees. It would sure help if the pipes coming from the plumbing fixtures were in neat rows and columns so that I could connect them all for a given bathroom with a single feeder line. The toilets from the bathroom stalls are in a nice aligned row, but the sinks and urinals are a slightly different distance from the bathroom wall. I know, I can pick one representative and then try to align the nearby pipes to the same X or Y value as that one, as long as they still fit within the XY bounds of the plumbing fixture and the room itself. (In reality, the pipes would drop down through the walls behind the toilets anyway so that they can pass through multiple levels of floors.)

Armed with this plan, ... countless hours and a dozen special cases later I have it working. The exit point logic was the most difficult because I had to handle the case where the main line exits straight into the side of the building, at a right angle to avoid an obstacle, or drops down to the floor below to exit if there's no other option on the current floor. It's hundreds of lines of source code, but only takes an average of 120us per building to do the pipe routing. I was even able to add fittings around the joints and caps on the pipe ends. I've visited dozens of buildings in-game and I can't find any pipes clipping through geometry, and only minor instances of pipes clipping through each other when the exit pipe is too close to a feeder pipe.

Some of the smaller pipes still pass by or slightly under the lights. It might make sense to move the lights below the pipes, but then they come close to colliding with the player's head and don't provide enough clearance for the cars. Maybe the pipes should cut through holes in the beams rather than hanging under them? I don't know, I guess I don't want to spend hours changing it again.

I've disabled shadows for horizontal pipes because they don't look right when I treat light fixtures as point lights rather than area lights. A pipe that crosses the center of the rectangular light should produce a blurry shadow rather than blocking all of the light. Vertical pipes that go into the floor do cast shadows, and also collide with the player.

Here are some more screenshots showing the network of pipes in the ceiling. Some of the pipes continue through the walls into other areas. Sorry if pipes are difficult to see due to the poor ceiling lighting, but that's typical of parking garages.





Parking garage with pipes in the ceiling but no cars.

After looking at these screenshots, I decided the lights are too bright and blueish. I reduced their color temperature to something of a warm yellow color, which looks more like the lighting you typically see in an underground parking garage. Here are more parking garage examples.

Parking garage with a lower color temperature yellow light.




Note that people (zombies in gameplay mode) and rats are placed in and can navigate in parking garages. There's nothing of value to steal down here, so I'm not sure why the player could come down to this level. Maybe I should hide valuable items in cars? That's one idea I'll consider.

Future Work

One problem I haven't solved is how to connect the ramp from the upper level of the parking garage outside the building. Cars need to enter and exit somehow. I have no easy way to add geometry outside of the building's bounding cube or lower/remove the terrain around the ramp, so the only option is to have it exit somewhere on the ground floor of the building, preferably near a corner or exterior wall. I can cut a hole into the floor above the ramp, but there will be other objects already placed there such as walls, offices, and hallways. (Remember, the entire building interior is generated before the parking garage.) Maybe the ramp has to be placed first, then the rooms assigned, then the parking garage filled in? The problem here is that my office building floorplan templates don't have a way to mark some corner as off-limits for offices and hallways. This would make all buildings with parking garages have an L-shaped floorplan with the corner cut out, which is more difficult to divide into rooms. I could merge all rooms that overlap the ramp and convert them from offices to some other room type, but then how do I remove the walls? I don't have support for a different floorplan on the ground floor compared to the floors above.

On top of this, if I add an exit door/hole, then the parking garage contents will be visible from outside the building. That means I have to generate it before the player enters the building, which ruins all of my plans. Sigh. So far I haven't figured this out, so I let the ramp end at the concrete ceiling under the ground floor for now. I suppose the cars will be stuck down there until I fix this.

What's next, other than solving the ramp problem? I was thinking that maybe I can work on the lighting and electrical system. I can add a circuit breaker panel in the basement or parking garage that controls the room lights for the entire building. Currently I don't even have light switches down there. I think that would be an interesting project, though I'm not sure how it would contribute to gameplay. Another idea is to add some chance of lights flickering, having a dim glow, or being completely out. That's pretty typical of the lower level of maintenance seen in a parking garage. This way the player can at least steal the broken, low value lights.

Thursday, February 10, 2022

Procedural Bookcases

Here's a sampling of some of the procedural bookcases I'm generating for 3DWorld's building interiors. These are placed in many of the rooms of houses, and libraries in office buildings. Each bookcase has a unique collection of books that can be individually removed by the player, carried around, put down, and opened up.

A bookcase has between three and five shelves containing books. Most books have the spine facing out, and a few have it facing in. Books are occasionally tilted to lean on another book or laid flat on a shelf. I've also mixed in some gaps and completely empty shelves. Here are some examples.


 


 

Books come in random widths, heights, thicknesses, and colors. Their titles are randomly chosen from a list of 5000 popular book titles. I haven't yet been able to come up with a way to generate realistically sounding titles. Some books have authors listed under their titles on the front cover. Author names consist of a male or female first name randomly selected from a list of around 1000 each, plus a last name I generate using a sort of Markov chain approach that includes a list of vowel and consonant word parts. In addition, some books have cover images that are randomly selected from one of my other procedural generated 3DWorld screenshots show on my GitHub project page. Books can be opened, showing pages randomly selected from various papers around my house that I captured with my phone camera. Here are some of the books I happened to find in this house.


Some books I took off the shelves and arrayed out on the floor. Huh, the two open books have the same page image.

Monday, January 24, 2022

Rats in Buildings

I'm working on adding rats to the procedural building interiors of 3DWorld. Why rats rather than some more common animals such as dogs or cats? I have several reasons. First, I have pet rats myself, two boys and four girls. I bought the parents at a pet store and they had 17 babies, four of which I kept as pets. I have some knowledge of their behavior that will help with modeling them, while most other people wouldn't notice if I get something wrong with their movements. Rats fit well with zombie themed games like the one I'm creating. Rats are easier to animate than dogs and cats because they're small, fast, and low to the ground. Their leg movements aren't as large or visible, which means I can get away with doing a simple procedural animation. Finally, rats have lots of interesting places to hide in my buildings: under beds, under tables, in closets, etc.

I found a free 3D rat model on Turbosquid and edited the materials to make it look a bit better with smooth normals and reduced specular lighting. I then added config options to add rats to random rooms on the ground floors and basements of buildings. A random location is chosen until one is found that's not intersecting a room object. This was the easy part!

A rat runs across the floor toward me, but I'm a ghost so it doesn't see me.

The more difficult steps were defining their behaviors/movements, path finding, and collision detection. Rats have a different style of movement compared to the cars and people I've previously added to 3DWorld's cities. They tend to run in a straight line, with frequent stops and direction changes, often under objects and along walls. I attempted to imitate this movement style by choosing paths consisting of variable length straight segments combined with occasional small direction changes. I also added in a weak "wall following behavior" from robotics, and logic to prefer destinations that are sources of cover such as under furniture and in closets. Each path segment has a randomly chosen speed with gradual full body turns between segments.

Rats collide with pretty much everything on the floor including walls, doors, stairs, furniture, plumbing fixtures, placed objects, dynamic objects (such as soccer balls and basketballs), people, the player, and other rats. The collision query is a modified line segment intersection test where I expand the room objects by the cross sectional radius of the rat. In other words, the max of its half width and half height. Static objects are queried prior to choosing a new path segment to verify that segment is traversable. If not, I generate a new candidate segment and try again. Dynamic objects are queried each frame the rat is moving to handle the case where an object moves from the time the rat started on the current segment. This makes rats able to dynamically dodge some types of objects and adapt to the player moving furniture.

The behavior I implemented allows rats to randomly explore the rooms on the current floor of their building. This isn't particularly interesting behavior by itself. Adding in fear, avoidance, and hiding behaviors was challenging, but greatly increases the fun in following rats around. My rat AIs are afraid of sounds and the sight of people including the player. The degree of fear is determined by the volume and distance of the sound, and the distance and visibility of the player. I considered adding in fear of sudden light changes as well, but it seemed easier to make the click of the light switch cause fear and lump that in the category of "sounds." They're much less fearful when the sound or person is in a different room, and not at all afraid of people they can't see due to occluders such as walls. Rats accumulate fear from these events, and their fear levels decrease slowly over time. They track the location/direction of the last sound or person they observed and prefer to avoid that area.

There are two fear behaviors: hiding and avoidance. The hiding behavior is triggered when a protected spot under an object is visible in a line of sight test from their current position. In the case of multiple possible hiding spots they choose the best one based on a factor of distance from their current position, distance to whatever scared them, and amount of protection. The protection of a hiding spot is determined by how small a space it is and how well it covers the rat. Small spaces are best, as are spaces that fully cover all of the rat's body. In addition, there's a preference to avoid locations that are occupied by another rat to help prevent excessive rat-rat collisions when they compete for the same space.

Once hidden, the rat will face the player or sound and wait. If the player moves the object the rat is hiding under, it will attempt to run under the object in the new location. Or maybe it will choose a different nearby object to hide under. If the player steals the object then the rat will find another hiding spot, if one is available. Once the player has left or the sound has faded, the rat will come out of hiding and continue to explore the building.

Two rats are scared of me and hiding under the table while looking at me. Sometimes they hide under the chairs.

When there are no available hiding spots, a rat will attempt to move away from the source of fear at a faster than usual speed. This is referred to as "running away." I found it tricky to handle the case where the player forces the rat into the dead end corner of the room because there's no way for the rat to run away. At first the rat would either freeze halfway in the wall, or spin in circles. I fixed this by changing the behavior so that the rat attempts to run past the player along one of the walls. This makes chasing rats extra fun because you can somewhat force them to go in a particular direction, at least until they find something to hide under. I had to add a key to disable rat fear so that I could more easily debug their movement without having to chase them around.

The rat motion controller works by directly setting the velocity of the rat, and setting a target direction. This is different from both people and cars. In particular, the car motion controller produces acceleration while the player and people AI controller sets position. I chose this method for rats because it seems to work well given their agile movements, sudden turns, and straight line direction. It does however mean that I can't reuse much of my existing code and had to write most of the rat logic from scratch. Of course, the fact that rats move around on the floor and don't have to climb stairs makes them significantly simpler to model than people. (In reality my pet rats do in fact climb up and down the stairs in my house.)

I still have to work on rat animations. My existing animation system for people doesn't work because it assumes two legs, while rats have four. Biped and quadruped animations are very different! I can reuse the existing animation framework, but the leg motions need to be written as a separate block of code. I'll try to post one or more YouTube videos when I have rat leg animations completed.

Update: Okay, it only took an hour or two to write the first version of leg animations. I used a slow motion reference video to help with getting the motion between the various legs in sync. It appears as though rats move diagonally opposite legs and then alternate with the other opposing pair. Feet lift up between the backward and forward stroke. They also bounce a bit with their backs going up and down as they walk, but I'm not attempting to model that part right now. That leg motion looks like sine waves to me, so that's how I implemented it. I decided to go with a pair of forward/back and up/down translates rather than a rotation like I use with people because the code is simpler and has less constants to tune. After writing some vertex shader math here is what I have:


Note that the rat animation and movement is playing back at around half speed to make it easier to see. I start off by walking into the room and turning on the light, which scares the rat so that it hides under the table. Then I back away and let it come out of hiding. At this point I switched the camera to flight/noclip mode to avoid scaring the rats so that I could get closer to them without triggering their fear behavior. I lost sight of the first rat, and after looking around some rooms I found them both in the kitchen. I scared them a bit more by closing the door, which made them both hide under the table. Collision detection between two rats is partially working. Bedrooms have more interesting hiding spots, but it's somewhat time consuming to chase them across the hallway into the bedroom area.

The animation is working but could be improved. In particular, it would look better if the upper part of the legs moved. Unfortunately, the thigh is merged into the body for this model and doesn't really rotate without distorting the body, so I gave up on that approach. Maybe I'll experiment with rotations later to see if they look any better. Of course it's actually more difficult to tell if the animations are correct when the rats move at normal speed and they're running away from you.

Okay, so now I have rats in 3DWorld's procedural buildings. What purpose do they serve in gameplay? I haven't quite figured that out yet, though I have a few ideas. Maybe they're worth money if they can be caught? Maybe they attack the player (if they're in a large enough group) and do damage? Maybe they squeak and alert zombies? Maybe they can be picked up and thrown at zombies? I don't know how much sense that last idea makes, but it certainly sounds like fun! I do like the challenge of having to chase a rat around and move or block its hiding spots. Of course it's nowhere near as difficult as chasing a real rat around a real house.

Saturday, January 8, 2022

Building Interior Fails

I recently attempted to add interiors to non cube shaped buildings. This includes cylindrical buildings with round sides, buildings with more than four sides, and buildings where stacked floors are rotated from the floors below. I was curious to see what the interiors of these buildings would look like. Note that all of the walls, doors, ceilings, floors, and rooms are still axis aligned cubes. They just stick out from the curved building exterior walls in interesting ways, giving some pretty amusing results.

Maybe it was a stupid idea to try adding rectangular rooms to round/cylindrical buildings? But this was in the name of science!

Some of the furniture, appliances, and plumbing fixtures are placed outside or partially outside the building exterior walls as well. This mostly happens at the corners of buildings. The lighting is wrong because there are no windows to "let in the sunlight", and I'm not sure where (if anywhere) the room ceiling lights are placed. I'm actually surprised I didn't get an errors from the building interior generation code. I suppose it doesn't care where the walls are and only uses the bounding cube of each part of the building.

Now that's what I call a corner office! Or maybe a windowless (and floor-less) office? I guess it works as long as you don't mind sitting halfway inside the wall of the building.

Even bathrooms can suffer from this problem.

Our bathroom doesn't need a ceiling fan because it gets so much fresh air from outside. You can even have a conversation with the person across the street while sitting on the toilet.

Stairs that would normally be against the inside of the exterior building wall are now on the outside of the wall. This made me consider adding fire escapes to the outside of some of the larger buildings. The biggest problem I can think of is that the lighting system won't work properly for these fire escapes for the reason mentioned above. It could take considerable work to fix that.

Yeah, those stairs are *supposed* to look like that. It's a .. fire escape! That's right. At least there are railings on some of the sides.

I attempted to fix some of these problems by removing objects placed outside the building. That fixes the biggest problems, but there are still issues. The internal floorplan doesn't make any sense, has empty areas, and doesn't fully connect the rooms. I've given up on this for now but left it on my TODO list, which is now on Trello.