One of the key features of Paint the Town Red that we’re adding to elevate the over-the-top gore from what we had in the 7DFPS game jam version is the that the main shapes of the enemies like the head, torso and upper and lower arms and legs are made up of "voxels". What that essentially means is instead of the head being one big cube-shaped mesh, it's a shape based on the combination of a series of smaller cubes. Being able to then dynamically change the cubes that make it up by changing or removing some and having the overall head shape reflect that means we can have chunks of the head or other body part be severed and destroyed when hit.
Creating the Mesh
The naive approach to rendering voxel objects would be to simply make each voxel a separate renderable cube mesh in the game. While that might work for certain types of games where there might only be a very limited number of voxels, it's not a viable approach for a game where you're concerned with hitting high framerates and not having tight restrictions on the number of voxels.
The approach that we've taken with creating the mesh to be rendered for each voxel-based object is to create a single mesh that contains the triangles of all the exposed faces of the voxels. So something like the head of an enemy may start as a 10x10x10 3d grid/matrix of voxels that are all intact. If we look at all these voxels and check what each of their 6 faces are touching, we can see when a face is touching nothing (i.e. not touching another voxel) and add that face as 2 triangles to our objects overall mesh. We also need to account for whether that voxel face requires a UV coordinates for a texture and pass that information to the new vertices along with other particular rendering information for that voxel stored in the vertices for our custom shader.
This approach involves looking over the state of all voxels at creation and every time they change and recreating the entire mesh. Fortunately our voxel mesh creation code is already very mature and efficient as it was brought across from another game we were working on prior to Paint the Town Red.
The function to define the mesh can easily be multi-threaded and the results applied upon completion for rendering. Currently however, the voxel mesh creation code is not a performance concern at all compared to things we have a little less control over like physics, animation and pathfinding.
We had some basic dismemberment in the game jam version where we'd separate the head or limbs depending on the weapon used and some degree of randomness. Now that we've switched to voxels, not only can we sever these body parts in their entirety, but they can be damaged and severed in any spot and way imaginable.
When a voxel object is modified we can look over all the voxels and separate them into groups with other voxels that they're directly or indirectly connected to. An intact object will have one group where you can find a path from one voxel to any other through the voxels they're touching. When there is more than one group, we need to disconnect separate chunks from the rest.
The process of sorting the voxels into these groups to check for dismemberment is the slowest part of the voxel system and there are a number of different cases to handle. For each group of voxels in a particular voxel object such as the head or the lower left arm, we need to determine if it's attached to anything else on the body and in what way. To answer these questions, certain voxels in an object are set as connection points along with information about the connection type and what they connect to. We can then determine how to handle the dismemberment of a group of voxels by seeing if there are any connection points in the group and if so, what they they are.
For example, in the image above you can see 2 small groups of voxels have been severed from the head. The head contains 4 connection points which are 4 central voxels at the bottom of the head where it connects to the neck/body. The small chunks of voxels in the image don't contain these so this is the most simple case where we simply need to break these groups off into new, separate objects with their own collision and physics. The head is essentially duplicated with only those voxels being set in the new objects, while also being removed from the original. Bruising and blood effects are also copied across for consistency.
Things get more complicated when a severed group contains connection points. Below you can see a debug view of a character with the connection points shown as yellow cubes. Imagine we cut clean through the center of the left foream with a sword. Our scan of the object after the sliced voxels have been removed will show that there are 2 groups of voxels and each contain connection points. The upper portion of the forearm contains a connection point which serves as the primary attachment point for the object. Whether this point is intact determines whether the forearm remains connected to the upper arm. In this case we don't need to do anything with the upper portion of the forearm. The lower portion however contains a connection point that says it's connected to the hand object. Severing this is much more complicated than the head chunks above as we need to first create a new object with these voxels in the same way as the head example, but then also need to take the hand and its child objects (fingers, the katana in the below image, other unseen collision objects) and have them all reparent to the new lower forearm object and enter what is essentially their ragdoll state.
When removing voxels and creating new objects with a subset of the voxels of the original, we also need to update the collision geometry to match the new shape of the object. Because of how collision intersection algorithms work in physics engines, the collision geometry needs to be convex. Fortunately Unity handles the convex hull generation for the most part, although it does have some issues with recalculating the center of mass which we need to solve ourselves.
The most extreme cases where you cut through both arms and the body and the hips all in one motion work now without issue, but the incremental steps through development to reach that point led to some pretty fantastic bugs. I have captured and continue to capture videos throughout development whenever something interesting is going on. The video below was from a pretty great stage in developing the dismemberment system where all sorts of odd things could and would happen.
One of my favourite aspects of our voxel characters is that not only can we have different visuals for the voxels in terms of colour and texture, but we can also give them different attributes. This doesn't just set whether they have a texture (like the outer most face of skin voxels) or their colour, but also things like whether they can be only damaged by certain objects or their resistance to damage from weapons. This primarily manifests in the bone voxels being more difficult to break through than skin, flesh and brain voxels (which each have different resistances also) and that gives some great gameplay opportunities. If you're using a weapon that does more voxel damage than "health" damage to enemies, trying to damage the brain (which will instantly kill the enemy) could be important. To reach the brain you might need to try attacking the same point on the head where the brain is exposed so there's more chance to break through.
Alternatively you might find that some weapons do little voxel damage and aren't actually capable of breaking through bone, only the surrounding flesh. Along with other weapon characteristics (health damage, break-ability, stick-into-person-ability, throw damage, etc), this adds to the complexity of the weapons and combat in subtle ways that don't affect the ability to get into the game, but add depth for those that play longer and want to do better.
Having the different voxel types in the internal structures of the enemies also opens opportunities for weapons and effects that target different voxel types differently. Already in the disco level we have the afros setup to only be able to take damage from (be cut by) bladed weapons, although they also reduce the damage taken from shots that pass through the hair. But one could image a sci-fi level where some effect leaves only bones behind for example.
There's a lot more involved in the voxel tech than discussed here such as how we handle the weapon collisions and in the future when I have a bit more time (perhaps after the game is out in Early Access) I'll be able to go into more detail on everything.
I'm very happy with how it's turned out so far and it should make watching other people play the game much more interesting. It makes everything much more dynamic and different every time you swing a weapon.