Saturday, October 16, 2021

Procedural City: Residential Updates

I've been working on a variety of different aspects of 3DWorld's procedural city over the past few weeks rather than a single larger topic. I decided to just combine everything together rather than making lots of small posts. This post shows the major additions and improvements I've made, starting with exterior changes and ending with interior changes.

Cars Entering/Leaving Driveways

This task took by far the most time due to the complexity of the logic required to implement it. Up until now, cars would choose destination intersections and drive between them, meaning they were always actively moving on the road network. Parked cars stayed parked forever. I added a config file option to allow a certain percentage of cars to enter driveways and temporarily park. After a few minutes have elapsed, they start up again, back out of the driveway, and continue to a different destination. This was more complex than it sounds because I had to handle both the car physics for these turns and the logic to avoid hitting other cars and people in the process.

I started with a reservation system for driveways so that no two cars try to use the same driveway at the same time. Some cars are always parked and will reserve their driveway forever, but the other driveways are available to the first taker. I limit usable driveways to ones that are long enough to fit the current car or truck without requiring the garage door to be open. While destination intersections can be in other cities, destination driveways are always in the city the car is currently in. This simplifies the logic a bit.

The most difficult step was determining when it was safe for cars to enter and leave driveways. They always pull in, and either back out or pull out depending on the orientation they started in. The tricky case is when a car is making a left turn into a driveway and needs to check for traffic in the opposite lane that it will be crossing. It's not enough to check for cars blocking the driveway at the current point in time, they need to find all cars that can potentially cross that path for the duration of their turn. This involves locating nearby cars on the current road and cars about to turn onto the road and predicting where they will be a few seconds in the future using their distance, current speed, and max speed. The test is somewhat conservative to add a safety margin. The case where a car is backing or pulling out is similar, except it needs additional time when backing out to switch from reverse to forward. However, once they're on the road, cars behind them will see them and stop if needed. So it doesn't need to be as conservative. Cars exiting driveways must also check for cars stopped at a red light and blocking the driveway, and wait until their path is clear.

Sometimes when the driveway is close to an intersection, it can be blocked for long periods of time, preventing cars from entering or leaving it. It's generally okay to let a car sit there and wait to exit a driveway. However, there's only one lane (no dedicated left turn lane), so a car stopped waiting for a left turn will block traffic on its side of the road. This can lead to gridlock, especially when this happens on both sides of the road at the same time. My fix is to have the car only wait a short time and continue to loop around the block if the driveway is blocked by a stopped car. This can result in the car looping around the block forever, but at least the system doesn't deadlock.

Here's a video showing a car entering and leaving a driveway. The video is sped up a bit, and the time a car waits in the driveway was reduced from a few minutes to a few seconds. Yes, I added back up lights.


The next step was to make these cars avoid hitting pedestrians who were walking on the sidewalk between the road and driveway. This was challenging because car and pedestrian update logic is run on two different threads at the same time, which means the cars and pedestrians can't directly access each other. I couldn't figure out how to make the cars stop to avoid pedestrians, but I did eventually figure out how to make people stop at the edges of driveways to avoid cars. The logic for this is different for cars entering driveways, leaving driveways, and parked in driveways. It's similar to the logic used for pedestrians crossing roads, except with driveways instead. It's similar to the car driveway entrance and exit checks as well because it also involves predicting the paths of nearby cars.

The system works in many cases, but there are some modes of failure. For example, pedestrians can block each other or push each other into driveways. It's possible for a car to start backing up just as someone crossed into the driveway behind it. I could add a flag to the driveway indicating a person was in it to keep cars from moving. But I'm worried that this can lead to a dependency cycle where one or more pedestrians and one or more cars are all waiting for each other to move, and none of them can make progress.

There's also the case where someone will walk around an obstacle such as a fire hydrant or another person, slightly into the road, bypassing the driveway. To handle this case, I extend the driveway partially out into the road. But then there's the even more difficult case where a pedestrian decides to cross the road directly in front of the driveway. They may stop there waiting for a car to pass before crossing, blocking the driveway in the process. It's not clear how to fix this. Even if the car was aware of this person, all it could do was wait for the person to move. So it seems that this situation must be resolved by the pedestrian moving ... somewhere. Maybe to the side of the driveway? Maybe I shouldn't let people cross in front of driveways in the first place? It's difficult to work on this because this situation is rare, and the simulation isn't deterministic enough for me to reproduce the same failure mode between tests. I saw this particular failure only once.

Here's a video where a pedestrian stops to wait for the car to enter the driveway rather than being run over.

The first time I saw this interaction there was another pedestrian coming from the other direction later who had to wait for the car to exit the driveway. That would have made a better video, but I failed to record it correctly that time and wasn't able to witness this happening again. Minor changes to the AI logic results in major changes in the simulation.

Above Ground Swimming Pools and Chain Link Fences

I showed off some in-ground swimming pools in the previous blog post. This time I've added above ground pools as well. In addition, I've placed chain link fences around the houses to separate the pools (typically in private back yards) from the public area and road.

Residential neighborhood with both in-ground and above-ground pools, surrounded by fences.


The fence placement logic was more complex than I expected because it had to avoid crossing the driveway, other fences/walls/hedges, trees, and AC units on the side of the house. I also added checks for fences blocking porches and exterior doors. The final placement algorithm is able to fully fence off around 90% of pools and will remove the pool and fence segments when it fails. This should ensure no pools are publicly accessible and all meet local safety requirements. Maybe people will be hit by cars when crossing driveways, but they won't be drowning in their neighbors' pools.

These screenshots show how a pool is fenced off on both sides of the house, and a close-up of a chain link fence.

A house with a back yard in-ground pool fenced off with a chain link fence on both sides.

Close-up of a chain line fence constructed of metal cube bars and an alpha mask chain link texture. The gap on the left is there because it doesn't know the width of the wall/fence/hedges that will be placed on the left side at the time where the fence is added.

Improved Hedges

Residential plots are separated by a combination of fences, walls, and hedges. I was previously drawing hedges as simple textured cubes. That may be okay for fences and walls, but plants are shaped more organically than cubes. They look fine from a distance, but bad close up. I decided to generate a cube-shaped shell of leaves draw as individual quads in random orientations, and instance these over nearby hedges. This adds no measurable frame time but definitely improves the look. The new hedges have more detail and volume, though you can still see the underlying cubes if you get close enough.

New and improved hedges that have random leaves sticking out of a textured cube to give them more detail and volume.

Umbrellas

This was a silly one-off change. I thought it would be interesting to add umbrellas to most of the pedestrians when they were walking outside in the rain. (Yes, I do have full weather simulation and effects that work in all gameplay modes.) I found a simple umbrella 3D model online and added code to draw it above 75% of people when it's raining outside. Here is what that looks like.

A partly cloudy, rainy day where most of the people on the sidewalks are carrying umbrellas.

Pushable Objects

Now we get to the changes I made to building interiors.

I added a gameplay action key to allow the player to push objects placed inside buildings. This works with smaller objects that can be picked up and carried such as chairs and small tables. It also works with large furniture and appliances that are too heavy for the player to steal such as refrigerators, couches, beds, dressers, and bookcases. Basically anything not attached to the wall or floor is pushable. The heavier the item, the longer it takes to push it around. I allow the player to push items into closets but not out of their starting rooms, to keep the pushing logic simple. Items can be pushed into doorways or against doors to prevent them from being used. This is helpful for keeping zombies out of certain rooms, but can also restrict the player's movement. I later added another key to pull objects so that they can be moved away from doors, etc.

There are now separate keys for "take", "interact", "push", "pull", and "flashlight". I had to add multiple player crosshair colors and shapes to indicate which items can be taken vs. interacted with vs. only pushed/pulled.

Rolls of Tape

This was a fun mini-project to work on. I already had spray paint, markers, and toilet paper that the player could make a mess with in gameplay mode. I decided to add rolls of duct tape that could be used to stick tape on objects and wrap it around building interiors. Tape rolls of various colors can be found in drawers, in boxes, and on shelves. These can be picked up and used by the player. The action key will toggle between "stick tape to nearest object" and "rip off tape and stick the end to something". In between the tape will unroll as the player walks around, wrapping around objects such as walls, doors, and furniture along the way. The tape roll will shrink along the way and eventually run out, but the player can also backtrack to remove and recover the last unstuck segment.

Here's an example screenshot of two colors of tape stretched across a hallway.

My artwork created with black and gray duct tape stuck to the walls of this hallway.

This doesn't work perfectly. Sometimes tape will go slightly through objects, or won't stick to them in the correct places because their bounding/collision shapes don't exactly match how they're drawn. There's also no collision detection between the player and building AI people vs. the tape. Oh, and it doesn't quite work right if you try to pull a line of tape into the elevator with you and go to a different floor. I have no idea how to handle that case. I suppose the tape should break as it likely would in that situation in real life. But on the plus side it's fast enough that you can put down a ton of tape, and it stays in the buildings forever.

There's no real gameplay mechanic for tape at the moment. It's the same for spray paint, markers, and toilet paper. Maybe at some point I'll allow the player to block off doorways, hallways, or objects with tape to limit the movement of enemy zombies. It could be too overpowered though. I also feel that the tape should restrict the player as well, but then I would have to add a tape removal mechanic to keep the player from trapping themselves in a room. (This is also why I had to add a "pull" action to counter the "push" action.)

Improved Basements

The basements in some houses didn't look very much like basements, and were a bit disorienting with their random interconnected rooms. I added more basement items, including water heaters with pipes and laundry baskets in laundry rooms. I also made the exterior walls a darker concrete block texture that has good contrast with the stucco/plaster interior walls. Knowing where the exterior walls are helps the player navigate in the basements of large/complex houses in the absence of windows.

Here's an example of the new basement look. The planet picture looks a bit odd here, but I don't feel the need to change that right now.

The new basement look: water heater, laundry basket, and concrete block exterior walls. I'm still holding the infamous tape roll I used in that hallway above.
   

Close-up of a water heater, complete with warning label. Only some of them have the label.

Clothing

I've finally started to add clothing, beginning with colored shirts hanging in closets. Right now they're simple 2D textured billboards with alpha masks to make the edges of the quads (outside of the shirt fabric) transparent. I haven't figured out how to make them properly 3D, due to a combination of performance issues with high vertex count models, and the lack of free shirt 3D models. At least they're properly hung on the hangers and cast nice shadows. And as always, the player can steal both the shirts and the hangers.

Colored shirts hanging in the closet, drawn as alpha masked billboard quads.

I plan to make them sway back and forth when the player bumps into them eventually. That seems like a lot of complexity to add for such a simple effect though. Maybe I'll also add some designs or logos to shirts.

26 comments:

  1. Good to see that you got dragging objects working! That seems like a very tactile addition to the game systems.
    If you got a 3D model of the clothes, then you could blend between the quad version when looking approximately head on to the 3D model when you're looking edge on. Not sure if that would actually help performance though, and as they would probably be pretty low poly to begin with.

    ReplyDelete
  2. Movable objects definitely work to block access to doors for zombies (and the player), though I'm not sure if it's worth the trouble to do this in actual gameplay. It's definitely an interesting feature, and makes buildings more interactive.

    Actually I don't think there's any performance problem with drawing 3D models of clothes. I'm already drawing a complex 3D model for every clothes hanger. They're only drawn when the player has opened the closet and is looking into it anyway. The real problem is that I haven't been able to find any free 3D models of clothes on hangers in OBJ file format.

    ReplyDelete
    Replies
    1. What do you mean by scale? The physical size of the clothing? Number of polygons? Number of clothing items/shirts? None of this really matters too much, 3DWorld + my workflow already have the ability to scale, remesh, and split out individual objects. For example, I've managed to use 11 different car models from anywhere from ~10K to millions of polygons in a variety of different units, in some cases with background objects that I had to remove.

      Delete
    2. OK! Here you go!
      https://sketchfab.com/3d-models/hangers-and-tee-shirt-44e260855c7f4c1ca611af8d2370195a
      Let me know if it works for you and I'll do a long-sleeve shirt, and some pants as well.

      Delete
    3. Thanks! I don't see the file Hangers.mtl. Do you have this available somewhere? I can create my own if needed.

      Delete
  3. Hmm. I figured Sketchfab would auto-generate one. Just re-uploaded it with an MTL file. You should be able to re-download it now.

    ReplyDelete
    Replies
    1. It looks like the same file as before. Maybe it takes time for the changes to show up?

      Delete
    2. Well it sort of works, though there are some issues I need to address. I used my own material file for now.

      I was thinking that I can separate out the objects but no, I only have support for separating out the material. This works on the shirt since it's one material, but not on all the hanger types because they share materials. I tried splitting the file on objects, but then the vertex indices aren't correct. I could hack the obj file reader to extract a particular object and export as my internal format.

      I can only change color per-instance, not the texture. The dark texture doesn't recolor well. I can either change to a lighter texture, use all white and only colors, or load the model multiple times with different textures.

      This model is the first one I've tried to use for interiors that's not a closed volume and has two sided triangles. I need to disable back face culling per-model type so that the player can't see through the arm holes. But then lighting doesn't work when viewed on the inside. Maybe I have to use the "plants" shader for this, which has two sided lighting enabled, and put these shirts into a different instance vector.

      It could take some time to properly integrate this because I need to add several new features. I've already added like a dozen features for this sort of thing, but every model is different. Part of the problem is that I've been too picky and tried to find models that "just worked".

      Thanks!

      Delete
    3. I can pretty easily get you a modified shirt, and the hangers all as seperate files. I don't want to bother uploading them all to Sketchfab though, so you can download the whole batch here:
      https://peripheralarbor.com/commission/Hangers%20and%20Shirt.zip
      The Tee shirt texture is just an example. Solid colors should work fine.

      Delete
    4. The tee-shirt in that zip file is a closed volume, so it should work with your single-sided pipeline now.
      I also put all the hangers and the shirt in the same spot, so you can use the same offset for them all to get it to line up correctly.

      Delete
    5. Thanks for making all of these changes! I'll get back to this tomorrow.

      Delete
    6. No problem! I've added some hanging pants as well:
      https://sketchfab.com/3d-models/hangers-and-pants-37b92fbde5ca45448c50fc2cb6a376d8
      And, just for you, I've added the individual models to the zip file:
      http://peripheralarbor.com/commission/Hangers%20Pants%20and%20Shirt.zip

      Delete
    7. Did a long sleeve shirt too:
      https://skfb.ly/oqqyn
      And updated the zip file:
      http://peripheralarbor.com/commission/Hangers%20Pants%20and%20Shirt.zip

      Delete
    8. Thanks! It's going to take me some time to integrate all of these models into my closets. I think I need a vector of hanger models and a vector of clothing models, similar to how I handle cars and people. Except in this case I assume they're all in the same scale and coordinate system so I don't have to try every possible orientation for every model as "up".

      I guess I'll have to choose a random hanger, maybe per closet/floor/building so that they match better. Then each one is {empty, tee shirt, long sleeve shirt, pants} with some probability.

      The pants texture looks crazy. Is that ambient occlusion lighting?

      Delete
  4. My pleasure! I looked around and came to the same conclusion as you, that hanging clothes are hard to find, so I figured I'd invest in making them. Should be useful to other people too!

    It would match well to have all the same type per closet... if you look in my closet there's a bunch of mismatched hangers though! Be careful with the pants too, as the two "bar" hangers won't work with pants.

    Yeah, I baked the AO in to the pants. You can replace it with a flat texture if it looks better, but I figured I'd go through the trouble since there's not going to be much call for doing custom logos and stuff on the pants. You should be able to re-color them by multiplying the texture by whatever color you want, so the baked lighting persists.

    ReplyDelete
    Replies
    1. I guess I need to replace my model array with an array of vectors so that each model type can have multiple 3D models, then I can have multiple hanger and shirt models. In theory I can add additional models for the other items as well. I'm not sure how I'm going to prevent bar hangers with pants unless I ass special cases in the C++ code since that rule is difficult to add to the config file.

      At this point I have a closet full of shirts with what I believe is your website on them. I assume you're okay with me posting screenshots of this?

      I had to take the spaces out of the image filenames. I also had to fix the filename in shirt.mtl to use the "jpg" extension rather than the incorrect "png" extension that was there.

      Now I need to adjust the placement of the clothes and hangers to match the hanger rod and make them rotate properly when the player hits them. And figure out how to change the colors and/or textures of the shirts.

      Delete
    2. Okay, I almost have it working, just a few issues to resolve. The tee shirt is fine. The long sleeve shirt has a bad triangle with the wrong vertex winding order or something that shows as a hole (3DWorld says there's a self intersecting polygon.) The larger size clothes hangers also clip through the long sleeve shirt model for some reason (maybe not aligned on my end). The pants look wrong, but maybe it's just because of the two sided polygon back face culling and lighting problem I had with the original shirt. The hangers all look fine.

      Delete
    3. I think the reason why the long sleeve shirt clips through the wider clothes hangers is because its bounding cube isn't centered on the center of the hanger. The sleeves extend further out on one side compared to the other. I'm rotating about the vertical axis at the bounding cube center point when placing shirts in the rotated 90 orientation. So I either need to specify a rotation point in the config file somehow, always rotate about (0,0) (if your model happens to be centered), or only add these shirts to the wire hangers.

      Delete
    4. I intentionally centered all the models, so you should be able to use 0,0 as your rotation point. I'll take a look at the bad triangle, maybe get it fixed this evening.
      For the hanger config with different clothes, I would make the hanger type dependent on the type of clothes, so you can list each hanger type that is allowable with each clothing type. If you ever add spaghetti strap dresses, for instance, the plastic wire hanger has the little hooks for the straps which no other hanger type has.

      Delete
    5. New version of the zip file above is up. I had failed to set the origin of the long shirt to 0,0 so that may have been the cause of the problem with the hanger alignment. I think I fixed the bad polygon as well, but let me know if that persists.
      Also updated the mtl file to point to the jpg instead of the png, though you can just keep your version, as nothing else has changed.

      Delete
    6. As to the pants, they are water-tight and properly normalized, so single-sided polygons shouldn't cause any issues. I have them "flat" shaded instead of "smooth" shaded, so maybe that's the problem? Otherwise, I'll need a bit more description to troubleshoot.

      Delete
  5. Oh, and yes, it's fine to use the included texture for the shirts. I figured you would generate some randomized text to go on them, but one step at a time I suppose.

    ReplyDelete
    Replies
    1. Thanks for the updates. I added an option to rotate objects around (0,0) rather than the bounding cube center, and that seems to fix the problem with the long sleeve shirt clipping through the hanger.

      I added an option to disable back face culling for individual models. I'm not sure why, but I have to enable this for the pants to draw correctly.

      Your updated shirt model fixed the "self intersecting polygon" warning and removes that hole in the mesh. That's good, because when I disable back face culling I have a different problem where the inside of the material clips through the outside of one of the sleeves and has incorrect lighting. I'm still not using two sided lighting in the shader because it's more expensive to always use, and more expensive to switch between shaders between models.

      At this point everything appears to draw correctly. I still have to experiment with the vertical alignment of the hangers on the hanger rod and the clothes on the hangers. Then I have to add an option to use a custom texture, and try an all white texture with various colors.

      I'm not sure how to handle the matching of hangers to clothes. Right now I just have a set of hangers and a set of clothes, with no real way for the generator to know what pairs are valid. I want to have this specified in the config text file somehow rather than hard-coding into the source code.

      If you want to continue this over email, my email address is @gmail.com, all lowercase. Thanks!

      Delete
    2. Your new shirt model also fixes the problem with the inside surface of the sleeve clipping through the outside surface.

      I finally have the placement of the clothing on the hangers at the correct height. I know your have them at the same relative coordinate space in your files. But I have to have the objects relative to each other in a chain for everything to work: shirts and pants relative to hanger, hanger relative to hanger rod, hanger rod relative to closet. This ensures everything rotates properly on the hanger rod when the player bumps into them.

      I see what you mean about the bar hangers not working with pants. I can't come up with any clean way of enforcing that in the model config file. The best I can do is some hack where if (clothing_id == 2 && hanger_id >= 3) {clothing_id = 0;} to change from pants to shirt if the hanger rod is a bar type.

      I still need to work out the colors and textures, probably sometime tomorrow. Then I can post some screenshots.

      Delete
    3. Okay, I think I have everything working now. I fixed all of the various problems with back faces and objects clipping through each other. I replaced the hard-coded pants vs. hanger type check with a system that identifies shirts vs. pants and hanger types by parsing the filename and looking for "shirt", "pants", and "bar hanger". I have everything rotating properly when pushed by the player, and they show up as the correct types of items in the player inventory. I used a mix of gray textured shirts and untextured shirts in brighter colors, but left the pants as gray. The hanger types should all match within a given closet. The indirect lighting and shadows in closets could use improvement, but that's a different topic.

      I made a short blog post showing some example closet interiors. I think it can still use some work, but it definitely looks better than it did before when I had flat textured 2D quads for shirts. Thanks for all your help!

      Delete