123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- /*
- * 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);
- }
- }
|