|
@@ -58,8 +58,6 @@
|
|
|
|
|
|
const clothFunction = plane( restDistance * xSegs, restDistance * ySegs );
|
|
const clothFunction = plane( restDistance * xSegs, restDistance * ySegs );
|
|
|
|
|
|
- const cloth = new Cloth( xSegs, ySegs );
|
|
|
|
-
|
|
|
|
const GRAVITY = 981 * 1.4;
|
|
const GRAVITY = 981 * 1.4;
|
|
const gravity = new THREE.Vector3( 0, - GRAVITY, 0 ).multiplyScalar( MASS );
|
|
const gravity = new THREE.Vector3( 0, - GRAVITY, 0 ).multiplyScalar( MASS );
|
|
|
|
|
|
@@ -75,119 +73,116 @@
|
|
const ballSize = 60; //40
|
|
const ballSize = 60; //40
|
|
|
|
|
|
const tmpForce = new THREE.Vector3();
|
|
const tmpForce = new THREE.Vector3();
|
|
|
|
+ const diff = new THREE.Vector3();
|
|
|
|
|
|
|
|
+ class Particle {
|
|
|
|
|
|
- function plane( width, height ) {
|
|
|
|
|
|
+ constructor( x, y, z, mass ) {
|
|
|
|
|
|
- return function ( u, v, target ) {
|
|
|
|
|
|
+ this.position = new THREE.Vector3();
|
|
|
|
+ this.previous = new THREE.Vector3();
|
|
|
|
+ this.original = new THREE.Vector3();
|
|
|
|
+ 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();
|
|
|
|
|
|
- const x = ( u - 0.5 ) * width;
|
|
|
|
- const y = ( v + 0.5 ) * height;
|
|
|
|
- const z = 0;
|
|
|
|
|
|
+ // init
|
|
|
|
|
|
- target.set( x, y, z );
|
|
|
|
|
|
+ clothFunction( x, y, this.position ); // position
|
|
|
|
+ clothFunction( x, y, this.previous ); // previous
|
|
|
|
+ clothFunction( x, y, this.original );
|
|
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function Particle( x, y, z, mass ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- this.position = new THREE.Vector3();
|
|
|
|
- this.previous = new THREE.Vector3();
|
|
|
|
- this.original = new THREE.Vector3();
|
|
|
|
- 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
|
|
|
|
|
|
- // init
|
|
|
|
|
|
+ addForce( force ) {
|
|
|
|
|
|
- clothFunction( x, y, this.position ); // position
|
|
|
|
- clothFunction( x, y, this.previous ); // previous
|
|
|
|
- clothFunction( x, y, this.original );
|
|
|
|
|
|
+ this.a.add(
|
|
|
|
+ this.tmp2.copy( force ).multiplyScalar( this.invMass )
|
|
|
|
+ );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- // Force -> Acceleration
|
|
|
|
|
|
+ // Performs Verlet integration
|
|
|
|
|
|
- Particle.prototype.addForce = function ( force ) {
|
|
|
|
|
|
+ integrate( timesq ) {
|
|
|
|
|
|
- this.a.add(
|
|
|
|
- this.tmp2.copy( force ).multiplyScalar( this.invMass )
|
|
|
|
- );
|
|
|
|
|
|
+ const 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 );
|
|
|
|
|
|
- // Performs Verlet integration
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- Particle.prototype.integrate = function ( timesq ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- const newPos = this.tmp.subVectors( this.position, this.previous );
|
|
|
|
- newPos.multiplyScalar( DRAG ).add( this.position );
|
|
|
|
- newPos.add( this.a.multiplyScalar( timesq ) );
|
|
|
|
|
|
+ class Cloth {
|
|
|
|
|
|
- this.tmp = this.previous;
|
|
|
|
- this.previous = this.position;
|
|
|
|
- this.position = newPos;
|
|
|
|
|
|
+ constructor( w = 10, h = 10 ) {
|
|
|
|
|
|
- this.a.set( 0, 0, 0 );
|
|
|
|
|
|
+ this.w = w;
|
|
|
|
+ this.h = h;
|
|
|
|
|
|
- };
|
|
|
|
|
|
+ const particles = [];
|
|
|
|
+ const constraints = [];
|
|
|
|
|
|
|
|
+ // Create particles
|
|
|
|
|
|
- const diff = new THREE.Vector3();
|
|
|
|
|
|
+ for ( let v = 0; v <= h; v ++ ) {
|
|
|
|
|
|
- function satisfyConstraints( p1, p2, distance ) {
|
|
|
|
|
|
+ for ( let u = 0; u <= w; u ++ ) {
|
|
|
|
|
|
- diff.subVectors( p2.position, p1.position );
|
|
|
|
- const currentDist = diff.length();
|
|
|
|
- if ( currentDist === 0 ) return; // prevents division by 0
|
|
|
|
- const correction = diff.multiplyScalar( 1 - distance / currentDist );
|
|
|
|
- const correctionHalf = correction.multiplyScalar( 0.5 );
|
|
|
|
- p1.position.add( correctionHalf );
|
|
|
|
- p2.position.sub( correctionHalf );
|
|
|
|
|
|
+ particles.push(
|
|
|
|
+ new Particle( u / w, v / h, 0, MASS )
|
|
|
|
+ );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- function Cloth( w, h ) {
|
|
|
|
|
|
+ // Structural
|
|
|
|
|
|
- w = w || 10;
|
|
|
|
- h = h || 10;
|
|
|
|
- this.w = w;
|
|
|
|
- this.h = h;
|
|
|
|
|
|
+ for ( let v = 0; v < h; v ++ ) {
|
|
|
|
|
|
- const particles = [];
|
|
|
|
- const constraints = [];
|
|
|
|
|
|
+ for ( let u = 0; u < w; u ++ ) {
|
|
|
|
|
|
- // Create particles
|
|
|
|
- for ( let v = 0; v <= h; v ++ ) {
|
|
|
|
|
|
+ constraints.push( [
|
|
|
|
+ particles[ index( u, v ) ],
|
|
|
|
+ particles[ index( u, v + 1 ) ],
|
|
|
|
+ restDistance
|
|
|
|
+ ] );
|
|
|
|
|
|
- for ( let u = 0; u <= w; u ++ ) {
|
|
|
|
|
|
+ constraints.push( [
|
|
|
|
+ particles[ index( u, v ) ],
|
|
|
|
+ particles[ index( u + 1, v ) ],
|
|
|
|
+ restDistance
|
|
|
|
+ ] );
|
|
|
|
|
|
- particles.push(
|
|
|
|
- new Particle( u / w, v / h, 0, MASS )
|
|
|
|
- );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // Structural
|
|
|
|
-
|
|
|
|
- for ( let v = 0; v < h; v ++ ) {
|
|
|
|
-
|
|
|
|
- for ( let u = 0; u < w; u ++ ) {
|
|
|
|
|
|
+ for ( let u = w, v = 0; v < h; v ++ ) {
|
|
|
|
|
|
constraints.push( [
|
|
constraints.push( [
|
|
particles[ index( u, v ) ],
|
|
particles[ index( u, v ) ],
|
|
particles[ index( u, v + 1 ) ],
|
|
particles[ index( u, v + 1 ) ],
|
|
restDistance
|
|
restDistance
|
|
|
|
+
|
|
] );
|
|
] );
|
|
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for ( let v = h, u = 0; u < w; u ++ ) {
|
|
|
|
+
|
|
constraints.push( [
|
|
constraints.push( [
|
|
particles[ index( u, v ) ],
|
|
particles[ index( u, v ) ],
|
|
particles[ index( u + 1, v ) ],
|
|
particles[ index( u + 1, v ) ],
|
|
@@ -196,66 +191,71 @@
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
- for ( let u = w, v = 0; v < h; v ++ ) {
|
|
|
|
|
|
+ // While many systems use shear and bend springs,
|
|
|
|
+ // the relaxed constraints model seems to be just fine
|
|
|
|
+ // using structural springs.
|
|
|
|
+ // Shear
|
|
|
|
+ // const diagonalDist = Math.sqrt(restDistance * restDistance * 2);
|
|
|
|
|
|
- constraints.push( [
|
|
|
|
- particles[ index( u, v ) ],
|
|
|
|
- particles[ index( u, v + 1 ) ],
|
|
|
|
- restDistance
|
|
|
|
|
|
|
|
- ] );
|
|
|
|
|
|
+ // for (v=0;v<h;v++) {
|
|
|
|
+ // for (u=0;u<w;u++) {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ // constraints.push([
|
|
|
|
+ // particles[index(u, v)],
|
|
|
|
+ // particles[index(u+1, v+1)],
|
|
|
|
+ // diagonalDist
|
|
|
|
+ // ]);
|
|
|
|
|
|
- for ( let v = h, u = 0; u < w; u ++ ) {
|
|
|
|
|
|
+ // constraints.push([
|
|
|
|
+ // particles[index(u+1, v)],
|
|
|
|
+ // particles[index(u, v+1)],
|
|
|
|
+ // diagonalDist
|
|
|
|
+ // ]);
|
|
|
|
|
|
- constraints.push( [
|
|
|
|
- particles[ index( u, v ) ],
|
|
|
|
- particles[ index( u + 1, v ) ],
|
|
|
|
- restDistance
|
|
|
|
- ] );
|
|
|
|
|
|
+ // }
|
|
|
|
+ // }
|
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
+ this.particles = particles;
|
|
|
|
+ this.constraints = constraints;
|
|
|
|
|
|
- // While many systems use shear and bend springs,
|
|
|
|
- // the relaxed constraints model seems to be just fine
|
|
|
|
- // using structural springs.
|
|
|
|
- // Shear
|
|
|
|
- // const diagonalDist = Math.sqrt(restDistance * restDistance * 2);
|
|
|
|
|
|
+ function index( u, v ) {
|
|
|
|
|
|
|
|
+ return u + v * ( w + 1 );
|
|
|
|
|
|
- // for (v=0;v<h;v++) {
|
|
|
|
- // for (u=0;u<w;u++) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- // constraints.push([
|
|
|
|
- // particles[index(u, v)],
|
|
|
|
- // particles[index(u+1, v+1)],
|
|
|
|
- // diagonalDist
|
|
|
|
- // ]);
|
|
|
|
|
|
+ this.index = index;
|
|
|
|
|
|
- // constraints.push([
|
|
|
|
- // particles[index(u+1, v)],
|
|
|
|
- // particles[index(u, v+1)],
|
|
|
|
- // diagonalDist
|
|
|
|
- // ]);
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- // }
|
|
|
|
- // }
|
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ function plane( width, height ) {
|
|
|
|
|
|
- this.particles = particles;
|
|
|
|
- this.constraints = constraints;
|
|
|
|
|
|
+ return function ( u, v, target ) {
|
|
|
|
|
|
- function index( u, v ) {
|
|
|
|
|
|
+ const x = ( u - 0.5 ) * width;
|
|
|
|
+ const y = ( v + 0.5 ) * height;
|
|
|
|
+ const z = 0;
|
|
|
|
|
|
- return u + v * ( w + 1 );
|
|
|
|
|
|
+ target.set( x, y, z );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
|
|
- this.index = index;
|
|
|
|
|
|
+ function satisfyConstraints( p1, p2, distance ) {
|
|
|
|
+
|
|
|
|
+ diff.subVectors( p2.position, p1.position );
|
|
|
|
+ const currentDist = diff.length();
|
|
|
|
+ if ( currentDist === 0 ) return; // prevents division by 0
|
|
|
|
+ const correction = diff.multiplyScalar( 1 - distance / currentDist );
|
|
|
|
+ const correctionHalf = correction.multiplyScalar( 0.5 );
|
|
|
|
+ p1.position.add( correctionHalf );
|
|
|
|
+ p2.position.sub( correctionHalf );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
@@ -375,6 +375,8 @@
|
|
|
|
|
|
/* testing cloth simulation */
|
|
/* testing cloth simulation */
|
|
|
|
|
|
|
|
+ const cloth = new Cloth( xSegs, ySegs );
|
|
|
|
+
|
|
const pinsFormation = [];
|
|
const pinsFormation = [];
|
|
pins = [ 6 ];
|
|
pins = [ 6 ];
|
|
|
|
|
|
@@ -580,17 +582,6 @@
|
|
gui.add( params, 'enableWind' ).name( 'Enable wind' );
|
|
gui.add( params, 'enableWind' ).name( 'Enable wind' );
|
|
gui.add( params, 'showBall' ).name( 'Show ball' );
|
|
gui.add( params, 'showBall' ).name( 'Show ball' );
|
|
gui.add( params, 'togglePins' ).name( 'Toggle pins' );
|
|
gui.add( params, 'togglePins' ).name( 'Toggle pins' );
|
|
- //
|
|
|
|
-
|
|
|
|
- if ( typeof TESTING !== 'undefined' ) {
|
|
|
|
-
|
|
|
|
- for ( let i = 0; i < 50; i ++ ) {
|
|
|
|
-
|
|
|
|
- simulate( 500 - 10 * i );
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|