I was watching some YouTube videos of urban exploration where people were exploring storm drains under a city, and this gave me something else to add to 3DWorld's procedural cities. I've improved basements in the past few posts by adding interrogation rooms and machine rooms. Now it's time to add underground tunnels. I originally thought of connecting these to the water (ocean), but none of the cities are close to water. Also, the water is far from the player spawn point near the center of the island. So instead I decided to connect tunnels to basement rooms instead.
I started by iterating over all of the locked ("false") doors I had added at the end of extended basement hallways and long rooms to make them appear larger. Any door that was far enough underground and had empty space surrounding it on three sides was a candidate for placing a sewer tunnel. I added a vault-style heavy metal door with a wheel handle connecting the room to the tunnel that was open to the player. Tunnels run perpendicular to the room and door in both directions and may have right angle bends in their paths. I added metal grates in the form of vertical bars near the ends of the tunnels and made the very ends black. This, combined with dark fog, made the tunnels appear to continue past the grates.
Here is a view of a sewer tunnel system under the ground with the terrain and grass drawing disabled. This network runs around the perimeter of the extended basement of an office building, but in general it can go anywhere there's available space. I may even connect tunnels between multiple buildings in the future like I already did with extended basement hallways.
View from outside a tunnel system that runs under the ground. The terrain and grass drawing has been disabled. The dark rectangle on the bottom right is the sewage quad drawn under the bend.
Tunnels have dark brown sewage flowing inside them with a randomly chosen level and flow rate. They also have smaller pipes connecting to them on the sides and thin metal pipes passing through them along the top. I added rats on the ground in some segments that squeak and run to the nearest grate when the player approaches. I also added spiders that walk back and forth upside down along tunnel ceilings. Spiders don't currently interact with the player.
Tunnels are drawn as cylinders, with quarter torus sections for bends. It actually took me quite a bit of experimentation to figure out what the correct bend geometry was. Fortunately, I had already added half torus drawing for bucket handles, so a quarter torus was simple to add. I used a concrete texture for the walls and applied the wet effect to these to give them streaks of darker color. Speaking of darkness, there's no light inside these tunnels, so the player must bring their own flashlight or candle to see anything. See how dark this is? It's completely disorienting to walk through here without a light.
Flashlight view of a sewer tunnel near the place where it connects to a basement room, with a spider visible on the ceiling.
I did add logic for zombies to follow the player into the tunnels with a recursive path finding algorithm that forms a path between the connected cylinder and quarter torus sections. These are currently dead-end death traps for the player. Maybe I should add some reward at the end of the tunnel? I'm not sure what to place here. A vertical shaft leading back up to the surface that serves as an escape path? That could work in cities where the ground above is flat.
I'm considering adding T-junctions to sewer systems. I haven't yet figured out the mathematical function needed to join cylinders into a T-junction in a smooth and clean way. I do have a T-junction where the tunnel meets the room doorway, but this is square on the doorway side. It's easier to cut a square hole into a cylinder (remove half/180 degrees of the cylinder) than a circular hole. 3DWorld has CSG support for cubes, some types of cube + axis aligned cylinder operations, and a few others, but no cylinder vs. cylinder operations. This would be a problem for vertical escape shafts as well. It would be so much easier if I used rectangular tunnels. The problem is that I already have extended basements, which aren't much different from rectangular tunnels. The main difference is the lack of sewage on the floor!
Here is an earlier video where I explore a sewer tunnel with a flashlight and candle as light sources.
The player can find both light sources in buildings. Flashlights shine further with a narrow beam. Candles light only nearby objects, but in all directions. There are no ceiling lights, shadows, or indirect lighting.
Here's another video where I added spiders to the ceilings.
I think I'm done with basements for now. I may add underground shopping malls sometime in the future if I can figure out how to do it efficiently. The next item on my task list is writing the logic for cars to navigate city parking lots. I'm sure that will be challenging, considering how difficult cars have been to work with in the past.
I've been adding new room types to 3DWorld's procedural buildings lately. I wrote a blog post on conference rooms, then a short post on interrogation rooms. The topic of this post is machine rooms. I've been wanting to add procedurally generated industrial type machines/devices to basements for a while, ever since I revisited basement pipes. I've also wanted to add more variety to extended basement rooms.
I've been working on adding code to route basement and parking garage pipes down the extended basement hallways and into leaf rooms. But it only makes sense if there are objects in these rooms to connect the pipes to. I do have bathrooms with sinks and toilets down in the basement, though not every building has these. Another option is to add more water heaters and furnaces similar to the ones placed in house basements and office building utility rooms. The appliances I've created are more residential sized and look out of place in a large open basement room though.
I added a new type of building room object called a "machine" that's represented as a bounding box placed on the floor and against a wall of the room. All placement and collision detection handles this as a box. However, the drawing code is free to add any details it wants when the vertex data is generated and sent to the GPU. None of this geometry is stored CPU side, it's all created as temporary data that's drawn later. An added benefit of machines being placed in basements is that the vertex data doesn't need to be generated until the player enters the building. This allows me to create very detailed geometry without the need to worry about optimizing for memory or generation time, since these are only found in one building at a time. I do technically create machines for multiple buildings ahead of time to help with visualization and debugging, but I don't think it makes much of a performance difference.
Each machine consists of a concrete slab base and one or two main components, which can either be rectangular/cube shaped or vertical cylinders (such as storage tanks). These larger components have smaller boxes and cylinders attached to their exterior surfaces with different colors and textures. I then added some special decoration objects that are already used in other places in buildings. These include AC units, vents, valve/wheel handles, and breaker boxes. Some breaker boxes are open to expose the interior. To finish things off, I added a variety of pipes and ducts connecting the components together and going into the walls, ceilings, and floors around the machines. These use multiple materials including plastics and metals (copper, brass, bronze, steel, and rusted). I used special checks to make sure these added detail objects don't intersect each other or clip through adjacent walls or machines in incorrect ways.
I found ten free textures online to use with these machines. Most are some type of metal panel or plate texture, some of them with rust and scratches. Only one of them has a matching normal map. I may look for better textures with normal maps later, but these are fine for now. I haven't put any effort into aligning textures to geometry because it's too difficult to automate this for this many different textures.
Here is an example of a long and narrow machine room.
Machine room with random machines lined up against the walls. Each one is unique.
Note that these rooms are somewhat brighter than they normally appear to make them easier to see. I prefer to have these deep basement rooms be darker. Here I have indirect lighting enabled, though it doesn't work very well due to the way rooms are randomly and sparsely placed, compared to the dense rectangular floorplan of above-ground building interiors. At least the shadows on the lower walls and floors are correct.
Machines are placed along the sides in a way that allows space for the player and building AI people to cross the room between them. I added extra clearance for doors so that they're not blocked. The rooms in these screenshots have up to 10 machines for testing purposes, though for normal buildings I'll probably set the max count to 4.
A different machine room with different machines and many cracks on the walls, ceilings, and floors.
I definitely like the way the cracks and water damage fit with the scratched and rusty metal textures I'm using with machines. The crack effect only applies to the walls, ceilings and floors though. I don't think cracks go well with metal surfaces. I did apply the wet/water damage texture effect to machines. This give the surfaces a darker but more shiny appearance. Here's another machine room with water damage and stains but no cracks.
A machine room in the basement of a house, with plaster walls.
I think I'm done with new room types for now. I plan to continue to work on extended basements in the near future. Next up is underground tunnels/pipes that the player can walk through. I'll likely have a blog post on that topic within the next few weeks.
The interrogation room can be found deep inside the extended basement, after a long walk down dark and narrow passageways. A single empty chair sits in the center of the room under the light, facing the door. Buckets of unknown colored liquids are placed around the chair. Bottles and bits of trash are strewn across the dirty floor. The walls, ceiling, and floor are wet, stained, and cracked. A ladder with an unclear purpose leans against a wall.
An interrogation room with cracked walls. It's not very bright in here, so I lit it up with my flashlight.
Interrogation rooms are relatively rare and are only placed in extended basements of buildings with water damage. They can appear in both houses and office buildings. Sometimes the chair has fallen over backwards, and sometimes the light doesn't work. Broken lights shower sparks over the chair. The ladder is optional. This is the only room that currently contains the new bucket-with-liquid object.
These rooms don't serve a purpose yet. They're mostly just there to be creepy, though I didn't go so far as to add blood, tools of torture, or anything like that. I might not add this at all, since it's not supposed to be that type of game.
Also, take a look at my new basement cracked surfaces effect in the screenshot above. I replaced the old shader code with new code that uses a Voronoi grid and an intensity mask to apply cracks to selected areas. It's based off of this Shadertoy shader. Cracks are added as a layer that modifies the material properties of the surface it's applied to.
I'm pretty happy with the look of glass upper retail floors added in my earlier post. Maybe they're not very practical for public spaces, but they make the room look larger while also allowing for increased usable space. I need more glass in these buildings. It would be interesting to have glass walls as well. Maybe for conference rooms? We have some real conference rooms with glass walls/windows at my work office.
The first step is to choose which rooms will be conference rooms. Since most of the buildings in 3DWorld share the same vertical floorplan across many floors, it makes the most sense to have one or more dedicated conference room on each floor. The ground floors of office buildings do have some special rooms such as storage rooms, security rooms, and utility rooms, so I can have the floorplanner overwrite the conference room with a special room type on the first floor.
Since I want to add glass walls to conference rooms, it makes the most sense to place them adjacent to hallways. A glass wall between two conference rooms, or a conference room and an office, would be odd. And I don't even want to think about what a glass wall shared with a bathroom would be like! I also have a constraint that the conference room can't have an exterior wall, since the interior window won't alpha blend properly with the exterior windows. (I spent a long time trying to solve this with glass floors and never found a solution I was happy with.) Fortunately, this means I don't need to worry about walkways connecting between buildings that end at conference rooms. The candidates are all rooms next to an interior hallway that have doors that either open into a secondary hallway that meets the other hallway at a 90 degree angle, or to another parallel hallway one row over. Another requirement is that conference rooms don't have any overlapping stairs or elevators. This rules out some rooms that have notches cut out where elevators are placed along the sides of the main hallway.
There is also a size preference. I want to add large conference room tables and many chairs to these rooms, with space around the sides for people to walk. The conference room should be the largest room that meets the placement requirements. I gather a list of all legal room candidates, find the largest one, and select all rooms within 90% of the largest room's area as possible conference rooms. Then one or more of these rooms are selected. Any room that has a conflict (such as intersecting stairs) on one floor is changed to a different room type such as an office for that floor. This sometimes happens with conference rooms at the top or bottom of a building stacked partition where stairs extend above or below.
Each conference room has a single large table that runs most of the length of the room, with as many rolling office chairs as will fit along each side. One of the walls at an end of the table has a large TV hanging from it, and another wall has a large whiteboard. Neither of these can be placed over the window or door(s). I found a conference room phone 3D model that I placed on most of the tables in a random position and orientation. Then I added some random items on top of the tables such as laptops, pizza boxes, books, cups, and water bottles. Some rooms have trash cans, recycling bins, and/or potted plants. Maybe I should add pens, pencils, and papers as well.
Here is what an example conference room looks like. There are actually two adjacent conference rooms with glass windows on this hallway. The doors open into the next hallway over, toward the back.
Two conference rooms in an office building, viewed from the hallway through glass windows.
I added proper collision detection for these glass panels that works with the player, building people, zombies, rats, snakes, insects, thrown objects, etc. Spiders can climb on the surface. I had to fix a number of problems related to lighting and occlusion culling so that objects on the other side of the glass were lit and visible even though there was no doorway between the rooms. I was originally worried that the AI would have path finding problems if a zombie saw the player on the other side of the glass but couldn't pass through it. But no, the existing system worked fine and the zombie walked out the door and around the corner to reach me. They target the last known location of the player when the player isn't visible.
Lighting isn't perfect for these rooms. Light will pass through the glass from the adjacent room, but the sides of the glass are only lit by lights on the same side. This means that the glass on the inside of a room will appear dark when the room lights are off but the adjacent room lights are on. This is one reason why I turned all the lights on for these screenshots. I did make sure to flag conference room lights with motion detection logic so that they automatically come on when the player enters, which helps to hide this problem. The player can manually switch them off though.
Conference room in an office building, viewed from inside.
Sometimes the window is on the shorter wall of the room. The conference room below has a long whiteboard and short glass window. This room is large enough that it needs two ceiling lights, rather than the more common (and efficient) one light per room. I believe there were 7 chairs on each side of the table.
Larger conference room with a long table that has a phone and a laptop on it. There are 14 chairs.
I haven't made these windows reflective yet. That's a complex process that reduces framerate significantly as some parts of the building interior must be drawn twice. Most indoor windows are designed to be anti-reflective anyway, so the reflections are only visible at sharp angles. I don't feel that the benefit of adding reflections justifies their cost.
While writing this post I though that it would be interesting if the player could break, or at least crack, these windows. Maybe throwing a large enough object at the window will create a crack, similar to throwing a ball at a mirror. The problem is that the largest object the player can actually throw is a ball. I don't think a basketball or soccer ball would normally crack a window. Maybe I should add some new object that can be thrown, where the only purpose is to crack windows. Should the player be able to throw a fire extinguisher? That would do it. Currently fire extinguishers can only be dropped. Or maybe the player can throw a laptop left on the conference room table, or the phone? Should I allow zombies to break through windows to get to the player?
I added elevated walkways and skyways to 3DWorld's procedural cities earlier this year. The skyway with its moving belts on the floor provides the player with a transportation system that's several times faster than walking, assuming they're not setting the movement speed higher like I often do when debugging. The problem is that the skyway is very high up in the air, and it takes a long time to get to it by taking the stairs or elevator from inside a building. This means that it's not a very effective transportation system, considering the skyway is also limited to a single city.
I need to add a faster way for the player to gain elevation without passing through a building. One idea I had was to add elevators that start at ground level in an open area of the city near the sidewalk and extend upward to connect to the side of a walkway. I had this idea around the same time as my escalators, which I showed in the previous post. I decided to make these elevators have transparent glass walls similar to the upper glass floors I added to retail areas. However, I haven't made these glass elevators reflective, so the process has been easier. People don't use these elevators (yet) either. All they have to do is avoid walking into the elevator shaft.
Elevators are placed at the same position as the support pillar under the walkway, off to one side. This is typically around the midpoint of the walkway span. The pillar region has already been chosen to be in an open area free of other objects, which makes it likely that the elevator shaft can fit there as well. The elevator is omitted if there's a collision with another object such as a building. This includes objects that block the entrance to the elevator on the lower level.
Here is an example of an elevator connected to a relatively low walkway between two buildings that's two floors tall. There are doors at both the top and bottom that open and close to prevent the player from falling into the elevator shaft or being crushed by the elevator at the bottom. The upper floor doors open to the sides like normal elevator doors. I couldn't find a good way to add side opening doors at the bottom because of the way the elevator shaft is narrow and vertical, so I added a door that slides upward instead. It's really more of a gate - a grid of black metal bars that doesn't block the player's view through the glass walls of the elevator.
City walkway elevator viewed from the outside. Elevators have glass walls, a metal frame and platform, and a gate with bars guarding the bottom entrance.
These elevators have no player controls. They're fully automatic and will anticipate the player's movement so that they're open and waiting on whatever floor the player is currently on. They will change floors as the player changes elevation inside buildings or by flying in debug/noclip mode. They're also pretty safe as the glass walls and doors prevent the player from falling into or out of the elevator shaft. I did add gameplay fall damage as a "reward" just in case the player can find a way around this system.
I cut a hole in the exterior wall on one side of the walkway to add the elevator entrance. This had to be done for both sides of the walkway wall since the exterior wall is "owned" by the city while the interior is owned by one of the connected buildings. The entrance on each floor has typical dark window/door trim added around it to cover up the edges of the walls. I set up buildings so that the walkway interior is drawn when the player is in the elevator shaft. It's typically blocked by closed doors when the player is outside of the elevator. Elevators use outdoor city lighting with sun and moon light + shadows since the majority of their surfaces are outside the walkway.
City walkway elevator with doors open, seen from inside the walkway
These elevators use the same door opening/closing and bell sounds as interior building elevators. In fact a lot of the ideas and code were copied from indoor elevators. Once I've worked out the logic for these types of objects it's much easier to reuse in other places.
Here is a short video where I travel up and down an elevator leading to a high walkway that connects to the skyway.
I added skyways to 3DWorld's procedural cities a few posts ago. These included horizontal moving walkways that the player could use to get around more quickly. This got me thinking that I should add escalators, since I already had stairs and elevators. At first I considered using escalators to replace the short sets of stairs that lead from walkways to skyways above them, but these are uncommon and don't have much space to work with.
Escalators
The first question was, where do I put escalators? I want them to be placed inside procedural buildings somewhere. I already have stairs and elevators connecting the floors of my buildings together. Escalators need more space, and are ideal for rooms more than one floor tall. How about placing them in tall retail areas? There's plenty of open space, both horizontally and vertically. I decided to add a pair of escalators going in opposite directions next to each other along the center line of the long dimension of the room. I start by placing them in the center of the room and then shift them alternately to either side until I find an open place between existing stairs, elevators, and exterior doors.
Originally, the escalator placement failed some of the time because the
stairs and elevators were spaced out in the center of the primary
hallway in the office building floors above the retail room. But now
I've moved some of the elevators to the sides of the hallway, which
frees up more space for escalators in the smaller buildings. You can see
one of these side elevators in the screenshot below.
Here's an early version of a working escalator prototype. The bottom floor/steps part is animated in my attempt to recreate the movement pattern of escalator stairs. The player can ride it up and down, though there's no floor connected at the top. All you can do is fall off the high end. Walking up and down is much faster than standing and letting the escalator move you. Objects such as balls and animals collide with escalators as well, though they can't actually move with the escalator.
Glass Floors
The next question to decide was what to put at the top of escalators. I wanted to add another level to the retail section, something different than standard tile floor. I like working with reflections, so I added a transparent glass floor. Maybe this isn't very realistic for multi-level public interior spaces where one person can look up at someone above them. But I had already added this before someone pointed out this problem. Each floor is currently a single rectangle. In the future, I may add a second shape to form an "L". As I expected, the combination of transparency and reflectivity made this extra challenging.
First, the transparency: In OpenGL (and other graphics APIs), transparent objects must be drawn after the opaque objects for them to properly alpha blend with geometry drawn behind them. The problem is that the glass floor is next to windows that lead outside the building. This means that exterior objects such as the terrain, grass, trees, roads, etc. must be drawn before the glass floor. This is a problem because buildings are drawn first, and interiors use a different lighting system from exteriors. I had to draw the glass floors in a final pass after everything else in the scene was drawn. I didn't want the overhead of setting up the interior lighting pipeline a second time in one frame, so I changed the lighting system to use a custom ambient value for each face of the glass. This technically works because it allows the glass to be lit from both the interior lights and the outside sun, from either the top or the bottom side. It avoids the unrealistic dark surface on the bottom "unlit" side of the glass. The only downside is that the floor doesn't receive shadows from other objects. I think this looks acceptable, especially when reflections are added.
Reflections were the next challenge. I only needed to make the top surface reflective, so that the reflection was only enabled when the player was standing on top of the glass floor. 3DWorld doesn't write a buffer that contains normals that can be used for screen space reflections, so I had to render the scene a second time from a reflected camera. This works the same way as bathroom mirrors and swimming pool water surfaces, except it's an entire floor of a large room filled with tons of objects. Compare this to swimming pool rooms and flooded basements that have sparse object placement, and mirrors that have a small reflected region. On top of this, the exterior is visible through windows and must be drawn as well. It was quite difficult to get fast reflections that didn't halve the framerate. (Keep in mind that I'm targeting older computers for 3DWorld, so my goal is 120+ FPS on my gaming PC.) I had to rewrite parts of the object occlusion culling system and implement horizontal clipping planes for drawing.
Finally, it was time to add objects to the upper glass floor. I started by adding railings along the open end to keep the player and AI people from falling off. Then I added additional rows of "shelf rack" objects on the upper floor above the rows on the lower floor where there was space. Any shelf racks that partially fit were cut to shorter lengths as long as the clipped rack was above a certain minimum length. I made sure to add extra padding around the end of the escalator and any openings of stairs and elevators that reached the second floor. This works, though the spaces can be tight in some cases.
Here is what the escalators and glass floor looked like after these changes.
A pair of moving escalators in the retail area of a building leading up to a glass partial floor.
The escalator has upper and lower straight ends and a 45 degree polygon in the middle. I didn't have the patience to try and write the code to create proper curved sides like I see in most real world escalators. These shapes are much easier to write collision checks for anyway. Each escalator has a vertical pillar at the high end to help support it and the glass floor attached to it. I thought this would give them a more interesting look than filling in the area under the escalator with material.
Side view of the same pair of escalators. Maybe they need more depth to the sloped part? From this angle they look like a pair of high heeled shoes!
Here you can see some building AI people managed to make it up the escalator to this empty glass floor. It's easier to test reflections of dynamic objects without the shelf racks in the way. (Well, that's true, but in reality I've added these screenshots out of order.) I actually started by removing the transparency effect and setting the Fresnel term to 1.0 to turn these floors into a perfect mirror. That made it more obvious when something was missing or otherwise wrong with the reflection.
A glass floor at the top of the escalators with a railing and not much else to do. The reflections of the people and the interior of this room are visible in the glass.
It looks questionable when the escalator is in the center of a large building and half the building footprint is a single unsupported glass floor. That must be some pretty strong glass! So for the larger areas, I added vertical columns that pass from the floor below, through the glass, and into the ceiling. Technically these already existed on the floor below. I then added horizontal steel beams under the glass connecting the pillars with the exterior walls and each other. I suppose it looks more structurally stable this way. Here is what this looks like before objects were added to the upper floor.
A larger reflective glass floor area with an elevator through it, plus vertical support columns and horizontal metal support beams.
Note that this floor has an elevator going through it. Two elevators in fact, though the one on the left doesn't have enough clearance from the escalator to have a door on that side. Only the right side elevator can access the glass floor. The glass is clipped out in the elevator shaft so that you don't pass through it when riding in the elevator. It should be possible for enclosed stairs to pass through the glass floor, though I've never seen this happen in a building I've visited. I believe the reason is that stairs are placed in the center of the hallway, the escalator is placed next to them, and the glass floor is always the smaller half of the room area (which excludes the stairs).
And here is the same area with shelf racks added. You can see the reflections of the racks and their contained objects on the floor.
The same reflective glass floor as above, but with racks of shelves added on top to give people a reason to come up here.
AI People
Did I say the glass floor transparency and reflections were challenging?
They were easy compared to updating the AI logic. It actually wasn't that bad to
make the normal building people use escalators. They work like stairs,
except they must select the correct "up" or "down" escalator based on
the direction they're headed. People walk up and down escalators rather
than standing and riding them because it was easier than trying to blend
in an idle animation. I might attempt the standing/idle version of escalators sometime later.
Now that I have people getting to these floors, I have to give them a destination so that they don't simply stand there at the top of the escalator. Some random spot between the racks should work. I just need to make sure they stay over the glass area and don't walk out over the rest of the retail area where they float in the air. The problem with the existing logic is that it assumes each floor covers the entire horizontal extents of the room. I had to add logic to clip the walkable area to the bounds of the glass floor to many places. But then I realized the real bug was that people didn't consider the railing a collider, and making them avoid the railing fixed most of the remaining problems.
The final step was to get the zombie AI to behave itself while chasing the player around the escalators and glass floors. Two of the more difficult primary objectives were to (a) chase the player to any reachable area, and (b) not get stuck around the escalators. This took me many, many iterations over the course of more than a week. There were just so many special cases and rare failures to deal with:
What if the player goes up the down escalator? Do we follow them going the wrong direction, or take the correct escalator and try to catch them up top? And if the player stops on the escalator, do we go up to the glass floor on the up escalator, then go down on the down escalator after them? Or wait at the bottom for them instead?
What if another zombie is going the wrong way on the escalator we're on? What if a zombie is stopped at the top or bottom of our escalator? Do we stay (walk in place) on the escalator waiting for them to move? Push them to the side? Clip through them?
What if the player no-clips/flies (aka cheats) to a spot above the retail area that's not over the glass floor? Do we go to the lower floor and stand under them? Clip through the railing and walk on air to get to them? Go to the closest reachable point to them on the upper floor? Ignore them and go about our business of selecting random destinations?
What if there are stairs through or adjacent to the glass floor that don't connect to it, or we don't have a path to them, we're on the stairs, and the player is on the glass floor? Zombies really wanted to clip through the stairs walls in that case. (Technically, if the stairs pass through the floor, then they can be on the floor while also on the stairs.)
What if someone pushed a zombie onto an escalator that it wasn't planning to use? Or off of an escalator? For stairs I just snap the person to the nearest point outside the stairs, but for escalators that point may not be over the glass floor.
In the end I just had to keep adding special cases to handle situations like this, test, and iterate until everything was working. Every time I fixed one problem I encountered another. In the process I had to add missing error checks, convert fatal errors to debug printouts, add more debug visualizations, and things like that. I suppose those changes are generally useful. It mostly works now, though it's not perfect. There are two minor issues that I haven't solved and have flagged as accepted behavior.
Sometimes a zombie will get pushed by another zombie into that spot exactly between the two escalators. In this case the zombie effectively gets trapped because it intersects both escalators at the same time and can't leave that spot without clipping through at least one escalator. So it just stands there and looks at the player. I think this is acceptable though because it can still attack the player when using either escalator. They're effectively camping the escalators. It's actually worse for the player because it makes the escalators unusable and may trap the player on the upper floor if there is no other exit. On the plus side, I don't believe it's possible for more than one zombie to be stuck in this location at the same time.
The second problem is when a zombie exits the stairwell onto the ground floor of the retail room, sees the player on the glass floor, and immediately re-enters the stairwell. It's nonfatal because they'll eventually reach the floor above or below, then make their way back to the retail room to chase the player. This behavior seems to be random and somewhat rare. I'm not sure what causes it. It's difficult to debug because I don't know until after I see them enter the stairs, so I can't query the system for their state before they made that decision. I attempted to fix this multiple times. Each time I thought the fix worked because I didn't catch a zombie doing this, but I saw it happen again later when I was trying to debug something else.
Here's a final video showing the player and AIs using escalators and walking on the reflective glass floor. Maybe later I'll record a video of zombies in the retail room.
At this point I'm calling escalators and glass floors "done enough". I may get back to them later. Next up: glass elevators to city elevated walkways. I already have elevators, walkways, and the drawing code for glass surfaces, so why not?
I've been working on water rendering for indoor and outdoor swimming pools and flooded basements on and off for the past month or so. This has been going on in parallel to my work on interior dirt and damage, escalators, and glass floors. I'm actually writing both blog posts at the same time, but water was technically started first, so it will be published first. Working on water is pretty fun and a nice break from the more difficult parts of development such as AI navigation and building object placement logic. The theme here is to make water look more realistic, but also add a varying degree of dirt and mess to match with the recent changes to basements. This post is mostly a slideshow of screenshots of water surfaces in various states of cleanliness.
But the water shader isn't the only thing that's new. Now some swimming pools and flooded rooms have fish! Someone actually suggested it a while back. Adding fish to these areas didn't feel very realistic at the time, but I changed my mind because I felt it would be interesting. I'm always looking to add moving elements to these mostly static scenes. Plus the fish near the surface can generate water ripples to improve the realism of the water surface.
Fish are drawn and animated using the same code as the fish I added to fish tanks some months ago. I had to write a new system for their movement logic though since it's more complex. Fish in fish tanks have a rectangular area to swim in and only need to avoid colliding with each other. However, fish in swimming pools have to avoid the pool stairs, sloped floor, and ladder as well. Fish in basements have quite a few objects to avoid, such as walls and doors. And in both cases, fish will run away from the player when you get too close to them.
Indoor Pools
Indoor pools come in a variety of shapes, sizes, water quality, and states of disrepair. These are controlled by randomly chosen constants that vary parameters such as color, light attenuation/scattering, and other shader effects. If anyone is interested, the updated GLSL water shader source code can be found in my GitHub repo here.
We have clean pools with blue tinted water like the one below. The majority of pools are clean like this.
Underground swimming pool with fish swimming in the water. This is the original clean look. Oops, I forgot to enable indirect lighting.
At the other extreme, we have pools with cloudy, green, algae filled water and dirt all over the walls, ceilings, and floors. They can still have fish in them though, and the poor water quality doesn't harm the player. It only makes things more difficult to see when walking underwater.
An indoor swimming pool full of algae with dirty walls, ceiling, and floor. Would you swim in this? Would you *drink* this?
Some extra long extended basement rooms have fog in them, or maybe some sort of smoke or haze. This isn't really related to swimming pools, but it does give them a creepy feel. You never know what might be lurking in the shadows at the far end of this pool.
Long and narrow basement swimming pool with green water and fish in a dark, dirty, and foggy room.
Flooded Basements
The lower floors of extended basements ("backrooms") sometimes have water in them as well. The water level varies randomly, from a shallow, almost invisible later, to water so deep the lowest floor is completely submerged. Fish live in these flooded levels, though these mazes of rooms can be so large that the fish are hard to find. As with swimming pools, flooded rooms have a range of water quality represented by a random mixture of mud/dirt (brown) and algae (green).
I remember seeing one basement with a shallow layer of cloudy green water on the floor that made it look like a swamp. But I wasn't able to find it again as these colors are somewhat rare, so here's a screenshot of murky green-black water. (I have indirect lighting intentionally disabled here so that it looks darker.)
Dark, flooded basement with medium depth green water full of mud and algae.
Then we have rooms with a very shallow layer of water on the floor. The layer is so thin that it's hard to tell what color the water is, but I'm pretty sure this one has clean water. Ripples look pretty good in shallow water. The walls and floor are dirty though. It looks odd with water stains under the water, so I guess these dark stains are dirt, mold, or something else.
Dirty flooded basement with a thin layer of clear water on the floor and some blue colored lights.
Here's an interesting looking area with a completely flooded lower level and water on the floor of the level above. I like the way the colored lights interact with the water. There's no visible dirt in this basement, though I believe the water is slightly muddy.
A relatively clean basement with an entire flooded level and submerged stairs, with orange lighting.
Outdoor Pools
Next, we move outdoors into the sunlight to view some exterior swimming pools. There are two styles of pool: round above ground, and rectangular in-ground.
I've made two changes to these pools. First, they now have caustic light patterns drawn on underwater surfaces visible to the sun. This is done with a texture decal applied to the surfaces that varies in brightness and color to match the sun color and angle. Shadows are also applied to this layer.
Here's a screenshot of a round above ground pool in someone's back yard. This pool has crystal clear water and the sunlight reaches to the bottom.
Round above ground pool with clean, clear water and caustic light patterns on the inside sides and bottom.
And here's a nice clean rectangular in-ground pool with a sloped bottom. The pool is next to a back yard covered deck/porch. This one is also very clean and blue, just like the pool at my (real life) house currently is. (Yes, it makes for a good reference.)
Square in-ground swimming pool with sloped bottom and caustic patterns visible under the water.
But we can have dirty green pools outdoors as well as indoors. In fact they're probably more common outdoors where the sunlight makes the algae grow. The water is cloudy and opaque, and no light reaches the bottom to produce caustics. In fact, you can't even see the bottom of the pool ladder. The owner of this house really needs to do a better job cleaning and maintaining their pool!
Rectangular in-ground pool with cloudy green water. No caustics are visible in this one. It needs more chlorine.
These back yard pools are often fenced off and unreachable to the player. Sometimes they're accessible through the house if it has both a front and back door like the house in the second screenshot. They're visible through chain link fences, but it's easier to take screenshots using my debug "flight mode".