was trying to build a flag based on the cloth example in the three.js documentation but I have trouble in defining the constraints for the flag.
Link to the example:
https://threejs.org/examples/#webgl_animation_cloth
Source code of the example: https://github.com/mrdoob/three.js/blob/e0a31ea77c0242ae29ef6e951fa588216e1d247f/examples/webgl_animation_cloth.html
I had already tried my best to comment every block of code since the code has ~300 lines.
In my interpretation, function SatisfyConstraints
makes a subtraction between two points in order to get the differentiation vector . Then it calculates the length and derive corrections from it.
The āsimulateā function at the end of the code uses SatisfyConstraints
to regulate how the flag should be flown.
var container, renderer, scene, camera, controls;
var near, far;
var params = {
enableWind: true,
showBall: false
}
var DAMPING = 0.03;
var MASS = 0.15;
var DRAG = 1 - DAMPING;
var DistanceUntilRest = 25;
var GRAVITY = 981 * 1.4;
var gravityVector = new THREE.Vector3( 0, - GRAVITY, 0 ).multiplyScalar( MASS ); // F = mg
var ForceVector = new THREE.Vector3();
var windForce = new THREE.Vector3( 0, 0, 0 );
var TIMESTEP = 18 / 1000;
var TIMESTEP_SQ = TIMESTEP * TIMESTEP;
var xSegs = 10, ySegs = 10;
var clothFunction = planeEquation( DistanceUntilRest * xSegs, DistanceUntilRest * ySegs );
var cloth = new Cloth(xSegs, ySegs);
function planeEquation (width, height) {
return function (a, b, target) {
var x = (a - 0.5) * width;
var y = (b + 0.5) * height;
var z = 0; // idk why z has to be 0
target.set( x, y, z);
}
}
function Particles (x, y, z, mass) {
this.position = new THREE.Vector3();
this.previous = new THREE.Vector3();
this.original = new THREE.Vector3();
this.acceleration = new THREE.Vector3( 0 , 0 , 0);
this.mass = mass;
this.invMass = 1 / mass;
this.FVector = new THREE.Vector3();
this.tmp = new THREE.Vector3();
// initializing clothFunction so planeEquation can set the target
clothFunction(x, y, this.position);
clothFunction(x, y, this.previous);
clothFunction(x, y, this.original);
Particles.prototype.addForce = function (force) {
this.acceleration.add( this.FVector.copy(force).multiplyScalar(this.invMass) )
};
// after each time sequence the new position adds itself by the amount of drag and acceleration
Particles.prototype.integrate = function (timeSequence) {
var newPos = this.tmp.subVectors(this.position, this.previous);
newPos.multiplyScalar(DRAG).add(this.position);
newPos.add( this.acceleration.multiplyScalar( timeSequence ) );
// passes tmp down to this.previous then assumes this.position as the new position
this.tmp = this.previous;
this.previous = this.position;
this.position = newPos;
// resetting acceleration
this.acceleration.set(0, 0, 0);
}
var diff = new THREE.Vector3();
function SatisfyConstraints (point, next_point, distance) {
diff.subVectors(next_point.position, point.position);
var currentDist = diff.length();
if (currentDist === 0) return;
var correction = diff.multiplyScalar(1 - distance / currentDist);
var correctionHalf = correction.multiplyScalar(0.5);
point.position.add( correctionHalf );
next_point.position.sub( correctionHalf );
}
function Cloth(width, height) {
this.width = width || 10;
this.height = height || 10;
var particles = [], constraints = [];
var i, j, u, v;
for (i = 0; i <= height; i++ ) {
for (j = 0; j <= width; j++) {
particles.push( new Particles( i / width, j / height, 0, MASS) )
}
}
// Providing structure for cloth - similar to drawing
for (i = 0; i <= height; i++ ) {
for (j = 0; j <= width; j++) {
constraints.push([
particles[index(i, j)],
particles[index(i, j + 1)],
DistanceUntilRest
]);
constraints.push([
particles[index(i, j)],
particles[index(i + 1, j)],
DistanceUntilRest
])
}
}
for ( u = width, v = 0; v < height; v ++ ) {
constraints.push( [
particles[ index( u, v ) ],
particles[ index( u, v + 1 ) ],
DistanceUntilRest
] );
}
for ( v = height, u = 0; u < width ; u ++ ) {
constraints.push( [
particles[ index( u, v ) ],
particles[ index( u + 1, v ) ],
DistanceUntilRest
] );
}
this.particles = particles;
this.constraints = constraints;
function index( u, v ) {
return u + v * ( width + 1 );
}
this.index = index;
}
function simulate( time ) {
if ( ! lastTime ) {
lastTime = time;
return;
}
var i, j, il, particles, particle, constraints, constraint;
// Aerodynamics forces
if ( params.enableWind ) {
var indx;
var normal = new THREE.Vector3();
var indices = clothGeometry.index;
var normals = clothGeometry.attributes.normal;
particles = cloth.particles;
for ( i = 0, il = indices.count; i < il; i += 3 ) {
for ( j = 0; j < 3; j ++ ) {
indx = indices.getX( i + j );
normal.fromBufferAttribute( normals, indx );
tmpForce.copy( normal ).normalize().multiplyScalar( normal.dot( windForce ) );
particles[ indx ].addForce( tmpForce );
}
}
}
for ( particles = cloth.particles, i = 0, il = particles.length; i < il; i ++ ) {
particle = particles[ i ];
particle.addForce( gravityVector );
particle.integrate( TIMESTEP_SQ );
}
// Start Constraints
constraints = cloth.constraints;
il = constraints.length;
for ( i = 0; i < il; i ++ ) {
constraint = constraints[ i ];
SatisfyConstraints( constraint[ 0 ], constraint[ 1 ], constraint[ 2 ] );
}
var pos;
// Ball Constraints
ballPosition.z = - Math.sin( Date.now() / 600 ) * 90; //+ 40;
ballPosition.x = Math.cos( Date.now() / 400 ) * 70;
if ( params.showBall ) {
sphere.visible = true;
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 );
}
}
} else {
sphere.visible = false;
}
// Floor Constraints
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;
}
}
}
I also attempted to use position.copy(point), position.copy(next_point)
in SatisfyConstraints)
to copy the coordinates and paste the passed parameters into a new variable but it doesnāt work.
function SatisfyConstraints (point, next_point, distance) {
var pointPos, next_pointPos;
pointPos.position.copy(point);
next_pointPos.position.copy(next_point);
diff.subVectors(pointPos, next_pointPos);
...
}
Iād expected SatisfyConstraints
to function as usual but instead I received the error message as follows:
Uncaught TypeError: Cannot read property āpositionā of undefined
at SatisfyConstraints (simulation3D.html:102)
at simulate (simulation3D.html:212)
at animate (simulation3D.html:419)