cannon
Version:
A lightweight 3D physics engine written in JavaScript.
336 lines (299 loc) • 12.7 kB
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>