| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251 |
- /* 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
- */
- var camera, scene, renderer, controls = null;
- var s_oRender;
- 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 = 0xaaaaaa;
- var solidMaterial = new THREE.MeshPhongMaterial({color: materialColor, specular: 0x111111, shininess: 50});
- //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.MeshPhongMaterial({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, 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 = 1024;
- var SHADOW_MAP_HEIGHT = 1024;
- var MARGIN = 0;
- var SCREEN_WIDTH = s_iCanvasResizeWidth + s_iCanvasOffsetWidth;
- var SCREEN_HEIGHT = s_iCanvasResizeHeight + s_iCanvasOffsetHeight;
- var container;
- var windowHalfX = SCREEN_WIDTH / 2;
- var windowHalfY = SCREEN_HEIGHT / 2;
- init();
- animate();
- function init() {
- container = document.createElement('div');
- document.body.appendChild(container);
- // Camera
- if (CAMERA_TEST_TRACKBALL) {
- NEAR = 5;
- camera = new THREE.PerspectiveCamera(45, SCREEN_WIDTH / SCREEN_HEIGHT, NEAR, FAR);
- camera.lookAt(new THREE.Vector3(CAMERA_TEST_LOOK_AT.x, CAMERA_TEST_LOOK_AT.y, CAMERA_TEST_LOOK_AT.z));
- camera.position.set(0, 500, 500);
- camera.up.set(0, 0, 1);
- } else {
- camera = createOrthoGraphicCamera();
- }
- // SCENE
- scene = that.scene = new THREE.Scene();
- scene.fog = new THREE.Fog(0x7ec0ee, FAR * 0.5, FAR);
- // LIGHTS
- ambient = new THREE.AmbientLight(0x444444);
- scene.add(ambient);
- light = new THREE.DirectionalLight(0xffffcc, 1);
- light.position.set(180, 0, 180);
- light.target.position.set(0, 0, 0);
- light.castShadow = true;
- light.shadow.camera.near = 10;
- light.shadow.camera.far = 100;//camera.far;
- light.shadow.camera.fov = 30;
- light.shadowMapBias = 0.0139;
- light.shadowMapDarkness = 0.1;
- light.shadow.mapSize.width = SHADOW_MAP_WIDTH;
- light.shadow.mapSize.height = SHADOW_MAP_HEIGHT;
- // light.shadowCameraVisible = true;
- // new THREE.CameraHelper(light.shadow.camera);
- scene.add(light);
- scene.add(camera);
- // add plane for raycasting
- //var oPlaneGeometry = new THREE.PlaneBufferGeometry(500, 500, 50, 50);
- // s_oRayCasterMesh = new THREE.Mesh(oPlaneGeometry, new THREE.MeshLambertMaterial({color: Math.random() * 0xffffff}));
- // scene.add( s_oRayCasterMesh );
- // RENDERER
- if (SHOW_3D_RENDER) {
- renderer = new THREE.WebGLRenderer({clearColor: 0x000000, clearAlpha: 0.5, antialias: true, alpha: true});
- } else {
- renderer = new THREE.CanvasRenderer({clearColor: 0x000000, clearAlpha: 0.5, antialias: false, alpha: true});
- }
- renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
- renderer.domElement.style.position = "relative";
- renderer.domElement.style.top = MARGIN + 'px';
- renderer.domElement.style.opacity = OPACITY_INTENSITY_3D;
- 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.shadowMap.enabled = 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
- if (CAMERA_TEST_TRACKBALL) {
- 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 = s_iCanvasResizeWidth + s_iCanvasOffsetWidth * 2;
- SCREEN_HEIGHT = s_iCanvasResizeHeight + s_iCanvasOffsetHeight * 2;
- if (CAMERA_TEST_TRACKBALL) {
- controls.screen.width = SCREEN_WIDTH;
- controls.screen.height = SCREEN_HEIGHT;
- }
- }
- function render() {
- if (CAMERA_TEST_TRACKBALL || CAMERA_TEST_TRANSFORM && controls !== null) {
- controls.update();
- }
- renderer.clear();
- renderer.render(that.scene, camera);
- }
- s_oRender = render;
- 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.createTransformControl = function (oMesh, oBody) {
- controls = new THREE.TransformControls(camera, renderer.domElement);
- // controls.addEventListener('change', s_oRender);
- scene.add(oMesh);
- controls.attach(oMesh, oBody);
- scene.add(controls);
- console.log("CREATE");
- window.addEventListener('keydown', function (event) {
- switch (event.keyCode) {
- case 81: // Q
- controls.setSpace(controls.space === "local" ? "world" : "local");
- break;
- case 17: // Ctrl
- controls.setTranslationSnap(100);
- controls.setRotationSnap(THREE.Math.degToRad(15));
- break;
- case 87: // W
- controls.setMode("translate");
- break;
- case 69: // E
- controls.setMode("rotate");
- break;
- case 82: // R
- controls.setMode("scale");
- break;
- case 187:
- case 107: // +, =, num+
- controls.setSize(controls.size + 0.1);
- break;
- case 189:
- case 109: // -, _, num-
- controls.setSize(Math.max(controls.size - 0.1, 0.1));
- break;
- }
- });
- window.addEventListener('keyup', function (event) {
- switch (event.keyCode) {
- case 17: // Ctrl
- controls.setTranslationSnap(null);
- controls.setRotationSnap(null);
- break;
- }
- });
- };
- CANNON.Demo.prototype.getWorld = function () {
- return this.world;
- };
- CANNON.Demo.prototype.addVisual = function (body, material) {
- var s = this.settings;
- // What geometry should be used?
- var mesh;
- if (body instanceof CANNON.Body) {
- mesh = this.shape2mesh(body, material);
- }
- 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);
- }
- return 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, material) {
- 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);
- if (material === undefined) {
- mesh = new THREE.Mesh(sphere_geometry, this.currentMaterial);
- } else {
- mesh = new THREE.Mesh(sphere_geometry, material);
- }
- mesh.castShadow = true;
- 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;
- if (material === undefined) {
- ground = new THREE.Mesh(geometry, this.currentMaterial);
- } else {
- ground = new THREE.Mesh(geometry, material);
- }
- ground.scale.set(100, 100, 100);
- submesh.add(ground);
- ground.castShadow = false;
- 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);
- if (material === undefined) {
- mesh = new THREE.Mesh(box_geometry, this.currentMaterial);
- } else {
- mesh = new THREE.Mesh(box_geometry, material);
- }
- 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();
- if (material === undefined) {
- mesh = new THREE.Mesh(geo, this.currentMaterial);
- } else {
- mesh = new THREE.Mesh(geo, material);
- }
- 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();
- if (material === undefined) {
- mesh = new THREE.Mesh(geometry, this.currentMaterial);
- } else {
- mesh = new THREE.Mesh(geometry, material);
- }
- 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();
- if (material === undefined) {
- mesh = new THREE.Mesh(geometry, this.currentMaterial);
- } else {
- mesh = new THREE.Mesh(geometry, material);
- }
- 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);
- }
- this.camera = function () {
- return camera;
- };
- this.getScene = function () {
- return scene;
- };
- return obj;
- };
|