Monday, June 5, 2023

Cockroaches in Buildings!

I can't resist adding another "animal" type to 3DWorld's procedural buildings. It was a lot of fun (and also a lot of work) to add rats, spiders, snakes, and flies. Now that I have all of the framework in place for animal drawing, animation, physics, collision detection, behavior, etc., it should be easy to add more animal or insect types. Cockroaches are somewhat of a cross between rats and flies. (Well, assuming you only have the four animals/insects mentioned above to work with.) They walk on the floor and run away when scared like rats do. Their placement and drawing code is closest to that used for flies. I was able to reuse enough code from existing animals that it took me less than a week to add cockroaches, compared to several weeks for rats and spiders.

Cockroaches have some unique behavior characteristics. They only spawn in dark rooms away from the player, building AI people, and zombies. They have a random walk => stop => turn cycle similar to rats with some degree of random speed variation. When they're startled by a light, a sound, the player, or another person, they immediately run toward the closest wall in a direction away from whatever scared them. When reaching a wall or other object flagged as a hiding spot, cockroaches disappear inside and are respawned somewhere else in the building. Their new location will be somewhere dark and out of view of the player. If they instead hit an object they can't hide under, they turn away from the object, suspend their fear state for a few seconds, and walk in a random path.

So far I haven't assigned any gameplay purpose for cockroaches. They don't affect the player or zombies. The player can't pick them up, but they can be squished by dropping objects such as books on them the same way as spiders. However, it's quite challenging to squish cockroaches due to their fast speed, small size, and ability to hide under objects. Maybe I should track kill counts for both spiders and cockroaches. As a placeholder for this, I add $10 damage for each squished bug for building cleaning costs. (For reference, cleaning the blood stains from a player death is $100.)

I originally drew cockroaches as simple stretched brown hemispheres: long and flat. I didn't bother to add legs because it was difficult to get close enough to see them. Later, I decided they looked too plain, so I found a 3D model online that looks much better. So far I haven't felt the need to animate their legs because they're so small and move so quickly. The 3D model rendering code is almost identical to that used for rats, in particular because they're both always oriented in the XY plane on the ground.

Here's a video showing a building with 1,000 cockroaches. They scatter when I turn on the light and approach them. Note that these are drawn at an incorrect height and their legs are under the surface of the floor. I've fixed that in the other screenshot and video below.

And here's a screenshot showing the 3D cockroach model I used. Yeah, it's pretty small and hard to see.

Cockroach on the floor, drawn with a 3D model.

Here is a video showing me attempting to drop books on cockroaches to squish them. It's challenging and took me a number of tries to get the first one. They have yellow blood!

Do you have a suggestion for what animal or insect I should add next? If so, please leave me a comment.

Wednesday, May 31, 2023

Procedural Buildings: Breakable Objects and Particle Effects

I have a long list of smaller additions and improvements to show, so I'll split them up by category. This post will show some of the breakable and broken objects I've added to buildings, and the particle system I implemented to add glowing spark effects to broken electrical objects. Some of these have been around for a while, while others were recently added.

Broken Car Windows

I added the ability for the player to break into parked cars in office building parking garages to steal items. This was done a year or so ago, but I don't remember posting a screenshot or explaining it on this blog. Each car window can individually be broken once by using the player action key on the car. Some cars happen to be unlocked and can be looted without breaking the window. I can't easily modify each instance of the car's 3D model to remove a window, but I can draw a pile of broken glass on the ground at the player's feet. As you might expect, each pile of glass is unique. This glass will remain in the building forever to help players keep track of which cars they've already broken into. This was an easy change to make. I may decide to go back later and make the glass shards dynamic physics objects, or possibly allow the player to pick them up for some gameplay purpose.

A car in a parking garage with broken glass from the window on the ground.

Broken Mirrors

Throwing an object at the center of a building mirror will break it and add a crack decal texture over the reflective surface. One of two crack textures is randomly selected for each mirror. This works with dresser mirrors, office bathroom wall mirrors, and house medicine cabinet mirrors. While the player can drop various types of inventory items, the only object types that can be thrown are basketballs and soccer balls. Therefore, these are the only objects that can be used to break mirrors and other glass surfaces.

Breaking mirrors plays a loud glass shattering sound, which of course is guaranteed to attract zombies. Breaking a mirror also unlocks the "7 Years of Bad Luck" achievement.

Mirrors can be broken by throwing balls at them. Broken mirrors are cracked but still reflective.


I've considered making windows breakable, but so far haven't figured out how to do this. Windows aren't individual objects but are instead drawn along the entire side of the building, and are visible through cutouts in the walls. I would need to split these up into individual window panes somehow, then add the crack texture to each broken one. Some of the house windows are already divided vertically or horizontally into 2-4 panes, which further complicates things. A second challenge is adding a transparent overlay to an object that's already transparent in a way that makes the alpha blending correct. Of course, writing about it here will increase the likelihood that I actually complete this feature later.

Broken TVs and Computer Monitors

The player has been able to throw balls at TV and computer monitor screens to break them for a while now. The new feature to showcase is the particle system I've added that will spawn glowing sparks around the area where the ball and screen meet, as long as the device was powered by electricity. These sparks are physics objects affected by gravity with collision detection enabled for all room objects. I was able to reuse most of the basketball and soccer ball physics and collision code for particles, with some adjustments to physical constants to make their paths more pleasing. For example, sparks use lower gravity so that they remain in the air for longer.

Each broken object spawns a new group of particles that will disappear after their lifetime expires, which is currently set to a few seconds. This way only a small number of particles will be active at any given time and won't affect performance much. Sparks are drawn with an emissive colored material stretched out along the length of their velocity vector. Their color follows the normal black body light emission profile as their temperature decreases over their lifetime: white => yellow => orange => red => black. Light emission decreases over time, and transparency increases.

Later I decided to make these particles act as small light sources to illuminate the walls, ceiling, floor, and other nearby objects. The screenshot shows light sources, while the (older) video was taken before this was added.

Still screenshot of a TV screen that's just been broken with a basketball, with sparks flying around the room and producing light.

 
 

Broken Lights

There are two types of broken lights. A while back I added flickering lights with a random chance to occur in basements and parking garages. These lights are mostly on but turn off for a few frames every few seconds.

The new type of light I added is even more broken: normally off, with an occasional flash of light and a continuous shower of sparks. This was a convenient way to reuse the particle system I developed for TVs in a setting where the sparks are constantly visible, rather than only being seen briefly for a few seconds. These types of lights are only added to house extended basements rooms, giving them a dark and creepy atmosphere. Broken lights are always initialized to the off state and must be turned on by the player to make them active. This increases the amount of surprise when the player encounters a shower of sparks rather than a lit room. So far I haven't decided if I want the building AI people to turn these lights on as well.

Still screenshot of a broken light emitting glowing sparks that fall to the floor. Maybe they should catch the rug on fire?


It's possible to have a large number of broken lights in a building due to the way occlusion culling works. Since they're conveniently only added to basement rooms, I don't have to worry about them being visible through windows. The player can only see the lights and the sparks when the clipped light volume is visible. This means I can reuse the existing light bounding cube occlusion queries to skip generating, updating, and drawing the particles when the light is blocked from view by a wall, ceiling, or floor of the building. As soon as the player turns to leave the room or walks around the corner, these lights will become inactive and won't take any resources to draw or update.

Future Work

Broken lights don't really affect gameplay, except for reducing zombie (and player) visibility due to the dark room. Maybe I should have sparks catch rugs on fire? It sure seems like that rug should be bursting into flames in the screenshot and video above. That could damage the player and also either distract or kill zombies. Okay, I'm adding that to my TODO list.

I have plenty of other ideas for breakable objects. I could add a tool such as a crowbar, hammer, or bat that can break objects. I could allow some inventory items such as cups and vases to be dropped or thrown on the floor to break. I could provide a way for the player to create holes in the walls, ceilings,  floors, or doors. There are so many possibilities. I need to take some time to think about how gameplay will work and the various ways in which breaking/destroying objects would benefit the player.

Monday, May 8, 2023

Procedural Buildings: Connecting Basements

Some of the houses in 3DWorld have extended basements consisting of a maze-like tree of underground hallways and rooms. These can run under the house and nearby buildings, as long as no other buildings are intersected. I like these mazes. I like anything involving underground networks to explore where the player can get lost, in particular in a horror game setting. The current limited size of each house's extended basement prevents me from adding larger mazes. It's not just a problem with available space, it's also a problem with reduced performance when adding too many rooms to a house or making it's bounding cube too large. The various building view culling methods I'm using are less effective when many buildings overlap in 3D space.

One workaround for this is to allow basements to connect between multiple houses. In fact, they can connect together in a massive dungeon maze that runs under the entire residential neighborhood. This case is made easier because all houses are at the same elevation, so I don't even need to add stairs to connect rooms at different levels. I decided to start by grouping houses by city, and attempting to connect each house in the city to every other house placed after it. This is quadratic in runtime, though only for a few hundred houses at a time since they don't all have extended basements. The final runtime for this step was only 19ms for all four residential cities combined. I didn't even have to make the code multi-threaded.

If two nearby rooms that project in X or Y can be found for each of these houses, a new hallway room is added to connect them. I cut a hole in one of each of the room's walls to insert a door. I placed a red light on the ceiling of this room to make it stand out more. This way I can find and debug connector rooms by disabling collision detection and flying below the ground looking upward for the red lights. I'm glad I added this for debugging because I certainly found a lot of problems with this system! Every room had some new bug. I'm sure I haven't found and fixed them all yet.

The only downside is that this was very complex and difficult to get right. Not only does it need to look correct, but the AI and physics must work as well. On the visuals side, I had to make sure the occlusion culling didn't flag the other house as invisible as it normally does with other buildings when the player is underground. Then I had to work out the lighting and shadows so that the connector room could cast and receive both light and shadows from the rooms of the two houses that connected on either end. These had to be updated when the user toggled the lights or opened/closed the door from either side. In addition, the ambient lighting system had to consider light coming through the open connector door.

Physics was also quite challenging. I had to update the player collision detection to allow walking between buildings without being blocked by the basement bounding cube, and without the player being considered outside a building for one frame and popping out of the building on the ground above. This last problem took me many attempts to fix! In fact, I think it can still happen in rare occasions when a building person or zombie pushes the player into a wall near the connector room.

Players had to be able to open or close the connector room doors from either side, which means I had to support interacting with a door belonging to a different building from the one the player was inside. Next, I had to allow the player to carry inventory items between buildings without them being deposited in the player's permanent loot collection as would normally happen when exiting a building. Building animals and people AI were updated to handle these connector rooms. I didn't quite figure out how to have them cross between buildings, but at least they don't get stuck there. Zombies will follow the player right up to the connecting door and wait for a few seconds before walking off. This provides the player with an escape route. I think that's fair because it's far too easy to get cornered or lost while being chased in a basement maze. However, chances are some zombie in the other building heard you and is on the way to find you.

Probably the most difficult feature was adding support for basketballs and soccer balls that could be rolled, kicked, or thrown through the doorway into the next building. These objects have physics state owned by their contained building. I had to transfer this state from one building to another in a way that preserved the properties such as position, velocity, and momentum. There were various special cases to handle. For example, the player can stand in one building while holding the ball through the doorway into the other building. The physics must be seamless across the boundary.

Here are some examples of extended basement connector rooms.

Extended basement connector hallway with red light, a picture on the wall, and a rug on the floor.

Hallway connecting one house basement to an underground storage room in a different house.

Room connecting two house basements. You can tell that the connector room belongs to the building the player is in because the carpet matches.

They look very similar, especially with the red lights. At the moment this is a somewhat intentional way to let the player know they're passing between buildings. There's also a "Welcome to <NAME> Residence" text to let the player know whose house they're entering. Maybe I should select a random light color from a list of colors? I'm not sure. I experimented with different colors and I definitely like red the best because it produces a sense of danger and has a good contrast with the off-white color of other ceiling lights.

What's next? Maybe I'll figure out how to have people and animals cross between buildings. Maybe I'll connect office building basements and parking garages in a similar way. There are so many possibilities...

Sunday, April 30, 2023

Procedural Buildings: Improvements to Exterior Detailing

I've been working on various aspects of 3DWorld's procedural cities during the past month or so since my last blog post. I don't have a single topic that's large enough to write an entire post on. Just recently I did add a system for connecting the extended basements of houses together into huge underground mazes, but I don't have any good screenshots to show of this yet. So instead I'll review some of the improvements I've made to building exterior geometry. These are not in chronological order.

Scrolling Signs

First up we have scrolling company name signs. Signs were initially discussed in my February post. I did eventually get back to improving them, which was partially inspired by scrolling text I saw on some of the buildings in a video of someone else's project. Their effect looked very different from what I wanted to achieve, though it was the same high level idea. I applied the scrolling effect to the illuminated night time signs on the roofs of tall city office buildings, since these are the most visible to the player.

This took a surprising amount of work to get right. It was relatively easy to make the text move one character at a time to the right and wrap characters from the end back to the front. Smooth scrolling was much more difficult because it required updating my text drawing system to handle partial characters so that I could have the last character split across the last and first position as it wrapped around. This worked by duplicating one of the characters so that it was at both the beginning and the end, and then clipping the outer edges of each to the correct final size.

Then came the even more difficult task of making this work with my non-fixed width font. Some of the characters (such as 'i') are much narrower than others (such as 'M'). This means that the scrolling rate is different for each character, and narrow characters spend less time split across the ends than wide characters. I had to first render each character to a temporary buffer to determine the start and end positions within the sign display area. Then I was able to calculate a mapping from each character to the point in time where its left edge passes the scrolling point. From this I could take the game time, calculate the fractional component of the scrolling position from the period, and look up the time value to get the first character to draw. The delta time between the character appearing and the current time value was used to determine how far along that character the movement was, or the split position in the case of the last character.

This still doesn't work perfectly, but it's much better than my previous two attempts (not shown). Here's a video showing several scrolling signs. YouTube seems to have made this blurry and hard to see. I'll add a screenshot after the video, but you'll have to imagine how the text looks like when scrolling and just trust me that it works. Unless it's just me and others don't see a blurry video?


Still image of some scrolling neon company signs attached high up on tall office towers. Shown at night, with a bloom postprocessing effect applied.

 

Exterior Trim

Houses were looking too plain, in particular the windows. What had previously been limiting my ability to add exterior details was the way in which building geometry is generated and drawn. I'm creating the exteriors of every building ahead of time and drawing them at large distances of over a mile because they're actually visible that far away. Even the tiny windows can be seen in the distance! This is especially true of glowing windows at night. I can get away with adding a ton of interior detail to buildings because both their generation and drawing can be deferred until the player is close enough to see these details through the windows. The difference in drawn distances for exterior vs. interior is user-configurable but generally as much as 5-10x.

The reason I can't draw exterior geometry as interior objects is because they use different lighting models and shaders. Exterior lighting consists of directional sun and/or moon light, hemispherical ambient lighting from the sky and ground, and a 2D grid of point lights from sources such as streetlights and headlights that are mostly active at night. Interior lighting is a combination of room-based point lights in a 3D grid, combined with optional indirect lighting computed in the background when entering buildings. If I were to draw building exterior objects as interior geometry, they would appear too dark because sun and moon light is not enabled and building interior room lights are on the other sides of the walls. You can actually see this problem in some of the earlier "Welcome" signs I placed above doors. These looked gray but were actually a bright white color with only a small amount of ambient lighting.

Wait, but what about other exterior objects? For example, I have some screenshots with house fire escapes. I cheated! Fire escapes are black, and the player can't tell that the lighting is wrong. Okay, what about those house number signs such as the one shown here? They're not black. And neither are the flags I added to houses. Well, these are actually objects that belong to the city itself, not the house they're attached to. They're drawn by the same system that draws street signs and manhole covers. This is why you only see them in cities and not in the randomly placed secondary buildings I have scattered around my world. Right, then what about gutters on houses? I don't remember ever discussing them in my blog, but maybe some of you noticed them on my recent house screenshots. Well, these are part of the exterior and are generated and batch drawn for the entire city at once. Their geometry is simple enough that they don't add much overhead compared to the exterior walls, roof, doors, etc. And skylights? My skylight drawing solution cheated as well by using an invisible room light above/outside the building pointing in the direction of the sun to light the top surface. No, up until now I didn't have a system to make detailed exterior lit objects work properly.

The underside and edges of gutters can be see on the house and garage. They're simple quads for the bottoms and triangles for the ends. Sorry, I don't have any good screenshots of the old gray Welcome signs because I tried to hide their poor lighting from everyone in previous posts. Oh, and yes, some of the house roofs have overhangs now.

It's time to change that. Now the building drawing has an additional rendering pass for per-building objects that are placed on the exterior and drawn with sun/moon lighting. Yes, this adds some frame time overhead, but it's not very significant. The real cost was the complexity required in the control flow and a system to swap shaders while drawing spatial blocks of buildings. My solution is a bit ... odd, though, because these objects are still part of the building "interior" and are generated at the same time as the interior. Which means they don't have the same long draw distance as exteriors, which means they can't be too large, or they'll obviously pop in and out as the player's distance to the building changes.

The first step was to change the "Welcome" signs and other small items to use the new system. Then I realized the exterior faces of some of the windowing coverings had the same problem. I fixed blinds and bathroom glass block windows to draw their exterior surfaces as what I suppose I have to call "exterior interior geometry." I didn't realize how broken that looked until I fixed it! Finally, it's time to add new content. This includes proper 3D window frames, window pane separators for houses (apparently called muntins and mullions), window sills, and exterior wall trim. Window frames and sills were added to some styles of office buildings as well. Window style varies per wall/floor of each house.

I definitely needed to improve the look of windows because their exteriors were too plain and flat. The lighter window border I had was drawn with a low resolution alpha mask texture originally designed to be viewed from a distance. This didn't match with the detailed trim shown on the interior of the window. I did brighten the window border in the texture and doubled the resolution; that helped, but wasn't enough. Now the new windows with physical geometry look more 3D, detailed, and brighter:

Exterior of a large house with properly lit window frames, pane dividers, window sills, blinds, and signs.

Next, I wanted to add some separation between the ground floor and upper floors of houses. Many houses use different materials for these, for example, brick or stone on the bottom and wood, siding, or stucco on the top. It's easier to start with a simple band around the perimeter of the house than jumping into a whole new system of combining multiple exterior wall materials on a single building. (I did in fact experiment with siding, though I haven't enabled it and I'm not ready to get into that topic yet.) A thin white band to match the windows is an easy place to start. Remember, I can't make anything too large because it's only visible when the player is close by. I'll also only enable this for 20% of buildings to make it a somewhat rare decoration. Well, rare may be too strong a term because it's not nearly as rare as houses that look like duplexes, triangular office buildings, and office buildings with overhangs. So I'll go with "uncommon" instead.

House with window trim and exterior wall trim at the top of the first floor that wraps all the way around.

House trim is always white because I feel it makes them look clean and bright. Office buildings have multiple shades of trim from gray to black that are designed to improve contrast with the average brightness of their exterior walls. Here's an office building with dark glay-black trim.

"Dark Mode" office building.

I'm still thinking of what else I can add to buildings. I can potentially include significant object detail since it's only drawn for nearby buildings. Drawing cubes around every window may seem like a lot of geometry, but it's far, far less than what's drawn for the building interiors.


Ceiling Fans

Did I say I was going to discuss exterior geometry? I thought I had a third topic, but I suppose I forgot the last topic in the process of writing that long section above. Having only two topics isn't enough, and I just realized that I haven't shown my ceiling fans yet.

The more animated and interactive objects in buildings the better, right? So how about one that's both: a ceiling fan that can spin and is controlled by the player. I even put a light on it. However, it did take a surprising amount of time to find a suitable 3D model, and I'm still not very happy with it. I had to have a model that satisfied the following constraints:

  • Free. Because my goal with the 3DWorld project is to not spend any money on it, so that I can make it free and open source without "losing money."
  • Not too high poly, so that I can spam these into every room if I want to.
  • Mostly white in color so that I can potentially repaint it, though so far I haven't done this.
  • Relatively wide and short so that it fits in the limited ceiling space of my houses without clipping through the player's or AI's heads.
  • Two separate objects/meshes for the blade vs. base so that I can animate it to make it spin. Or, in the case of the model I ended up using, completely symmetric so that I can spin the entire model and the player doesn't know the base is also spinning. (Though in my case it's not perfectly symmetric and you can see the base rotating).
  • Something that allows a light to be easily attached to the bottom and that can be set as emissive when on. This rules out any fans that come with fancy light fixtures or multiple lights.
  • A model that loads properly in Assimp or my own OBJ reader. In particular, I had to rule out a few with missing embedded textures.

Out of the ~100 models I found online, only one really fit all requirements, and not very well at that. But it is what it is, a crappy ceiling fan is better than no fan at all. It's not that it's a bad model, it's just very simple and plain. Behold, we have the bedroom ceiling fan:

Ceiling fan in the center of a bedroom. Trust me, it's currently spinning. I don't know why I didn't include a YouTube video of this. Certainly not because it would look just as blurry as the scrolling neon signs in the video above.
 

I guess this works? The room is a bit darker than I would like because the light is lower than the ceiling and the upward light is blocked by the fan itself. I'm sure it would look better with indirect lighting. What's that? You demand to see that screenshot as well? Fine, I'll go create another one. Here it is:

Room with ceiling fan, with indirect lighting!

Yes, this definitely looks better. If only it didn't come with a framerate cost. I'm not sure why the base of the fan is black though. Maybe it's because it's halfway buried in the ceiling, or its lighting is blocked by an occluder from the fan that's too large, or the fact that it's spinning makes the lighting computation wrong. Ah, wait, I think it's because the normals are backwards on that top part of the fan.

Anyway, it's properly interactive. The player can switch the fan and light on and off together with that light switch on the left. And it's also gated by some breaker in the breaker box in the basement, if there is one. Maybe sometime in the future I can include these in gameplay somehow. Are they zombie distractions? A fun, crazy way to expand ball or duct tape physics? I guess you'll have to wait and see.

Monday, March 20, 2023

Procedural Buildings: Interiors for Non-Cube Buildings

I think I'm going to continue with these shorter posts every week or two since they're easier to write in a single night. I'm continuing to work on adding a variety of smaller features to 3DWorld's procedural buildings. It seems there are an infinite number of these changes to make.

One item that's been on my TODO list on Trello for awhile is generating interiors for non-axis aligned cube shaped buildings. This includes buildings formed from rotated cubes, cylinders, cylindrical sections (with a flat side), and N-gon buildings with 3 or 5-8 sides. These shapes are difficult to work with because everything is so much easier when using cubes: Boolean operations, intersection queries, point containment, collision detection. Each of these is a few lines of code when using cubes, but tens to a hundred lines of code for arbitrary polygons and curved shapes. To make things a bit easier, I've diverged from the normal way I do things in 3DWorld and represented cylinders as 36 sided polygons rather than actual mathematical cylinders. This allows me to work with N-sided polygons for all of the possible building shapes, including cubes. Another simplification is to skip buildings composed of intersecting shapes for now and only generate interiors for vertically stacked sections.

I've decided that I want to continue using X-Y axis aligned interior walls for now due to their simplicity. This means I can really only divide the interior up into 2 or 4 pie slices by adding an X oriented and/or Y oriented wall through the center of each building, depending on space. Most buildings are large enough for both walls. This makes rooms bounded on two sides by interior walls, with exterior walls on the remaining side(s). Each room is effectively a cube clipped to the building exterior shape. This allows me to reuse most of the existing room code with an additional outer polygon test for queries such as collision detection, point containment, cube overlap, etc. For example, objects can be placed in rooms like normal with an additional check that they're completely contained inside the building polygon. Since these polygons are always convex, it's sufficient to test that each vertex of the bounding cube of the room object is contained in the polygon.

The difficult part is implementing this for every query, in particular all of the collision and containment tests done for dynamic objects. This includes people inside buildings, balls, rats, spiders, snakes, flies, and, of course, the player. It's not enough to handle collisions with the exterior walls. All of the AI controllers for these must be aware of the valid building/room bounds and avoid walking into walls in the first place.

Here's an example of the interior of a cylindrical building divided into four equal sized rooms. The rooms are rather large. Maybe I should fill some of them with office cubicles?

Interior of a cylindrical building that's divided into four pie slice rooms, one with stairs.

You can see from this picture that I've added proper handling of stairs, ceiling lights, and even the wall trim along the edges of the floor. Stairs and elevators were particularly difficult for two reasons. First, they must be placed so that they don't intersect the exterior walls on either the floor above or below, and they also have enough space for people to enter them from each end without leaving the building. Second, I had to write the polygon clipping code that cuts a square hole for stairs and elevators into a circular ceiling and floor polygon. This was very math heavy and took me several hours to get right.

Some stacked building sections are probably too small to be practical and don't have enough space for both a wall and stairs. Here is an example of one such building. I'm not quite sure what to do with this one.

Interior of an oddly shaped, narrow, non-cube building with a single room containing stairs. This one is a cube with chamfered edges.

There's one other glaring issue: This type of building has no windows or exterior doors. Right now I only have support for axis aligned cube windows and doors. The way windows are cut into walls using constructive solid geometry (CSG) on cubes with the stencil buffer doesn't really apply to curved/angled wall sections. I have windows on the exterior textures, but not on the interiors. It looks a bit odd when the player is inside these windowless buildings, though it may matter less in gameplay mode when the player is looking for items and avoiding the zombies.

Doors are interactive as they can be opened and closed by the player, which makes them more difficult to model on non-cube buildings. I probably need to find a way to add doors to one side of at least the non-cylinder N-gon shapes. At the very least I can add them to the axis aligned edges of hexagons and octagons. I added the code for this as an experiment. The doors look fine from the outside of the building and work correctly for letting players in and out, but they don't have their door openings cut into the interior walls. This is because I haven't yet written a CSG subtraction of the door from the angled building sides. So the doors don't actually show the interior or exterior through them when open. This looks wrong, but I suppose it's better than having no doors at all.

Next, I assigned the interior room types to offices so that the larger rooms can be filled with cubicles.

Round office building room filled with cubicles, with indirect lighting enabled.

The cubicle placement algorithm is smart enough to only add cubicles that are fully contained in the room, and to leave paths on the sides of the cubicles and between the doors. I think this looks much better than mostly empty rooms with a single desk or table. Sometime in the future I might add different room objects that can be placed in this style of building.

Just for reference, here is what some of these buildings look like from the outside. You can see there are cylindrical/oval buildings, some with a number of sides at odd angles, and stacks of building parts.

Exterior of some non-cube shaped office buildings. Sorry, no cars or people in these screenshots.

 

Saturday, March 11, 2023

Procedural Buildings: Flies

I've made some new additions to 3DWorld's procedural buildings: bugs. No, not that type of bug! I'm not talking about software bugs, I'm talking about insects. Flies, in particular. I've already added walking animals (rats), animals that crawl on walls (spiders ... which technically aren't animals), and animals that slide on the floor (snakes). I'm missing something that flies. I do have butterflies flying around outdoors, but they're not inside buildings.

I initially drew flies as black spheres. This looked fine initially because they were so small, but when I added logic for them to fly around the player the simple sphere model broke down. So I started by copying and pasting the code to generate the geometry for spiders, since flies have a similar look. They're both black, have bodies that can be formed from transformed spheres, have multiple eyes, and have thin legs that can be made with cylinders and cones. The differences are minor. For example, flies have only six legs rather than eight. The biggest difference is that flies have wings, and it's the wings rather than the legs that are animated. Wing animation is a simple sine wave transition between horizontal and mostly vertical, similar to how I animated butterfly wings. Wings are small and partially transparent, though the alpha blending doesn't work very well due to a lack of proper depth sorting.

Flies will fly around randomly with smooth direction changes and will avoid colliding with walls and room objects. They follow zombies around when a target gets close to them. They'll also follow the player if a trashcan, toilet, urinal, or dead animal is picked up and stored in their inventory. (Note that the player can't actually carry a urinal.) They don't do any damage or have any other gameplay effect other than getting in the player's view, buzzing constantly, and generally being annoying. Here is what it looks like to be swarmed by flies:

Flies follow the player inside a house when the player picks up a trashcan.

I recorded a video of a swarm of flies following the player around. Aren't they annoying? I'm hoping they distract the player from watching and avoiding the zombies.

At this point I have all of the components in place for adding other insect and animal types to 3DWorld's buildings. I have support for walking on the floor, climbing walls and objects, and flying through rooms. It's fun to add new creatures!


Saturday, February 25, 2023

Procedural Building Skylights

I'm always looking to improve the realism and variety of 3DWorld's procedurally generated buildings. One problem I have with some of the larger office buildings is that they don't have enough natural light. Sometimes they don't even have enough artificial light when the placement algorithm fails to add light fixtures to the ceiling. I can fix this (at least for the top floor) by adding skylights to some of the buildings with flat roofs.

This was no easy task, and required implementing and fixing a variety of systems. What makes this especially challenging is that skylights form an interface between the interior and exterior of the building. These two parts are drawn using entirely different approaches and code, so it's not clear exactly how a skylight fits into this system. For example, interiors are only illuminated by room point light sources, while exteriors are illuminated by directional lights from the sun and moon. The solution I ended up using was drawing the transparent glass as part of a building's exterior, while the horizontal metal bars separating and supporting the glass panes are drawn as part of the interior. More on that below.

Before I jump into drawing and lighting skylights, I have to go over how they're placed and incorporated into the building's exterior geometry. The easiest case is to only add skylights to buildings with large flat roofs. This limits skylights to office buildings, since houses always have sloped roofs. (Plus, I'm not sure how well skylights combine with attics.) In addition, skylights are only added to cube-shaped (non-rounded/angled) buildings because these are the only type of buildings with generated interiors. If there's no interior, then what's visible through the skylight? Empty space? The ground?

Once a suitable roof is chosen, the skylight is dropped somewhere within it, in most cases near the center. The size is chosen randomly from between 20% and 40% of the roof size in each dimension. Ideally we want to place the skylight in some sort of public area like a hallway or lobby, but unfortunately rooms haven't been assigned yet. In fact the walls haven't been added at this point. Skylights can be seen on distant buildings when flying above them long before the interior walls and room objects can be seen, so we must generate them first. I guess the room placement algorithm will have to handle this later. As it turns out, this breaks my floor-planning system. Office building floor plans are shared across multiple floors and can't easily be customized for the top floor to handle a skylight. The template-based hallway assignment system of larger buildings also isn't flexible enough to work around a skylight that's larger than a single room. I want to support large skylights without this size limitation.

So in the end I simply cut the walls, doors, and door frames a bit shorter so that they can fit under the skylight, and I draw their top surfaces since they're visible in this situation. That leaves the elevators to deal with. Ideally the tops of the elevators shouldn't reach the skylight. Real elevators usually have machinery in the space above them. However, some skylights cover the entire valid elevator placement area, so there's no way to place an elevator that doesn't intersect a skylight. The best I can do is clip the top of the elevator in the same way as the walls so that it just touches the bottom glass of the skylight. The problem here is that the light and inside roof of the elevator car can extend above this point. I had to hack around this with some not-quite-volume-preserving adjustments to the elevator car z-values when it reaches the top floor. We just squish the ceiling down a little bit. It's difficult to spot though, so I expect users to not even notice.

Now that we've chosen the skylight location and hacked the geometry under it to make it look correct to the casual observer, it's time to cut the hole out of the roof. Fortunately, I can reuse the code that cuts holes in the roof for the roof access stairs. This is probably the only part of skylight addition where I can reuse existing code! The next step is to fill in that hole with a partially transparent glass plane that's recessed slightly vertically. Which of course doesn't "just work" because alpha blending is disabled for building exteriors. No problem, I can simply add the skylight as a fake "window" and that surprisingly worked the first time. I don't have to worry about lighting from the interior lights on the glass because skylights are transparent enough that the user can't tell if the lighting is incorrect.

The next step is to add the white metal horizontal supports for the glass panes. At first I wasn't sure how to do this because they need to be lit from the sun or moon above, and from the room lights below. Exterior objects are only lit by the sun/moon, while interior objects are only lit by room lights. I think that's too much geometry for the exterior, so it makes more sense to have the bars be interior room geometry. As for the sunlight from above, I can place an invisible room light in the air far above the skylight with a projected volume that exactly covers the skylight. No one said room lights had to be *in* a room! As a matter of fact, I can have this fake light illuminate the walls, floors, and objects inside the building below the skylight. If I make the bars shadow casters, I can even have them cast nice shadow lines on objects below.

With all of those additions, we have skylights that look like this viewed from above:

Office building skylight as viewed from above the roof, with a person standing in front of the elevator.

Maybe the bars and tops of the walls should have more color contrast? I'm not sure. Skylights look mostly correct when viewed from below (inside the building) as well:

A different skylight viewed from inside the building. The horizontal bars cast shadows on the walls and floors.

Did you notice something off about those two screenshots above? The sun shines on both walls of the hallway in the first screenshot, and the shadows of the bars on the far wall aren't parallel in the second screenshot. What's going on? This is because the real sun is a directional light source nearly infinitely far away. Shadows cast from parallel objects remain parallel. But the building interior system doesn't support directional lights, it only supports point lights and spotlights. My fake sun is really like a super bright spotlight hanging high up in the air above the building. The light rays diverge from that point, which is why the shadows cast on the walls and floors look that way. I don't really have a good solution for this, so I'm hoping no one notices.

Ah, but there's another problem. What do I do with the room lights that would normally be placed on the ceilings? I tried putting them on the bottom of the skylight, but that just looks wrong. Maybe I need a smaller type of light that goes onto the horizontal bars? Maybe it only matters at night when there's no light coming in from the sky. For now I can ignore this problem. There's a similar problem with floor signs above stairs. In that case it's easy enough to move the signs off to the side, and attach them to the stairs wall that holds the railing rather than to the underside of the skylight.

The next feature to implement is indirect lighting for skylights. (In reality, this task was easier to do, so I added indirect lighting before direct lighting for skylights.) Once again, I can treat skylights as windows, except that they're horizontal rather than vertical. They're also quite a bit larger than windows and need far more light rays for a smooth (non-noisy) indirect lighting result. I went with 8x more rays, which makes skylight indirect lighting quite expensive to compute. Here is what indirect lighting looks like for the same skylight as in the above screenshot:

The same skylight as above, this time with indirect lighting enabled.

Maybe that's too bright near the top? I'm using the same light intensity as I use for regular windows though. I think this is correct: skylights do let in much more direct sunlight than windows, at least when the sun is high in the sky. So this could be "correct" lighting for a skylight this large at midday.

Skylights can be placed over offices as well. Unfortunately, these offices usually don't have ceiling lights and are very dark at night. I could put a light on the wall if I had implemented non-vertical spotlights.

Another skylight above an office. There was no room to place a light fixture on the ceiling.

I did, however, add a check to avoid placing bathrooms under skylights. It just feels odd to fly over a building where I can see into the bathroom. I'm not sure if this applies to real buildings or not. My previous house in fact had a skylight in the bathroom, but I'm not sure what exactly a person "flying" above it would actually be able to see.

All of that discussion above was on placing and drawing skylights. There was even more work to be done. For example, skylights increase the ambient lighting in the building, which affects things like zombie visibility in gameplay mode. They also need to participate in occlusion culling. Objects that were previously hidden from the player by the roof may now be visible through a skylight when the player is above the building. The skylight must be treated as a visibility portal in the roof. I can't remember exactly what else I had to change in the code to implement skylights, but it was a lot of work. The only thing I didn't have to change was player collision detection because the skylight can be walked on the same as the roof.