// Create some local variables for convenience
var state = false, // `true` when the simulation is running
viewport = document.getElementById( 'viewport' ), // The canvas element we're going to use
// If your computer supports it, switch to the WebGLRenderer for a much smoother experience
// most of the lag and jittering you see in the simulation is from the CanvasRenderer, not the physics
renderer = new THREE.CanvasRenderer({ canvas: viewport }), // Create the renderer
//renderer = new THREE.WebGLRenderer({ canvas: viewport }), // Create the renderer
scene = new THREE.Scene, // Create the scene
camera = new THREE.PerspectiveCamera( 35, 1, 1, 1000 ),
ball_geometry = new THREE.SphereGeometry( 3 ), // Create the ball geometry with a radius of `3`
ball_material = new THREE.MeshLambertMaterial({ color: 0x0000ff, overdraw: true }), // Balls will be blue
large_ball_geometry = new THREE.SphereGeometry( 4 ), // Create the ball geometry with a radius of `4`
large_ball_material = new THREE.MeshLambertMaterial({ color: 0x00ff00, overdraw: true }), // Large balls are be green
time_last_run, // used to calculate simulation delta
world, // This will hold the Cannon.js objects
shape, body,
balls = []; // Array of all balls added to the scene
renderer.setSize( viewport.clientWidth, viewport.clientHeight );
camera.position.set( -10, 30, -200 );
camera.lookAt( scene.position ); // Look at the center of the scene
scene.add( camera );
function addLights() {
var ambientLight = new THREE.AmbientLight( 0x555555 );
scene.add( ambientLight );
var directionalLight = new THREE.DirectionalLight( 0xffffff );
directionalLight.position.set( -.5, .5, -1.5 ).normalize();
scene.add( directionalLight );
}
function buildScene() {
// Create the physics world
world = new CANNON.World;
world.gravity.set( 0, -10, 0 );
world.broadphase = new CANNON.NaiveBroadphase();
world.solver.iterations = 10; // Use 10 iterations each time the simulation is run
var ramp_geometry = new THREE.CubeGeometry( 50, 2, 10 ),
material_red = new THREE.MeshLambertMaterial({ color: 0xdd0000, overdraw: true }),
material_green = new THREE.MeshLambertMaterial({ color: 0x00bb00, overdraw: true });
var ramp_1 = new THREE.Mesh( ramp_geometry, material_red );
scene.add( ramp_1 );
shape = new CANNON.Box(new CANNON.Vec3( 25, 1, 5 )); // "25" is half the ramp's width, "1" is half the height, and "5" is half the depth
body = new CANNON.RigidBody( 0, shape ); // a mass of "0" indicates this object is static
// position the ramp
ramp_1.position.set( -20, 25, 0 );
ramp_1.useQuaternion = true;
ramp_1.quaternion.setFromAxisAngle( new THREE.Vector3( 0, 0, 1 ), -Math.PI / 32 );
body.position.set( -20, 25, 0 );
body.quaternion.set( ramp_1.quaternion.x, ramp_1.quaternion.y, ramp_1.quaternion.z, ramp_1.quaternion.w );
world.add( body );
var ramp_2 = new THREE.Mesh( ramp_geometry, material_red );
scene.add( ramp_2 );
shape = new CANNON.Box(new CANNON.Vec3( 25, 1, 5 )); // "25" is half the ramp's width, "1" is half the height, and "5" is half the depth
body = new CANNON.RigidBody( 0, shape ); // a mass of "0" indicates this object is static
// position the ramp
ramp_2.position.set( 25, 5, 0 );
ramp_2.useQuaternion = true;
ramp_2.quaternion.setFromAxisAngle( new THREE.Vector3( 0, 0, 1 ), Math.PI / 16 );
body.position.set( 25, 5, 0 );
body.quaternion.set( ramp_2.quaternion.x, ramp_2.quaternion.y, ramp_2.quaternion.z, ramp_2.quaternion.w );
world.add( body );
// Create the floor
var floor = new THREE.Mesh( new THREE.PlaneGeometry( 100, 50 ), material_red );
scene.add( floor );
floor.position.y = -15; // position the floor
shape = new CANNON.Box(new CANNON.Vec3( 50, .01, 25 )); // "50" is half the floor's width, ".01" is a small number representing a plane, and "25" is half the depth
body = new CANNON.RigidBody( 0, shape ); // a mass of "0" indicates this object is static
body.position.set( 0, -15, 0 );
world.add( body );
}
function addBall() {
var ball, shape, mass, body;
if ( !state ) return;
if ( Math.random() >= .25 ) {
ball = new THREE.Mesh( ball_geometry, ball_material );
shape = new CANNON.Sphere( 3 ); // radius is "3"
mass = 5;
} else {
ball = new THREE.Mesh( large_ball_geometry, large_ball_material );
shape = new CANNON.Sphere( 4 ); // radius is "4"
mass = 10;
}
ball.useQuaternion = true;
scene.add( ball );
body = new CANNON.RigidBody( mass, shape );
body.mesh = ball; // Save a reference to the 3D mesh
ball.position.set(
Math.random() * 40 - 20, // Random positon between -20 and 20
50,
Math.random() * 2 - 1
);
body.position.set(
ball.position.x, ball.position.y, ball.position.z
);
world.add( body );
balls.push( body );
}
function updateWorld() {
requestAnimationFrame( updateWorld );
if ( !state ) return;
var delta, now = (new Date()).getTime(),
i;
if ( time_last_run ) {
delta = ( now - time_last_run ) / 1000;
} else {
delta = 1 / 60;
}
time_last_run = now;
world.step( delta * 2 ); // double the speed of the simulation
// Update the scene objects
for ( i = 0; i < balls.length; i++ ) {
balls[i].position.copy( balls[i].mesh.position ); // Copy balls[i].position into balls[i].mesh.position
balls[i].quaternion.copy( balls[i].mesh.quaternion ); // Copy balls[i].quaternion into balls[i].mesh.quaternion
}
renderer.render( scene, camera );
}
addLights();
buildScene();
document.getElementById( 'startStop' ).addEventListener('click',
function() {
if ( this.innerHTML === 'Start' ) {
this.innerHTML = 'Stop';
time_last_run = (new Date()).getTime();
state = true;
} else {
this.innerHTML = 'Start';
state = false;
}
}
)
updateWorld();
setInterval( addBall, 500 );
<button id="startStop">Start</button><br />
<canvas id="viewport" width="600" height="600"></canvas>
External resources loaded into this fiddle: