Cloth simulation Y segments stop animating

115 Views Asked by At

I'm trying to learn and understand how Three.js works. I've started by creating a cloth simulation but I have run into a glitch. It seems setting the cloth to a width above 25, makes the segments start to cluster.

I'm setting the width in Cloth.js file where var ySegs = 60;, in the console i'm receiving the warning "THREE.WebGLRenderer: image is not power of two (1800x2819). Resized to 2048x2048", are these problems related? It would be greatly appreciated if someone could help me understand what is causing this problem here.

// Cloth.js
var DAMPING = 0.03;
var DRAG = 1 - DAMPING;
var MASS = 0.1;
var restDistance = 25;

var xSegs = 12;
var ySegs = 25;

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 satisfyConstraints( 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 constraints = [];

 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 ++ ) {

   constraints.push( [
    particles[ index( u, v ) ],
    particles[ index( u, v + 1 ) ],
    restDistance
   ] );

   constraints.push( [
    particles[ index( u, v ) ],
    particles[ index( u + 1, v ) ],
    restDistance
   ] );

  }

 }

 for ( u = w, v = 0; v < h; v ++ ) {

  constraints.push( [
   particles[ index( u, v ) ],
   particles[ index( u, v + 1 ) ],
   restDistance

  ] );

 }

 for ( v = h, u = 0; u < w; u ++ ) {

  constraints.push( [
   particles[ index( u, v ) ],
   particles[ index( u + 1, v ) ],
   restDistance
  ] );

}


 this.particles = particles;
 this.constraints = constraints;

 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, constraints, constraint;

 // 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 Constraints

 constraints = cloth.constraints;
 il = constraints.length;

 for ( i = 0; i < il; i ++ ) {

  constraint = constraints[ i ];
  satisfyConstraints( constraint[ 0 ], constraint[ 1 ], constraint[ 2 ] );

 }

 // Ball Constraints

 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 ];
   var pos = particle.position;
   diff.subVectors( pos, ballPosition );
   if ( diff.length() < ballSize ) {

    // collided
    diff.normalize().multiplyScalar( ballSize );
    pos.copy( ballPosition ).add( diff );

   }

  }

 }


 // 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;

  }

 }

 // Pin Constraints

 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 );

 }
}

   /* Testing */
   var pinsFormation = [];
   var pins = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
   pinsFormation.push( pins );
   pins = pinsFormation[ 0 ];
   function togglePins() {
    pins = pinsFormation[ ~~ ( Math.random() * pinsFormation.length ) ];
   }

            var colours = [
                {bg: 'rgb(56, 255, 211)', flag:'assets/images/rectangle_3.png'},
                {bg: 'rgb(255, 190, 73)', flag:'assets/images/rectangle_1.png'},
                {bg: 'rgb(227, 195, 255)', flag:'assets/images/rectangle_2.png'}
            ];
            var colourChoice = colours[ ~~ ( Math.random() * colours.length ) ];

   if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
   var container, stats;
   var camera, scene, renderer;
   var clothGeometry;
   var sphere;
   var object;
   init();
   animate();
   function init() {
    container = document.createElement('div');
                container.setAttribute("id", "canvas-cnt");
    document.body.appendChild( container );
    // scene
    scene = new THREE.Scene();
    scene.background = new THREE.Color(colourChoice.bg);
    
    // camera
    camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
    camera.position.x = 0;
    camera.position.y = -300;
    camera.position.z = 1800;
    scene.add(camera);

    // lights
    var light, materials;
    scene.add( new THREE.AmbientLight( 0x666666 ) );
    light = new THREE.DirectionalLight( 0xdfebff, 1.75 );
    light.position.set( 50, 200, 100 );
    light.position.multiplyScalar( 1.3 );
    light.castShadow = true;
    light.shadow.mapSize.width = 1024;
    light.shadow.mapSize.height = 1024;
    var d = 300;
    light.shadow.camera.left = - d;
    light.shadow.camera.right = d;
    light.shadow.camera.top = d;
    light.shadow.camera.bottom = - d;
    light.shadow.camera.far = 1000;
    scene.add( light );

    // cloth material
    var loader = new THREE.TextureLoader();
    var clothTexture = loader.load(colourChoice.flag);
    clothTexture.wrapS = clothTexture.wrapT = THREE.RepeatWrapping;
    clothTexture.anisotropy = 16;
    var clothMaterial = new THREE.MeshPhongMaterial( {
     specular: 0x030303,
     map: clothTexture,
     side: THREE.DoubleSide,
     alphaTest: 0.5
    } );
    // cloth geometry
    clothGeometry = new THREE.ParametricGeometry( clothFunction, cloth.w, cloth.h );
    clothGeometry.dynamic = true;
                console.log(cloth.w, cloth.h);
    var uniforms = { texture:  { value: clothTexture } };
    var vertexShader = document.getElementById( 'vertexShaderDepth' ).textContent;
    var fragmentShader = document.getElementById( 'fragmentShaderDepth' ).textContent;
    // cloth mesh
    object = new THREE.Mesh( clothGeometry, clothMaterial );
    object.position.set( 0, 130, 0 );

                object.rotateZ( Math.PI / 2 );
    object.castShadow = true;
    scene.add( object );
    object.customDepthMaterial = new THREE.ShaderMaterial( {
     uniforms: uniforms,
     vertexShader: vertexShader,
     fragmentShader: fragmentShader,
     side: THREE.DoubleSide
    } );
    // sphere
    var ballGeo = new THREE.SphereGeometry( ballSize, 20, 20 );
    var ballMaterial = new THREE.MeshPhongMaterial( { color: 0xaaaaaa } );
    sphere = new THREE.Mesh( ballGeo, ballMaterial );
    sphere.castShadow = true;
    sphere.receiveShadow = true;
    scene.add( sphere );

    // renderer
    renderer = new THREE.WebGLRenderer( { antialias: true } );
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.shadowMap.renderSingleSided = false;
    container.appendChild( renderer.domElement );
    renderer.gammaInput = true;
    renderer.gammaOutput = true;
    renderer.shadowMap.enabled = true;
    // controls
    var controls = new THREE.OrbitControls( camera, renderer.domElement );
    controls.maxPolarAngle = Math.PI * 0.5;
    controls.minDistance = 1000;
    controls.maxDistance = 7500;
    // performance monitor
    stats = new Stats();
    container.appendChild( stats.dom );
    //
    window.addEventListener( 'resize', onWindowResize, false );
    sphere.visible = ! true;
   }
   //
   function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize( window.innerWidth, window.innerHeight );
   }
   //
   function animate() {
    requestAnimationFrame( animate );
    var time = Date.now();
    var windStrength = 40;
    
    windForce.set( 1000, 1000, 4200 )
    windForce.normalize()
    windForce.multiplyScalar( windStrength );
    simulate( time );
    render();
    stats.update();
   }
   function render() {
    var p = cloth.particles;
    for ( var i = 0, il = p.length; i < il; i ++ ) {
     clothGeometry.vertices[ i ].copy( p[ i ].position );
    }
    clothGeometry.computeFaceNormals();
    clothGeometry.computeVertexNormals();
    clothGeometry.normalsNeedUpdate = true;
    clothGeometry.verticesNeedUpdate = true;
    
    camera.lookAt( scene.position );
    renderer.render( scene, camera );
   }
<script type="x-shader/x-fragment" id="fragmentShaderDepth">
    #include <packing>
    uniform sampler2D texture;
    varying vec2 vUV;
    void main() {
        vec4 pixel = texture2D( texture, vUV );
        if ( pixel.a < 0.5 ) discard;
        gl_FragData[ 0 ] = packDepthToRGBA( gl_FragCoord.z );
    }
</script>

<script type="x-shader/x-vertex" id="vertexShaderDepth">
    varying vec2 vUV;
    void main() {
        vUV = 0.75 * uv;
        vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
        gl_Position = projectionMatrix * mvPosition;
    }
</script>

0

There are 0 best solutions below