cannon.demo.js 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251
  1. /* global CANNON,THREE,Detector */
  2. CANNON = CANNON || {};
  3. /**
  4. * Demo framework class. If you want to learn how to connect Cannon.js with Three.js, please look at the examples/ instead.
  5. * @class Demo
  6. * @constructor
  7. * @param {Object} options
  8. */
  9. var camera, scene, renderer, controls = null;
  10. var s_oRender;
  11. CANNON.Demo = function (options) {
  12. var that = this;
  13. // API
  14. this.addScene = addScene;
  15. this.restartCurrentScene = restartCurrentScene;
  16. this.changeScene = changeScene;
  17. this.start = start;
  18. var sceneFolder;
  19. // Global settings
  20. var settings = this.settings = {
  21. stepFrequency: 60,
  22. quatNormalizeSkip: 2,
  23. quatNormalizeFast: true,
  24. gx: 0,
  25. gy: 0,
  26. gz: 0,
  27. iterations: 3,
  28. tolerance: 0.0001,
  29. k: 1e6,
  30. d: 3,
  31. scene: 0,
  32. paused: false,
  33. rendermode: "solid",
  34. constraints: false,
  35. contacts: false, // Contact points
  36. cm2contact: false, // center of mass to contact points
  37. normals: false, // contact normals
  38. axes: false, // "local" frame axes
  39. particleSize: 0.1,
  40. shadows: false,
  41. aabbs: false,
  42. profiling: false,
  43. maxSubSteps: 3
  44. };
  45. // Extend settings with options
  46. options = options || {};
  47. for (var key in options) {
  48. if (key in settings) {
  49. settings[key] = options[key];
  50. }
  51. }
  52. if (settings.stepFrequency % 60 !== 0) {
  53. throw new Error("stepFrequency must be a multiple of 60.");
  54. }
  55. var bodies = this.bodies = [];
  56. var visuals = this.visuals = [];
  57. var scenes = [];
  58. var gui = null;
  59. var smoothie = null;
  60. var smoothieCanvas = null;
  61. var scenePicker = {};
  62. var three_contactpoint_geo = new THREE.SphereGeometry(0.1, 6, 6);
  63. var particleGeo = this.particleGeo = new THREE.SphereGeometry(1, 16, 8);
  64. // Material
  65. var materialColor = 0xaaaaaa;
  66. var solidMaterial = new THREE.MeshPhongMaterial({color: materialColor, specular: 0x111111, shininess: 50});
  67. //THREE.ColorUtils.adjustHSV( solidMaterial.color, 0, 0, 0.9 );
  68. var wireframeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff, wireframe: true});
  69. this.currentMaterial = solidMaterial;
  70. var contactDotMaterial = new THREE.MeshPhongMaterial({color: 0xff0000});
  71. var particleMaterial = this.particleMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
  72. // Geometry caches
  73. var contactMeshCache = new GeometryCache(function () {
  74. return new THREE.Mesh(three_contactpoint_geo, contactDotMaterial);
  75. });
  76. var cm2contactMeshCache = new GeometryCache(function () {
  77. var geometry = new THREE.Geometry();
  78. geometry.vertices.push(new THREE.Vector3(0, 0, 0));
  79. geometry.vertices.push(new THREE.Vector3(1, 1, 1));
  80. return new THREE.Line(geometry, new THREE.LineBasicMaterial({color: 0xff0000}));
  81. });
  82. var bboxGeometry = new THREE.BoxGeometry(1, 1, 1);
  83. var bboxMaterial = new THREE.MeshBasicMaterial({
  84. color: materialColor,
  85. wireframe: true
  86. });
  87. var bboxMeshCache = new GeometryCache(function () {
  88. return new THREE.Mesh(bboxGeometry, bboxMaterial);
  89. });
  90. var distanceConstraintMeshCache = new GeometryCache(function () {
  91. var geometry = new THREE.Geometry();
  92. geometry.vertices.push(new THREE.Vector3(0, 0, 0));
  93. geometry.vertices.push(new THREE.Vector3(1, 1, 1));
  94. return new THREE.Line(geometry, new THREE.LineBasicMaterial({color: 0xff0000}));
  95. });
  96. var p2pConstraintMeshCache = new GeometryCache(function () {
  97. var geometry = new THREE.Geometry();
  98. geometry.vertices.push(new THREE.Vector3(0, 0, 0));
  99. geometry.vertices.push(new THREE.Vector3(1, 1, 1));
  100. return new THREE.Line(geometry, new THREE.LineBasicMaterial({color: 0xff0000}));
  101. });
  102. var normalMeshCache = new GeometryCache(function () {
  103. var geometry = new THREE.Geometry();
  104. geometry.vertices.push(new THREE.Vector3(0, 0, 0));
  105. geometry.vertices.push(new THREE.Vector3(1, 1, 1));
  106. return new THREE.Line(geometry, new THREE.LineBasicMaterial({color: 0x00ff00}));
  107. });
  108. var axesMeshCache = new GeometryCache(function () {
  109. var mesh = new THREE.Object3D();
  110. //mesh.useQuaternion = true;
  111. var origin = new THREE.Vector3(0, 0, 0);
  112. var gX = new THREE.Geometry();
  113. var gY = new THREE.Geometry();
  114. var gZ = new THREE.Geometry();
  115. gX.vertices.push(origin);
  116. gY.vertices.push(origin);
  117. gZ.vertices.push(origin);
  118. gX.vertices.push(new THREE.Vector3(1, 0, 0));
  119. gY.vertices.push(new THREE.Vector3(0, 1, 0));
  120. gZ.vertices.push(new THREE.Vector3(0, 0, 1));
  121. var lineX = new THREE.Line(gX, new THREE.LineBasicMaterial({color: 0xff0000}));
  122. var lineY = new THREE.Line(gY, new THREE.LineBasicMaterial({color: 0x00ff00}));
  123. var lineZ = new THREE.Line(gZ, new THREE.LineBasicMaterial({color: 0x0000ff}));
  124. mesh.add(lineX);
  125. mesh.add(lineY);
  126. mesh.add(lineZ);
  127. return mesh;
  128. });
  129. function restartGeometryCaches() {
  130. contactMeshCache.restart();
  131. contactMeshCache.hideCached();
  132. cm2contactMeshCache.restart();
  133. cm2contactMeshCache.hideCached();
  134. distanceConstraintMeshCache.restart();
  135. distanceConstraintMeshCache.hideCached();
  136. normalMeshCache.restart();
  137. normalMeshCache.hideCached();
  138. }
  139. // Create physics world
  140. var world = this.world = new CANNON.World();
  141. world.broadphase = new CANNON.NaiveBroadphase();
  142. var renderModes = ["solid", "wireframe"];
  143. function updategui() {
  144. if (gui) {
  145. // First level
  146. for (var i in gui.__controllers) {
  147. gui.__controllers[i].updateDisplay();
  148. }
  149. // Second level
  150. for (var f in gui.__folders) {
  151. for (var i in gui.__folders[f].__controllers) {
  152. gui.__folders[f].__controllers[i].updateDisplay();
  153. }
  154. }
  155. }
  156. }
  157. var light, ambient, stats, info;
  158. function setRenderMode(mode) {
  159. if (renderModes.indexOf(mode) === -1) {
  160. throw new Error("Render mode " + mode + " not found!");
  161. }
  162. switch (mode) {
  163. case "solid":
  164. that.currentMaterial = solidMaterial;
  165. light.intensity = 1;
  166. ambient.color.setHex(0x222222);
  167. break;
  168. case "wireframe":
  169. that.currentMaterial = wireframeMaterial;
  170. light.intensity = 0;
  171. ambient.color.setHex(0xffffff);
  172. break;
  173. }
  174. function setMaterial(node, mat) {
  175. if (node.material) {
  176. node.material = mat;
  177. }
  178. for (var i = 0; i < node.children.length; i++) {
  179. setMaterial(node.children[i], mat);
  180. }
  181. }
  182. for (var i = 0; i < visuals.length; i++) {
  183. setMaterial(visuals[i], that.currentMaterial);
  184. }
  185. settings.rendermode = mode;
  186. }
  187. /**
  188. * Add a scene to the demo app
  189. * @method addScene
  190. * @param {String} title Title of the scene
  191. * @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.
  192. */
  193. function addScene(title, initfunc) {
  194. if (typeof (title) !== "string") {
  195. throw new Error("1st argument of Demo.addScene(title,initfunc) must be a string!");
  196. }
  197. if (typeof (initfunc) !== "function") {
  198. throw new Error("2nd argument of Demo.addScene(title,initfunc) must be a function!");
  199. }
  200. scenes.push(initfunc);
  201. var idx = scenes.length - 1;
  202. scenePicker[title] = function () {
  203. changeScene(idx);
  204. };
  205. sceneFolder.add(scenePicker, title);
  206. }
  207. /**
  208. * Restarts the current scene
  209. * @method restartCurrentScene
  210. */
  211. function restartCurrentScene() {
  212. var N = bodies.length;
  213. for (var i = 0; i < N; i++) {
  214. var b = bodies[i];
  215. b.position.copy(b.initPosition);
  216. b.velocity.copy(b.initVelocity);
  217. if (b.initAngularVelocity) {
  218. b.angularVelocity.copy(b.initAngularVelocity);
  219. b.quaternion.copy(b.initQuaternion);
  220. }
  221. }
  222. }
  223. function makeSureNotZero(vec) {
  224. if (vec.x === 0.0) {
  225. vec.x = 1e-6;
  226. }
  227. if (vec.y === 0.0) {
  228. vec.y = 1e-6;
  229. }
  230. if (vec.z === 0.0) {
  231. vec.z = 1e-6;
  232. }
  233. }
  234. function updateVisuals() {
  235. var N = bodies.length;
  236. // Read position data into visuals
  237. for (var i = 0; i < N; i++) {
  238. var b = bodies[i], visual = visuals[i];
  239. visual.position.copy(b.position);
  240. if (b.quaternion) {
  241. visual.quaternion.copy(b.quaternion);
  242. }
  243. }
  244. // Render contacts
  245. contactMeshCache.restart();
  246. if (settings.contacts) {
  247. // if ci is even - use body i, else j
  248. for (var ci = 0; ci < world.contacts.length; ci++) {
  249. for (var ij = 0; ij < 2; ij++) {
  250. var mesh = contactMeshCache.request(),
  251. c = world.contacts[ci],
  252. b = ij === 0 ? c.bi : c.bj,
  253. r = ij === 0 ? c.ri : c.rj;
  254. mesh.position.set(b.position.x + r.x, b.position.y + r.y, b.position.z + r.z);
  255. }
  256. }
  257. }
  258. contactMeshCache.hideCached();
  259. // Lines from center of mass to contact point
  260. cm2contactMeshCache.restart();
  261. if (settings.cm2contact) {
  262. for (var ci = 0; ci < world.contacts.length; ci++) {
  263. for (var ij = 0; ij < 2; ij++) {
  264. var line = cm2contactMeshCache.request(),
  265. c = world.contacts[ci],
  266. b = ij === 0 ? c.bi : c.bj,
  267. r = ij === 0 ? c.ri : c.rj;
  268. line.scale.set(r.x, r.y, r.z);
  269. makeSureNotZero(line.scale);
  270. line.position.copy(b.position);
  271. }
  272. }
  273. }
  274. cm2contactMeshCache.hideCached();
  275. distanceConstraintMeshCache.restart();
  276. p2pConstraintMeshCache.restart();
  277. if (settings.constraints) {
  278. // Lines for distance constraints
  279. for (var ci = 0; ci < world.constraints.length; ci++) {
  280. var c = world.constraints[ci];
  281. if (!(c instanceof CANNON.DistanceConstraint)) {
  282. continue;
  283. }
  284. var nc = c.equations.normal;
  285. var bi = nc.bi, bj = nc.bj, line = distanceConstraintMeshCache.request();
  286. var i = bi.id, j = bj.id;
  287. // Remember, bj is either a Vec3 or a Body.
  288. var v;
  289. if (bj.position) {
  290. v = bj.position;
  291. } else {
  292. v = bj;
  293. }
  294. line.scale.set(v.x - bi.position.x,
  295. v.y - bi.position.y,
  296. v.z - bi.position.z);
  297. makeSureNotZero(line.scale);
  298. line.position.copy(bi.position);
  299. }
  300. // Lines for distance constraints
  301. for (var ci = 0; ci < world.constraints.length; ci++) {
  302. var c = world.constraints[ci];
  303. if (!(c instanceof CANNON.PointToPointConstraint)) {
  304. continue;
  305. }
  306. var n = c.equations.normal;
  307. var bi = n.bi, bj = n.bj, relLine1 = p2pConstraintMeshCache.request(), relLine2 = p2pConstraintMeshCache.request(), diffLine = p2pConstraintMeshCache.request();
  308. var i = bi.id, j = bj.id;
  309. relLine1.scale.set(n.ri.x, n.ri.y, n.ri.z);
  310. relLine2.scale.set(n.rj.x, n.rj.y, n.rj.z);
  311. diffLine.scale.set(-n.penetrationVec.x, -n.penetrationVec.y, -n.penetrationVec.z);
  312. makeSureNotZero(relLine1.scale);
  313. makeSureNotZero(relLine2.scale);
  314. makeSureNotZero(diffLine.scale);
  315. relLine1.position.copy(bi.position);
  316. relLine2.position.copy(bj.position);
  317. n.bj.position.vadd(n.rj, diffLine.position);
  318. }
  319. }
  320. p2pConstraintMeshCache.hideCached();
  321. distanceConstraintMeshCache.hideCached();
  322. // Normal lines
  323. normalMeshCache.restart();
  324. if (settings.normals) {
  325. for (var ci = 0; ci < world.contacts.length; ci++) {
  326. var c = world.contacts[ci];
  327. var bi = c.bi, bj = c.bj, line = normalMeshCache.request();
  328. var i = bi.id, j = bj.id;
  329. var n = c.ni;
  330. var b = bi;
  331. line.scale.set(n.x, n.y, n.z);
  332. makeSureNotZero(line.scale);
  333. line.position.copy(b.position);
  334. c.ri.vadd(line.position, line.position);
  335. }
  336. }
  337. normalMeshCache.hideCached();
  338. // Frame axes for each body
  339. axesMeshCache.restart();
  340. if (settings.axes) {
  341. for (var bi = 0; bi < bodies.length; bi++) {
  342. var b = bodies[bi], mesh = axesMeshCache.request();
  343. mesh.position.copy(b.position);
  344. if (b.quaternion) {
  345. mesh.quaternion.copy(b.quaternion);
  346. }
  347. }
  348. }
  349. axesMeshCache.hideCached();
  350. // AABBs
  351. bboxMeshCache.restart();
  352. if (settings.aabbs) {
  353. for (var i = 0; i < bodies.length; i++) {
  354. var b = bodies[i];
  355. if (b.computeAABB) {
  356. if (b.aabbNeedsUpdate) {
  357. b.computeAABB();
  358. }
  359. // Todo: cap the infinite AABB to scene AABB, for now just dont render
  360. if (isFinite(b.aabb.lowerBound.x) &&
  361. isFinite(b.aabb.lowerBound.y) &&
  362. isFinite(b.aabb.lowerBound.z) &&
  363. isFinite(b.aabb.upperBound.x) &&
  364. isFinite(b.aabb.upperBound.y) &&
  365. isFinite(b.aabb.upperBound.z) &&
  366. b.aabb.lowerBound.x - b.aabb.upperBound.x != 0 &&
  367. b.aabb.lowerBound.y - b.aabb.upperBound.y != 0 &&
  368. b.aabb.lowerBound.z - b.aabb.upperBound.z != 0) {
  369. var mesh = bboxMeshCache.request();
  370. mesh.scale.set(b.aabb.lowerBound.x - b.aabb.upperBound.x,
  371. b.aabb.lowerBound.y - b.aabb.upperBound.y,
  372. b.aabb.lowerBound.z - b.aabb.upperBound.z);
  373. mesh.position.set((b.aabb.lowerBound.x + b.aabb.upperBound.x) * 0.5,
  374. (b.aabb.lowerBound.y + b.aabb.upperBound.y) * 0.5,
  375. (b.aabb.lowerBound.z + b.aabb.upperBound.z) * 0.5);
  376. }
  377. }
  378. }
  379. }
  380. bboxMeshCache.hideCached();
  381. }
  382. if (!Detector.webgl) {
  383. Detector.addGetWebGLMessage();
  384. }
  385. var SHADOW_MAP_WIDTH = 1024;
  386. var SHADOW_MAP_HEIGHT = 1024;
  387. var MARGIN = 0;
  388. var SCREEN_WIDTH = s_iCanvasResizeWidth + s_iCanvasOffsetWidth;
  389. var SCREEN_HEIGHT = s_iCanvasResizeHeight + s_iCanvasOffsetHeight;
  390. var container;
  391. var windowHalfX = SCREEN_WIDTH / 2;
  392. var windowHalfY = SCREEN_HEIGHT / 2;
  393. init();
  394. animate();
  395. function init() {
  396. container = document.createElement('div');
  397. document.body.appendChild(container);
  398. // Camera
  399. if (CAMERA_TEST_TRACKBALL) {
  400. NEAR = 5;
  401. camera = new THREE.PerspectiveCamera(45, SCREEN_WIDTH / SCREEN_HEIGHT, NEAR, FAR);
  402. camera.lookAt(new THREE.Vector3(CAMERA_TEST_LOOK_AT.x, CAMERA_TEST_LOOK_AT.y, CAMERA_TEST_LOOK_AT.z));
  403. camera.position.set(0, 500, 500);
  404. camera.up.set(0, 0, 1);
  405. } else {
  406. camera = createOrthoGraphicCamera();
  407. }
  408. // SCENE
  409. scene = that.scene = new THREE.Scene();
  410. scene.fog = new THREE.Fog(0x7ec0ee, FAR * 0.5, FAR);
  411. // LIGHTS
  412. ambient = new THREE.AmbientLight(0x444444);
  413. scene.add(ambient);
  414. light = new THREE.DirectionalLight(0xffffcc, 1);
  415. light.position.set(180, 0, 180);
  416. light.target.position.set(0, 0, 0);
  417. light.castShadow = true;
  418. light.shadow.camera.near = 10;
  419. light.shadow.camera.far = 100;//camera.far;
  420. light.shadow.camera.fov = 30;
  421. light.shadowMapBias = 0.0139;
  422. light.shadowMapDarkness = 0.1;
  423. light.shadow.mapSize.width = SHADOW_MAP_WIDTH;
  424. light.shadow.mapSize.height = SHADOW_MAP_HEIGHT;
  425. // light.shadowCameraVisible = true;
  426. // new THREE.CameraHelper(light.shadow.camera);
  427. scene.add(light);
  428. scene.add(camera);
  429. // add plane for raycasting
  430. //var oPlaneGeometry = new THREE.PlaneBufferGeometry(500, 500, 50, 50);
  431. // s_oRayCasterMesh = new THREE.Mesh(oPlaneGeometry, new THREE.MeshLambertMaterial({color: Math.random() * 0xffffff}));
  432. // scene.add( s_oRayCasterMesh );
  433. // RENDERER
  434. if (SHOW_3D_RENDER) {
  435. renderer = new THREE.WebGLRenderer({clearColor: 0x000000, clearAlpha: 0.5, antialias: true, alpha: true});
  436. } else {
  437. renderer = new THREE.CanvasRenderer({clearColor: 0x000000, clearAlpha: 0.5, antialias: false, alpha: true});
  438. }
  439. renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
  440. renderer.domElement.style.position = "relative";
  441. renderer.domElement.style.top = MARGIN + 'px';
  442. renderer.domElement.style.opacity = OPACITY_INTENSITY_3D;
  443. container.appendChild(renderer.domElement);
  444. // Add info
  445. info = document.createElement('div');
  446. info.style.position = 'absolute';
  447. info.style.top = '10px';
  448. info.style.width = '100%';
  449. info.style.textAlign = 'center';
  450. info.innerHTML = '<a href="http://github.com/schteppe/cannon.js">cannon.js</a> - javascript 3d physics';
  451. container.appendChild(info);
  452. document.addEventListener('mousemove', onDocumentMouseMove);
  453. window.addEventListener('resize', onWindowResize);
  454. renderer.setClearColor(scene.fog.color, 1);
  455. renderer.autoClear = false;
  456. // renderer.shadowMap.enabled = true;
  457. // renderer.shadowMapSoft = true;
  458. // Smoothie
  459. smoothieCanvas = document.createElement("canvas");
  460. smoothieCanvas.width = SCREEN_WIDTH;
  461. smoothieCanvas.height = SCREEN_HEIGHT;
  462. smoothieCanvas.style.opacity = 0.5;
  463. smoothieCanvas.style.position = 'absolute';
  464. smoothieCanvas.style.top = '0px';
  465. smoothieCanvas.style.zIndex = 90;
  466. container.appendChild(smoothieCanvas);
  467. smoothie = new SmoothieChart({
  468. labelOffsetY: 50,
  469. maxDataSetLength: 100,
  470. millisPerPixel: 2,
  471. grid: {
  472. strokeStyle: 'none',
  473. fillStyle: 'none',
  474. lineWidth: 1,
  475. millisPerLine: 250,
  476. verticalSections: 6
  477. },
  478. labels: {
  479. fillStyle: 'rgb(180, 180, 180)'
  480. }
  481. });
  482. smoothie.streamTo(smoothieCanvas);
  483. // Create time series for each profile label
  484. var lines = {};
  485. var colors = [[255, 0, 0], [0, 255, 0], [0, 0, 255], [255, 255, 0], [255, 0, 255], [0, 255, 255]];
  486. var i = 0;
  487. for (var label in world.profile) {
  488. var c = colors[i % colors.length];
  489. lines[label] = new TimeSeries({
  490. label: label,
  491. fillStyle: "rgb(" + c[0] + "," + c[1] + "," + c[2] + ")",
  492. maxDataLength: 500
  493. });
  494. i++;
  495. }
  496. // Add a random value to each line every second
  497. world.addEventListener("postStep", function (evt) {
  498. for (var label in world.profile)
  499. lines[label].append(world.time * 1000, world.profile[label]);
  500. });
  501. // Add to SmoothieChart
  502. var i = 0;
  503. for (var label in world.profile) {
  504. var c = colors[i % colors.length];
  505. smoothie.addTimeSeries(lines[label], {
  506. strokeStyle: "rgb(" + c[0] + "," + c[1] + "," + c[2] + ")",
  507. //fillStyle:"rgba("+c[0]+","+c[1]+","+c[2]+",0.3)",
  508. lineWidth: 2
  509. });
  510. i++;
  511. }
  512. world.doProfiling = false;
  513. smoothie.stop();
  514. smoothieCanvas.style.display = "none";
  515. // STATS
  516. stats = new Stats();
  517. stats.domElement.style.position = 'absolute';
  518. stats.domElement.style.top = '0px';
  519. stats.domElement.style.zIndex = 100;
  520. container.appendChild(stats.domElement);
  521. if (window.dat != undefined) {
  522. gui = new dat.GUI();
  523. gui.domElement.parentNode.style.zIndex = 120;
  524. // Render mode
  525. var rf = gui.addFolder('Rendering');
  526. rf.add(settings, 'rendermode', {Solid: "solid", Wireframe: "wireframe"}).onChange(function (mode) {
  527. setRenderMode(mode);
  528. });
  529. rf.add(settings, 'contacts');
  530. rf.add(settings, 'cm2contact');
  531. rf.add(settings, 'normals');
  532. rf.add(settings, 'constraints');
  533. rf.add(settings, 'axes');
  534. rf.add(settings, 'particleSize').min(0).max(1).onChange(function (size) {
  535. for (var i = 0; i < visuals.length; i++) {
  536. if (bodies[i] instanceof CANNON.Particle)
  537. visuals[i].scale.set(size, size, size);
  538. }
  539. });
  540. rf.add(settings, 'shadows').onChange(function (shadows) {
  541. if (shadows) {
  542. renderer.shadowMapAutoUpdate = true;
  543. } else {
  544. renderer.shadowMapAutoUpdate = false;
  545. renderer.clearTarget(light.shadowMap);
  546. }
  547. });
  548. rf.add(settings, 'aabbs');
  549. rf.add(settings, 'profiling').onChange(function (profiling) {
  550. if (profiling) {
  551. world.doProfiling = true;
  552. smoothie.start();
  553. smoothieCanvas.style.display = "block";
  554. } else {
  555. world.doProfiling = false;
  556. smoothie.stop();
  557. smoothieCanvas.style.display = "none";
  558. }
  559. });
  560. // World folder
  561. var wf = gui.addFolder('World');
  562. // Pause
  563. wf.add(settings, 'paused').onChange(function (p) {
  564. /*if(p){
  565. smoothie.stop();
  566. } else {
  567. smoothie.start();
  568. }*/
  569. });
  570. wf.add(settings, 'stepFrequency', 60, 60 * 10).step(60);
  571. var maxg = 100;
  572. wf.add(settings, 'gx', -maxg, maxg).onChange(function (gx) {
  573. if (!isNaN(gx)) {
  574. world.gravity.set(gx, settings.gy, settings.gz);
  575. }
  576. });
  577. wf.add(settings, 'gy', -maxg, maxg).onChange(function (gy) {
  578. if (!isNaN(gy))
  579. world.gravity.set(settings.gx, gy, settings.gz);
  580. });
  581. wf.add(settings, 'gz', -maxg, maxg).onChange(function (gz) {
  582. if (!isNaN(gz))
  583. world.gravity.set(settings.gx, settings.gy, gz);
  584. });
  585. wf.add(settings, 'quatNormalizeSkip', 0, 50).step(1).onChange(function (skip) {
  586. if (!isNaN(skip)) {
  587. world.quatNormalizeSkip = skip;
  588. }
  589. });
  590. wf.add(settings, 'quatNormalizeFast').onChange(function (fast) {
  591. world.quatNormalizeFast = !!fast;
  592. });
  593. // Solver folder
  594. var sf = gui.addFolder('Solver');
  595. sf.add(settings, 'iterations', 1, 50).step(1).onChange(function (it) {
  596. world.solver.iterations = it;
  597. });
  598. sf.add(settings, 'k', 10, 10000000).onChange(function (k) {
  599. that.setGlobalSpookParams(settings.k, settings.d, 1 / settings.stepFrequency);
  600. });
  601. sf.add(settings, 'd', 0, 20).step(0.1).onChange(function (d) {
  602. that.setGlobalSpookParams(settings.k, settings.d, 1 / settings.stepFrequency);
  603. });
  604. sf.add(settings, 'tolerance', 0.0, 10.0).step(0.01).onChange(function (t) {
  605. world.solver.tolerance = t;
  606. });
  607. // Scene picker
  608. sceneFolder = gui.addFolder('Scenes');
  609. sceneFolder.open();
  610. }
  611. // Trackball controls
  612. if (CAMERA_TEST_TRACKBALL) {
  613. controls = new THREE.TrackballControls(camera, renderer.domElement);
  614. controls.rotateSpeed = 1.0;
  615. controls.zoomSpeed = 1.2;
  616. controls.panSpeed = 0.2;
  617. controls.noZoom = false;
  618. controls.noPan = false;
  619. controls.staticMoving = false;
  620. controls.dynamicDampingFactor = 0.3;
  621. var radius = 100;
  622. controls.minDistance = 0.0;
  623. controls.maxDistance = radius * 1000;
  624. controls.keys = [65, 83, 68]; // [ rotateKey, zoomKey, panKey ]
  625. controls.screen.width = SCREEN_WIDTH;
  626. controls.screen.height = SCREEN_HEIGHT;
  627. }
  628. }
  629. var t = 0, newTime, delta;
  630. function animate() {
  631. requestAnimationFrame(animate);
  632. if (!settings.paused) {
  633. updateVisuals();
  634. updatePhysics();
  635. }
  636. render();
  637. stats.update();
  638. }
  639. var lastCallTime = 0;
  640. function updatePhysics() {
  641. // // Step world
  642. // var timeStep = 1 / settings.stepFrequency;
  643. //
  644. // var now = Date.now() / 1000;
  645. //
  646. // if (!lastCallTime) {
  647. // // last call time not saved, cant guess elapsed time. Take a simple step.
  648. // world.step(timeStep);
  649. // lastCallTime = now;
  650. // return;
  651. // }
  652. //
  653. // var timeSinceLastCall = now - lastCallTime;
  654. //
  655. // world.step(timeStep, timeSinceLastCall, settings.maxSubSteps);
  656. //
  657. // lastCallTime = now;
  658. }
  659. function onDocumentMouseMove(event) {
  660. mouseX = (event.clientX - windowHalfX);
  661. mouseY = (event.clientY - windowHalfY);
  662. }
  663. function onWindowResize(event) {
  664. SCREEN_WIDTH = s_iCanvasResizeWidth + s_iCanvasOffsetWidth * 2;
  665. SCREEN_HEIGHT = s_iCanvasResizeHeight + s_iCanvasOffsetHeight * 2;
  666. if (CAMERA_TEST_TRACKBALL) {
  667. controls.screen.width = SCREEN_WIDTH;
  668. controls.screen.height = SCREEN_HEIGHT;
  669. }
  670. }
  671. function render() {
  672. if (CAMERA_TEST_TRACKBALL || CAMERA_TEST_TRANSFORM && controls !== null) {
  673. controls.update();
  674. }
  675. renderer.clear();
  676. renderer.render(that.scene, camera);
  677. }
  678. s_oRender = render;
  679. document.addEventListener('keypress', function (e) {
  680. if (e.keyCode) {
  681. switch (e.keyCode) {
  682. case 32: // Space - restart
  683. restartCurrentScene();
  684. break;
  685. case 104: // h - toggle widgets
  686. if (stats.domElement.style.display == "none") {
  687. stats.domElement.style.display = "block";
  688. info.style.display = "block";
  689. } else {
  690. stats.domElement.style.display = "none";
  691. info.style.display = "none";
  692. }
  693. break;
  694. case 97: // a - AABBs
  695. settings.aabbs = !settings.aabbs;
  696. updategui();
  697. break;
  698. case 99: // c - constraints
  699. settings.constraints = !settings.constraints;
  700. updategui();
  701. break;
  702. case 112: // p
  703. settings.paused = !settings.paused;
  704. updategui();
  705. break;
  706. case 115: // s
  707. var timeStep = 1 / settings.stepFrequency;
  708. world.step(timeStep);
  709. updateVisuals();
  710. break;
  711. case 109: // m - toggle materials
  712. var idx = renderModes.indexOf(settings.rendermode);
  713. idx++;
  714. idx = idx % renderModes.length; // begin at 0 if we exceeded number of modes
  715. setRenderMode(renderModes[idx]);
  716. updategui();
  717. break;
  718. case 49:
  719. case 50:
  720. case 51:
  721. case 52:
  722. case 53:
  723. case 54:
  724. case 55:
  725. case 56:
  726. case 57:
  727. // Change scene
  728. // Only for numbers 1-9 and if no input field is active
  729. if (scenes.length > e.keyCode - 49 && !document.activeElement.localName.match(/input/)) {
  730. changeScene(e.keyCode - 49);
  731. }
  732. break;
  733. }
  734. }
  735. });
  736. function changeScene(n) {
  737. that.dispatchEvent({type: 'destroy'});
  738. settings.paused = false;
  739. updategui();
  740. buildScene(n);
  741. }
  742. function start() {
  743. buildScene(0);
  744. }
  745. function buildScene(n) {
  746. // Remove current bodies and visuals
  747. var num = visuals.length;
  748. for (var i = 0; i < num; i++) {
  749. world.remove(bodies.pop());
  750. var mesh = visuals.pop();
  751. that.scene.remove(mesh);
  752. }
  753. // Remove all constraints
  754. while (world.constraints.length) {
  755. world.removeConstraint(world.constraints[0]);
  756. }
  757. // Run the user defined "build scene" function
  758. scenes[n]();
  759. // Read the newly set data to the gui
  760. settings.iterations = world.solver.iterations;
  761. settings.gx = world.gravity.x + 0.0;
  762. settings.gy = world.gravity.y + 0.0;
  763. settings.gz = world.gravity.z + 0.0;
  764. settings.quatNormalizeSkip = world.quatNormalizeSkip;
  765. settings.quatNormalizeFast = world.quatNormalizeFast;
  766. updategui();
  767. restartGeometryCaches();
  768. }
  769. function GeometryCache(createFunc) {
  770. var that = this, geometries = [], gone = [];
  771. this.request = function () {
  772. if (geometries.length) {
  773. geo = geometries.pop();
  774. } else {
  775. geo = createFunc();
  776. }
  777. scene.add(geo);
  778. gone.push(geo);
  779. return geo;
  780. };
  781. this.restart = function () {
  782. while (gone.length) {
  783. geometries.push(gone.pop());
  784. }
  785. };
  786. this.hideCached = function () {
  787. for (var i = 0; i < geometries.length; i++) {
  788. scene.remove(geometries[i]);
  789. }
  790. };
  791. }
  792. };
  793. CANNON.Demo.prototype = new CANNON.EventTarget();
  794. CANNON.Demo.constructor = CANNON.Demo;
  795. CANNON.Demo.prototype.setGlobalSpookParams = function (k, d, h) {
  796. var world = this.world;
  797. // Set for all constraints
  798. for (var i = 0; i < world.constraints.length; i++) {
  799. var c = world.constraints[i];
  800. for (var j = 0; j < c.equations.length; j++) {
  801. var eq = c.equations[j];
  802. eq.setSpookParams(k, d, h);
  803. }
  804. }
  805. // Set for all contact materals
  806. for (var i = 0; i < world.contactmaterials.length; i++) {
  807. var cm = world.contactmaterials[i];
  808. cm.contactEquationStiffness = k;
  809. cm.frictionEquationStiffness = k;
  810. cm.contactEquationRelaxation = d;
  811. cm.frictionEquationRelaxation = d;
  812. }
  813. world.defaultContactMaterial.contactEquationStiffness = k;
  814. world.defaultContactMaterial.frictionEquationStiffness = k;
  815. world.defaultContactMaterial.contactEquationRelaxation = d;
  816. world.defaultContactMaterial.frictionEquationRelaxation = d;
  817. };
  818. CANNON.Demo.prototype.createTransformControl = function (oMesh, oBody) {
  819. controls = new THREE.TransformControls(camera, renderer.domElement);
  820. // controls.addEventListener('change', s_oRender);
  821. scene.add(oMesh);
  822. controls.attach(oMesh, oBody);
  823. scene.add(controls);
  824. console.log("CREATE");
  825. window.addEventListener('keydown', function (event) {
  826. switch (event.keyCode) {
  827. case 81: // Q
  828. controls.setSpace(controls.space === "local" ? "world" : "local");
  829. break;
  830. case 17: // Ctrl
  831. controls.setTranslationSnap(100);
  832. controls.setRotationSnap(THREE.Math.degToRad(15));
  833. break;
  834. case 87: // W
  835. controls.setMode("translate");
  836. break;
  837. case 69: // E
  838. controls.setMode("rotate");
  839. break;
  840. case 82: // R
  841. controls.setMode("scale");
  842. break;
  843. case 187:
  844. case 107: // +, =, num+
  845. controls.setSize(controls.size + 0.1);
  846. break;
  847. case 189:
  848. case 109: // -, _, num-
  849. controls.setSize(Math.max(controls.size - 0.1, 0.1));
  850. break;
  851. }
  852. });
  853. window.addEventListener('keyup', function (event) {
  854. switch (event.keyCode) {
  855. case 17: // Ctrl
  856. controls.setTranslationSnap(null);
  857. controls.setRotationSnap(null);
  858. break;
  859. }
  860. });
  861. };
  862. CANNON.Demo.prototype.getWorld = function () {
  863. return this.world;
  864. };
  865. CANNON.Demo.prototype.addVisual = function (body, material) {
  866. var s = this.settings;
  867. // What geometry should be used?
  868. var mesh;
  869. if (body instanceof CANNON.Body) {
  870. mesh = this.shape2mesh(body, material);
  871. }
  872. if (mesh) {
  873. // Add body
  874. this.bodies.push(body);
  875. this.visuals.push(mesh);
  876. body.visualref = mesh;
  877. body.visualref.visualId = this.bodies.length - 1;
  878. //mesh.useQuaternion = true;
  879. this.scene.add(mesh);
  880. }
  881. return mesh;
  882. };
  883. CANNON.Demo.prototype.addVisuals = function (bodies) {
  884. for (var i = 0; i < bodies.length; i++) {
  885. this.addVisual(bodies[i]);
  886. }
  887. };
  888. CANNON.Demo.prototype.removeVisual = function (body) {
  889. if (body.visualref) {
  890. var bodies = this.bodies,
  891. visuals = this.visuals,
  892. old_b = [],
  893. old_v = [],
  894. n = bodies.length;
  895. for (var i = 0; i < n; i++) {
  896. old_b.unshift(bodies.pop());
  897. old_v.unshift(visuals.pop());
  898. }
  899. var id = body.visualref.visualId;
  900. for (var j = 0; j < old_b.length; j++) {
  901. if (j !== id) {
  902. var i = j > id ? j - 1 : j;
  903. bodies[i] = old_b[j];
  904. visuals[i] = old_v[j];
  905. bodies[i].visualref = old_b[j].visualref;
  906. bodies[i].visualref.visualId = i;
  907. }
  908. }
  909. body.visualref.visualId = null;
  910. this.scene.remove(body.visualref);
  911. body.visualref = null;
  912. }
  913. };
  914. CANNON.Demo.prototype.removeAllVisuals = function () {
  915. while (this.bodies.length) {
  916. this.removeVisual(this.bodies[0]);
  917. }
  918. };
  919. CANNON.Demo.prototype.shape2mesh = function (body, material) {
  920. var wireframe = this.settings.renderMode === "wireframe";
  921. var obj = new THREE.Object3D();
  922. for (var l = 0; l < body.shapes.length; l++) {
  923. var shape = body.shapes[l];
  924. var mesh;
  925. switch (shape.type) {
  926. case CANNON.Shape.types.SPHERE:
  927. var sphere_geometry = new THREE.SphereGeometry(shape.radius, 8, 8);
  928. if (material === undefined) {
  929. mesh = new THREE.Mesh(sphere_geometry, this.currentMaterial);
  930. } else {
  931. mesh = new THREE.Mesh(sphere_geometry, material);
  932. }
  933. mesh.castShadow = true;
  934. break;
  935. case CANNON.Shape.types.PARTICLE:
  936. mesh = new THREE.Mesh(this.particleGeo, this.particleMaterial);
  937. var s = this.settings;
  938. mesh.scale.set(s.particleSize, s.particleSize, s.particleSize);
  939. break;
  940. case CANNON.Shape.types.PLANE:
  941. var geometry = new THREE.PlaneGeometry(10, 10, 4, 4);
  942. mesh = new THREE.Object3D();
  943. var submesh = new THREE.Object3D();
  944. var ground;
  945. if (material === undefined) {
  946. ground = new THREE.Mesh(geometry, this.currentMaterial);
  947. } else {
  948. ground = new THREE.Mesh(geometry, material);
  949. }
  950. ground.scale.set(100, 100, 100);
  951. submesh.add(ground);
  952. ground.castShadow = false;
  953. ground.receiveShadow = true;
  954. mesh.add(submesh);
  955. break;
  956. case CANNON.Shape.types.BOX:
  957. var box_geometry = new THREE.BoxGeometry(shape.halfExtents.x * 2,
  958. shape.halfExtents.y * 2,
  959. shape.halfExtents.z * 2);
  960. if (material === undefined) {
  961. mesh = new THREE.Mesh(box_geometry, this.currentMaterial);
  962. } else {
  963. mesh = new THREE.Mesh(box_geometry, material);
  964. }
  965. break;
  966. case CANNON.Shape.types.CONVEXPOLYHEDRON:
  967. var geo = new THREE.Geometry();
  968. // Add vertices
  969. for (var i = 0; i < shape.vertices.length; i++) {
  970. var v = shape.vertices[i];
  971. geo.vertices.push(new THREE.Vector3(v.x, v.y, v.z));
  972. }
  973. for (var i = 0; i < shape.faces.length; i++) {
  974. var face = shape.faces[i];
  975. // add triangles
  976. var a = face[0];
  977. for (var j = 1; j < face.length - 1; j++) {
  978. var b = face[j];
  979. var c = face[j + 1];
  980. geo.faces.push(new THREE.Face3(a, b, c));
  981. }
  982. }
  983. geo.computeBoundingSphere();
  984. geo.computeFaceNormals();
  985. if (material === undefined) {
  986. mesh = new THREE.Mesh(geo, this.currentMaterial);
  987. } else {
  988. mesh = new THREE.Mesh(geo, material);
  989. }
  990. break;
  991. case CANNON.Shape.types.HEIGHTFIELD:
  992. var geometry = new THREE.Geometry();
  993. var v0 = new CANNON.Vec3();
  994. var v1 = new CANNON.Vec3();
  995. var v2 = new CANNON.Vec3();
  996. for (var xi = 0; xi < shape.data.length - 1; xi++) {
  997. for (var yi = 0; yi < shape.data[xi].length - 1; yi++) {
  998. for (var k = 0; k < 2; k++) {
  999. shape.getConvexTrianglePillar(xi, yi, k === 0);
  1000. v0.copy(shape.pillarConvex.vertices[0]);
  1001. v1.copy(shape.pillarConvex.vertices[1]);
  1002. v2.copy(shape.pillarConvex.vertices[2]);
  1003. v0.vadd(shape.pillarOffset, v0);
  1004. v1.vadd(shape.pillarOffset, v1);
  1005. v2.vadd(shape.pillarOffset, v2);
  1006. geometry.vertices.push(
  1007. new THREE.Vector3(v0.x, v0.y, v0.z),
  1008. new THREE.Vector3(v1.x, v1.y, v1.z),
  1009. new THREE.Vector3(v2.x, v2.y, v2.z)
  1010. );
  1011. var i = geometry.vertices.length - 3;
  1012. geometry.faces.push(new THREE.Face3(i, i + 1, i + 2));
  1013. }
  1014. }
  1015. }
  1016. geometry.computeBoundingSphere();
  1017. geometry.computeFaceNormals();
  1018. if (material === undefined) {
  1019. mesh = new THREE.Mesh(geometry, this.currentMaterial);
  1020. } else {
  1021. mesh = new THREE.Mesh(geometry, material);
  1022. }
  1023. break;
  1024. case CANNON.Shape.types.TRIMESH:
  1025. var geometry = new THREE.Geometry();
  1026. var v0 = new CANNON.Vec3();
  1027. var v1 = new CANNON.Vec3();
  1028. var v2 = new CANNON.Vec3();
  1029. for (var i = 0; i < shape.indices.length / 3; i++) {
  1030. shape.getTriangleVertices(i, v0, v1, v2);
  1031. geometry.vertices.push(
  1032. new THREE.Vector3(v0.x, v0.y, v0.z),
  1033. new THREE.Vector3(v1.x, v1.y, v1.z),
  1034. new THREE.Vector3(v2.x, v2.y, v2.z)
  1035. );
  1036. var j = geometry.vertices.length - 3;
  1037. geometry.faces.push(new THREE.Face3(j, j + 1, j + 2));
  1038. }
  1039. geometry.computeBoundingSphere();
  1040. geometry.computeFaceNormals();
  1041. if (material === undefined) {
  1042. mesh = new THREE.Mesh(geometry, this.currentMaterial);
  1043. } else {
  1044. mesh = new THREE.Mesh(geometry, material);
  1045. }
  1046. break;
  1047. default:
  1048. throw "Visual type not recognized: " + shape.type;
  1049. }
  1050. mesh.receiveShadow = true;
  1051. mesh.castShadow = true;
  1052. if (mesh.children) {
  1053. for (var i = 0; i < mesh.children.length; i++) {
  1054. mesh.children[i].castShadow = true;
  1055. mesh.children[i].receiveShadow = true;
  1056. if (mesh.children[i]) {
  1057. for (var j = 0; j < mesh.children[i].length; j++) {
  1058. mesh.children[i].children[j].castShadow = true;
  1059. mesh.children[i].children[j].receiveShadow = true;
  1060. }
  1061. }
  1062. }
  1063. }
  1064. var o = body.shapeOffsets[l];
  1065. var q = body.shapeOrientations[l];
  1066. mesh.position.set(o.x, o.y, o.z);
  1067. mesh.quaternion.set(q.x, q.y, q.z, q.w);
  1068. obj.add(mesh);
  1069. }
  1070. this.camera = function () {
  1071. return camera;
  1072. };
  1073. this.getScene = function () {
  1074. return scene;
  1075. };
  1076. return obj;
  1077. };