-
-
Save jasonfarrell/11425e073b5c99b61daa to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Cloth Simulation using a relaxed constrains solver | |
*/ | |
// Suggested Readings | |
// Advanced Character Physics by Thomas Jakobsen Character | |
// http://freespace.virgin.net/hugo.elias/models/m_cloth.htm | |
// http://en.wikipedia.org/wiki/Cloth_modeling | |
// http://cg.alexandra.dk/tag/spring-mass-system/ | |
// Real-time Cloth Animation http://www.darwin3d.com/gamedev/articles/col0599.pdf | |
var DAMPING = 0.03; | |
var DRAG = 1 - DAMPING; | |
var MASS = .1; | |
var restDistance = 25; | |
var xSegs = 10; // | |
var ySegs = 10; // | |
var clothFunction = plane(restDistance * xSegs, restDistance * ySegs); | |
var cloth = new Cloth(xSegs, ySegs); | |
var GRAVITY = 981 * 1.4; // | |
var gravity = new THREE.Vector3( 0, -GRAVITY, 0 ).multiplyScalar(MASS); | |
var TIMESTEP = 18 / 1000; | |
var TIMESTEP_SQ = TIMESTEP * TIMESTEP; | |
var pins = []; | |
var wind = true; | |
var windStrength = 2; | |
var windForce = new THREE.Vector3(0,0,0); | |
var ballPosition = new THREE.Vector3(0, -45, 0); | |
var ballSize = 60; //40 | |
var tmpForce = new THREE.Vector3(); | |
var lastTime; | |
function plane(width, height) { | |
return function(u, v) { | |
var x = (u-0.5) * width; | |
var y = (v+0.5) * height; | |
var z = 0; | |
return new THREE.Vector3(x, y, z); | |
}; | |
} | |
function Particle(x, y, z, mass) { | |
this.position = clothFunction(x, y); // position | |
this.previous = clothFunction(x, y); // previous | |
this.original = clothFunction(x, y); | |
this.a = new THREE.Vector3(0, 0, 0); // acceleration | |
this.mass = mass; | |
this.invMass = 1 / mass; | |
this.tmp = new THREE.Vector3(); | |
this.tmp2 = new THREE.Vector3(); | |
} | |
// Force -> Acceleration | |
Particle.prototype.addForce = function(force) { | |
this.a.add( | |
this.tmp2.copy(force).multiplyScalar(this.invMass) | |
); | |
}; | |
// Performs verlet integration | |
Particle.prototype.integrate = function(timesq) { | |
var newPos = this.tmp.subVectors(this.position, this.previous); | |
newPos.multiplyScalar(DRAG).add(this.position); | |
newPos.add(this.a.multiplyScalar(timesq)); | |
this.tmp = this.previous; | |
this.previous = this.position; | |
this.position = newPos; | |
this.a.set(0, 0, 0); | |
} | |
var diff = new THREE.Vector3(); | |
function satisifyConstrains(p1, p2, distance) { | |
diff.subVectors(p2.position, p1.position); | |
var currentDist = diff.length(); | |
if (currentDist==0) return; // prevents division by 0 | |
var correction = diff.multiplyScalar(1 - distance/currentDist); | |
var correctionHalf = correction.multiplyScalar(0.5); | |
p1.position.add(correctionHalf); | |
p2.position.sub(correctionHalf); | |
} | |
function Cloth(w, h) { | |
w = w || 10; | |
h = h || 10; | |
this.w = w; | |
this.h = h; | |
var particles = []; | |
var constrains = []; | |
var u, v; | |
// Create particles | |
for (v=0;v<=h;v++) { | |
for (u=0;u<=w;u++) { | |
particles.push( | |
new Particle(u/w, v/h, 0, MASS) | |
); | |
} | |
} | |
// Structural | |
for (v=0;v<h;v++) { | |
for (u=0;u<w;u++) { | |
constrains.push([ | |
particles[index(u, v)], | |
particles[index(u, v+1)], | |
restDistance | |
]); | |
constrains.push([ | |
particles[index(u, v)], | |
particles[index(u+1, v)], | |
restDistance | |
]); | |
} | |
} | |
for (u=w, v=0;v<h;v++) { | |
constrains.push([ | |
particles[index(u, v)], | |
particles[index(u, v+1)], | |
restDistance | |
]); | |
} | |
for (v=h, u=0;u<w;u++) { | |
constrains.push([ | |
particles[index(u, v)], | |
particles[index(u+1, v)], | |
restDistance | |
]); | |
} | |
// While many system uses shear and bend springs, | |
// the relax constrains model seem to be just fine | |
// using structural springs. | |
// Shear | |
// var diagonalDist = Math.sqrt(restDistance * restDistance * 2); | |
// for (v=0;v<h;v++) { | |
// for (u=0;u<w;u++) { | |
// constrains.push([ | |
// particles[index(u, v)], | |
// particles[index(u+1, v+1)], | |
// diagonalDist | |
// ]); | |
// constrains.push([ | |
// particles[index(u+1, v)], | |
// particles[index(u, v+1)], | |
// diagonalDist | |
// ]); | |
// } | |
// } | |
this.particles = particles; | |
this.constrains = constrains; | |
function index(u, v) { | |
return u + v * (w + 1); | |
} | |
this.index = index; | |
} | |
function simulate(time) { | |
if (!lastTime) { | |
lastTime = time; | |
return; | |
} | |
var i, il, particles, particle, pt, constrains, constrain; | |
// Aerodynamics forces | |
if (wind) { | |
var face, faces = clothGeometry.faces, normal; | |
particles = cloth.particles; | |
for (i=0,il=faces.length;i<il;i++) { | |
face = faces[i]; | |
normal = face.normal; | |
tmpForce.copy(normal).normalize().multiplyScalar(normal.dot(windForce)); | |
particles[face.a].addForce(tmpForce); | |
particles[face.b].addForce(tmpForce); | |
particles[face.c].addForce(tmpForce); | |
} | |
} | |
for (particles = cloth.particles, i=0, il = particles.length | |
;i<il;i++) { | |
particle = particles[i]; | |
particle.addForce(gravity); | |
particle.integrate(TIMESTEP_SQ); | |
} | |
// Start Constrains | |
constrains = cloth.constrains, | |
il = constrains.length; | |
for (i=0;i<il;i++) { | |
constrain = constrains[i]; | |
satisifyConstrains(constrain[0], constrain[1], constrain[2]); | |
} | |
// Ball Constrains | |
ballPosition.z = -Math.sin(Date.now()/600) * 90 ; //+ 40; | |
ballPosition.x = Math.cos(Date.now()/400) * 70 | |
if (sphere.visible) | |
for (particles = cloth.particles, i=0, il = particles.length | |
;i<il;i++) { | |
particle = particles[i]; | |
pos = particle.position; | |
diff.subVectors(pos, ballPosition); | |
if (diff.length() < ballSize) { | |
// collided | |
diff.normalize().multiplyScalar(ballSize); | |
pos.copy(ballPosition).add(diff); | |
} | |
} | |
// Floor Constains | |
for (particles = cloth.particles, i=0, il = particles.length | |
;i<il;i++) { | |
particle = particles[i]; | |
pos = particle.position; | |
if (pos.y < -250) { | |
pos.y = -250; | |
} | |
} | |
// Pin Constrains | |
for (i=0, il=pins.length;i<il;i++) { | |
var xy = pins[i]; | |
var p = particles[xy]; | |
p.position.copy(p.original); | |
p.previous.copy(p.original); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment