UNPKG

cannon

Version:

A lightweight 3D physics engine written in JavaScript.

309 lines (237 loc) 10.7 kB
<!DOCTYPE html> <html lang="en"> <head> <title>cannon.js cloth simulation</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> <style> body { font-family: Monospace; background-color: #000; color: #000; margin: 0px; overflow: hidden; } #info { text-align: center; padding: 10px; z-index: 10; width: 100%; position: absolute; } a { text-decoration: underline; cursor: pointer; } </style> </head> <body> <script src="../libs/Three.js"></script> <script src="../libs/TrackballControls.js"></script> <script src="../libs/Detector.js"></script> <script src="../build/cannon.js"></script> <script> var dt = 1/60, R = 0.2; var clothMass = 1; // 1 kg in total var clothSize = 1; // 1 meter var Nx = 12; var Ny = 12; var mass = clothMass / Nx*Ny; var restDistance = clothSize/Nx; var ballSize = 0.1; var clothFunction = plane(restDistance * Nx, restDistance * Ny); 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); }; } if ( ! Detector.webgl ) Detector.addGetWebGLMessage(); var container, stats; var camera, scene, renderer; var clothGeometry; var sphereMesh, sphereBody; var object; var particles = []; var world; initCannon(); init(); animate(); function initCannon(){ world = new CANNON.World(); world.broadphase = new CANNON.NaiveBroadphase(); world.gravity.set(0,-9.82,0); world.solver.iterations = 20; // Materials var clothMaterial = new CANNON.Material(); var sphereMaterial = new CANNON.Material(); var clothSphereContactMaterial = new CANNON.ContactMaterial( clothMaterial, sphereMaterial, 0.0, // friction coefficient 0.0 // restitution ); // Adjust constraint equation parameters for ground/ground contact clothSphereContactMaterial.contactEquationStiffness = 1e9; clothSphereContactMaterial.contactEquationRelaxation = 3; // Add contact material to the world world.addContactMaterial(clothSphereContactMaterial); // Create sphere var sphereShape = new CANNON.Sphere(ballSize*1.3); sphereBody = new CANNON.Body({ mass: 0 }); sphereBody.addShape(sphereShape); sphereBody.position.set(0,0,0); world.add(sphereBody); // Create cannon particles for ( var i = 0, il = Nx+1; i !== il; i++ ) { particles.push([]); for ( var j = 0, jl = Ny+1; j !== jl; j++ ) { var idx = j*(Nx+1) + i; var p = clothFunction(i/(Nx+1), j/(Ny+1)); var particle = new CANNON.Body({ mass: j==Ny ? 0 : mass }); particle.addShape(new CANNON.Particle()); particle.linearDamping = 0.5; particle.position.set( p.x, p.y-Ny * 0.9 * restDistance, p.z ); particles[i].push(particle); world.add(particle); particle.velocity.set(0,0,-0.1*(Ny-j)); } } function connect(i1,j1,i2,j2){ world.addConstraint( new CANNON.DistanceConstraint(particles[i1][j1],particles[i2][j2],restDistance) ); } for(var i=0; i<Nx+1; i++){ for(var j=0; j<Ny+1; j++){ if(i<Nx) connect(i,j,i+1,j); if(j<Ny) connect(i,j,i,j+1); } } } function init() { container = document.createElement( 'div' ); document.body.appendChild( container ); // scene scene = new THREE.Scene(); scene.fog = new THREE.Fog( 0x000000, 500, 10000 ); // camera camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 0.5, 10000 ); camera.position.set(Math.cos( Math.PI/4 ) * 3, 0, Math.sin( Math.PI/4 ) * 3); scene.add( camera ); // Controls controls = new THREE.TrackballControls( camera ); controls.rotateSpeed = 1.0; controls.zoomSpeed = 1.2; controls.panSpeed = 0.8; controls.noZoom = false; controls.noPan = false; controls.staticMoving = true; controls.dynamicDampingFactor = 0.3; controls.keys = [ 65, 83, 68 ]; // lights var light, materials; scene.add( new THREE.AmbientLight( 0x666666 ) ); light = new THREE.DirectionalLight( 0xffffff, 1.75 ); var d = 5; light.position.set( d, d, d ); light.castShadow = true; //light.shadowCameraVisible = true; light.shadowMapWidth = 1024*2; light.shadowMapHeight = 1024*2; light.shadowCameraLeft = -d; light.shadowCameraRight = d; light.shadowCameraTop = d; light.shadowCameraBottom = -d; light.shadowCameraFar = 3*d; light.shadowCameraNear = d; light.shadowDarkness = 0.5; scene.add( light ); /* light = new THREE.DirectionalLight( 0xffffff, 0.35 ); light.position.set( 0, -1, 0 ); scene.add( light ); */ // cloth material var clothTexture = THREE.ImageUtils.loadTexture( 'sunflower.jpg' ); // circuit_pattern.png clothTexture.wrapS = clothTexture.wrapT = THREE.RepeatWrapping; clothTexture.anisotropy = 16; var clothMaterial = new THREE.MeshPhongMaterial( { alphaTest: 0.5, ambient: 0x000000, color: 0xffffff, specular: 0x333333, emissive: 0x222222, //shininess: 5, map: clothTexture, side: THREE.DoubleSide } ); // cloth geometry clothGeometry = new THREE.ParametricGeometry( clothFunction, Nx, Ny, true ); clothGeometry.dynamic = true; clothGeometry.computeFaceNormals(); // cloth mesh object = new THREE.Mesh(clothGeometry, clothMaterial); object.position.set(0, 0, 0); object.castShadow = true; //object.receiveShadow = true; scene.add( object ); // sphere var ballGeo = new THREE.SphereGeometry( ballSize, 20, 20 ); var ballMaterial = new THREE.MeshPhongMaterial( { color: 0x888888 } ); sphereMesh = new THREE.Mesh( ballGeo, ballMaterial ); sphereMesh.castShadow = true; //sphereMesh.receiveShadow = true; scene.add( sphereMesh ); renderer = new THREE.WebGLRenderer( { antialias: true } ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setClearColor( scene.fog.color ); container.appendChild( renderer.domElement ); renderer.gammaInput = true; renderer.gammaOutput = true; renderer.physicallyBasedShading = true; renderer.shadowMapEnabled = true; window.addEventListener( 'resize', onWindowResize, false ); camera.lookAt( sphereMesh.position ); } // function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); controls.handleResize(); renderer.setSize( window.innerWidth, window.innerHeight ); } function animate() { requestAnimationFrame( animate ); controls.update(); world.step(dt); var t = world.time; sphereBody.position.set(R * Math.sin(t), 0, R * Math.cos(t)); render(); } function render() { for ( var i = 0, il = Nx+1; i !== il; i++ ) { for ( var j = 0, jl = Ny+1; j !== jl; j++ ) { var idx = j*(Nx+1) + i; clothGeometry.vertices[idx].copy(particles[i][j].position); } } clothGeometry.computeFaceNormals(); clothGeometry.computeVertexNormals(); clothGeometry.normalsNeedUpdate = true; clothGeometry.verticesNeedUpdate = true; sphereMesh.position.copy(sphereBody.position); renderer.render( scene, camera ); } </script> </body> </html>