API Docs for: 0.6.1
Show:

File: src/demo/Demo.js

/* global CANNON,THREE,Detector */
CANNON = CANNON || {};
/**
 * Demo framework class. If you want to learn how to connect Cannon.js with Three.js, please look at the examples/ instead.
 * @class Demo
 * @constructor
 * @param {Object} options
 */
CANNON.Demo = function(options){
 var that = this;
 // API
 this.addScene = addScene;
 this.restartCurrentScene = restartCurrentScene;
 this.changeScene = changeScene;
 this.start = start;
 var sceneFolder;
 // Global settings
 var settings = this.settings = {
 stepFrequency: 60,
 quatNormalizeSkip: 2,
 quatNormalizeFast: true,
 gx: 0,
 gy: 0,
 gz: 0,
 iterations: 3,
 tolerance: 0.0001,
 k: 1e6,
 d: 3,
 scene: 0,
 paused: false,
 rendermode: "solid",
 constraints: false,
 contacts: false, // Contact points
 cm2contact: false, // center of mass to contact points
 normals: false, // contact normals
 axes: false, // "local" frame axes
 particleSize: 0.1,
 shadows: false,
 aabbs: false,
 profiling: false,
 maxSubSteps:3
 };
 // Extend settings with options
 options = options || {};
 for(var key in options){
 if(key in settings){
 settings[key] = options[key];
 }
 }
 if(settings.stepFrequency % 60 !== 0){
 throw new Error("stepFrequency must be a multiple of 60.");
 }
 var bodies = this.bodies = [];
 var visuals = this.visuals = [];
 var scenes = [];
 var gui = null;
 var smoothie = null;
 var smoothieCanvas = null;
 var scenePicker = {};
 var three_contactpoint_geo = new THREE.SphereGeometry( 0.1, 6, 6);
 var particleGeo = this.particleGeo = new THREE.SphereGeometry( 1, 16, 8 );
 // Material
 var materialColor = 0xdddddd;
 var solidMaterial = new THREE.MeshLambertMaterial( { color: materialColor } );
 //THREE.ColorUtils.adjustHSV( solidMaterial.color, 0, 0, 0.9 );
 var wireframeMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff, wireframe:true } );
 this.currentMaterial = solidMaterial;
 var contactDotMaterial = new THREE.MeshLambertMaterial( { color: 0xff0000 } );
 var particleMaterial = this.particleMaterial = new THREE.MeshLambertMaterial( { color: 0xff0000 } );
 // Geometry caches
 var contactMeshCache = new GeometryCache(function(){
 return new THREE.Mesh( three_contactpoint_geo, contactDotMaterial );
 });
 var cm2contactMeshCache = new GeometryCache(function(){
 var geometry = new THREE.Geometry();
 geometry.vertices.push(new THREE.Vector3(0,0,0));
 geometry.vertices.push(new THREE.Vector3(1,1,1));
 return new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0xff0000 } ) );
 });
 var bboxGeometry = new THREE.BoxGeometry(1,1,1);
 var bboxMaterial = new THREE.MeshBasicMaterial({
 color: materialColor,
 wireframe:true
 });
 var bboxMeshCache = new GeometryCache(function(){
 return new THREE.Mesh(bboxGeometry,bboxMaterial);
 });
 var distanceConstraintMeshCache = new GeometryCache(function(){
 var geometry = new THREE.Geometry();
 geometry.vertices.push(new THREE.Vector3(0,0,0));
 geometry.vertices.push(new THREE.Vector3(1,1,1));
 return new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0xff0000 } ) );
 });
 var p2pConstraintMeshCache = new GeometryCache(function(){
 var geometry = new THREE.Geometry();
 geometry.vertices.push(new THREE.Vector3(0,0,0));
 geometry.vertices.push(new THREE.Vector3(1,1,1));
 return new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0xff0000 } ) );
 });
 var normalMeshCache = new GeometryCache(function(){
 var geometry = new THREE.Geometry();
 geometry.vertices.push(new THREE.Vector3(0,0,0));
 geometry.vertices.push(new THREE.Vector3(1,1,1));
 return new THREE.Line( geometry, new THREE.LineBasicMaterial({color:0x00ff00}));
 });
 var axesMeshCache = new GeometryCache(function(){
 var mesh = new THREE.Object3D();
 //mesh.useQuaternion = true;
 var origin = new THREE.Vector3(0,0,0);
 var gX = new THREE.Geometry();
 var gY = new THREE.Geometry();
 var gZ = new THREE.Geometry();
 gX.vertices.push(origin);
 gY.vertices.push(origin);
 gZ.vertices.push(origin);
 gX.vertices.push(new THREE.Vector3(1,0,0));
 gY.vertices.push(new THREE.Vector3(0,1,0));
 gZ.vertices.push(new THREE.Vector3(0,0,1));
 var lineX = new THREE.Line( gX, new THREE.LineBasicMaterial({color:0xff0000}));
 var lineY = new THREE.Line( gY, new THREE.LineBasicMaterial({color:0x00ff00}));
 var lineZ = new THREE.Line( gZ, new THREE.LineBasicMaterial({color:0x0000ff}));
 mesh.add(lineX);
 mesh.add(lineY);
 mesh.add(lineZ);
 return mesh;
 });
 function restartGeometryCaches(){
 contactMeshCache.restart();
 contactMeshCache.hideCached();
 cm2contactMeshCache.restart();
 cm2contactMeshCache.hideCached();
 distanceConstraintMeshCache.restart();
 distanceConstraintMeshCache.hideCached();
 normalMeshCache.restart();
 normalMeshCache.hideCached();
 }
 // Create physics world
 var world = this.world = new CANNON.World();
 world.broadphase = new CANNON.NaiveBroadphase();
 var renderModes = ["solid","wireframe"];
 function updategui(){
 if(gui){
 // First level
 for (var i in gui.__controllers){
 gui.__controllers[i].updateDisplay();
 }
 // Second level
 for (var f in gui.__folders){
 for (var i in gui.__folders[f].__controllers){
 gui.__folders[f].__controllers[i].updateDisplay();
 }
 }
 }
 }
 var light, scene, ambient, stats, info;
 function setRenderMode(mode){
 if(renderModes.indexOf(mode) === -1){
 throw new Error("Render mode "+mode+" not found!");
 }
 switch(mode){
 case "solid":
 that.currentMaterial = solidMaterial;
 light.intensity = 1;
 ambient.color.setHex(0x222222);
 break;
 case "wireframe":
 that.currentMaterial = wireframeMaterial;
 light.intensity = 0;
 ambient.color.setHex(0xffffff);
 break;
 }
 function setMaterial(node,mat){
 if(node.material){
 node.material = mat;
 }
 for(var i=0; i<node.children.length; i++){
 setMaterial(node.children[i],mat);
 }
 }
 for(var i=0; i<visuals.length; i++){
 setMaterial(visuals[i],that.currentMaterial);
 }
 settings.rendermode = mode;
 }
 /**
 * Add a scene to the demo app
 * @method addScene
 * @param {String} title Title of the scene
 * @param {Function} initfunc A function that takes one argument, app, and initializes a physics scene. The function runs app.setWorld(body), app.addVisual(body), app.removeVisual(body) etc.
 */
 function addScene(title,initfunc){
 if(typeof(title) !== "string"){
 throw new Error("1st argument of Demo.addScene(title,initfunc) must be a string!");
 }
 if(typeof(initfunc)!=="function"){
 throw new Error("2nd argument of Demo.addScene(title,initfunc) must be a function!");
 }
 scenes.push(initfunc);
 var idx = scenes.length-1;
 scenePicker[title] = function(){
 changeScene(idx);
 };
 sceneFolder.add(scenePicker,title);
 }
 /**
 * Restarts the current scene
 * @method restartCurrentScene
 */
 function restartCurrentScene(){
 var N = bodies.length;
 for(var i=0; i<N; i++){
 var b = bodies[i];
 b.position.copy(b.initPosition);
 b.velocity.copy(b.initVelocity);
 if(b.initAngularVelocity){
 b.angularVelocity.copy(b.initAngularVelocity);
 b.quaternion.copy(b.initQuaternion);
 }
 }
 }
 function makeSureNotZero(vec){
 if(vec.x===0.0){
 vec.x = 1e-6;
 }
 if(vec.y===0.0){
 vec.y = 1e-6;
 }
 if(vec.z===0.0){
 vec.z = 1e-6;
 }
 }
 function updateVisuals(){
 var N = bodies.length;
 // Read position data into visuals
 for(var i=0; i<N; i++){
 var b = bodies[i], visual = visuals[i];
 visual.position.copy(b.position);
 if(b.quaternion){
 visual.quaternion.copy(b.quaternion);
 }
 }
 // Render contacts
 contactMeshCache.restart();
 if(settings.contacts){
 // if ci is even - use body i, else j
 for(var ci=0; ci < world.contacts.length; ci++){
 for(var ij=0; ij < 2; ij++){
 var mesh = contactMeshCache.request(),
 c = world.contacts[ci],
 b = ij===0 ? c.bi : c.bj,
 r = ij===0 ? c.ri : c.rj;
 mesh.position.set( b.position.x + r.x , b.position.y + r.y , b.position.z + r.z );
 }
 }
 }
 contactMeshCache.hideCached();
 // Lines from center of mass to contact point
 cm2contactMeshCache.restart();
 if(settings.cm2contact){
 for(var ci=0; ci<world.contacts.length; ci++){
 for(var ij=0; ij < 2; ij++){
 var line = cm2contactMeshCache.request(),
 c = world.contacts[ci],
 b = ij===0 ? c.bi : c.bj,
 r = ij===0 ? c.ri : c.rj;
 line.scale.set( r.x, r.y, r.z);
 makeSureNotZero(line.scale);
 line.position.copy(b.position);
 }
 }
 }
 cm2contactMeshCache.hideCached();
 distanceConstraintMeshCache.restart();
 p2pConstraintMeshCache.restart();
 if(settings.constraints){
 // Lines for distance constraints
 for(var ci=0; ci<world.constraints.length; ci++){
 var c = world.constraints[ci];
 if(!(c instanceof CANNON.DistanceConstraint)){
 continue;
 }
 var nc = c.equations.normal;
 var bi=nc.bi, bj=nc.bj, line = distanceConstraintMeshCache.request();
 var i=bi.id, j=bj.id;
 // Remember, bj is either a Vec3 or a Body.
 var v;
 if(bj.position){
 v = bj.position;
 } else {
 v = bj;
 }
 line.scale.set( v.x-bi.position.x,
 v.y-bi.position.y,
 v.z-bi.position.z );
 makeSureNotZero(line.scale);
 line.position.copy(bi.position);
 }
 // Lines for distance constraints
 for(var ci=0; ci<world.constraints.length; ci++){
 var c = world.constraints[ci];
 if(!(c instanceof CANNON.PointToPointConstraint)){
 continue;
 }
 var n = c.equations.normal;
 var bi=n.bi, bj=n.bj, relLine1 = p2pConstraintMeshCache.request(), relLine2 = p2pConstraintMeshCache.request(), diffLine = p2pConstraintMeshCache.request();
 var i=bi.id, j=bj.id;
 relLine1.scale.set( n.ri.x, n.ri.y, n.ri.z );
 relLine2.scale.set( n.rj.x, n.rj.y, n.rj.z );
 diffLine.scale.set( -n.penetrationVec.x, -n.penetrationVec.y, -n.penetrationVec.z );
 makeSureNotZero(relLine1.scale);
 makeSureNotZero(relLine2.scale);
 makeSureNotZero(diffLine.scale);
 relLine1.position.copy(bi.position);
 relLine2.position.copy(bj.position);
 n.bj.position.vadd(n.rj,diffLine.position);
 }
 }
 p2pConstraintMeshCache.hideCached();
 distanceConstraintMeshCache.hideCached();
 // Normal lines
 normalMeshCache.restart();
 if(settings.normals){
 for(var ci=0; ci<world.contacts.length; ci++){
 var c = world.contacts[ci];
 var bi=c.bi, bj=c.bj, line=normalMeshCache.request();
 var i=bi.id, j=bj.id;
 var n = c.ni;
 var b = bi;
 line.scale.set(n.x,n.y,n.z);
 makeSureNotZero(line.scale);
 line.position.copy(b.position);
 c.ri.vadd(line.position,line.position);
 }
 }
 normalMeshCache.hideCached();
 // Frame axes for each body
 axesMeshCache.restart();
 if(settings.axes){
 for(var bi=0; bi<bodies.length; bi++){
 var b = bodies[bi], mesh=axesMeshCache.request();
 mesh.position.copy(b.position);
 if(b.quaternion){
 mesh.quaternion.copy(b.quaternion);
 }
 }
 }
 axesMeshCache.hideCached();
 // AABBs
 bboxMeshCache.restart();
 if(settings.aabbs){
 for(var i=0; i<bodies.length; i++){
 var b = bodies[i];
 if(b.computeAABB){
 if(b.aabbNeedsUpdate){
 b.computeAABB();
 }
 // Todo: cap the infinite AABB to scene AABB, for now just dont render
 if( isFinite(b.aabb.lowerBound.x) &&
 isFinite(b.aabb.lowerBound.y) &&
 isFinite(b.aabb.lowerBound.z) &&
 isFinite(b.aabb.upperBound.x) &&
 isFinite(b.aabb.upperBound.y) &&
 isFinite(b.aabb.upperBound.z) &&
 b.aabb.lowerBound.x - b.aabb.upperBound.x != 0 &&
 b.aabb.lowerBound.y - b.aabb.upperBound.y != 0 &&
 b.aabb.lowerBound.z - b.aabb.upperBound.z != 0){
 var mesh = bboxMeshCache.request();
 mesh.scale.set( b.aabb.lowerBound.x - b.aabb.upperBound.x,
 b.aabb.lowerBound.y - b.aabb.upperBound.y,
 b.aabb.lowerBound.z - b.aabb.upperBound.z);
 mesh.position.set( (b.aabb.lowerBound.x + b.aabb.upperBound.x)*0.5,
 (b.aabb.lowerBound.y + b.aabb.upperBound.y)*0.5,
 (b.aabb.lowerBound.z + b.aabb.upperBound.z)*0.5);
 }
 }
 }
 }
 bboxMeshCache.hideCached();
 }
 if (!Detector.webgl){
 Detector.addGetWebGLMessage();
 }
 var SHADOW_MAP_WIDTH = 512;
 var SHADOW_MAP_HEIGHT = 512;
 var MARGIN = 0;
 var SCREEN_WIDTH = window.innerWidth;
 var SCREEN_HEIGHT = window.innerHeight - 2 * MARGIN;
 var camera, controls, renderer;
 var container;
 var NEAR = 5, FAR = 2000;
 var sceneHUD, cameraOrtho, hudMaterial;
 var mouseX = 0, mouseY = 0;
 var windowHalfX = window.innerWidth / 2;
 var windowHalfY = window.innerHeight / 2;
 init();
 animate();
 function init() {
 container = document.createElement( 'div' );
 document.body.appendChild( container );
 // Camera
 camera = new THREE.PerspectiveCamera( 24, SCREEN_WIDTH / SCREEN_HEIGHT, NEAR, FAR );
 camera.up.set(0,0,1);
 camera.position.set(0,30,20);
 // SCENE
 scene = that.scene = new THREE.Scene();
 scene.fog = new THREE.Fog( 0x222222, 1000, FAR );
 // LIGHTS
 ambient = new THREE.AmbientLight( 0x222222 );
 scene.add( ambient );
 light = new THREE.SpotLight( 0xffffff );
 light.position.set( 30, 30, 40 );
 light.target.position.set( 0, 0, 0 );
 light.castShadow = true;
 light.shadowCameraNear = 10;
 light.shadowCameraFar = 100;//camera.far;
 light.shadowCameraFov = 30;
 light.shadowMapBias = 0.0039;
 light.shadowMapDarkness = 0.5;
 light.shadowMapWidth = SHADOW_MAP_WIDTH;
 light.shadowMapHeight = SHADOW_MAP_HEIGHT;
 //light.shadowCameraVisible = true;
 scene.add( light );
 scene.add( camera );
 // RENDERER
 renderer = new THREE.WebGLRenderer( { clearColor: 0x000000, clearAlpha: 1, antialias: false } );
 renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
 renderer.domElement.style.position = "relative";
 renderer.domElement.style.top = MARGIN + 'px';
 container.appendChild( renderer.domElement );
 // Add info
 info = document.createElement( 'div' );
 info.style.position = 'absolute';
 info.style.top = '10px';
 info.style.width = '100%';
 info.style.textAlign = 'center';
 info.innerHTML = '<a href="http://github.com/schteppe/cannon.js">cannon.js</a> - javascript 3d physics';
 container.appendChild( info );
 document.addEventListener('mousemove',onDocumentMouseMove);
 window.addEventListener('resize',onWindowResize);
 renderer.setClearColor( scene.fog.color, 1 );
 renderer.autoClear = false;
 renderer.shadowMapEnabled = true;
 renderer.shadowMapSoft = true;
 // Smoothie
 smoothieCanvas = document.createElement("canvas");
 smoothieCanvas.width = SCREEN_WIDTH;
 smoothieCanvas.height = SCREEN_HEIGHT;
 smoothieCanvas.style.opacity = 0.5;
 smoothieCanvas.style.position = 'absolute';
 smoothieCanvas.style.top = '0px';
 smoothieCanvas.style.zIndex = 90;
 container.appendChild( smoothieCanvas );
 smoothie = new SmoothieChart({
 labelOffsetY:50,
 maxDataSetLength:100,
 millisPerPixel:2,
 grid: {
 strokeStyle:'none',
 fillStyle:'none',
 lineWidth: 1,
 millisPerLine: 250,
 verticalSections: 6
 },
 labels: {
 fillStyle:'rgb(180, 180, 180)'
 }
 });
 smoothie.streamTo(smoothieCanvas);
 // Create time series for each profile label
 var lines = {};
 var colors = [[255, 0, 0],[0, 255, 0],[0, 0, 255],[255,255,0],[255,0,255],[0,255,255]];
 var i=0;
 for(var label in world.profile){
 var c = colors[i%colors.length];
 lines[label] = new TimeSeries({
 label : label,
 fillStyle : "rgb("+c[0]+","+c[1]+","+c[2]+")",
 maxDataLength : 500,
 });
 i++;
 }
 // Add a random value to each line every second
 world.addEventListener("postStep",function(evt) {
 for(var label in world.profile)
 lines[label].append(world.time * 1000, world.profile[label]);
 });
 // Add to SmoothieChart
 var i=0;
 for(var label in world.profile){
 var c = colors[i%colors.length];
 smoothie.addTimeSeries(lines[label],{
 strokeStyle : "rgb("+c[0]+","+c[1]+","+c[2]+")",
 //fillStyle:"rgba("+c[0]+","+c[1]+","+c[2]+",0.3)",
 lineWidth:2
 });
 i++;
 }
 world.doProfiling = false;
 smoothie.stop();
 smoothieCanvas.style.display = "none";
 // STATS
 stats = new Stats();
 stats.domElement.style.position = 'absolute';
 stats.domElement.style.top = '0px';
 stats.domElement.style.zIndex = 100;
 container.appendChild( stats.domElement );
 if(window.dat!=undefined){
 gui = new dat.GUI();
 gui.domElement.parentNode.style.zIndex=120;
 // Render mode
 var rf = gui.addFolder('Rendering');
 rf.add(settings,'rendermode',{Solid:"solid",Wireframe:"wireframe"}).onChange(function(mode){
 setRenderMode(mode);
 });
 rf.add(settings,'contacts');
 rf.add(settings,'cm2contact');
 rf.add(settings,'normals');
 rf.add(settings,'constraints');
 rf.add(settings,'axes');
 rf.add(settings,'particleSize').min(0).max(1).onChange(function(size){
 for(var i=0; i<visuals.length; i++){
 if(bodies[i] instanceof CANNON.Particle)
 visuals[i].scale.set(size,size,size);
 }
 });
 rf.add(settings,'shadows').onChange(function(shadows){
 if(shadows){
 renderer.shadowMapAutoUpdate = true;
 } else {
 renderer.shadowMapAutoUpdate = false;
 renderer.clearTarget( light.shadowMap );
 }
 });
 rf.add(settings,'aabbs');
 rf.add(settings,'profiling').onChange(function(profiling){
 if(profiling){
 world.doProfiling = true;
 smoothie.start();
 smoothieCanvas.style.display = "block";
 } else {
 world.doProfiling = false;
 smoothie.stop();
 smoothieCanvas.style.display = "none";
 }
 });
 // World folder
 var wf = gui.addFolder('World');
 // Pause
 wf.add(settings, 'paused').onChange(function(p){
 /*if(p){
 smoothie.stop();
 } else {
 smoothie.start();
 }*/
 });
 wf.add(settings, 'stepFrequency',60,60*10).step(60);
 var maxg = 100;
 wf.add(settings, 'gx',-maxg,maxg).onChange(function(gx){
 if(!isNaN(gx)){
 world.gravity.set(gx,settings.gy,settings.gz);
 }
 });
 wf.add(settings, 'gy',-maxg,maxg).onChange(function(gy){
 if(!isNaN(gy))
 world.gravity.set(settings.gx,gy,settings.gz);
 });
 wf.add(settings, 'gz',-maxg,maxg).onChange(function(gz){
 if(!isNaN(gz))
 world.gravity.set(settings.gx,settings.gy,gz);
 });
 wf.add(settings, 'quatNormalizeSkip',0,50).step(1).onChange(function(skip){
 if(!isNaN(skip)){
 world.quatNormalizeSkip = skip;
 }
 });
 wf.add(settings, 'quatNormalizeFast').onChange(function(fast){
 world.quatNormalizeFast = !!fast;
 });
 // Solver folder
 var sf = gui.addFolder('Solver');
 sf.add(settings, 'iterations',1,50).step(1).onChange(function(it){
 world.solver.iterations = it;
 });
 sf.add(settings, 'k',10,10000000).onChange(function(k){
 that.setGlobalSpookParams(settings.k,settings.d,1/settings.stepFrequency);
 });
 sf.add(settings, 'd',0,20).step(0.1).onChange(function(d){
 that.setGlobalSpookParams(settings.k,settings.d,1/settings.stepFrequency);
 });
 sf.add(settings, 'tolerance',0.0,10.0).step(0.01).onChange(function(t){
 world.solver.tolerance = t;
 });
 // Scene picker
 sceneFolder = gui.addFolder('Scenes');
 sceneFolder.open();
 }
 // Trackball controls
 controls = new THREE.TrackballControls( camera, renderer.domElement );
 controls.rotateSpeed = 1.0;
 controls.zoomSpeed = 1.2;
 controls.panSpeed = 0.2;
 controls.noZoom = false;
 controls.noPan = false;
 controls.staticMoving = false;
 controls.dynamicDampingFactor = 0.3;
 var radius = 100;
 controls.minDistance = 0.0;
 controls.maxDistance = radius * 1000;
 //controls.keys = [ 65, 83, 68 ]; // [ rotateKey, zoomKey, panKey ]
 controls.screen.width = SCREEN_WIDTH;
 controls.screen.height = SCREEN_HEIGHT;
 }
 var t = 0, newTime, delta;
 function animate(){
 requestAnimationFrame( animate );
 if(!settings.paused){
 updateVisuals();
 updatePhysics();
 }
 render();
 stats.update();
 }
 var lastCallTime = 0;
 function updatePhysics(){
 // Step world
 var timeStep = 1 / settings.stepFrequency;
 var now = Date.now() / 1000;
 if(!lastCallTime){
 // last call time not saved, cant guess elapsed time. Take a simple step.
 world.step(timeStep);
 lastCallTime = now;
 return;
 }
 var timeSinceLastCall = now - lastCallTime;
 world.step(timeStep, timeSinceLastCall, settings.maxSubSteps);
 lastCallTime = now;
 }
 function onDocumentMouseMove( event ) {
 mouseX = ( event.clientX - windowHalfX );
 mouseY = ( event.clientY - windowHalfY );
 }
 function onWindowResize( event ) {
 SCREEN_WIDTH = window.innerWidth;
 SCREEN_HEIGHT = window.innerHeight;
 renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
 camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT;
 camera.updateProjectionMatrix();
 controls.screen.width = SCREEN_WIDTH;
 controls.screen.height = SCREEN_HEIGHT;
 camera.radius = ( SCREEN_WIDTH + SCREEN_HEIGHT ) / 4;
 }
 function render(){
 controls.update();
 renderer.clear();
 renderer.render( that.scene, camera );
 }
 document.addEventListener('keypress',function(e){
 if(e.keyCode){
 switch(e.keyCode){
 case 32: // Space - restart
 restartCurrentScene();
 break;
 case 104: // h - toggle widgets
 if(stats.domElement.style.display=="none"){
 stats.domElement.style.display = "block";
 info.style.display = "block";
 } else {
 stats.domElement.style.display = "none";
 info.style.display = "none";
 }
 break;
 case 97: // a - AABBs
 settings.aabbs = !settings.aabbs;
 updategui();
 break;
 case 99: // c - constraints
 settings.constraints = !settings.constraints;
 updategui();
 break;
 case 112: // p
 settings.paused = !settings.paused;
 updategui();
 break;
 case 115: // s
 var timeStep = 1 / settings.stepFrequency;
 world.step(timeStep);
 updateVisuals();
 break;
 case 109: // m - toggle materials
 var idx = renderModes.indexOf(settings.rendermode);
 idx++;
 idx = idx % renderModes.length; // begin at 0 if we exceeded number of modes
 setRenderMode(renderModes[idx]);
 updategui();
 break;
 case 49:
 case 50:
 case 51:
 case 52:
 case 53:
 case 54:
 case 55:
 case 56:
 case 57:
 // Change scene
 // Only for numbers 1-9 and if no input field is active
 if(scenes.length > e.keyCode-49 && !document.activeElement.localName.match(/input/)){
 changeScene(e.keyCode-49);
 }
 break;
 }
 }
 });
 function changeScene(n){
 that.dispatchEvent({ type: 'destroy' });
 settings.paused = false;
 updategui();
 buildScene(n);
 }
 function start(){
 buildScene(0);
 }
 function buildScene(n){
 // Remove current bodies and visuals
 var num = visuals.length;
 for(var i=0; i<num; i++){
 world.remove(bodies.pop());
 var mesh = visuals.pop();
 that.scene.remove(mesh);
 }
 // Remove all constraints
 while(world.constraints.length){
 world.removeConstraint(world.constraints[0]);
 }
 // Run the user defined "build scene" function
 scenes[n]();
 // Read the newly set data to the gui
 settings.iterations = world.solver.iterations;
 settings.gx = world.gravity.x+0.0;
 settings.gy = world.gravity.y+0.0;
 settings.gz = world.gravity.z+0.0;
 settings.quatNormalizeSkip = world.quatNormalizeSkip;
 settings.quatNormalizeFast = world.quatNormalizeFast;
 updategui();
 restartGeometryCaches();
 }
 function GeometryCache(createFunc){
 var that=this, geometries=[], gone=[];
 this.request = function(){
 if(geometries.length){
 geo = geometries.pop();
 } else{
 geo = createFunc();
 }
 scene.add(geo);
 gone.push(geo);
 return geo;
 };
 this.restart = function(){
 while(gone.length){
 geometries.push(gone.pop());
 }
 };
 this.hideCached = function(){
 for(var i=0; i<geometries.length; i++){
 scene.remove(geometries[i]);
 }
 };
 }
};
CANNON.Demo.prototype = new CANNON.EventTarget();
CANNON.Demo.constructor = CANNON.Demo;
CANNON.Demo.prototype.setGlobalSpookParams = function(k,d,h){
 var world = this.world;
 // Set for all constraints
 for(var i=0; i<world.constraints.length; i++){
 var c = world.constraints[i];
 for(var j=0; j<c.equations.length; j++){
 var eq = c.equations[j];
 eq.setSpookParams(k,d,h);
 }
 }
 // Set for all contact materals
 for(var i=0; i<world.contactmaterials.length; i++){
 var cm = world.contactmaterials[i];
 cm.contactEquationStiffness = k;
 cm.frictionEquationStiffness = k;
 cm.contactEquationRelaxation = d;
 cm.frictionEquationRelaxation = d;
 }
 world.defaultContactMaterial.contactEquationStiffness = k;
 world.defaultContactMaterial.frictionEquationStiffness = k;
 world.defaultContactMaterial.contactEquationRelaxation = d;
 world.defaultContactMaterial.frictionEquationRelaxation = d;
};
CANNON.Demo.prototype.getWorld = function(){
 return this.world;
};
CANNON.Demo.prototype.addVisual = function(body){
 var s = this.settings;
 // What geometry should be used?
 var mesh;
 if(body instanceof CANNON.Body){
 mesh = this.shape2mesh(body);
 }
 if(mesh) {
 // Add body
 this.bodies.push(body);
 this.visuals.push(mesh);
 body.visualref = mesh;
 body.visualref.visualId = this.bodies.length - 1;
 //mesh.useQuaternion = true;
 this.scene.add(mesh);
 }
};
CANNON.Demo.prototype.addVisuals = function(bodies){
 for (var i = 0; i < bodies.length; i++) {
 this.addVisual(bodies[i]);
 }
};
CANNON.Demo.prototype.removeVisual = function(body){
 if(body.visualref){
 var bodies = this.bodies,
 visuals = this.visuals,
 old_b = [],
 old_v = [],
 n = bodies.length;
 for(var i=0; i<n; i++){
 old_b.unshift(bodies.pop());
 old_v.unshift(visuals.pop());
 }
 var id = body.visualref.visualId;
 for(var j=0; j<old_b.length; j++){
 if(j !== id){
 var i = j>id ? j-1 : j;
 bodies[i] = old_b[j];
 visuals[i] = old_v[j];
 bodies[i].visualref = old_b[j].visualref;
 bodies[i].visualref.visualId = i;
 }
 }
 body.visualref.visualId = null;
 this.scene.remove(body.visualref);
 body.visualref = null;
 }
};
CANNON.Demo.prototype.removeAllVisuals = function(){
 while(this.bodies.length) {
 this.removeVisual(this.bodies[0]);
 }
};
CANNON.Demo.prototype.shape2mesh = function(body){
 var wireframe = this.settings.renderMode === "wireframe";
 var obj = new THREE.Object3D();
 for (var l = 0; l < body.shapes.length; l++) {
 var shape = body.shapes[l];
 var mesh;
 switch(shape.type){
 case CANNON.Shape.types.SPHERE:
 var sphere_geometry = new THREE.SphereGeometry( shape.radius, 8, 8);
 mesh = new THREE.Mesh( sphere_geometry, this.currentMaterial );
 break;
 case CANNON.Shape.types.PARTICLE:
 mesh = new THREE.Mesh( this.particleGeo, this.particleMaterial );
 var s = this.settings;
 mesh.scale.set(s.particleSize,s.particleSize,s.particleSize);
 break;
 case CANNON.Shape.types.PLANE:
 var geometry = new THREE.PlaneGeometry(10, 10, 4, 4);
 mesh = new THREE.Object3D();
 var submesh = new THREE.Object3D();
 var ground = new THREE.Mesh( geometry, this.currentMaterial );
 ground.scale.set(100, 100, 100);
 submesh.add(ground);
 ground.castShadow = true;
 ground.receiveShadow = true;
 mesh.add(submesh);
 break;
 case CANNON.Shape.types.BOX:
 var box_geometry = new THREE.BoxGeometry( shape.halfExtents.x*2,
 shape.halfExtents.y*2,
 shape.halfExtents.z*2 );
 mesh = new THREE.Mesh( box_geometry, this.currentMaterial );
 break;
 case CANNON.Shape.types.CONVEXPOLYHEDRON:
 var geo = new THREE.Geometry();
 // Add vertices
 for (var i = 0; i < shape.vertices.length; i++) {
 var v = shape.vertices[i];
 geo.vertices.push(new THREE.Vector3(v.x, v.y, v.z));
 }
 for(var i=0; i < shape.faces.length; i++){
 var face = shape.faces[i];
 // add triangles
 var a = face[0];
 for (var j = 1; j < face.length - 1; j++) {
 var b = face[j];
 var c = face[j + 1];
 geo.faces.push(new THREE.Face3(a, b, c));
 }
 }
 geo.computeBoundingSphere();
 geo.computeFaceNormals();
 mesh = new THREE.Mesh( geo, this.currentMaterial );
 break;
 case CANNON.Shape.types.HEIGHTFIELD:
 var geometry = new THREE.Geometry();
 var v0 = new CANNON.Vec3();
 var v1 = new CANNON.Vec3();
 var v2 = new CANNON.Vec3();
 for (var xi = 0; xi < shape.data.length - 1; xi++) {
 for (var yi = 0; yi < shape.data[xi].length - 1; yi++) {
 for (var k = 0; k < 2; k++) {
 shape.getConvexTrianglePillar(xi, yi, k===0);
 v0.copy(shape.pillarConvex.vertices[0]);
 v1.copy(shape.pillarConvex.vertices[1]);
 v2.copy(shape.pillarConvex.vertices[2]);
 v0.vadd(shape.pillarOffset, v0);
 v1.vadd(shape.pillarOffset, v1);
 v2.vadd(shape.pillarOffset, v2);
 geometry.vertices.push(
 new THREE.Vector3(v0.x, v0.y, v0.z),
 new THREE.Vector3(v1.x, v1.y, v1.z),
 new THREE.Vector3(v2.x, v2.y, v2.z)
 );
 var i = geometry.vertices.length - 3;
 geometry.faces.push(new THREE.Face3(i, i+1, i+2));
 }
 }
 }
 geometry.computeBoundingSphere();
 geometry.computeFaceNormals();
 mesh = new THREE.Mesh(geometry, this.currentMaterial);
 break;
 case CANNON.Shape.types.TRIMESH:
 var geometry = new THREE.Geometry();
 var v0 = new CANNON.Vec3();
 var v1 = new CANNON.Vec3();
 var v2 = new CANNON.Vec3();
 for (var i = 0; i < shape.indices.length / 3; i++) {
 shape.getTriangleVertices(i, v0, v1, v2);
 geometry.vertices.push(
 new THREE.Vector3(v0.x, v0.y, v0.z),
 new THREE.Vector3(v1.x, v1.y, v1.z),
 new THREE.Vector3(v2.x, v2.y, v2.z)
 );
 var j = geometry.vertices.length - 3;
 geometry.faces.push(new THREE.Face3(j, j+1, j+2));
 }
 geometry.computeBoundingSphere();
 geometry.computeFaceNormals();
 mesh = new THREE.Mesh(geometry, this.currentMaterial);
 break;
 default:
 throw "Visual type not recognized: "+shape.type;
 }
 mesh.receiveShadow = true;
 mesh.castShadow = true;
 if(mesh.children){
 for(var i=0; i<mesh.children.length; i++){
 mesh.children[i].castShadow = true;
 mesh.children[i].receiveShadow = true;
 if(mesh.children[i]){
 for(var j=0; j<mesh.children[i].length; j++){
 mesh.children[i].children[j].castShadow = true;
 mesh.children[i].children[j].receiveShadow = true;
 }
 }
 }
 }
 var o = body.shapeOffsets[l];
 var q = body.shapeOrientations[l];
 mesh.position.set(o.x, o.y, o.z);
 mesh.quaternion.set(q.x, q.y, q.z, q.w);
 obj.add(mesh);
 }
 return obj;
};
 

AltStyle によって変換されたページ (->オリジナル) /