Wednesday, September 9, 2015

Movable Objects Physics

This time I'm not going to talk about graphics, at least not very much. I'm going to talk primarily about physics - in particular, the new movable objects I have been working on over the past few weeks. This post is going to be less technical than usual, since I'm not going to get into the details of how I implemented object physics. I'm just going to explain what the system can do and show lots of short videos. It's just too hard to capture physics in screenshots.

But, for those of you who like to see pretty screenshots, here is the latest version of my trees in tiled terrain mode. They look a bit different from the last tree post a few months back, and in my opinion they look better. I increased the lengths of the smallest level branches so that the leaves are more spread out, rather than being clustered together.

The latest version of tile terrain mode trees, now with more leaf detail.

Okay, back to movable object physics. Movable objects are a new type of physics object in 3DWorld that are somewhere between standard static objects and dynamic objects. They're really pretty close to the way platforms (such as elevators, crushers, and moving doors) are implemented. Here are the basic types of objects supported by 3DWorld:
  • Static scene objects. Most objects are of this type, since they are the most efficient to process: No dynamic updates, no physics, can use a static draw buffer, precomputed lighting, etc.
  • Destroyable objects: Static objects that don't move but can be destroyed (removed)
  • Platform objects: Move along a predetermined path when activated, no physics, some precomputation is still possible.
  • Movable objects: Movement controlled by the player, AI, gravity, and other movable and platform objects. Movement is generally sparse/rare so physics is only applied when needed, but can't precompute anything.
  • Dynamic objects: Objects that typically have a limited lifetime and are assumed to move each frame (players, weapons, projectiles, pickup items, fragments, particles, etc.) Limited to collision spheres only for simple and efficient collision/physics.
Movable objects are more general than dynamic objects since they can have any standard collision shape, but they are less efficient. 3DWorld can efficiently handle thousands of dynamic objects but probably only a few hundred to a thousand movable objects.

The supported basic primitive shapes for movable objects are {cube, sphere, cylinder, cone, truncated cone, capsule, polygon, ramp, and extruded (cookie cutter) polygon}. Yes, I have implemented exact collision detection between all of these shape classes, some 30 functions in total. It's not entirely complete and stable for all combinations, but it works well enough. This has some advantages over the standard polygon meshes used in most games:
  • More efficient (an analytical sphere is much faster than one formed from a thousand triangles)
  • Curved surfaces can be rendered at near pixel accuracy using a smooth LOD transition
  • Seamless texture coordinates can be auto generated with simple code in most cases
  • Collision can be exact, and generally involves solving some equations rather than iteration
  • Objects have true volume, which means their mass can be calculated for physics
  • Objects always form a closed surface and inside vs. outside is clear (except for polygons)
I have implemented quite a few "effects" where movable objects interact with the rest of the world in interesting ways. Most of these can be classified as physics or collision effects. They have been implemented using object-object collision detection and almost nothing else that must be special cased on the object shape type.

One of the important effects I wanted to have was stacking. Movable objects can be stacked on top of each other and the whole stack moved around. Stacks can be created statically in the scene object file, or dynamically by dropping objects off ledges onto each other. Stacks can also be pushed over by standing on something to get at the top object on the stack. I also added horizontal stacking (like a train of objects) after recording the videos so you won't see them in action yet.

The remainder of this blog post consists of 11 short videos of movable objects in action. I feel that this topic is much easier to demonstrate using video rather than static images and walls of text like most of my previous posts. Since the free version of Fraps limits video recording to 10 seconds, none of them are longer than that, which is why I had to create so many. Keep in mind that there was no special setup for making these videos, I could have done all of these events in one 3DWorld run. Some of the videos have sound, in particular the ones involving weapons. I really need to add more sound effects for pushing, falling, colliding, etc. [Update: I did add more sounds! But I'm not going to re-record all of these videos.]

Ramp 1
This video demonstrates sliding a stack of two wooden crates (cubes) up and down a ramp and over the grass. Note how the crates crush the grass in their path. In addition, it shows how a sphere and a cylinder can be pushed over the edge of the wall. If you look closely, the sphere smoothly rolls over the wall (though the texture doesn't move to show that it's rolling).

Ramp 2
The previous ramp video was truncated at the 10s mark, so I had to split it into two parts. This time I'm sliding a crate up a ramp that's also a movable object. When I push them together, both the crate and ramp slide on the ground, until the ramp hits the wall of the fountain and stops. At this point, I can finish pushing the crate up the ramp and into the water. Then I push a wooden barrel (horizontal cylinder) up the ramp and into the water. The "boing" sound is the placeholder sound for player jumping.

Elevator 1
Here I push a stack of two metal boxes (Borg cubes!) and a brick cube into the elevator, which goes up and then down. On the way down I push the metal cubes off at one floor and then push the bricks off the back of the elevator.

Elevator 2
The first elevator video doesn't use real physics. Can you spot the error? The cubes were directly controlled by the elevator's velocity, which is a hack. In the next video I have fixed the problem so that the cubes are in free fall when the elevator is going down. The video starts with the elevator stopped on the top floor. You'll notice a small jump in each cube when the elevator starts its descent - what is that? The jumps are due to the unrealistic constant velocity of the elevator. When it starts moving at a fixed velocity, it has an infinite downward acceleration. Since the cubes fall due to true gravity, they can't accelerate to the elevator's velocity instantly. Instead, they accelerate due to gravity under free fall from rest and only collide with the elevator when they exceed its velocity and catch up. The bottom cube has to catch up to the elevator, and the top cube has to catch up to the bottom one. After that, the cubes descend with the elevator at a constant velocity. I'm not entirely sure it's correct now, but it looks plausible for an elevator with infinite acceleration.

Falling Bricks
This video demonstrates falling object and stacked object physics. It starts with a brick cube resting on a wooden board. I shoot the board with the M16, shattering it, and the bricks fall to the ground below. Then I accidentally fall off the edge when trying to look at the bricks and land with a bloody splat. Also note the shell casings and the bullet holes that stay on the bricks as they fall. Since bullet hole decals are independent world entities, it took a bit of extra trickery to bind them to a known position on the object so that they track the object's position as it falls. Oh, and yes, I finally gave the M16 some recoil, which is why I keep falling off of things when I use it.

Ball Push
Here I push a rolling ball with a glass cube, then push it off the edge with a movable wooden ramp. The ball is a dynamic object, rather than a movable static object, so the rolling physics/texture rotation works with it. See how the glass cube isn't a uniform transparency? The glass is modeled as a scattering medium with real optical thickness, where each pixel's ray is intersected with the cube and the light attenuation is accumulated along the ray's optical path through the cube. Windows work the same way.

In this video you can see that the local light source shadows are updated as the boxes are pushed on the floor. The shadows are only recreated when a dynamic object moves, to save CPU and GPU time. Technically, the movable boxes aren't dynamic objects, so they don't trigger the shadow updates themselves. But, the player is a dynamic object, and moving around near the light will trigger shadow updates. Since only the player can move objects (currently), everything should work well enough.

Indirect Lighting
3DWorld uses precomputed indirect lighting stored in a 3D volume texture. This allows objects that weren't present during lighting computation to still receive light by looking up the lighting texture data in the shader. However, the objects can't actually modify the indirect lighting in an efficient way. This video shows a white cube being pushed around through various lighting conditions, where indirect lighting dominates. The transitions from lit to dark areas are smooth. The hallway in the middle of the video has motion activated blue lights in the ceiling that contribute a direct lighting component as well.

Destroyable Crate
Here is an example of how nearby explosions can destroy moving objects. I push a wooden crate next to an exploding tank, then shoot the tank, causing it to explode and destroy the crate in the process. The shattering glass sound is from one of the lights on the ceiling that is also destroyed.

Floating 1
Physics is fun, especially when it includes water! At least I think so. I have implemented physically-based buoyancy in 3DWorld. Objects with density < 1.0 float, and objects with density > 1.0 sink. Float height and sink rate are functions of density. The video starts with the lake water in the corner of the office building scene frozen as ice, where I (the player) am standing on the ice and all of the objects are resting on the ice. When I increase the temperature using a hotkey, the ice immediately melts and I fall along with the other objects. Some of them float, and some of them sink, depending on their density relative to water (1.0). The floating objects bob up and down with the waves on the surface of the water. The cube object materials are (from left to right in decreasing density):
  • Metal, density = 4.0 (typical metal density is ~2-8)
  • Marble, density = 2.8
  • Glass, density = 2.5
  • Brick, density = 2.0
  • Wood, density = 0.7 (wood density ranges anywhere from 0.4 to 0.9 so I picked a mid value)
  • Styrofoam, density = 0.2 (random guess)

A note on bricks: It seems odd to me that brick has only twice the density of water. I would have thought that bricks are heavier than that, but I guess not. Here is a fun experiment - try picking up a brick that's out of the water, then submerge it in water and try picking it up again. Does it feel like half the weight when it's in water? Maybe the next time you need to move a stack of bricks, you should flood the area first and it will be half the work. Of course then you'll spend extra energy moving yourself through the water, so it might not help that much:)

Floating 2
I forgot to enable the underwater bubbles in the first video, and I didn't want to have to move all of the objects into their correct place and redo the floating/sinking video. So I quickly put them in less orderly positions and recorded a new video that includes bubbles. This time I stacked the Styrofoam onto the bricks so that they separate in the water. If the bricks were on top they would both float, which may not be correct, but the water system treats each object independently and the bricks are technically out of the water. Also note that the underwater effect includes a full screen blur postprocessing pass, which is why the image looks a bit fuzzy.

Teleporter + Fall
This is my favorite video. I push a crate through the lobby teleporter onto the roof, then go through the teleporter myself. I push the crate over a skylight, stand on top, fire the shotgun at the skylight until the glass breaks, then the crate with me on top falls four stories back to the lobby level. The falling damage is nearly enough to kill me, but I survive and get to see the crate with shotgun shell casings sitting on the floor. I think the fall would normally be fatal but for some reason or other the crate reduces the damage. Don't try this at home!

Bonus: Water Fun
Did I say 11 videos? Well, here is #12. It was so much fun creating these videos that I decided to create one that mixed water, destruction, new sounds, and drowning. Here I have a metal box resting on top of a wooden crate on the ice. I push a large wood plank down the stairs and out onto the ice. Then I jump onto it and melt the ice into water. You can see that the metal box floats on top of the crate on the surface of the water. Then I shoot the crate out from under the box with the M16, causing the box to fall into the water. The crate explosion creates a huge wave that's probably a bit excessive, but that's okay. Then I shoot out the plank from under me and fall into the water, taking some damage from the sharp floating wood fragments. Finally, I destroy the metal box, fire some more M16 rounds under the water for good measure (and to show off the bubbles), then drown. That was fun!

Yes, this post is long. But the time taken for you to read it is small compared to the time I took to write it ... which is small compared to the time it took me to implement all of this ... which is small compared to the time it took me to write the thousands of lines of those ~30 primitive object intersection functions. Sure, I could have probably found the code for some of it online - I tried - but it seems like no one can get that sort of code stable and correct in all cases. So many opportunities for divide-by-zeros, so many places where epsilon tolerances are needed. Maybe I should open source that code some day.

Maybe I'll add more videos later. Keep looking for them.

Update: I added water buoyancy physics for two stacked objects that uses their combined mass, the displacement volume of the bottom object, and the water level of the upper object. I'm not sure it's physically correct, but it looks right. A metal box on a styrofoam block now sinks more slowly than the metal by itself. A wooden block on styrofoam floats low in the water. Bricks on styrofoam sink until the bricks go below the water, then sit there with the buoyancy of the styrofoam keeping the bricks from sinking completely. Wood on bricks sinks faster than the bricks alone until the wood goes under the water, at which point they separate and the bricks sink more slowly while the wood floats. I haven't had a chance to make a video of this yet, but I'm sure you can guess what it would look like.