Updated Curtain and How to Improve Your Verlet Cloth Simulator
Click here to view curtain updated (Most of the update was behind the scene stuff, but tearing was added as well as a few adjustments. Right click and drag to tear. Press ‘r’ to restart, and ‘g’ to toggle gravity).
Click here to view Part 1 (How to build a Verlet Integrated cloth simulator)
So last week I made a blog post discussing how I made Curtain. It described things like the physics and mostly, the Verlet Integration. Today I’d like to go into more detail on how to further improve the Verlet Integration method used by making it more consistent and easier to tune. I’ll also be talking about some of the changes done to Curtain.
Tearing
I added this shortly after releasing Curtain. Links could be torn if you were to tug too hard, or right click and drag. This was pretty simple to implement. Remove links to tear. In the constraint solve just check if the distance between the 2 particles are more than a certain amount, then remove the link.
// In Curtain, curtainTearSensitivity is 50. if (d > curtainTearSensitivity) particle.removeLink(this)
Interaction
I also made an update to the way the mouse interacts with particles. Before, particles within reach were simply pushed towards the direction the mouse was moving.
// pmouseX and pmouseY are the previous mouse positions // they are used to calculate mouse velocity position.x += (mouseX - pmouseX) * mouseStrength * deltaTime; position.y += (mouseY - pmouseY) * mouseStrength * deltaTime
Since the cloth could be torn, it would be difficult to pick it up from the ground. I decided to make this a bit more convenient by giving the particle position and velocity a little more influence on the mouse.
// add mouse velocity on to position position += mouse - pmouse // pull lastPosition towards position // if we set the lastPosition to Position, then you can't throw the cloth // if it's left alone, the particle will get pushed ahead of the cursor and get thrown unintentionally lastPosition = (lastPosition + position*2) / 3
Verlet Integration with a proper time step
The Verlet Integration formula I was using before was based off of Jonathan Dummer’s Time-Corrected Verlet Integration (I honestly don’t know who invented it). This formula was meant to correct movement in a system where the time step wasn’t constant. In the fabric simulation, however, this didn’t work out too well. The curtain would sink to the ground and do other odd things when the frame rate was too low. I was advised that it’s a bad idea to have a simulator dependent on frame rate.
First I had to ditch Jonathan Dummer’s Time-Corrected Verlet method, then revert it back to the original Verlet method.
// calculate next position velocity = position - lastPosition newPosition = position + velocity + 0.5 * acceleration * deltaTime * deltaTime // reset values lastPosition = position position = newPosition acceleration = 0
Next, the deltaTime needed to be more inconsistent. A lot of what I do is also done in Glenn Fiedler’s Fix Your Timestep article. For each frame, you run the physics update multiple times with a fixed time step. For example, if the frame before took 100ms to update, we would need to run the update physics 4 times if the timestep was set to 25ms.
The issue with this is that if each frame were 104 ms (for example’s sake) and the timestep was 25, we would only be able to run the physics update 4 times and 4 ms would be left over. After 1000 frames, there would be 4 seconds of accumulation error. Fixing this is simple. Take the accumulation (in this case it would be 4), and add it on to the total deltaTime for the next frame.
// constant timestep = 15 in global variables
// millis() is the amount of milliseconds that have passed since initialization
currentTime = millis()
// remember deltaTime is measured in milliseconds
deltaTime = currentTime - previousTime
// reset previousTime for next frame
previousTime = currentTime
// timeStepAmt is the number of times the timeStep can fit in deltaTime + leftover from last frame
// eg. deltaTime = 104, timeStep = 25, timeStepAmt = 4
int timeStepAmt = (deltaTime + leftOverDeltaTime) / timestep
// in leftOver, we take the total deltatime,
// and subtract the amount of time that will be accounted for
// eg. deltaTime = 104, timeStepAmt = 4, timeStep = 25, leftOver = 4
// 104 - (25 * 4) = 4
leftOver = deltaTime - (timeStepAmt * timestep)
// I convert my time step to seconds so the values in physics are a bit more understandable
// this is a matter of preference though.
timeStepSeconds = timestep / 1000
for (int step = 1; step <= timeStepAmt; step++) {
// solve the constraints 3 times.
// the more the constraints are solved, the more accurate the result
for (int x = 0; x < 3; x++) {
for (int i = 0; i < particles.size(); i++) {
Particle particle = (Particle) particles.get(i)
particle.solveConstraints()
}
}
// update each particle's position
for (int i = 0; i < particles.size(); i++) {
Particle particle = (Particle) particles.get(i)
particle.updatePhysics(timeStepSeconds)
}
}
My algorithm is heavily commented with long variable names. I would suggest checking on Glenn Fiedler’s article on timesteps if you want a more compact approach.
This method is a lot better if you need control. Accuracy can be modified by changing how many times constraints are solved and lowering or raising the fixed timestep. The same thing should happen no matter what the frame rate, physics wise.
Finally, after finding the particle’s new positions, rendering and mouse interaction is done separately.
/* A separate loop is used for rendering to conserve on resources
drawing the same links multiple times can take a LOT of time,
so that's done separately, as well as the mouse to particle
interactions, which needs to only happen once per particle. */
for (int i = 0; i < particles.size(); i++) {
Particle particle = (Particle) particles.get(i)
particle.updateInteractions()
particle.draw()
}
Resources
- gafferongames.com: Fix Your Timestep – Glenn Fiedler’s approach to fixing the timestep. Mine is a lot similar to his.
- OpenProcessing: Curtain – Curtain’s applet and source (written in Processing) is here.
















hi BlueThen,
my name is Joan Laporta, from Barcelona, Spain..
I’m working with your processing Curtain, a Kinect and Jitter to move it with my hands (or some part of my body)..
thanks a lot for your work, this Curtain is so beautiful.. :p
I would like to show you the results and comment some questions.. I have some problems when I move quitely in front of the Curtain it moves very strong.. can I change this force?
I’m trying to change all kind of variables (mouseInfluenceSize, mouseTearSize, gravity, ..) but don’t find a good solution..
Also, I would like to know if it could be possible to fix the bottom of the Curtain on the floor..
If you can give me an email, I could send you the video to see the results at the moment..
thank you for yor time,
Joan
Hi Joan, I think it’s awesome you’re using Curtain as part of your own creations! Please do email me ( bluethen at gmail.com ) and we’ll see what we can do.
HI BlueThen. Impressed by your work! As Joan wrote last year, I´m also trying to control your sketch with the data from the kinect (with synapse, i.e, OpenNi). I´m new with this kind of language. The problem I´m facing now is how to remove mouseX and mouseY (and of course pmouseX and pmouseY) and get that input from the kinect/synapse´s “x” & “y” coordinates, that are sent to processing using the oscP5 library. I could send you an email with further details if you prefer…thanks a lot for your efforts, and also for the clean explanations you provide,,,reading your code seems better than reading physics manuals
Hi catmac. Sorry for the delay. You’ll need some way to store the most recent location of kinect’s coordinates, and then move that over to a stored previous location. In place of mouseX/Y would be the most recent location, and pmouseX/Y the last location before that.
I don’t have a kinect so I don’t know how specifically it would work with that though.