Mesh Deformations

OK, I received a PM from someone asking about mesh deformation. And it seems that this is not really documented. For me to give someone a piece of code and let them use it is great, but I have a deeper feeling that you really need to understand this stuff yourself. (give a man a fish… and all)

Step 1: Point of Impact
You need the point of impact, and the relative velocity of the impact. These are given to you by the collision information on OnCollisionEnter or OnCollisionStay. In general, I use the length of the velocity to calculate the softness of a body and limit the amount of damage that could occur. For things like a vehicle, you would wan this on the root body. (the same root that should control the car)

How to test:
In OnCollisionEnter and OnCollisionStay use Debug.DrawLine from each contact in the direction and distance of relative velocity.

Vector3 point = collision.contacts[pointIndex].point;
Vector3 normal = collision.contacts[pointIndex].normal;
Vector3 impact = collision.relativeVelocity;

Step 2: Calculate the amount of collision
The relative velocity will serve two basic functions here. What direction, and how much impact there is. So lets first assume that our car runs into a wall. This wall calls OnCollisionEnter and gives you a relative direction to apply damage. It also says that we hit it for 1 unit of length. So we have, where, and what direction and how much. Now, we need all of this relative to the meshes that are in our model. So for each mesh, we want to use the InverseTransformPoint and InverseTransformDirection to get a completely relative point to our mesh. So this point is on the bumper of our vehicle. Now we go through each vertices on our bumper and calculate how far it is from the point of impact and what the percentage is in distance.

vertex = mesh.vertices[index];
float amount = Mathf.Clamp01(1 - Vector3.Distance(point, vertex) / impact.magnitude);

This formula shows you how much effect that the impact will have on that particular impact. Now we simply do a calculation in the direction based on the percentage of distance from the impact point.

mesh.vertices[index] -= normal * impact.magnitude * amount;

OK, so 2 basic steps to cause dimples on your model. This is a start. But, there is more.

Decrease damage calculation over OnCollisionStay:
OnCollisionStay offers the same relative velocity, but it does not take in any account that the collision is skidding. So when your traveling on the roof of your car, you still get massive collision length, even though you really are not impacting anything. To compensate for this, you will want to limit the impact length by the normal of the collision. This is done using the Dot product.

Vector3 impact = collision.relativeVelocity * Mathf.Clamp01(Vector3.Dot(collision.relativeVelocity.normalized, normal));

So now, you are traveling at 100 MPH but at 90 degrees from impact. This means that you get no damage because a 90 degree Dot is zero. :wink: Furthermore, since we are clamped we never get a negative impact.

Bounds increase performance:
It is no secret that if you have a vehicle with 10,000 vertices, this process will take a while to complete, especially if you ask it every frame. My suggestion here is to split your model into pieces. A bumper, fender and hood can be 20 times faster than an entire vehicle. So by using separate objects and bounds, we can figure out if we even need to run through all of the vertices to do deformation. This can be daunting though. You will need to increase the bounds of your objects by the length of the impact.

Vector3 max = bounds.max + Vector3.one * impact.magnitude; Vector3 min = bounds.min - Vector3.one * impact.magnitude; bounds.max = max; bounds.min = min;

When all else fails, put it off:
OK, yeah, this is a bit dumber, but it is a wonderful thing that you can do to help an ailing game. First, make as much use of each frame as you can. Lets say that you are trying to calculate a 10,000 vertices model 100 times in each frame. This will make your frame rate spike/drop every so often as it recalculates the model’s impacts. OR you can use a coroutine and/or multi-threading to calculate the data for you. Simply run a coroutine that calculates damage every cycle. If it reaches over a certain amount, just skip to the next frame and continue. To do this, you will have to develop a communication piece that stores collisions (in their local sense) and processes them in a first in, first out scenario. At the end of each mesh, simply ask. (how much time has passed since I started) if it is over a certain amount (like 0.02 seconds) then yield to the next frame. Most gamers would not balk at damage that shows up a frame or two later, but will, if they can barely control a vehicle because frame rate was too low.

Dimples are nice but I like cellulite:
OK, this title is either funny or disgusting. When you see damage on a car, you see crumpled metal, not cute little dimples. If we were using a realistic vertex collision model, then we wouldn’t have this problem. But, we can fake it. So we have a few basic models to work from in dealing with this. Each method below can be multiplied by a “randomness” amount which can either be overall, or on each piece of the vehicle.

  1. We can use a random number each vertex. This gives us randomness, a fast way to do it, but in the end will create splits in the model. As more damage is applied those splits become bigger.

  2. We could use a 3d or 2d noise system. This is expensive and slower than randomness but will offer amazing results. You can use a random scale for the noise. 2d noise is based on the normalized direction of the damage. This method is incredibly precise and if you do it right you will not get splits.

  3. Random by Percentage. This is a very simple method of calculating noise based on the distance (percentage wise) that a vertex is from the impact point. You start off by creating an array of 100 random values. Then you use an index of that by the percentage of distance in the collision to get a random number. This method is incredibly fast, doesn’t split and still offers great randomness at different impact scales.

I am by no means an expert in this, so if you have something to add, please do. There are no bad ideas!

You can use Impact Deformable, available on Unity Asset Store: Impact Deformable | Physics | Unity Asset Store