After seeing some awesome demonstrations of how materials can be so realistically simulated in games, I decided to have a crack at it myself and apply some of my knowledge from engineering. This is a little demonstration application made in Qt with C++ that models a square of a jelly-like material.
The model is made up of nodes of equal mass that are connected by massless damped springs. Each square of four nodes has an internal pressure that prevents it from collapsing. The nodes are also affected by gravity, are constantly slowed by an “air resistance” factor, and bounce off the walls, losing energy each time.
The calculations for each time step in detail:
- Force applied by each spring is calculated from the positions of the nodes it is connected to using hooke’s law. The distance between the nodes is compared against the “base” length of the spring, and this extension or compression is multiplied by the spring coefficient. This gives the magnitude of the force, so the vector can be calculated by multiplying by the normalised vector direction of the spring. The resulting force is stored in each end node.
- Force applied by the damper is calculated for each spring and applied to the end nodes in the same way. Damping resists the velocity of a body, so the force is the damping coefficient multiplied by the velocity, which is approximated from the current and previous extension of the spring.
- The internal pressure of each element is applied by calculating the average of the positions of the nodes in the element and adding a force to each outwards from this point in the same way it is with the spring- proportional to the extension or compression compared with the base length of this distance.
- For each node these forces are divided by its mass and added to the acceleration due to gravity to give the sum acceleration of the node. Since there is a constant timestep differentiation to give the velocity is simplified to adding the acceleration to the last velocity. At this point the resistance factor is multiplied by the velocity and the new position is calculated by adding the velocity onto the last position.
- After updating the positions, any collisions with the walls are detected and if there are any the velocity for that node is reversed normal to the wall.
After a lot of fine-tuning I managed to simulate a suitably “wibbly-wobbly” material, which can be adjusted with sliders at the bottom to make it more springy or viscous.
It does however have some limitations:
- There’s nothing to stop nodes crossing over other springs, which gets wacky fast and can cause the whole structure to collapse into a blob. The way that the internal pressure is calculated drives the structure in improbable directions when this happens.
- I have tried limiting the compression of the springs and implementing a minimum distance from the centre of gravity of each element. I’ve also tried scaling the spring coefficients to counteract the compression with a larger force. These methods simply cause other springs to stretch further to accomodate the node moving between them.
- Another method I have tried is basic collision detection against the springs and either reversing or removing the velocity. Reversing the velocity causes the internal nodes to start bouncing against each other, which really doesn’t look right. Removing it makes the node get stuck in the spring, and the whole structure can collapse still. Both those scenarios also don’t account for the node moving fast enough to cross the spring entirely in a single timestep.
- I could try the approach of adding angular springs at each corner that resist angles between springs of less than 90 degrees. I’m currently working on this.
- Jitter at the bottom of the screen. Since a node hitting the edge of the screen reverses its velocity, the springs and the weight of the structure drive the nodes to vibrate. I’m attempting to handle this by damping vibrations when nodes are at the edge of the screen.
- The damping coefficient is proportionate to the velocity of the node, but if the coefficient goes above the scaled equivalent of 1, it will make a force that drives it faster in the other direction. This makes the whole structure basically explode. The problem is that this “1” value is proportionate to the spring constant and the mass of the nodes, so I can’t reliably limit the slider at this point. Scaling the slider is a very viable solution, but for the moment the simulation resets to its default coefficients when this happens.