Tuesday, May 03, 2016

Cloth Demo

Springs are an integral part of any game programmer's arsenal. They are used everywhere: in cameras, ragdoll physics, water, soft-body physics, and the focus of this post: dynamic cloth. If you take quantum field theory to be the fabric of the universe, then energy springs are basically the fabric of the universe. (resist tangent)

Anyways, I made a "cloth," which could be better described as a 2d lattice of springs.

Looking at the interactions of the system as a whole can be overwhelming, so let's break it down. First let's work with the most basic structure: a point mass. Each vertex of the cloth is simply a point mass (also known as a Particle). Define a struct with the following members, and any others you'd like to add: a mass, a position, a velocity, a force, and a boolean indicating whether the point mass is fixed or not. Use any integrator you'd like: Euler works, but Verlet works better as it takes inertia into account. They are very similar, and there are many explanations of each method.

In addition to an integrator, also include a Post-Update method: an update function that runs immediately after your normal update. This Post-Update should perform the actual integration on the total force, while the update method should additively calculate force each frame. The usefulness of this will become apparent later. Simply put: since there are 4 distinct connections for all non-edge case PointMasses, we will need to find the cumulative force for all of them.

Next, we'll need to create a Spring class. This class will include the following members: two PointMasses, a spring constant, and a scalar equilibrium distance. You could add a breaking distance to define the max distance a spring can expand before it breaks, but I won't go over that in this post.

Next, we need to create a 2D array of PointMasses. While doing so, we want to create spring joints connecting each PointMass to the one above and to the left of it. The general algorithm for doing so is as follows:

 PointMass[][] pointMasses;
 Spring[] springs;
for (i < cloth_height):
  PointMass[] row;
  for (j < cloth_width):
    PointMass newPM;
    newPM.position = (initialPos.x + j * spawnDistance, 
                      initialPos.y + i * spawnDistance);

    //if there is a PM above this one
    if(i > 0):
      Spring newSpring;
      newSpring.PointMassA = newPM;
      newSpring.PointMassB =  pointMasses[i - 1][j];

    //make the top row fixed
    if(i == 0):
      newPM.isFixed = true;

    //if there is a PM to the left of this one
    if(j > 0):
      Spring newSpring;
      newSpring.PointMassA = newPM;
      newSpring.PointMassB =  pointMasses[i][j - 1];

Of course this is just pseudo-code, but you get the idea. Something to note: you can set any of the PointMasses to be fixed, which can lead to some interesting results.

I won't go into the spring integration method here, as it is also well documented online. One important note: make sure your force directions are correct!

And that's pretty much it, except one caveat. Since we update in-order, and since we are dealing with an interconnected lattice of springs, PointMasses indirectly effect all of the other PointMasses. So, if we move the bottom-right point on our cloth, the top left one may be effected. Say we update our PointMasses left-to-right, top-to-bottom. The new position of the bottom-right PointMass will update after we've already calculated the top right one.

We need to account for this, and the simplest (yet particularly inefficient) method I've found is to integrate multiple times in each frame: each time getting closer and closer to the actual position the PointMasses would be in ideally. If you have any better solution, or ideas for resolving this, email me or leave a comment! I'd love to find a better way to do this, but for now integrating multiple times is the best way I know.

No comments:

Post a Comment