UNPKG

cannon

Version:

A lightweight 3D physics engine written in JavaScript.

336 lines (299 loc) 12.7 kB
<!DOCTYPE html> <html> <head> <title>cannon.js - constraints demo</title> <meta charset="utf-8"> <link rel="stylesheet" href="css/style.css" type="text/css"/> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> </head> <body> <script src="../build/cannon.js"></script> <script src="../build/cannon.demo.js"></script> <script src="../libs/dat.gui.js"></script> <script src="../libs/Three.js"></script> <script src="../libs/TrackballControls.js"></script> <script src="../libs/Detector.js"></script> <script src="../libs/Stats.js"></script> <script src="../libs/smoothie.js"></script> <script> /** * In this demo, we demonstrate some constraint types. Constraints * removes degrees of freedom from bodies and forces them to move in * a way defined by the constraint. */ var demo = new CANNON.Demo(); demo.addScene("lock",function(){ var world = setupWorld(demo); world.solver.iterations = 50; world.gravity.set(0,0,-10); var size = 0.5; var boxShape = new CANNON.Box(new CANNON.Vec3(size,size,size)); var mass = 1; var space = 0.1*size; var N=10, last; for(var i=0; i<N; i++){ // Create a box var boxbody = new CANNON.Body({ mass: mass, shape: boxShape, position: new CANNON.Vec3((N-i-N/2)*(size*2+2*space), 0, size*6+space) }); world.add(boxbody); demo.addVisual(boxbody); if(last){ // Connect the current body to the last one var c = new CANNON.LockConstraint(boxbody, last); world.addConstraint(c); } // To keep track of which body was added last last = boxbody; } // Create stands var bodyA = new CANNON.Body({ mass: 0, shape: boxShape, position: new CANNON.Vec3((-N/2+1)*(size*2+2*space), 0, size*3) }); world.add(bodyA); demo.addVisual(bodyA); var bodyB = new CANNON.Body({ mass: 0, shape: boxShape, position: new CANNON.Vec3((N/2)*(size*2+2*space), 0, size*3) }); world.add(bodyB); demo.addVisual(bodyB); }); // We link together boxes in a chain demo.addScene("links",function(){ var world = setupWorld(demo); world.gravity.set(0,-1,-20); var size = 1; var boxShape = new CANNON.Box(new CANNON.Vec3(size,size*0.1,size)); var mass = 0; var space = 0.1*size; var N=10, last; for(var i=0; i<N; i++){ // Create a box var boxbody = new CANNON.Body({ mass: mass }); boxbody.addShape(boxShape); boxbody.position.set(0,0,(N-i)*(size*2+2*space) + size*2+space); boxbody.linearDamping=0.01; // Damping makes the movement slow down with time boxbody.angularDamping=0.01; world.add(boxbody); demo.addVisual(boxbody); if(i!=0){ // Connect the current body to the last one // We connect two corner points to each other. var c1 = new CANNON.PointToPointConstraint(boxbody,new CANNON.Vec3(-size,0,size+space),last,new CANNON.Vec3(-size,0,-size-space)); var c2 = new CANNON.PointToPointConstraint(boxbody,new CANNON.Vec3(size,0,size+space),last,new CANNON.Vec3(size,0,-size-space)); world.addConstraint(c1); world.addConstraint(c2); } else { // First body is now static. The rest should be dynamic. mass=0.3; } // To keep track of which body was added last last = boxbody; } }); // Particle cloth on sphere demo.addScene("cloth on sphere",function(){ var world = setupWorld(demo); world.solver.iterations = 10; // To construct the cloth we need Nrows*Ncols particles. var dist = 0.2; var mass = 0.5; var Nrows = 15, Ncols = 15; var bodies = {}; // bodies["i j"] => particle for(var i=0; i<Ncols; i++){ for(var j=0; j<Nrows; j++){ // Create a new body var body = new CANNON.Body({ mass: mass }); body.addShape(new CANNON.Particle()); body.position.set((i-Ncols*0.5)*dist,(j-Nrows*0.5)*dist,5); bodies[i+" "+j] = body; world.add(body); demo.addVisual(body); } } // To connect two particles, we use a distance constraint. This forces the particles to be at a constant distance from each other. function connect(i1,j1,i2,j2){ world.addConstraint(new CANNON.DistanceConstraint(bodies[i1+" "+j1],bodies[i2+" "+j2],dist)); } for(var i=0; i<Ncols; i++){ for(var j=0; j<Nrows; j++){ // Connect particle at position (i,j) to (i+1,j) and to (i,j+1). if(i<Ncols-1) connect(i,j,i+1,j); if(j<Nrows-1) connect(i,j,i,j+1); } } // Add the static sphere we throw the cloth on top of var sphere = new CANNON.Sphere(1.5); var body = new CANNON.Body({ mass: 0 }); body.addShape(sphere); body.position.set(0,0,3.5); world.add(body); demo.addVisual(body); }); // A pendulum made out of two spheres using a PointToPointConstraint demo.addScene("Sphere pendulum",function(){ var world = setupWorld(demo); var size = 1; var sphereShape = new CANNON.Sphere(size); var mass = 1; var spherebody = new CANNON.Body({ mass: mass }); spherebody.addShape(sphereShape); spherebody.position.set(0,0,size*3); spherebody.velocity.set(5,0,0); spherebody.linearDamping=0; spherebody.angularDamping=0; world.add(spherebody); demo.addVisual(spherebody); var spherebody2 = new CANNON.Body({ mass: 0 }); spherebody2.addShape(sphereShape); spherebody2.position.set(0,0,size*7); world.add(spherebody2); demo.addVisual(spherebody2); // Connect this body to the last one var c = new CANNON.PointToPointConstraint(spherebody,new CANNON.Vec3(0,0,size*2),spherebody2,new CANNON.Vec3(0,0,-size*2)); world.addConstraint(c); }); // Sphere chain demo.addScene("Sphere chain",function(){ var size = 0.5; var dist = size*2+0.12; var world = setupWorld(demo); //world.solver.setSpookParams(1e20,3); var sphereShape = new CANNON.Sphere(size); var mass = 1; var lastBody = null; var N = 20; world.solver.iterations = N; // To be able to propagate force throw the chain of N spheres, we need at least N solver iterations. for(var i=0; i<N; i++){ // Create a new body var spherebody = new CANNON.Body({ mass: i===0 ? 0 : mass }); spherebody.addShape(sphereShape); spherebody.position.set(0,0,(N-i)*dist); spherebody.velocity.x = i; world.add(spherebody); demo.addVisual(spherebody); // Connect this body to the last one added var c; if(lastBody!==null){ world.addConstraint(c = new CANNON.DistanceConstraint(spherebody,lastBody,dist)); } // Keep track of the lastly added body lastBody = spherebody; } }); // Particle cloth. Same as the previous cloth but here we make the first row of particles static, nailing the cloth it in space demo.addScene("Particle cloth",function(){ var world = setupWorld(demo); //world.solver.setSpookParams(1e20,3); world.solver.iterations = 18; var dist = 0.2; var mass = 0.5; var Nrows = 15, Ncols = 15; var bodies = {}; // bodies["i j"] => particle for(var i=0; i<Ncols; i++){ for(var j=0; j<Nrows; j++){ // Create a new body var body = new CANNON.Body({ mass: j==Nrows-1 ? 0 : mass }); body.addShape(new CANNON.Particle()); body.position.set(i*dist,0,j*dist+5); body.velocity.set(0, 3*(Math.sin(i*0.1)+Math.sin(j*0.1)),0); bodies[i+" "+j] = body; world.add(body); demo.addVisual(body); } } function connect(i1,j1,i2,j2){ world.addConstraint(new CANNON.DistanceConstraint(bodies[i1+" "+j1],bodies[i2+" "+j2],dist)); } for(var i=0; i<Ncols; i++){ for(var j=0; j<Nrows; j++){ if(i<Ncols-1) connect(i,j,i+1,j); if(j<Nrows-1) connect(i,j,i,j+1); } } }); // Particle 3d object // Distance constraints can be used to construct even cooler things, like this 3d block. demo.addScene("3D cloth structure",function(){ var world = setupWorld(demo); world.solver.iterations = 10; var dist = 1; var mass = 1; var Nx = 6, Ny = 3, Nz = 3; var bodies = {}; // bodies["i j k"] => particle for(var i=0; i<Nx; i++){ for(var j=0; j<Ny; j++){ for(var k=0; k<Nz; k++){ // Create a new body var body = new CANNON.Body({ mass: mass }); body.addShape(new CANNON.Particle()); body.position.set(i*dist,j*dist, k*dist + Nz*dist*0.3+1); body.velocity.set(0, 30*(Math.sin(i*0.1)+Math.sin(j*0.1)),0); bodies[i+" "+j+" "+k] = body; world.add(body); demo.addVisual(body); } } } function connect(i1,j1,k1,i2,j2,k2,len){ world.addConstraint(new CANNON.DistanceConstraint(bodies[i1+" "+j1+" "+k1],bodies[i2+" "+j2+" "+k2],len)); } for(var i=0; i<Nx; i++){ for(var j=0; j<Ny; j++){ for(var k=0; k<Nz; k++){ // normal directions if(i<Nx-1) connect(i,j,k, i+1,j,k, dist); if(j<Ny-1) connect(i,j,k, i,j+1,k, dist); if(k<Nz-1) connect(i,j,k, i,j,k+1, dist); // Diagonals if(i<Nx-1 && j<Ny-1 && k<Nz-1){ // 3d diagonals connect(i,j,k, i+1,j+1,k+1, Math.sqrt(3)*dist); connect(i+1,j,k, i,j+1,k+1, Math.sqrt(3)*dist); connect(i,j+1,k, i+1,j,k+1, Math.sqrt(3)*dist); connect(i,j,k+1, i+1,j+1,k, Math.sqrt(3)*dist); } // 2d diagonals if(i<Nx-1 && j<Ny-1){ connect(i+1,j,k, i,j+1,k, Math.sqrt(2)*dist); connect(i,j+1,k, i+1,j,k, Math.sqrt(2)*dist); } if(i<Nx-1 && k<Nz-1){ connect(i+1,j,k, i,j,k+1, Math.sqrt(2)*dist); connect(i,j,k+1, i+1,j,k, Math.sqrt(2)*dist); } if(j<Ny-1 && k<Nz-1){ connect(i,j+1,k, i,j,k+1, Math.sqrt(2)*dist); connect(i,j,k+1, i,j+1,k, Math.sqrt(2)*dist); } } } } }); function setupWorld(demo){ // Create world var world = demo.getWorld(); world.gravity.set(0,0,-40); world.broadphase = new CANNON.NaiveBroadphase(); world.solver.iterations = 10; // ground plane var groundShape = new CANNON.Plane(); var groundBody = new CANNON.Body({ mass: 0 }); groundBody.addShape(groundShape); groundBody.position.set(0,0,1); world.add(groundBody); demo.addVisual(groundBody); world.quatNormalizeFast = false; world.quatNormalizeSkip = 0; return world; }; demo.start(); </script> </body> </html>