├── LICENSE ├── README.md ├── bower.json ├── examples ├── body.html ├── collisions.html ├── compound.html ├── constraints_car.html ├── css │ └── styles.css ├── heightfield.html ├── images │ ├── grass.png │ ├── plywood.jpg │ ├── rocks.jpg │ └── wood.jpg ├── jenga.html ├── js │ ├── ammo.js │ ├── simplex-noise.js │ ├── stats.js │ ├── three.min.js │ └── tween.js ├── memorytest-compound.html ├── memorytest-convex.html ├── memorytest.html ├── models │ ├── mustang.js │ └── mustang_wheel.js ├── shapes.html └── vehicle.html ├── physi.js └── physijs_worker.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2012 Chandler Prall. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Physijs 2 | ======= 3 | #### Physics plugin for [three.js](https://github.com/mrdoob/three.js) 4 | 5 | Physijs brings a very easy to use interface to the three.js framework. One of the reasons three.js is so popular is because it is so incredibly easy for graphics newbies to get into 3D programming. Physijs takes that philosophy to heart and makes physics simulations just as easy to run. In fact, there are just [five easy steps](https://github.com/chandlerprall/Physijs/wiki/Basic-Setup) that must be taken to make a 3D scene come alive. 6 | 7 | #### How does Physijs work? 8 | Physijs is built on top of [ammo.js](https://github.com/kripken/ammo.js/) (although there is also a [cannon.js branch](https://github.com/chandlerprall/Physijs/tree/cannon)) and runs the physics simulation in a separate thread (via web worker) to avoid impacting in your application's performance and taking up your 3D rendering time. 9 | 10 | A lot of effort has been made to keep the style of code the same when using Physijs. Apart from [updating an object's position](https://github.com/chandlerprall/Physijs/wiki/Updating-an-object's-position-&-rotation), all of the normal three.js conventions remain the same. If you are used to three.js, you already know how to use the Physijs plugin. 11 | 12 | #### Who is this for? 13 | You, hopefully. If you are familiar with [three.js](https://github.com/mrdoob/three.js) and want to add physics to your scene, this is the plugin for you. No mucking about with shape definitions, keeping objects in their correct positions, or identifying collisions - simply use a few Physijs objects in place of three.js's and you'll automatically have a dynamic environment. 14 | 15 | If you need (or want) a feature not already included then add it to the [issue tracker](https://github.com/chandlerprall/Physijs/issues) or implement it yourself and send over a pull request. 16 | 17 | ### Examples 18 | [![rigid bodies](http://chandlerprall.github.io/Physijs/examples/body.jpg)](http://chandlerprall.github.io/Physijs/examples/body.html) 19 | [![collisions](http://chandlerprall.github.io/Physijs/examples/collisions.jpg)](http://chandlerprall.github.io/Physijs/examples/collisions.html) 20 | [![compound shapes](http://chandlerprall.github.io/Physijs/examples/compound.jpg)](http://chandlerprall.github.io/Physijs/examples/compound.html) 21 | [![all shapes](http://chandlerprall.github.io/Physijs/examples/shapes.jpg)](http://chandlerprall.github.io/Physijs/examples/shapes.html) 22 | [![jenga](http://chandlerprall.github.io/Physijs/examples/jenga.jpg)](http://chandlerprall.github.io/Physijs/examples/jenga.html) 23 | [![car constraints](http://chandlerprall.github.io/Physijs/examples/constraints_car.jpg)](http://chandlerprall.github.io/Physijs/examples/constraints_car.html) 24 | [![vehicle](http://chandlerprall.github.io/Physijs/examples/vehicle.jpg)](http://chandlerprall.github.io/Physijs/examples/vehicle.html) 25 | 26 | ### Features 27 | * Support for [multiple object shapes](https://github.com/chandlerprall/Physijs/wiki/Basic-Shapes), including custom convex or concave objects as well as heightmaps 28 | * [Material system](https://github.com/chandlerprall/Physijs/wiki/Materials) provides simple control over friction and restitution ("bounciness") 29 | * Integrated collision detection and events 30 | * Compound objects using the hierarchy system in three.js 31 | * Vehicle system 32 | * [Constraint systems](https://github.com/chandlerprall/Physijs/wiki/Constraints) such as point-to-point and hinge 33 | * Rotations using either euler or quaternion systems - your preference 34 | * Built seamlessly on top of three.js to keep the same convention and coding style 35 | 36 | ### In the Future 37 | * More (and better) optimizations 38 | * It would be awesome to have concave shape decomposition 39 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Physijs", 3 | "version": "0.1.0", 4 | "main": [ 5 | "physi.js", 6 | "physijs_worker.js" 7 | ], 8 | "ignore": [ 9 | ".jshintrc" 10 | ], 11 | "dependencies": { 12 | "threejs": "r59", 13 | "ammo.js": "kripken/ammo.js" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/body.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Rigid body - Physijs 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 177 | 178 | 179 | 180 |
181 |

Rigid Body + User Interaction

182 |

Move your cursor near the boxes to push them.

183 |
184 |
185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /examples/collisions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Collisions - Physijs 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 186 | 187 | 188 | 189 |
190 |

Collisions

191 |

Demonstrating the collision callback

192 |
193 |
194 | 195 | 196 | -------------------------------------------------------------------------------- /examples/compound.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Compound Shapes - Physijs 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 257 | 258 | 259 | 260 |
261 |

Compound Shapes

262 |

Using the object hierarchy in Three.js to create compound shapes.

263 |
264 |
265 | 266 | 267 | -------------------------------------------------------------------------------- /examples/constraints_car.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Constraints, Car - Physijs 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 274 | 275 | 276 | 277 |
278 |

Constraints - Car

279 |

Use the arrow keys to drive around.

280 |
281 |
282 | 283 | 284 | -------------------------------------------------------------------------------- /examples/css/styles.css: -------------------------------------------------------------------------------- 1 | html { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | body { 7 | margin: 0; 8 | padding: 0; 9 | font-family: Verdana; 10 | font-size: 10pt; 11 | overflow: hidden; 12 | } 13 | 14 | #heading { 15 | position: absolute; 16 | top: 0; 17 | width: 100%; 18 | margin: 0; 19 | padding: 0; 20 | background-color: #71b87b; 21 | text-align: center; 22 | } 23 | #heading h1 { 24 | margin: 0; 25 | padding: 6px 0; 26 | background-color: #64a36d; 27 | font-size: 16px; 28 | } 29 | #heading a { 30 | color: #4488ff; 31 | } -------------------------------------------------------------------------------- /examples/heightfield.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Heightfield - Physijs 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 208 | 209 | 210 | 211 |
212 |

Heightfield in Physijs

213 |

214 | Heightmapped Terrain - 215 |
216 |

217 |
218 |
219 | 220 | 221 | -------------------------------------------------------------------------------- /examples/images/grass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chandlerprall/Physijs/a063373229312b77eb8627dd7d955f92229495e7/examples/images/grass.png -------------------------------------------------------------------------------- /examples/images/plywood.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chandlerprall/Physijs/a063373229312b77eb8627dd7d955f92229495e7/examples/images/plywood.jpg -------------------------------------------------------------------------------- /examples/images/rocks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chandlerprall/Physijs/a063373229312b77eb8627dd7d955f92229495e7/examples/images/rocks.jpg -------------------------------------------------------------------------------- /examples/images/wood.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chandlerprall/Physijs/a063373229312b77eb8627dd7d955f92229495e7/examples/images/wood.jpg -------------------------------------------------------------------------------- /examples/jenga.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Jenga - Physijs 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 255 | 256 | 257 | 258 |
259 |

Jenga

260 |

Click & drag on the blocks to move them around.

261 |
262 |
263 | 264 | 265 | 266 | -------------------------------------------------------------------------------- /examples/js/simplex-noise.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // Ported from Stefan Gustavson's java implementation 3 | // http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf 4 | // Read Stefan's excellent paper for details on how this code works. 5 | // 6 | // Sean McCullough banksean@gmail.com 7 | 8 | /** 9 | * You can pass in a random number generator object if you like. 10 | * It is assumed to have a random() method. 11 | */ 12 | var SimplexNoise = (function() { 13 | var SimplexNoise = function(r) { 14 | if (r == undefined) r = Math; 15 | this.grad3 = [[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0], 16 | [1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1], 17 | [0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]]; 18 | this.p = []; 19 | for (var i=0; i<256; i++) { 20 | this.p[i] = Math.floor(r.random()*256); 21 | } 22 | // To remove the need for index wrapping, double the permutation table length 23 | this.perm = []; 24 | for(var i=0; i<512; i++) { 25 | this.perm[i]=this.p[i & 255]; 26 | } 27 | 28 | // A lookup table to traverse the simplex around a given point in 4D. 29 | // Details can be found where this table is used, in the 4D noise method. 30 | this.simplex = [ 31 | [0,1,2,3],[0,1,3,2],[0,0,0,0],[0,2,3,1],[0,0,0,0],[0,0,0,0],[0,0,0,0],[1,2,3,0], 32 | [0,2,1,3],[0,0,0,0],[0,3,1,2],[0,3,2,1],[0,0,0,0],[0,0,0,0],[0,0,0,0],[1,3,2,0], 33 | [0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0], 34 | [1,2,0,3],[0,0,0,0],[1,3,0,2],[0,0,0,0],[0,0,0,0],[0,0,0,0],[2,3,0,1],[2,3,1,0], 35 | [1,0,2,3],[1,0,3,2],[0,0,0,0],[0,0,0,0],[0,0,0,0],[2,0,3,1],[0,0,0,0],[2,1,3,0], 36 | [0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0], 37 | [2,0,1,3],[0,0,0,0],[0,0,0,0],[0,0,0,0],[3,0,1,2],[3,0,2,1],[0,0,0,0],[3,1,2,0], 38 | [2,1,0,3],[0,0,0,0],[0,0,0,0],[0,0,0,0],[3,1,0,2],[0,0,0,0],[3,2,0,1],[3,2,1,0]]; 39 | }; 40 | 41 | SimplexNoise.prototype.dot = function(g, x, y) { 42 | return g[0]*x + g[1]*y; 43 | }; 44 | 45 | SimplexNoise.prototype.noise = function(xin, yin) { 46 | var n0, n1, n2; // Noise contributions from the three corners 47 | // Skew the input space to determine which simplex cell we're in 48 | var F2 = 0.5*(Math.sqrt(3.0)-1.0); 49 | var s = (xin+yin)*F2; // Hairy factor for 2D 50 | var i = Math.floor(xin+s); 51 | var j = Math.floor(yin+s); 52 | var G2 = (3.0-Math.sqrt(3.0))/6.0; 53 | var t = (i+j)*G2; 54 | var X0 = i-t; // Unskew the cell origin back to (x,y) space 55 | var Y0 = j-t; 56 | var x0 = xin-X0; // The x,y distances from the cell origin 57 | var y0 = yin-Y0; 58 | // For the 2D case, the simplex shape is an equilateral triangle. 59 | // Determine which simplex we are in. 60 | var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords 61 | if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1) 62 | else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1) 63 | // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and 64 | // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where 65 | // c = (3-sqrt(3))/6 66 | var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords 67 | var y1 = y0 - j1 + G2; 68 | var x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords 69 | var y2 = y0 - 1.0 + 2.0 * G2; 70 | // Work out the hashed gradient indices of the three simplex corners 71 | var ii = i & 255; 72 | var jj = j & 255; 73 | var gi0 = this.perm[ii+this.perm[jj]] % 12; 74 | var gi1 = this.perm[ii+i1+this.perm[jj+j1]] % 12; 75 | var gi2 = this.perm[ii+1+this.perm[jj+1]] % 12; 76 | // Calculate the contribution from the three corners 77 | var t0 = 0.5 - x0*x0-y0*y0; 78 | if(t0<0) n0 = 0.0; 79 | else { 80 | t0 *= t0; 81 | n0 = t0 * t0 * this.dot(this.grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient 82 | } 83 | var t1 = 0.5 - x1*x1-y1*y1; 84 | if(t1<0) n1 = 0.0; 85 | else { 86 | t1 *= t1; 87 | n1 = t1 * t1 * this.dot(this.grad3[gi1], x1, y1); 88 | } 89 | var t2 = 0.5 - x2*x2-y2*y2; 90 | if(t2<0) n2 = 0.0; 91 | else { 92 | t2 *= t2; 93 | n2 = t2 * t2 * this.dot(this.grad3[gi2], x2, y2); 94 | } 95 | // Add contributions from each corner to get the final noise value. 96 | // The result is scaled to return values in the interval [-1,1]. 97 | return 70.0 * (n0 + n1 + n2); 98 | }; 99 | 100 | // 3D simplex noise 101 | SimplexNoise.prototype.noise3d = function(xin, yin, zin) { 102 | var n0, n1, n2, n3; // Noise contributions from the four corners 103 | // Skew the input space to determine which simplex cell we're in 104 | var F3 = 1.0/3.0; 105 | var s = (xin+yin+zin)*F3; // Very nice and simple skew factor for 3D 106 | var i = Math.floor(xin+s); 107 | var j = Math.floor(yin+s); 108 | var k = Math.floor(zin+s); 109 | var G3 = 1.0/6.0; // Very nice and simple unskew factor, too 110 | var t = (i+j+k)*G3; 111 | var X0 = i-t; // Unskew the cell origin back to (x,y,z) space 112 | var Y0 = j-t; 113 | var Z0 = k-t; 114 | var x0 = xin-X0; // The x,y,z distances from the cell origin 115 | var y0 = yin-Y0; 116 | var z0 = zin-Z0; 117 | // For the 3D case, the simplex shape is a slightly irregular tetrahedron. 118 | // Determine which simplex we are in. 119 | var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords 120 | var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords 121 | if(x0>=y0) { 122 | if(y0>=z0) 123 | { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order 124 | else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order 125 | else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order 126 | } 127 | else { // x0p+1E3)l=Math.round(o*1E3/(i-p)),q=Math.min(q,l),r=Math.max(r,l),j.textContent=l+" FPS ("+q+"-"+r+")",a=Math.min(30,30-l/ 7 | 100*30),f.appendChild(f.firstChild).style.height=a+"px",p=i,o=0}}}; 8 | 9 | -------------------------------------------------------------------------------- /examples/js/tween.js: -------------------------------------------------------------------------------- 1 | // tween.js r2 - http://github.com/sole/tween.js 2 | var TWEEN=TWEEN||function(){var a,e,c,d,f=[];return{start:function(g){c=setInterval(this.update,1E3/(g||60))},stop:function(){clearInterval(c)},add:function(g){f.push(g)},getAll:function(){return f},removeAll:function(){f=[]},remove:function(g){a=f.indexOf(g);a!==-1&&f.splice(a,1)},update:function(){a=0;e=f.length;for(d=(new Date).getTime();a1?1:b;i=n(b);for(h in c)a[h]=e[h]+c[h]*i;l!==null&&l.call(a,i);if(b==1){m!==null&&m.call(a);k!==null&&k.start();return false}return true}};TWEEN.Easing={Linear:{},Quadratic:{},Cubic:{},Quartic:{},Quintic:{},Sinusoidal:{},Exponential:{},Circular:{},Elastic:{},Back:{},Bounce:{}};TWEEN.Easing.Linear.EaseNone=function(a){return a}; 5 | TWEEN.Easing.Quadratic.EaseIn=function(a){return a*a};TWEEN.Easing.Quadratic.EaseOut=function(a){return-a*(a-2)};TWEEN.Easing.Quadratic.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a;return-0.5*(--a*(a-2)-1)};TWEEN.Easing.Cubic.EaseIn=function(a){return a*a*a};TWEEN.Easing.Cubic.EaseOut=function(a){return--a*a*a+1};TWEEN.Easing.Cubic.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a*a;return 0.5*((a-=2)*a*a+2)};TWEEN.Easing.Quartic.EaseIn=function(a){return a*a*a*a}; 6 | TWEEN.Easing.Quartic.EaseOut=function(a){return-(--a*a*a*a-1)};TWEEN.Easing.Quartic.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a*a*a;return-0.5*((a-=2)*a*a*a-2)};TWEEN.Easing.Quintic.EaseIn=function(a){return a*a*a*a*a};TWEEN.Easing.Quintic.EaseOut=function(a){return(a-=1)*a*a*a*a+1};TWEEN.Easing.Quintic.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a*a*a*a;return 0.5*((a-=2)*a*a*a*a+2)};TWEEN.Easing.Sinusoidal.EaseIn=function(a){return-Math.cos(a*Math.PI/2)+1}; 7 | TWEEN.Easing.Sinusoidal.EaseOut=function(a){return Math.sin(a*Math.PI/2)};TWEEN.Easing.Sinusoidal.EaseInOut=function(a){return-0.5*(Math.cos(Math.PI*a)-1)};TWEEN.Easing.Exponential.EaseIn=function(a){return a==0?0:Math.pow(2,10*(a-1))};TWEEN.Easing.Exponential.EaseOut=function(a){return a==1?1:-Math.pow(2,-10*a)+1};TWEEN.Easing.Exponential.EaseInOut=function(a){if(a==0)return 0;if(a==1)return 1;if((a*=2)<1)return 0.5*Math.pow(2,10*(a-1));return 0.5*(-Math.pow(2,-10*(a-1))+2)}; 8 | TWEEN.Easing.Circular.EaseIn=function(a){return-(Math.sqrt(1-a*a)-1)};TWEEN.Easing.Circular.EaseOut=function(a){return Math.sqrt(1- --a*a)};TWEEN.Easing.Circular.EaseInOut=function(a){if((a/=0.5)<1)return-0.5*(Math.sqrt(1-a*a)-1);return 0.5*(Math.sqrt(1-(a-=2)*a)+1)};TWEEN.Easing.Elastic.EaseIn=function(a){var e,c=0.1,d=0.4;if(a==0)return 0;if(a==1)return 1;d||(d=0.3);if(!c||c<1){c=1;e=d/4}else e=d/(2*Math.PI)*Math.asin(1/c);return-(c*Math.pow(2,10*(a-=1))*Math.sin((a-e)*2*Math.PI/d))}; 9 | TWEEN.Easing.Elastic.EaseOut=function(a){var e,c=0.1,d=0.4;if(a==0)return 0;if(a==1)return 1;d||(d=0.3);if(!c||c<1){c=1;e=d/4}else e=d/(2*Math.PI)*Math.asin(1/c);return c*Math.pow(2,-10*a)*Math.sin((a-e)*2*Math.PI/d)+1}; 10 | TWEEN.Easing.Elastic.EaseInOut=function(a){var e,c=0.1,d=0.4;if(a==0)return 0;if(a==1)return 1;d||(d=0.3);if(!c||c<1){c=1;e=d/4}else e=d/(2*Math.PI)*Math.asin(1/c);if((a*=2)<1)return-0.5*c*Math.pow(2,10*(a-=1))*Math.sin((a-e)*2*Math.PI/d);return c*Math.pow(2,-10*(a-=1))*Math.sin((a-e)*2*Math.PI/d)*0.5+1};TWEEN.Easing.Back.EaseIn=function(a){return a*a*(2.70158*a-1.70158)};TWEEN.Easing.Back.EaseOut=function(a){return(a-=1)*a*(2.70158*a+1.70158)+1}; 11 | TWEEN.Easing.Back.EaseInOut=function(a){if((a*=2)<1)return 0.5*a*a*(3.5949095*a-2.5949095);return 0.5*((a-=2)*a*(3.5949095*a+2.5949095)+2)};TWEEN.Easing.Bounce.EaseIn=function(a){return 1-TWEEN.Easing.Bounce.EaseOut(1-a)};TWEEN.Easing.Bounce.EaseOut=function(a){return(a/=1)<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+0.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+0.9375:7.5625*(a-=2.625/2.75)*a+0.984375}; 12 | TWEEN.Easing.Bounce.EaseInOut=function(a){if(a<0.5)return TWEEN.Easing.Bounce.EaseIn(a*2)*0.5;return TWEEN.Easing.Bounce.EaseOut(a*2-1)*0.5+0.5}; 13 | -------------------------------------------------------------------------------- /examples/memorytest-compound.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Memory Test - Physijs 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 190 | 191 | 192 | 193 |
194 |

Memory Test

195 |

196 | Total cubes . 197 | Current cubes . 198 | Current scene objects . 199 |

200 |
201 |
202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /examples/memorytest-convex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Memory Test - Physijs 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 183 | 184 | 185 | 186 |
187 |

Memory Test

188 |

189 | Total cubes . 190 | Current cubes . 191 | Current scene objects . 192 |

193 |
194 |
195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /examples/memorytest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Memory Test - Physijs 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 183 | 184 | 185 | 186 |
187 |

Memory Test

188 |

189 | Total cubes . 190 | Current cubes . 191 | Current scene objects . 192 |

193 |
194 |
195 | 196 | 197 | -------------------------------------------------------------------------------- /examples/shapes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Shape emitter - Physijs 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 255 | 256 | 257 | 258 |
259 |

Shapes in Physijs

260 |

261 | All of the supported shapes in Physijs - 262 |
263 |

264 |
265 |
266 | 267 | 268 | -------------------------------------------------------------------------------- /examples/vehicle.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Rigid body - Physijs 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 267 | 268 | 269 | 270 |
271 |

Vehicle System

272 |

Use arrow keys to drive | model by ludwig

273 |
274 |
275 | 276 | 277 | -------------------------------------------------------------------------------- /physi.js: -------------------------------------------------------------------------------- 1 | window.Physijs = (function() { 2 | 'use strict'; 3 | 4 | var SUPPORT_TRANSFERABLE, 5 | _is_simulating = false, 6 | _Physijs = Physijs, // used for noConflict method 7 | Physijs = {}, // object assigned to window.Physijs 8 | Eventable, // class to provide simple event methods 9 | getObjectId, // returns a unique ID for a Physijs mesh object 10 | getEulerXYZFromQuaternion, getQuatertionFromEuler, 11 | convertWorldPositionToObject, // Converts a world-space position to object-space 12 | addObjectChildren, 13 | 14 | _temp1, _temp2, 15 | _temp_vector3_1 = new THREE.Vector3, 16 | _temp_vector3_2 = new THREE.Vector3, 17 | _temp_matrix4_1 = new THREE.Matrix4, 18 | _quaternion_1 = new THREE.Quaternion, 19 | 20 | // constants 21 | MESSAGE_TYPES = { 22 | WORLDREPORT: 0, 23 | COLLISIONREPORT: 1, 24 | VEHICLEREPORT: 2, 25 | CONSTRAINTREPORT: 3 26 | }, 27 | REPORT_ITEMSIZE = 14, 28 | COLLISIONREPORT_ITEMSIZE = 5, 29 | VEHICLEREPORT_ITEMSIZE = 9, 30 | CONSTRAINTREPORT_ITEMSIZE = 6; 31 | 32 | Physijs.scripts = {}; 33 | 34 | Eventable = function() { 35 | this._eventListeners = {}; 36 | }; 37 | Eventable.prototype.addEventListener = function( event_name, callback ) { 38 | if ( !this._eventListeners.hasOwnProperty( event_name ) ) { 39 | this._eventListeners[event_name] = []; 40 | } 41 | this._eventListeners[event_name].push( callback ); 42 | }; 43 | Eventable.prototype.removeEventListener = function( event_name, callback ) { 44 | var index; 45 | 46 | if ( !this._eventListeners.hasOwnProperty( event_name ) ) return false; 47 | 48 | if ( (index = this._eventListeners[event_name].indexOf( callback )) >= 0 ) { 49 | this._eventListeners[event_name].splice( index, 1 ); 50 | return true; 51 | } 52 | 53 | return false; 54 | }; 55 | Eventable.prototype.dispatchEvent = function( event_name ) { 56 | var i, 57 | parameters = Array.prototype.splice.call( arguments, 1 ); 58 | 59 | if ( this._eventListeners.hasOwnProperty( event_name ) ) { 60 | for ( i = 0; i < this._eventListeners[event_name].length; i++ ) { 61 | this._eventListeners[event_name][i].apply( this, parameters ); 62 | } 63 | } 64 | }; 65 | Eventable.make = function( obj ) { 66 | obj.prototype.addEventListener = Eventable.prototype.addEventListener; 67 | obj.prototype.removeEventListener = Eventable.prototype.removeEventListener; 68 | obj.prototype.dispatchEvent = Eventable.prototype.dispatchEvent; 69 | }; 70 | 71 | getObjectId = (function() { 72 | var _id = 1; 73 | return function() { 74 | return _id++; 75 | }; 76 | })(); 77 | 78 | getEulerXYZFromQuaternion = function ( x, y, z, w ) { 79 | return new THREE.Vector3( 80 | Math.atan2( 2 * ( x * w - y * z ), ( w * w - x * x - y * y + z * z ) ), 81 | Math.asin( 2 * ( x * z + y * w ) ), 82 | Math.atan2( 2 * ( z * w - x * y ), ( w * w + x * x - y * y - z * z ) ) 83 | ); 84 | }; 85 | 86 | getQuatertionFromEuler = function( x, y, z ) { 87 | var c1, s1, c2, s2, c3, s3, c1c2, s1s2; 88 | c1 = Math.cos( y ); 89 | s1 = Math.sin( y ); 90 | c2 = Math.cos( -z ); 91 | s2 = Math.sin( -z ); 92 | c3 = Math.cos( x ); 93 | s3 = Math.sin( x ); 94 | 95 | c1c2 = c1 * c2; 96 | s1s2 = s1 * s2; 97 | 98 | return { 99 | w: c1c2 * c3 - s1s2 * s3, 100 | x: c1c2 * s3 + s1s2 * c3, 101 | y: s1 * c2 * c3 + c1 * s2 * s3, 102 | z: c1 * s2 * c3 - s1 * c2 * s3 103 | }; 104 | }; 105 | 106 | convertWorldPositionToObject = function( position, object ) { 107 | _temp_matrix4_1.identity(); // reset temp matrix 108 | 109 | // Set the temp matrix's rotation to the object's rotation 110 | _temp_matrix4_1.identity().makeRotationFromQuaternion( object.quaternion ); 111 | 112 | // Invert rotation matrix in order to "unrotate" a point back to object space 113 | _temp_matrix4_1.getInverse( _temp_matrix4_1 ); 114 | 115 | // Yay! Temp vars! 116 | _temp_vector3_1.copy( position ); 117 | _temp_vector3_2.copy( object.position ); 118 | 119 | // Apply the rotation 120 | 121 | return _temp_vector3_1.sub( _temp_vector3_2 ).applyMatrix4( _temp_matrix4_1 ); 122 | }; 123 | 124 | 125 | 126 | // Physijs.noConflict 127 | Physijs.noConflict = function() { 128 | window.Physijs = _Physijs; 129 | return Physijs; 130 | }; 131 | 132 | 133 | // Physijs.createMaterial 134 | Physijs.createMaterial = function( material, friction, restitution ) { 135 | var physijs_material = function(){}; 136 | physijs_material.prototype = material; 137 | physijs_material = new physijs_material; 138 | 139 | physijs_material._physijs = { 140 | id: material.id, 141 | friction: friction === undefined ? .8 : friction, 142 | restitution: restitution === undefined ? .2 : restitution 143 | }; 144 | 145 | return physijs_material; 146 | }; 147 | 148 | 149 | // Constraints 150 | Physijs.PointConstraint = function( objecta, objectb, position ) { 151 | if ( position === undefined ) { 152 | position = objectb; 153 | objectb = undefined; 154 | } 155 | 156 | this.type = 'point'; 157 | this.appliedImpulse = 0; 158 | this.id = getObjectId(); 159 | this.objecta = objecta._physijs.id; 160 | this.positiona = convertWorldPositionToObject( position, objecta ).clone(); 161 | 162 | if ( objectb ) { 163 | this.objectb = objectb._physijs.id; 164 | this.positionb = convertWorldPositionToObject( position, objectb ).clone(); 165 | } 166 | }; 167 | Physijs.PointConstraint.prototype.getDefinition = function() { 168 | return { 169 | type: this.type, 170 | id: this.id, 171 | objecta: this.objecta, 172 | objectb: this.objectb, 173 | positiona: this.positiona, 174 | positionb: this.positionb 175 | }; 176 | }; 177 | 178 | Physijs.HingeConstraint = function( objecta, objectb, position, axis ) { 179 | if ( axis === undefined ) { 180 | axis = position; 181 | position = objectb; 182 | objectb = undefined; 183 | } 184 | 185 | this.type = 'hinge'; 186 | this.appliedImpulse = 0; 187 | this.id = getObjectId(); 188 | this.scene = objecta.parent; 189 | this.objecta = objecta._physijs.id; 190 | this.positiona = convertWorldPositionToObject( position, objecta ).clone(); 191 | this.position = position.clone(); 192 | this.axis = axis; 193 | 194 | if ( objectb ) { 195 | this.objectb = objectb._physijs.id; 196 | this.positionb = convertWorldPositionToObject( position, objectb ).clone(); 197 | } 198 | }; 199 | Physijs.HingeConstraint.prototype.getDefinition = function() { 200 | return { 201 | type: this.type, 202 | id: this.id, 203 | objecta: this.objecta, 204 | objectb: this.objectb, 205 | positiona: this.positiona, 206 | positionb: this.positionb, 207 | axis: this.axis 208 | }; 209 | }; 210 | /* 211 | * low = minimum angle in radians 212 | * high = maximum angle in radians 213 | * bias_factor = applied as a factor to constraint error 214 | * relaxation_factor = controls bounce (0.0 == no bounce) 215 | */ 216 | Physijs.HingeConstraint.prototype.setLimits = function( low, high, bias_factor, relaxation_factor ) { 217 | this.scene.execute( 'hinge_setLimits', { constraint: this.id, low: low, high: high, bias_factor: bias_factor, relaxation_factor: relaxation_factor } ); 218 | }; 219 | Physijs.HingeConstraint.prototype.enableAngularMotor = function( velocity, acceleration ) { 220 | this.scene.execute( 'hinge_enableAngularMotor', { constraint: this.id, velocity: velocity, acceleration: acceleration } ); 221 | }; 222 | Physijs.HingeConstraint.prototype.disableMotor = function( velocity, acceleration ) { 223 | this.scene.execute( 'hinge_disableMotor', { constraint: this.id } ); 224 | }; 225 | 226 | Physijs.SliderConstraint = function( objecta, objectb, position, axis ) { 227 | if ( axis === undefined ) { 228 | axis = position; 229 | position = objectb; 230 | objectb = undefined; 231 | } 232 | 233 | this.type = 'slider'; 234 | this.appliedImpulse = 0; 235 | this.id = getObjectId(); 236 | this.scene = objecta.parent; 237 | this.objecta = objecta._physijs.id; 238 | this.positiona = convertWorldPositionToObject( position, objecta ).clone(); 239 | this.axis = axis; 240 | 241 | if ( objectb ) { 242 | this.objectb = objectb._physijs.id; 243 | this.positionb = convertWorldPositionToObject( position, objectb ).clone(); 244 | } 245 | }; 246 | Physijs.SliderConstraint.prototype.getDefinition = function() { 247 | return { 248 | type: this.type, 249 | id: this.id, 250 | objecta: this.objecta, 251 | objectb: this.objectb, 252 | positiona: this.positiona, 253 | positionb: this.positionb, 254 | axis: this.axis 255 | }; 256 | }; 257 | Physijs.SliderConstraint.prototype.setLimits = function( lin_lower, lin_upper, ang_lower, ang_upper ) { 258 | this.scene.execute( 'slider_setLimits', { constraint: this.id, lin_lower: lin_lower, lin_upper: lin_upper, ang_lower: ang_lower, ang_upper: ang_upper } ); 259 | }; 260 | Physijs.SliderConstraint.prototype.setRestitution = function( linear, angular ) { 261 | this.scene.execute( 262 | 'slider_setRestitution', 263 | { 264 | constraint: this.id, 265 | linear: linear, 266 | angular: angular 267 | } 268 | ); 269 | }; 270 | Physijs.SliderConstraint.prototype.enableLinearMotor = function( velocity, acceleration) { 271 | this.scene.execute( 'slider_enableLinearMotor', { constraint: this.id, velocity: velocity, acceleration: acceleration } ); 272 | }; 273 | Physijs.SliderConstraint.prototype.disableLinearMotor = function() { 274 | this.scene.execute( 'slider_disableLinearMotor', { constraint: this.id } ); 275 | }; 276 | Physijs.SliderConstraint.prototype.enableAngularMotor = function( velocity, acceleration ) { 277 | this.scene.execute( 'slider_enableAngularMotor', { constraint: this.id, velocity: velocity, acceleration: acceleration } ); 278 | }; 279 | Physijs.SliderConstraint.prototype.disableAngularMotor = function() { 280 | this.scene.execute( 'slider_disableAngularMotor', { constraint: this.id } ); 281 | }; 282 | 283 | Physijs.ConeTwistConstraint = function( objecta, objectb, position ) { 284 | if ( position === undefined ) { 285 | throw 'Both objects must be defined in a ConeTwistConstraint.'; 286 | } 287 | this.type = 'conetwist'; 288 | this.appliedImpulse = 0; 289 | this.id = getObjectId(); 290 | this.scene = objecta.parent; 291 | this.objecta = objecta._physijs.id; 292 | this.positiona = convertWorldPositionToObject( position, objecta ).clone(); 293 | this.objectb = objectb._physijs.id; 294 | this.positionb = convertWorldPositionToObject( position, objectb ).clone(); 295 | this.axisa = { x: objecta.rotation.x, y: objecta.rotation.y, z: objecta.rotation.z }; 296 | this.axisb = { x: objectb.rotation.x, y: objectb.rotation.y, z: objectb.rotation.z }; 297 | }; 298 | Physijs.ConeTwistConstraint.prototype.getDefinition = function() { 299 | return { 300 | type: this.type, 301 | id: this.id, 302 | objecta: this.objecta, 303 | objectb: this.objectb, 304 | positiona: this.positiona, 305 | positionb: this.positionb, 306 | axisa: this.axisa, 307 | axisb: this.axisb 308 | }; 309 | }; 310 | Physijs.ConeTwistConstraint.prototype.setLimit = function( x, y, z ) { 311 | this.scene.execute( 'conetwist_setLimit', { constraint: this.id, x: x, y: y, z: z } ); 312 | }; 313 | Physijs.ConeTwistConstraint.prototype.enableMotor = function() { 314 | this.scene.execute( 'conetwist_enableMotor', { constraint: this.id } ); 315 | }; 316 | Physijs.ConeTwistConstraint.prototype.setMaxMotorImpulse = function( max_impulse ) { 317 | this.scene.execute( 'conetwist_setMaxMotorImpulse', { constraint: this.id, max_impulse: max_impulse } ); 318 | }; 319 | Physijs.ConeTwistConstraint.prototype.setMotorTarget = function( target ) { 320 | if ( target instanceof THREE.Vector3 ) { 321 | target = new THREE.Quaternion().setFromEuler( new THREE.Euler( target.x, target.y, target.z ) ); 322 | } else if ( target instanceof THREE.Euler ) { 323 | target = new THREE.Quaternion().setFromEuler( target ); 324 | } else if ( target instanceof THREE.Matrix4 ) { 325 | target = new THREE.Quaternion().setFromRotationMatrix( target ); 326 | } 327 | this.scene.execute( 'conetwist_setMotorTarget', { constraint: this.id, x: target.x, y: target.y, z: target.z, w: target.w } ); 328 | }; 329 | Physijs.ConeTwistConstraint.prototype.disableMotor = function() { 330 | this.scene.execute( 'conetwist_disableMotor', { constraint: this.id } ); 331 | }; 332 | 333 | Physijs.DOFConstraint = function( objecta, objectb, position ) { 334 | if ( position === undefined ) { 335 | position = objectb; 336 | objectb = undefined; 337 | } 338 | this.type = 'dof'; 339 | this.appliedImpulse = 0; 340 | this.id = getObjectId(); 341 | this.scene = objecta.parent; 342 | this.objecta = objecta._physijs.id; 343 | this.positiona = convertWorldPositionToObject( position, objecta ).clone(); 344 | this.axisa = { x: objecta.rotation.x, y: objecta.rotation.y, z: objecta.rotation.z }; 345 | 346 | if ( objectb ) { 347 | this.objectb = objectb._physijs.id; 348 | this.positionb = convertWorldPositionToObject( position, objectb ).clone(); 349 | this.axisb = { x: objectb.rotation.x, y: objectb.rotation.y, z: objectb.rotation.z }; 350 | } 351 | }; 352 | Physijs.DOFConstraint.prototype.getDefinition = function() { 353 | return { 354 | type: this.type, 355 | id: this.id, 356 | objecta: this.objecta, 357 | objectb: this.objectb, 358 | positiona: this.positiona, 359 | positionb: this.positionb, 360 | axisa: this.axisa, 361 | axisb: this.axisb 362 | }; 363 | }; 364 | Physijs.DOFConstraint.prototype.setLinearLowerLimit = function( limit ) { 365 | this.scene.execute( 'dof_setLinearLowerLimit', { constraint: this.id, x: limit.x, y: limit.y, z: limit.z } ); 366 | }; 367 | Physijs.DOFConstraint.prototype.setLinearUpperLimit = function( limit ) { 368 | this.scene.execute( 'dof_setLinearUpperLimit', { constraint: this.id, x: limit.x, y: limit.y, z: limit.z } ); 369 | }; 370 | Physijs.DOFConstraint.prototype.setAngularLowerLimit = function( limit ) { 371 | this.scene.execute( 'dof_setAngularLowerLimit', { constraint: this.id, x: limit.x, y: limit.y, z: limit.z } ); 372 | }; 373 | Physijs.DOFConstraint.prototype.setAngularUpperLimit = function( limit ) { 374 | this.scene.execute( 'dof_setAngularUpperLimit', { constraint: this.id, x: limit.x, y: limit.y, z: limit.z } ); 375 | }; 376 | Physijs.DOFConstraint.prototype.enableAngularMotor = function( which ) { 377 | this.scene.execute( 'dof_enableAngularMotor', { constraint: this.id, which: which } ); 378 | }; 379 | Physijs.DOFConstraint.prototype.configureAngularMotor = function( which, low_angle, high_angle, velocity, max_force ) { 380 | this.scene.execute( 'dof_configureAngularMotor', { constraint: this.id, which: which, low_angle: low_angle, high_angle: high_angle, velocity: velocity, max_force: max_force } ); 381 | }; 382 | Physijs.DOFConstraint.prototype.disableAngularMotor = function( which ) { 383 | this.scene.execute( 'dof_disableAngularMotor', { constraint: this.id, which: which } ); 384 | }; 385 | 386 | // Physijs.Scene 387 | Physijs.Scene = function( params ) { 388 | var self = this; 389 | 390 | Eventable.call( this ); 391 | THREE.Scene.call( this ); 392 | 393 | this._worker = new Worker( Physijs.scripts.worker || 'physijs_worker.js' ); 394 | this._worker.transferableMessage = this._worker.webkitPostMessage || this._worker.postMessage; 395 | this._materials_ref_counts = {}; 396 | this._objects = {}; 397 | this._vehicles = {}; 398 | this._constraints = {}; 399 | 400 | var ab = new ArrayBuffer( 1 ); 401 | this._worker.transferableMessage( ab, [ab] ); 402 | SUPPORT_TRANSFERABLE = ( ab.byteLength === 0 ); 403 | 404 | this._worker.onmessage = function ( event ) { 405 | var _temp, 406 | data = event.data; 407 | 408 | if ( data instanceof ArrayBuffer && data.byteLength !== 1 ) { // byteLength === 1 is the worker making a SUPPORT_TRANSFERABLE test 409 | data = new Float32Array( data ); 410 | } 411 | 412 | if ( data instanceof Float32Array ) { 413 | 414 | // transferable object 415 | switch ( data[0] ) { 416 | case MESSAGE_TYPES.WORLDREPORT: 417 | self._updateScene( data ); 418 | break; 419 | 420 | case MESSAGE_TYPES.COLLISIONREPORT: 421 | self._updateCollisions( data ); 422 | break; 423 | 424 | case MESSAGE_TYPES.VEHICLEREPORT: 425 | self._updateVehicles( data ); 426 | break; 427 | 428 | case MESSAGE_TYPES.CONSTRAINTREPORT: 429 | self._updateConstraints( data ); 430 | break; 431 | } 432 | 433 | } else { 434 | 435 | if ( data.cmd ) { 436 | 437 | // non-transferable object 438 | switch ( data.cmd ) { 439 | case 'objectReady': 440 | _temp = data.params; 441 | if ( self._objects[ _temp ] ) { 442 | self._objects[ _temp ].dispatchEvent( 'ready' ); 443 | } 444 | break; 445 | 446 | case 'worldReady': 447 | self.dispatchEvent( 'ready' ); 448 | break; 449 | 450 | case 'vehicle': 451 | window.test = data; 452 | break; 453 | 454 | default: 455 | // Do nothing, just show the message 456 | console.debug('Received: ' + data.cmd); 457 | console.dir(data.params); 458 | break; 459 | } 460 | 461 | } else { 462 | 463 | switch ( data[0] ) { 464 | case MESSAGE_TYPES.WORLDREPORT: 465 | self._updateScene( data ); 466 | break; 467 | 468 | case MESSAGE_TYPES.COLLISIONREPORT: 469 | self._updateCollisions( data ); 470 | break; 471 | 472 | case MESSAGE_TYPES.VEHICLEREPORT: 473 | self._updateVehicles( data ); 474 | break; 475 | 476 | case MESSAGE_TYPES.CONSTRAINTREPORT: 477 | self._updateConstraints( data ); 478 | break; 479 | } 480 | 481 | } 482 | 483 | } 484 | }; 485 | 486 | 487 | params = params || {}; 488 | params.ammo = Physijs.scripts.ammo || 'ammo.js'; 489 | params.fixedTimeStep = params.fixedTimeStep || 1 / 60; 490 | params.rateLimit = params.rateLimit || true; 491 | this.execute( 'init', params ); 492 | }; 493 | Physijs.Scene.prototype = new THREE.Scene; 494 | Physijs.Scene.prototype.constructor = Physijs.Scene; 495 | Eventable.make( Physijs.Scene ); 496 | 497 | Physijs.Scene.prototype._updateScene = function( data ) { 498 | var num_objects = data[1], 499 | object, 500 | i, offset; 501 | 502 | for ( i = 0; i < num_objects; i++ ) { 503 | offset = 2 + i * REPORT_ITEMSIZE; 504 | object = this._objects[ data[ offset ] ]; 505 | 506 | if ( object === undefined ) { 507 | continue; 508 | } 509 | 510 | if ( object.__dirtyPosition === false ) { 511 | object.position.set( 512 | data[ offset + 1 ], 513 | data[ offset + 2 ], 514 | data[ offset + 3 ] 515 | ); 516 | } 517 | 518 | if ( object.__dirtyRotation === false ) { 519 | object.quaternion.set( 520 | data[ offset + 4 ], 521 | data[ offset + 5 ], 522 | data[ offset + 6 ], 523 | data[ offset + 7 ] 524 | ); 525 | } 526 | 527 | object._physijs.linearVelocity.set( 528 | data[ offset + 8 ], 529 | data[ offset + 9 ], 530 | data[ offset + 10 ] 531 | ); 532 | 533 | object._physijs.angularVelocity.set( 534 | data[ offset + 11 ], 535 | data[ offset + 12 ], 536 | data[ offset + 13 ] 537 | ); 538 | 539 | } 540 | 541 | if ( SUPPORT_TRANSFERABLE ) { 542 | // Give the typed array back to the worker 543 | this._worker.transferableMessage( data.buffer, [data.buffer] ); 544 | } 545 | 546 | _is_simulating = false; 547 | this.dispatchEvent( 'update' ); 548 | }; 549 | 550 | Physijs.Scene.prototype._updateVehicles = function( data ) { 551 | var vehicle, wheel, 552 | i, offset; 553 | 554 | for ( i = 0; i < ( data.length - 1 ) / VEHICLEREPORT_ITEMSIZE; i++ ) { 555 | offset = 1 + i * VEHICLEREPORT_ITEMSIZE; 556 | vehicle = this._vehicles[ data[ offset ] ]; 557 | 558 | if ( vehicle === undefined ) { 559 | continue; 560 | } 561 | 562 | wheel = vehicle.wheels[ data[ offset + 1 ] ]; 563 | 564 | wheel.position.set( 565 | data[ offset + 2 ], 566 | data[ offset + 3 ], 567 | data[ offset + 4 ] 568 | ); 569 | 570 | wheel.quaternion.set( 571 | data[ offset + 5 ], 572 | data[ offset + 6 ], 573 | data[ offset + 7 ], 574 | data[ offset + 8 ] 575 | ); 576 | } 577 | 578 | if ( SUPPORT_TRANSFERABLE ) { 579 | // Give the typed array back to the worker 580 | this._worker.transferableMessage( data.buffer, [data.buffer] ); 581 | } 582 | }; 583 | 584 | Physijs.Scene.prototype._updateConstraints = function( data ) { 585 | var constraint, object, 586 | i, offset; 587 | 588 | for ( i = 0; i < ( data.length - 1 ) / CONSTRAINTREPORT_ITEMSIZE; i++ ) { 589 | offset = 1 + i * CONSTRAINTREPORT_ITEMSIZE; 590 | constraint = this._constraints[ data[ offset ] ]; 591 | object = this._objects[ data[ offset + 1 ] ]; 592 | 593 | if ( constraint === undefined || object === undefined ) { 594 | continue; 595 | } 596 | 597 | _temp_vector3_1.set( 598 | data[ offset + 2 ], 599 | data[ offset + 3 ], 600 | data[ offset + 4 ] 601 | ); 602 | _temp_matrix4_1.extractRotation( object.matrix ); 603 | _temp_vector3_1.applyMatrix4( _temp_matrix4_1 ); 604 | 605 | constraint.positiona.addVectors( object.position, _temp_vector3_1 ); 606 | constraint.appliedImpulse = data[ offset + 5 ] ; 607 | } 608 | 609 | if ( SUPPORT_TRANSFERABLE ) { 610 | // Give the typed array back to the worker 611 | this._worker.transferableMessage( data.buffer, [data.buffer] ); 612 | } 613 | }; 614 | 615 | Physijs.Scene.prototype._updateCollisions = function( data ) { 616 | /** 617 | * #TODO 618 | * This is probably the worst way ever to handle collisions. The inherent evilness is a residual 619 | * effect from the previous version's evilness which mutated when switching to transferable objects. 620 | * 621 | * If you feel inclined to make this better, please do so. 622 | */ 623 | 624 | var i, j, offset, object, object2, id1, id2, 625 | collisions = {}, normal_offsets = {}; 626 | 627 | // Build collision manifest 628 | for ( i = 0; i < data[1]; i++ ) { 629 | offset = 2 + i * COLLISIONREPORT_ITEMSIZE; 630 | object = data[ offset ]; 631 | object2 = data[ offset + 1 ]; 632 | 633 | normal_offsets[ object + '-' + object2 ] = offset + 2; 634 | normal_offsets[ object2 + '-' + object ] = -1 * ( offset + 2 ); 635 | 636 | // Register collisions for both the object colliding and the object being collided with 637 | if ( !collisions[ object ] ) collisions[ object ] = []; 638 | collisions[ object ].push( object2 ); 639 | 640 | if ( !collisions[ object2 ] ) collisions[ object2 ] = []; 641 | collisions[ object2 ].push( object ); 642 | } 643 | 644 | // Deal with collisions 645 | for ( id1 in this._objects ) { 646 | if ( !this._objects.hasOwnProperty( id1 ) ) continue; 647 | object = this._objects[ id1 ]; 648 | 649 | // If object touches anything, ... 650 | if ( collisions[ id1 ] ) { 651 | 652 | // Clean up touches array 653 | for ( j = 0; j < object._physijs.touches.length; j++ ) { 654 | if ( collisions[ id1 ].indexOf( object._physijs.touches[j] ) === -1 ) { 655 | object._physijs.touches.splice( j--, 1 ); 656 | } 657 | } 658 | 659 | // Handle each colliding object 660 | for ( j = 0; j < collisions[ id1 ].length; j++ ) { 661 | id2 = collisions[ id1 ][ j ]; 662 | object2 = this._objects[ id2 ]; 663 | 664 | if ( object2 ) { 665 | // If object was not already touching object2, notify object 666 | if ( object._physijs.touches.indexOf( id2 ) === -1 ) { 667 | object._physijs.touches.push( id2 ); 668 | 669 | _temp_vector3_1.subVectors( object.getLinearVelocity(), object2.getLinearVelocity() ); 670 | _temp1 = _temp_vector3_1.clone(); 671 | 672 | _temp_vector3_1.subVectors( object.getAngularVelocity(), object2.getAngularVelocity() ); 673 | _temp2 = _temp_vector3_1.clone(); 674 | 675 | var normal_offset = normal_offsets[ object._physijs.id + '-' + object2._physijs.id ]; 676 | if ( normal_offset > 0 ) { 677 | _temp_vector3_1.set( 678 | -data[ normal_offset ], 679 | -data[ normal_offset + 1 ], 680 | -data[ normal_offset + 2 ] 681 | ); 682 | } else { 683 | normal_offset *= -1; 684 | _temp_vector3_1.set( 685 | data[ normal_offset ], 686 | data[ normal_offset + 1 ], 687 | data[ normal_offset + 2 ] 688 | ); 689 | } 690 | 691 | object.dispatchEvent( 'collision', object2, _temp1, _temp2, _temp_vector3_1 ); 692 | } 693 | } 694 | } 695 | 696 | } else { 697 | 698 | // not touching other objects 699 | object._physijs.touches.length = 0; 700 | 701 | } 702 | 703 | } 704 | 705 | this.collisions = collisions; 706 | 707 | if ( SUPPORT_TRANSFERABLE ) { 708 | // Give the typed array back to the worker 709 | this._worker.transferableMessage( data.buffer, [data.buffer] ); 710 | } 711 | }; 712 | 713 | Physijs.Scene.prototype.addConstraint = function ( constraint, show_marker ) { 714 | this._constraints[ constraint.id ] = constraint; 715 | this.execute( 'addConstraint', constraint.getDefinition() ); 716 | 717 | if ( show_marker ) { 718 | var marker; 719 | 720 | switch ( constraint.type ) { 721 | case 'point': 722 | marker = new THREE.Mesh( 723 | new THREE.SphereGeometry( 1.5 ), 724 | new THREE.MeshNormalMaterial 725 | ); 726 | marker.position.copy( constraint.positiona ); 727 | this._objects[ constraint.objecta ].add( marker ); 728 | break; 729 | 730 | case 'hinge': 731 | marker = new THREE.Mesh( 732 | new THREE.SphereGeometry( 1.5 ), 733 | new THREE.MeshNormalMaterial 734 | ); 735 | marker.position.copy( constraint.positiona ); 736 | this._objects[ constraint.objecta ].add( marker ); 737 | break; 738 | 739 | case 'slider': 740 | marker = new THREE.Mesh( 741 | new THREE.BoxGeometry( 10, 1, 1 ), 742 | new THREE.MeshNormalMaterial 743 | ); 744 | marker.position.copy( constraint.positiona ); 745 | // This rotation isn't right if all three axis are non-0 values 746 | // TODO: change marker's rotation order to ZYX 747 | marker.rotation.set( 748 | constraint.axis.y, // yes, y and 749 | constraint.axis.x, // x axis are swapped 750 | constraint.axis.z 751 | ); 752 | this._objects[ constraint.objecta ].add( marker ); 753 | break; 754 | 755 | case 'conetwist': 756 | marker = new THREE.Mesh( 757 | new THREE.SphereGeometry( 1.5 ), 758 | new THREE.MeshNormalMaterial 759 | ); 760 | marker.position.copy( constraint.positiona ); 761 | this._objects[ constraint.objecta ].add( marker ); 762 | break; 763 | 764 | case 'dof': 765 | marker = new THREE.Mesh( 766 | new THREE.SphereGeometry( 1.5 ), 767 | new THREE.MeshNormalMaterial 768 | ); 769 | marker.position.copy( constraint.positiona ); 770 | this._objects[ constraint.objecta ].add( marker ); 771 | break; 772 | } 773 | } 774 | 775 | return constraint; 776 | }; 777 | 778 | Physijs.Scene.prototype.onSimulationResume = function() { 779 | this.execute( 'onSimulationResume', { } ); 780 | }; 781 | 782 | Physijs.Scene.prototype.removeConstraint = function( constraint ) { 783 | if ( this._constraints[constraint.id ] !== undefined ) { 784 | this.execute( 'removeConstraint', { id: constraint.id } ); 785 | delete this._constraints[ constraint.id ]; 786 | } 787 | }; 788 | 789 | Physijs.Scene.prototype.execute = function( cmd, params ) { 790 | this._worker.postMessage({ cmd: cmd, params: params }); 791 | }; 792 | 793 | addObjectChildren = function( parent, object ) { 794 | var i; 795 | 796 | for ( i = 0; i < object.children.length; i++ ) { 797 | if ( object.children[i]._physijs ) { 798 | object.children[i].updateMatrix(); 799 | object.children[i].updateMatrixWorld(); 800 | 801 | _temp_vector3_1.setFromMatrixPosition( object.children[i].matrixWorld ); 802 | _quaternion_1.setFromRotationMatrix( object.children[i].matrixWorld ); 803 | 804 | object.children[i]._physijs.position_offset = { 805 | x: _temp_vector3_1.x, 806 | y: _temp_vector3_1.y, 807 | z: _temp_vector3_1.z 808 | }; 809 | 810 | object.children[i]._physijs.rotation = { 811 | x: _quaternion_1.x, 812 | y: _quaternion_1.y, 813 | z: _quaternion_1.z, 814 | w: _quaternion_1.w 815 | }; 816 | 817 | parent._physijs.children.push( object.children[i]._physijs ); 818 | } 819 | 820 | addObjectChildren( parent, object.children[i] ); 821 | } 822 | }; 823 | 824 | Physijs.Scene.prototype.add = function( object ) { 825 | THREE.Mesh.prototype.add.call( this, object ); 826 | 827 | if ( object._physijs ) { 828 | 829 | object.world = this; 830 | 831 | if ( object instanceof Physijs.Vehicle ) { 832 | 833 | this.add( object.mesh ); 834 | this._vehicles[ object._physijs.id ] = object; 835 | this.execute( 'addVehicle', object._physijs ); 836 | 837 | } else { 838 | 839 | object.__dirtyPosition = false; 840 | object.__dirtyRotation = false; 841 | this._objects[object._physijs.id] = object; 842 | 843 | if ( object.children.length ) { 844 | object._physijs.children = []; 845 | addObjectChildren( object, object ); 846 | } 847 | 848 | if ( object.material._physijs ) { 849 | if ( !this._materials_ref_counts.hasOwnProperty( object.material._physijs.id ) ) { 850 | this.execute( 'registerMaterial', object.material._physijs ); 851 | object._physijs.materialId = object.material._physijs.id; 852 | this._materials_ref_counts[object.material._physijs.id] = 1; 853 | } else { 854 | this._materials_ref_counts[object.material._physijs.id]++; 855 | } 856 | } 857 | 858 | // Object starting position + rotation 859 | object._physijs.position = { x: object.position.x, y: object.position.y, z: object.position.z }; 860 | object._physijs.rotation = { x: object.quaternion.x, y: object.quaternion.y, z: object.quaternion.z, w: object.quaternion.w }; 861 | 862 | // Check for scaling 863 | var mass_scaling = new THREE.Vector3( 1, 1, 1 ); 864 | if ( object._physijs.width ) { 865 | object._physijs.width *= object.scale.x; 866 | } 867 | if ( object._physijs.height ) { 868 | object._physijs.height *= object.scale.y; 869 | } 870 | if ( object._physijs.depth ) { 871 | object._physijs.depth *= object.scale.z; 872 | } 873 | 874 | this.execute( 'addObject', object._physijs ); 875 | 876 | } 877 | } 878 | }; 879 | 880 | Physijs.Scene.prototype.remove = function( object ) { 881 | if ( object instanceof Physijs.Vehicle ) { 882 | this.execute( 'removeVehicle', { id: object._physijs.id } ); 883 | while( object.wheels.length ) { 884 | this.remove( object.wheels.pop() ); 885 | } 886 | this.remove( object.mesh ); 887 | delete this._vehicles[ object._physijs.id ]; 888 | } else { 889 | THREE.Mesh.prototype.remove.call( this, object ); 890 | if ( object._physijs ) { 891 | delete this._objects[object._physijs.id]; 892 | this.execute( 'removeObject', { id: object._physijs.id } ); 893 | } 894 | } 895 | if ( object.material && object.material._physijs && this._materials_ref_counts.hasOwnProperty( object.material._physijs.id ) ) { 896 | this._materials_ref_counts[object.material._physijs.id]--; 897 | if(this._materials_ref_counts[object.material._physijs.id] == 0) { 898 | this.execute( 'unRegisterMaterial', object.material._physijs ); 899 | delete this._materials_ref_counts[object.material._physijs.id]; 900 | } 901 | } 902 | }; 903 | 904 | Physijs.Scene.prototype.setFixedTimeStep = function( fixedTimeStep ) { 905 | if ( fixedTimeStep ) { 906 | this.execute( 'setFixedTimeStep', fixedTimeStep ); 907 | } 908 | }; 909 | 910 | Physijs.Scene.prototype.setGravity = function( gravity ) { 911 | if ( gravity ) { 912 | this.execute( 'setGravity', gravity ); 913 | } 914 | }; 915 | 916 | Physijs.Scene.prototype.simulate = function( timeStep, maxSubSteps ) { 917 | var object_id, object, update; 918 | 919 | if ( _is_simulating ) { 920 | return false; 921 | } 922 | 923 | _is_simulating = true; 924 | 925 | for ( object_id in this._objects ) { 926 | if ( !this._objects.hasOwnProperty( object_id ) ) continue; 927 | 928 | object = this._objects[object_id]; 929 | 930 | if ( object.__dirtyPosition || object.__dirtyRotation ) { 931 | update = { id: object._physijs.id }; 932 | 933 | if ( object.__dirtyPosition ) { 934 | update.pos = { x: object.position.x, y: object.position.y, z: object.position.z }; 935 | object.__dirtyPosition = false; 936 | } 937 | 938 | if ( object.__dirtyRotation ) { 939 | update.quat = { x: object.quaternion.x, y: object.quaternion.y, z: object.quaternion.z, w: object.quaternion.w }; 940 | object.__dirtyRotation = false; 941 | } 942 | 943 | this.execute( 'updateTransform', update ); 944 | } 945 | } 946 | 947 | this.execute( 'simulate', { timeStep: timeStep, maxSubSteps: maxSubSteps } ); 948 | 949 | return true; 950 | }; 951 | 952 | 953 | // Phsijs.Mesh 954 | Physijs.Mesh = function ( geometry, material, mass ) { 955 | var index; 956 | 957 | if ( !geometry ) { 958 | return; 959 | } 960 | 961 | Eventable.call( this ); 962 | THREE.Mesh.call( this, geometry, material ); 963 | 964 | if ( !geometry.boundingBox ) { 965 | geometry.computeBoundingBox(); 966 | } 967 | 968 | this._physijs = { 969 | type: null, 970 | id: getObjectId(), 971 | mass: mass || 0, 972 | touches: [], 973 | linearVelocity: new THREE.Vector3, 974 | angularVelocity: new THREE.Vector3 975 | }; 976 | }; 977 | Physijs.Mesh.prototype = new THREE.Mesh; 978 | Physijs.Mesh.prototype.constructor = Physijs.Mesh; 979 | Eventable.make( Physijs.Mesh ); 980 | 981 | // Physijs.Mesh.mass 982 | Physijs.Mesh.prototype.__defineGetter__('mass', function() { 983 | return this._physijs.mass; 984 | }); 985 | Physijs.Mesh.prototype.__defineSetter__('mass', function( mass ) { 986 | this._physijs.mass = mass; 987 | if ( this.world ) { 988 | this.world.execute( 'updateMass', { id: this._physijs.id, mass: mass } ); 989 | } 990 | }); 991 | 992 | // Physijs.Mesh.applyCentralImpulse 993 | Physijs.Mesh.prototype.applyCentralImpulse = function ( force ) { 994 | if ( this.world ) { 995 | this.world.execute( 'applyCentralImpulse', { id: this._physijs.id, x: force.x, y: force.y, z: force.z } ); 996 | } 997 | }; 998 | 999 | // Physijs.Mesh.applyImpulse 1000 | Physijs.Mesh.prototype.applyImpulse = function ( force, offset ) { 1001 | if ( this.world ) { 1002 | this.world.execute( 'applyImpulse', { id: this._physijs.id, impulse_x: force.x, impulse_y: force.y, impulse_z: force.z, x: offset.x, y: offset.y, z: offset.z } ); 1003 | } 1004 | }; 1005 | 1006 | // Physijs.Mesh.applyTorque 1007 | Physijs.Mesh.prototype.applyTorque = function ( force ) { 1008 | if ( this.world ) { 1009 | this.world.execute( 'applyTorque', { id: this._physijs.id, torque_x: force.x, torque_y: force.y, torque_z: force.z } ); 1010 | } 1011 | }; 1012 | 1013 | // Physijs.Mesh.applyCentralForce 1014 | Physijs.Mesh.prototype.applyCentralForce = function ( force ) { 1015 | if ( this.world ) { 1016 | this.world.execute( 'applyCentralForce', { id: this._physijs.id, x: force.x, y: force.y, z: force.z } ); 1017 | } 1018 | }; 1019 | 1020 | // Physijs.Mesh.applyForce 1021 | Physijs.Mesh.prototype.applyForce = function ( force, offset ) { 1022 | if ( this.world ) { 1023 | this.world.execute( 'applyForce', { id: this._physijs.id, force_x: force.x, force_y : force.y, force_z : force.z, x: offset.x, y: offset.y, z: offset.z } ); 1024 | } 1025 | }; 1026 | 1027 | // Physijs.Mesh.getAngularVelocity 1028 | Physijs.Mesh.prototype.getAngularVelocity = function () { 1029 | return this._physijs.angularVelocity; 1030 | }; 1031 | 1032 | // Physijs.Mesh.setAngularVelocity 1033 | Physijs.Mesh.prototype.setAngularVelocity = function ( velocity ) { 1034 | if ( this.world ) { 1035 | this.world.execute( 'setAngularVelocity', { id: this._physijs.id, x: velocity.x, y: velocity.y, z: velocity.z } ); 1036 | } 1037 | }; 1038 | 1039 | // Physijs.Mesh.getLinearVelocity 1040 | Physijs.Mesh.prototype.getLinearVelocity = function () { 1041 | return this._physijs.linearVelocity; 1042 | }; 1043 | 1044 | // Physijs.Mesh.setLinearVelocity 1045 | Physijs.Mesh.prototype.setLinearVelocity = function ( velocity ) { 1046 | if ( this.world ) { 1047 | this.world.execute( 'setLinearVelocity', { id: this._physijs.id, x: velocity.x, y: velocity.y, z: velocity.z } ); 1048 | } 1049 | }; 1050 | 1051 | // Physijs.Mesh.setAngularFactor 1052 | Physijs.Mesh.prototype.setAngularFactor = function ( factor ) { 1053 | if ( this.world ) { 1054 | this.world.execute( 'setAngularFactor', { id: this._physijs.id, x: factor.x, y: factor.y, z: factor.z } ); 1055 | } 1056 | }; 1057 | 1058 | // Physijs.Mesh.setLinearFactor 1059 | Physijs.Mesh.prototype.setLinearFactor = function ( factor ) { 1060 | if ( this.world ) { 1061 | this.world.execute( 'setLinearFactor', { id: this._physijs.id, x: factor.x, y: factor.y, z: factor.z } ); 1062 | } 1063 | }; 1064 | 1065 | // Physijs.Mesh.setDamping 1066 | Physijs.Mesh.prototype.setDamping = function ( linear, angular ) { 1067 | if ( this.world ) { 1068 | this.world.execute( 'setDamping', { id: this._physijs.id, linear: linear, angular: angular } ); 1069 | } 1070 | }; 1071 | 1072 | // Physijs.Mesh.setCcdMotionThreshold 1073 | Physijs.Mesh.prototype.setCcdMotionThreshold = function ( threshold ) { 1074 | if ( this.world ) { 1075 | this.world.execute( 'setCcdMotionThreshold', { id: this._physijs.id, threshold: threshold } ); 1076 | } 1077 | }; 1078 | 1079 | // Physijs.Mesh.setCcdSweptSphereRadius 1080 | Physijs.Mesh.prototype.setCcdSweptSphereRadius = function ( radius ) { 1081 | if ( this.world ) { 1082 | this.world.execute( 'setCcdSweptSphereRadius', { id: this._physijs.id, radius: radius } ); 1083 | } 1084 | }; 1085 | 1086 | 1087 | // Physijs.PlaneMesh 1088 | Physijs.PlaneMesh = function ( geometry, material, mass ) { 1089 | var width, height; 1090 | 1091 | Physijs.Mesh.call( this, geometry, material, mass ); 1092 | 1093 | if ( !geometry.boundingBox ) { 1094 | geometry.computeBoundingBox(); 1095 | } 1096 | 1097 | width = geometry.boundingBox.max.x - geometry.boundingBox.min.x; 1098 | height = geometry.boundingBox.max.y - geometry.boundingBox.min.y; 1099 | 1100 | this._physijs.type = 'plane'; 1101 | this._physijs.normal = geometry.faces[0].normal.clone(); 1102 | this._physijs.mass = (typeof mass === 'undefined') ? width * height : mass; 1103 | }; 1104 | Physijs.PlaneMesh.prototype = new Physijs.Mesh; 1105 | Physijs.PlaneMesh.prototype.constructor = Physijs.PlaneMesh; 1106 | 1107 | // Physijs.HeightfieldMesh 1108 | Physijs.HeightfieldMesh = function ( geometry, material, mass, xdiv, ydiv) { 1109 | Physijs.Mesh.call( this, geometry, material, mass ); 1110 | 1111 | this._physijs.type = 'heightfield'; 1112 | this._physijs.xsize = geometry.boundingBox.max.x - geometry.boundingBox.min.x; 1113 | this._physijs.ysize = geometry.boundingBox.max.y - geometry.boundingBox.min.y; 1114 | this._physijs.xpts = (typeof xdiv === 'undefined') ? Math.sqrt(geometry.vertices.length) : xdiv + 1; 1115 | this._physijs.ypts = (typeof ydiv === 'undefined') ? Math.sqrt(geometry.vertices.length) : ydiv + 1; 1116 | // note - this assumes our plane geometry is square, unless we pass in specific xdiv and ydiv 1117 | this._physijs.absMaxHeight = Math.max(geometry.boundingBox.max.z,Math.abs(geometry.boundingBox.min.z)); 1118 | 1119 | var points = []; 1120 | 1121 | var a, b; 1122 | for ( var i = 0; i < geometry.vertices.length; i++ ) { 1123 | 1124 | a = i % this._physijs.xpts; 1125 | b = Math.round( ( i / this._physijs.xpts ) - ( (i % this._physijs.xpts) / this._physijs.xpts ) ); 1126 | points[i] = geometry.vertices[ a + ( ( this._physijs.ypts - b - 1 ) * this._physijs.ypts ) ].z; 1127 | 1128 | //points[i] = geometry.vertices[i]; 1129 | } 1130 | 1131 | this._physijs.points = points; 1132 | }; 1133 | Physijs.HeightfieldMesh.prototype = new Physijs.Mesh; 1134 | Physijs.HeightfieldMesh.prototype.constructor = Physijs.HeightfieldMesh; 1135 | 1136 | // Physijs.BoxMesh 1137 | Physijs.BoxMesh = function( geometry, material, mass ) { 1138 | var width, height, depth; 1139 | 1140 | Physijs.Mesh.call( this, geometry, material, mass ); 1141 | 1142 | if ( !geometry.boundingBox ) { 1143 | geometry.computeBoundingBox(); 1144 | } 1145 | 1146 | width = geometry.boundingBox.max.x - geometry.boundingBox.min.x; 1147 | height = geometry.boundingBox.max.y - geometry.boundingBox.min.y; 1148 | depth = geometry.boundingBox.max.z - geometry.boundingBox.min.z; 1149 | 1150 | this._physijs.type = 'box'; 1151 | this._physijs.width = width; 1152 | this._physijs.height = height; 1153 | this._physijs.depth = depth; 1154 | this._physijs.mass = (typeof mass === 'undefined') ? width * height * depth : mass; 1155 | }; 1156 | Physijs.BoxMesh.prototype = new Physijs.Mesh; 1157 | Physijs.BoxMesh.prototype.constructor = Physijs.BoxMesh; 1158 | 1159 | 1160 | // Physijs.SphereMesh 1161 | Physijs.SphereMesh = function( geometry, material, mass ) { 1162 | Physijs.Mesh.call( this, geometry, material, mass ); 1163 | 1164 | if ( !geometry.boundingSphere ) { 1165 | geometry.computeBoundingSphere(); 1166 | } 1167 | 1168 | this._physijs.type = 'sphere'; 1169 | this._physijs.radius = geometry.boundingSphere.radius; 1170 | this._physijs.mass = (typeof mass === 'undefined') ? (4/3) * Math.PI * Math.pow(this._physijs.radius, 3) : mass; 1171 | }; 1172 | Physijs.SphereMesh.prototype = new Physijs.Mesh; 1173 | Physijs.SphereMesh.prototype.constructor = Physijs.SphereMesh; 1174 | 1175 | 1176 | // Physijs.CylinderMesh 1177 | Physijs.CylinderMesh = function( geometry, material, mass ) { 1178 | var width, height, depth; 1179 | 1180 | Physijs.Mesh.call( this, geometry, material, mass ); 1181 | 1182 | if ( !geometry.boundingBox ) { 1183 | geometry.computeBoundingBox(); 1184 | } 1185 | 1186 | width = geometry.boundingBox.max.x - geometry.boundingBox.min.x; 1187 | height = geometry.boundingBox.max.y - geometry.boundingBox.min.y; 1188 | depth = geometry.boundingBox.max.z - geometry.boundingBox.min.z; 1189 | 1190 | this._physijs.type = 'cylinder'; 1191 | this._physijs.width = width; 1192 | this._physijs.height = height; 1193 | this._physijs.depth = depth; 1194 | this._physijs.mass = (typeof mass === 'undefined') ? width * height * depth : mass; 1195 | }; 1196 | Physijs.CylinderMesh.prototype = new Physijs.Mesh; 1197 | Physijs.CylinderMesh.prototype.constructor = Physijs.CylinderMesh; 1198 | 1199 | 1200 | // Physijs.CapsuleMesh 1201 | Physijs.CapsuleMesh = function( geometry, material, mass ) { 1202 | var width, height, depth; 1203 | 1204 | Physijs.Mesh.call( this, geometry, material, mass ); 1205 | 1206 | if ( !geometry.boundingBox ) { 1207 | geometry.computeBoundingBox(); 1208 | } 1209 | 1210 | width = geometry.boundingBox.max.x - geometry.boundingBox.min.x; 1211 | height = geometry.boundingBox.max.y - geometry.boundingBox.min.y; 1212 | depth = geometry.boundingBox.max.z - geometry.boundingBox.min.z; 1213 | 1214 | this._physijs.type = 'capsule'; 1215 | this._physijs.radius = Math.max(width / 2, depth / 2); 1216 | this._physijs.height = height; 1217 | this._physijs.mass = (typeof mass === 'undefined') ? width * height * depth : mass; 1218 | }; 1219 | Physijs.CapsuleMesh.prototype = new Physijs.Mesh; 1220 | Physijs.CapsuleMesh.prototype.constructor = Physijs.CapsuleMesh; 1221 | 1222 | 1223 | // Physijs.ConeMesh 1224 | Physijs.ConeMesh = function( geometry, material, mass ) { 1225 | var width, height, depth; 1226 | 1227 | Physijs.Mesh.call( this, geometry, material, mass ); 1228 | 1229 | if ( !geometry.boundingBox ) { 1230 | geometry.computeBoundingBox(); 1231 | } 1232 | 1233 | width = geometry.boundingBox.max.x - geometry.boundingBox.min.x; 1234 | height = geometry.boundingBox.max.y - geometry.boundingBox.min.y; 1235 | 1236 | this._physijs.type = 'cone'; 1237 | this._physijs.radius = width / 2; 1238 | this._physijs.height = height; 1239 | this._physijs.mass = (typeof mass === 'undefined') ? width * height : mass; 1240 | }; 1241 | Physijs.ConeMesh.prototype = new Physijs.Mesh; 1242 | Physijs.ConeMesh.prototype.constructor = Physijs.ConeMesh; 1243 | 1244 | 1245 | // Physijs.ConcaveMesh 1246 | Physijs.ConcaveMesh = function( geometry, material, mass ) { 1247 | var i, 1248 | width, height, depth, 1249 | vertices, face, triangles = []; 1250 | 1251 | Physijs.Mesh.call( this, geometry, material, mass ); 1252 | 1253 | if ( !geometry.boundingBox ) { 1254 | geometry.computeBoundingBox(); 1255 | } 1256 | 1257 | vertices = geometry.vertices; 1258 | 1259 | for ( i = 0; i < geometry.faces.length; i++ ) { 1260 | face = geometry.faces[i]; 1261 | if ( face instanceof THREE.Face3) { 1262 | 1263 | triangles.push([ 1264 | { x: vertices[face.a].x, y: vertices[face.a].y, z: vertices[face.a].z }, 1265 | { x: vertices[face.b].x, y: vertices[face.b].y, z: vertices[face.b].z }, 1266 | { x: vertices[face.c].x, y: vertices[face.c].y, z: vertices[face.c].z } 1267 | ]); 1268 | 1269 | } else if ( face instanceof THREE.Face4 ) { 1270 | 1271 | triangles.push([ 1272 | { x: vertices[face.a].x, y: vertices[face.a].y, z: vertices[face.a].z }, 1273 | { x: vertices[face.b].x, y: vertices[face.b].y, z: vertices[face.b].z }, 1274 | { x: vertices[face.d].x, y: vertices[face.d].y, z: vertices[face.d].z } 1275 | ]); 1276 | triangles.push([ 1277 | { x: vertices[face.b].x, y: vertices[face.b].y, z: vertices[face.b].z }, 1278 | { x: vertices[face.c].x, y: vertices[face.c].y, z: vertices[face.c].z }, 1279 | { x: vertices[face.d].x, y: vertices[face.d].y, z: vertices[face.d].z } 1280 | ]); 1281 | 1282 | } 1283 | } 1284 | 1285 | width = geometry.boundingBox.max.x - geometry.boundingBox.min.x; 1286 | height = geometry.boundingBox.max.y - geometry.boundingBox.min.y; 1287 | depth = geometry.boundingBox.max.z - geometry.boundingBox.min.z; 1288 | 1289 | this._physijs.type = 'concave'; 1290 | this._physijs.triangles = triangles; 1291 | this._physijs.mass = (typeof mass === 'undefined') ? width * height * depth : mass; 1292 | }; 1293 | Physijs.ConcaveMesh.prototype = new Physijs.Mesh; 1294 | Physijs.ConcaveMesh.prototype.constructor = Physijs.ConcaveMesh; 1295 | 1296 | 1297 | // Physijs.ConvexMesh 1298 | Physijs.ConvexMesh = function( geometry, material, mass ) { 1299 | var i, 1300 | width, height, depth, 1301 | points = []; 1302 | 1303 | Physijs.Mesh.call( this, geometry, material, mass ); 1304 | 1305 | if ( !geometry.boundingBox ) { 1306 | geometry.computeBoundingBox(); 1307 | } 1308 | 1309 | for ( i = 0; i < geometry.vertices.length; i++ ) { 1310 | points.push({ 1311 | x: geometry.vertices[i].x, 1312 | y: geometry.vertices[i].y, 1313 | z: geometry.vertices[i].z 1314 | }); 1315 | } 1316 | 1317 | 1318 | width = geometry.boundingBox.max.x - geometry.boundingBox.min.x; 1319 | height = geometry.boundingBox.max.y - geometry.boundingBox.min.y; 1320 | depth = geometry.boundingBox.max.z - geometry.boundingBox.min.z; 1321 | 1322 | this._physijs.type = 'convex'; 1323 | this._physijs.points = points; 1324 | this._physijs.mass = (typeof mass === 'undefined') ? width * height * depth : mass; 1325 | }; 1326 | Physijs.ConvexMesh.prototype = new Physijs.Mesh; 1327 | Physijs.ConvexMesh.prototype.constructor = Physijs.ConvexMesh; 1328 | 1329 | 1330 | // Physijs.Vehicle 1331 | Physijs.Vehicle = function( mesh, tuning ) { 1332 | tuning = tuning || new Physijs.VehicleTuning; 1333 | this.mesh = mesh; 1334 | this.wheels = []; 1335 | this._physijs = { 1336 | id: getObjectId(), 1337 | rigidBody: mesh._physijs.id, 1338 | suspension_stiffness: tuning.suspension_stiffness, 1339 | suspension_compression: tuning.suspension_compression, 1340 | suspension_damping: tuning.suspension_damping, 1341 | max_suspension_travel: tuning.max_suspension_travel, 1342 | friction_slip: tuning.friction_slip, 1343 | max_suspension_force: tuning.max_suspension_force 1344 | }; 1345 | }; 1346 | Physijs.Vehicle.prototype.addWheel = function( wheel_geometry, wheel_material, connection_point, wheel_direction, wheel_axle, suspension_rest_length, wheel_radius, is_front_wheel, tuning ) { 1347 | var wheel = new THREE.Mesh( wheel_geometry, wheel_material ); 1348 | wheel.castShadow = wheel.receiveShadow = true; 1349 | wheel.position.copy( wheel_direction ).multiplyScalar( suspension_rest_length / 100 ).add( connection_point ); 1350 | this.world.add( wheel ); 1351 | this.wheels.push( wheel ); 1352 | 1353 | this.world.execute( 'addWheel', { 1354 | id: this._physijs.id, 1355 | connection_point: { x: connection_point.x, y: connection_point.y, z: connection_point.z }, 1356 | wheel_direction: { x: wheel_direction.x, y: wheel_direction.y, z: wheel_direction.z }, 1357 | wheel_axle: { x: wheel_axle.x, y: wheel_axle.y, z: wheel_axle.z }, 1358 | suspension_rest_length: suspension_rest_length, 1359 | wheel_radius: wheel_radius, 1360 | is_front_wheel: is_front_wheel, 1361 | tuning: tuning 1362 | }); 1363 | }; 1364 | Physijs.Vehicle.prototype.setSteering = function( amount, wheel ) { 1365 | if ( wheel !== undefined && this.wheels[ wheel ] !== undefined ) { 1366 | this.world.execute( 'setSteering', { id: this._physijs.id, wheel: wheel, steering: amount } ); 1367 | } else if ( this.wheels.length > 0 ) { 1368 | for ( var i = 0; i < this.wheels.length; i++ ) { 1369 | this.world.execute( 'setSteering', { id: this._physijs.id, wheel: i, steering: amount } ); 1370 | } 1371 | } 1372 | }; 1373 | Physijs.Vehicle.prototype.setBrake = function( amount, wheel ) { 1374 | if ( wheel !== undefined && this.wheels[ wheel ] !== undefined ) { 1375 | this.world.execute( 'setBrake', { id: this._physijs.id, wheel: wheel, brake: amount } ); 1376 | } else if ( this.wheels.length > 0 ) { 1377 | for ( var i = 0; i < this.wheels.length; i++ ) { 1378 | this.world.execute( 'setBrake', { id: this._physijs.id, wheel: i, brake: amount } ); 1379 | } 1380 | } 1381 | }; 1382 | Physijs.Vehicle.prototype.applyEngineForce = function( amount, wheel ) { 1383 | if ( wheel !== undefined && this.wheels[ wheel ] !== undefined ) { 1384 | this.world.execute( 'applyEngineForce', { id: this._physijs.id, wheel: wheel, force: amount } ); 1385 | } else if ( this.wheels.length > 0 ) { 1386 | for ( var i = 0; i < this.wheels.length; i++ ) { 1387 | this.world.execute( 'applyEngineForce', { id: this._physijs.id, wheel: i, force: amount } ); 1388 | } 1389 | } 1390 | }; 1391 | 1392 | // Physijs.VehicleTuning 1393 | Physijs.VehicleTuning = function( suspension_stiffness, suspension_compression, suspension_damping, max_suspension_travel, friction_slip, max_suspension_force ) { 1394 | this.suspension_stiffness = suspension_stiffness !== undefined ? suspension_stiffness : 5.88; 1395 | this.suspension_compression = suspension_compression !== undefined ? suspension_compression : 0.83; 1396 | this.suspension_damping = suspension_damping !== undefined ? suspension_damping : 0.88; 1397 | this.max_suspension_travel = max_suspension_travel !== undefined ? max_suspension_travel : 500; 1398 | this.friction_slip = friction_slip !== undefined ? friction_slip : 10.5; 1399 | this.max_suspension_force = max_suspension_force !== undefined ? max_suspension_force : 6000; 1400 | }; 1401 | 1402 | return Physijs; 1403 | })(); 1404 | -------------------------------------------------------------------------------- /physijs_worker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var 3 | transferableMessage = self.webkitPostMessage || self.postMessage, 4 | 5 | // enum 6 | MESSAGE_TYPES = { 7 | WORLDREPORT: 0, 8 | COLLISIONREPORT: 1, 9 | VEHICLEREPORT: 2, 10 | CONSTRAINTREPORT: 3 11 | }, 12 | 13 | // temp variables 14 | _object, 15 | _vector, 16 | _transform, 17 | 18 | // functions 19 | public_functions = {}, 20 | getShapeFromCache, 21 | setShapeCache, 22 | createShape, 23 | reportWorld, 24 | reportVehicles, 25 | reportCollisions, 26 | reportConstraints, 27 | 28 | // world variables 29 | fixedTimeStep, // used when calling stepSimulation 30 | rateLimit, // sets whether or not to sync the simulation rate with fixedTimeStep 31 | last_simulation_time, 32 | last_simulation_duration = 0, 33 | world, 34 | transform, 35 | _vec3_1, 36 | _vec3_2, 37 | _vec3_3, 38 | _quat, 39 | // private cache 40 | _objects = {}, 41 | _vehicles = {}, 42 | _constraints = {}, 43 | _materials = {}, 44 | _objects_ammo = {}, 45 | _num_objects = 0, 46 | _num_wheels = 0, 47 | _num_constraints = 0, 48 | _object_shapes = {}, 49 | 50 | // The following objects are to track objects that ammo.js doesn't clean 51 | // up. All are cleaned up when they're corresponding body is destroyed. 52 | // Unfortunately, it's very difficult to get at these objects from the 53 | // body, so we have to track them ourselves. 54 | _motion_states = {}, 55 | // Don't need to worry about it for cached shapes. 56 | _noncached_shapes = {}, 57 | // A body with a compound shape always has a regular shape as well, so we 58 | // have track them separately. 59 | _compound_shapes = {}, 60 | 61 | // object reporting 62 | REPORT_CHUNKSIZE, // report array is increased in increments of this chunk size 63 | 64 | WORLDREPORT_ITEMSIZE = 14, // how many float values each reported item needs 65 | worldreport, 66 | 67 | COLLISIONREPORT_ITEMSIZE = 5, // one float for each object id, and a Vec3 contact normal 68 | collisionreport, 69 | 70 | VEHICLEREPORT_ITEMSIZE = 9, // vehicle id, wheel index, 3 for position, 4 for rotation 71 | vehiclereport, 72 | 73 | CONSTRAINTREPORT_ITEMSIZE = 6, // constraint id, offset object, offset, applied impulse 74 | constraintreport; 75 | 76 | var ab = new ArrayBuffer( 1 ); 77 | 78 | transferableMessage( ab, [ab] ); 79 | var SUPPORT_TRANSFERABLE = ( ab.byteLength === 0 ); 80 | 81 | getShapeFromCache = function ( cache_key ) { 82 | if ( _object_shapes[ cache_key ] !== undefined ) { 83 | return _object_shapes[ cache_key ]; 84 | } 85 | return null; 86 | }; 87 | 88 | setShapeCache = function ( cache_key, shape ) { 89 | _object_shapes[ cache_key ] = shape; 90 | } 91 | 92 | createShape = function( description ) { 93 | var cache_key, shape; 94 | 95 | _transform.setIdentity(); 96 | switch ( description.type ) { 97 | case 'plane': 98 | cache_key = 'plane_' + description.normal.x + '_' + description.normal.y + '_' + description.normal.z; 99 | if ( ( shape = getShapeFromCache( cache_key ) ) === null ) { 100 | _vec3_1.setX(description.normal.x); 101 | _vec3_1.setY(description.normal.y); 102 | _vec3_1.setZ(description.normal.z); 103 | shape = new Ammo.btStaticPlaneShape(_vec3_1, 0 ); 104 | setShapeCache( cache_key, shape ); 105 | } 106 | break; 107 | 108 | case 'box': 109 | cache_key = 'box_' + description.width + '_' + description.height + '_' + description.depth; 110 | if ( ( shape = getShapeFromCache( cache_key ) ) === null ) { 111 | _vec3_1.setX(description.width / 2); 112 | _vec3_1.setY(description.height / 2); 113 | _vec3_1.setZ(description.depth / 2); 114 | shape = new Ammo.btBoxShape(_vec3_1); 115 | setShapeCache( cache_key, shape ); 116 | } 117 | break; 118 | 119 | case 'sphere': 120 | cache_key = 'sphere_' + description.radius; 121 | if ( ( shape = getShapeFromCache( cache_key ) ) === null ) { 122 | shape = new Ammo.btSphereShape( description.radius ); 123 | setShapeCache( cache_key, shape ); 124 | } 125 | break; 126 | 127 | case 'cylinder': 128 | cache_key = 'cylinder_' + description.width + '_' + description.height + '_' + description.depth; 129 | if ( ( shape = getShapeFromCache( cache_key ) ) === null ) { 130 | _vec3_1.setX(description.width / 2); 131 | _vec3_1.setY(description.height / 2); 132 | _vec3_1.setZ(description.depth / 2); 133 | shape = new Ammo.btCylinderShape(_vec3_1); 134 | setShapeCache( cache_key, shape ); 135 | } 136 | break; 137 | 138 | case 'capsule': 139 | cache_key = 'capsule_' + description.radius + '_' + description.height; 140 | if ( ( shape = getShapeFromCache( cache_key ) ) === null ) { 141 | // In Bullet, capsule height excludes the end spheres 142 | shape = new Ammo.btCapsuleShape( description.radius, description.height - 2 * description.radius ); 143 | setShapeCache( cache_key, shape ); 144 | } 145 | break; 146 | 147 | case 'cone': 148 | cache_key = 'cone_' + description.radius + '_' + description.height; 149 | if ( ( shape = getShapeFromCache( cache_key ) ) === null ) { 150 | shape = new Ammo.btConeShape( description.radius, description.height ); 151 | setShapeCache( cache_key, shape ); 152 | } 153 | break; 154 | 155 | case 'concave': 156 | var i, triangle, triangle_mesh = new Ammo.btTriangleMesh; 157 | if (!description.triangles.length) return false 158 | 159 | for ( i = 0; i < description.triangles.length; i++ ) { 160 | triangle = description.triangles[i]; 161 | 162 | _vec3_1.setX(triangle[0].x); 163 | _vec3_1.setY(triangle[0].y); 164 | _vec3_1.setZ(triangle[0].z); 165 | 166 | _vec3_2.setX(triangle[1].x); 167 | _vec3_2.setY(triangle[1].y); 168 | _vec3_2.setZ(triangle[1].z); 169 | 170 | _vec3_3.setX(triangle[2].x); 171 | _vec3_3.setY(triangle[2].y); 172 | _vec3_3.setZ(triangle[2].z); 173 | 174 | triangle_mesh.addTriangle( 175 | _vec3_1, 176 | _vec3_2, 177 | _vec3_3, 178 | true 179 | ); 180 | } 181 | 182 | shape = new Ammo.btBvhTriangleMeshShape( 183 | triangle_mesh, 184 | true, 185 | true 186 | ); 187 | _noncached_shapes[description.id] = shape; 188 | break; 189 | 190 | case 'convex': 191 | var i, point, shape = new Ammo.btConvexHullShape; 192 | for ( i = 0; i < description.points.length; i++ ) { 193 | point = description.points[i]; 194 | 195 | _vec3_1.setX(point.x); 196 | _vec3_1.setY(point.y); 197 | _vec3_1.setZ(point.z); 198 | 199 | shape.addPoint(_vec3_1); 200 | 201 | } 202 | _noncached_shapes[description.id] = shape; 203 | break; 204 | 205 | case 'heightfield': 206 | 207 | var ptr = Ammo.allocate(4 * description.xpts * description.ypts, "float", Ammo.ALLOC_NORMAL); 208 | 209 | for (var f = 0; f < description.points.length; f++) { 210 | Ammo.setValue(ptr + f, description.points[f] , 'float'); 211 | } 212 | 213 | shape = new Ammo.btHeightfieldTerrainShape( 214 | description.xpts, 215 | description.ypts, 216 | ptr, 217 | 1, 218 | -description.absMaxHeight, 219 | description.absMaxHeight, 220 | 2, 221 | 0, 222 | false 223 | ); 224 | 225 | _vec3_1.setX(description.xsize/(description.xpts - 1)); 226 | _vec3_1.setY(description.ysize/(description.ypts - 1)); 227 | _vec3_1.setZ(1); 228 | 229 | shape.setLocalScaling(_vec3_1); 230 | _noncached_shapes[description.id] = shape; 231 | break; 232 | 233 | default: 234 | // Not recognized 235 | return; 236 | break; 237 | } 238 | 239 | return shape; 240 | }; 241 | 242 | public_functions.init = function( params ) { 243 | importScripts( params.ammo ); 244 | 245 | _transform = new Ammo.btTransform; 246 | _vec3_1 = new Ammo.btVector3(0,0,0); 247 | _vec3_2 = new Ammo.btVector3(0,0,0); 248 | _vec3_3 = new Ammo.btVector3(0,0,0); 249 | _quat = new Ammo.btQuaternion(0,0,0,0); 250 | 251 | REPORT_CHUNKSIZE = params.reportsize || 50; 252 | if ( SUPPORT_TRANSFERABLE ) { 253 | // Transferable messages are supported, take advantage of them with TypedArrays 254 | worldreport = new Float32Array(2 + REPORT_CHUNKSIZE * WORLDREPORT_ITEMSIZE); // message id + # of objects to report + chunk size * # of values per object 255 | collisionreport = new Float32Array(2 + REPORT_CHUNKSIZE * COLLISIONREPORT_ITEMSIZE); // message id + # of collisions to report + chunk size * # of values per object 256 | vehiclereport = new Float32Array(2 + REPORT_CHUNKSIZE * VEHICLEREPORT_ITEMSIZE); // message id + # of vehicles to report + chunk size * # of values per object 257 | constraintreport = new Float32Array(2 + REPORT_CHUNKSIZE * CONSTRAINTREPORT_ITEMSIZE); // message id + # of constraints to report + chunk size * # of values per object 258 | } else { 259 | // Transferable messages are not supported, send data as normal arrays 260 | worldreport = []; 261 | collisionreport = []; 262 | vehiclereport = []; 263 | constraintreport = []; 264 | } 265 | worldreport[0] = MESSAGE_TYPES.WORLDREPORT; 266 | collisionreport[0] = MESSAGE_TYPES.COLLISIONREPORT; 267 | vehiclereport[0] = MESSAGE_TYPES.VEHICLEREPORT; 268 | constraintreport[0] = MESSAGE_TYPES.CONSTRAINTREPORT; 269 | 270 | var collisionConfiguration = new Ammo.btDefaultCollisionConfiguration, 271 | dispatcher = new Ammo.btCollisionDispatcher( collisionConfiguration ), 272 | solver = new Ammo.btSequentialImpulseConstraintSolver, 273 | broadphase; 274 | 275 | if ( !params.broadphase ) params.broadphase = { type: 'dynamic' }; 276 | switch ( params.broadphase.type ) { 277 | case 'sweepprune': 278 | 279 | _vec3_1.setX(params.broadphase.aabbmin.x); 280 | _vec3_1.setY(params.broadphase.aabbmin.y); 281 | _vec3_1.setZ(params.broadphase.aabbmin.z); 282 | 283 | _vec3_2.setX(params.broadphase.aabbmax.x); 284 | _vec3_2.setY(params.broadphase.aabbmax.y); 285 | _vec3_2.setZ(params.broadphase.aabbmax.z); 286 | 287 | broadphase = new Ammo.btAxisSweep3( 288 | _vec3_1, 289 | _vec3_2 290 | ); 291 | 292 | break; 293 | 294 | case 'dynamic': 295 | default: 296 | broadphase = new Ammo.btDbvtBroadphase; 297 | break; 298 | } 299 | 300 | world = new Ammo.btDiscreteDynamicsWorld( dispatcher, broadphase, solver, collisionConfiguration ); 301 | 302 | fixedTimeStep = params.fixedTimeStep; 303 | rateLimit = params.rateLimit; 304 | 305 | transferableMessage({ cmd: 'worldReady' }); 306 | }; 307 | 308 | public_functions.registerMaterial = function( description ) { 309 | _materials[ description.id ] = description; 310 | }; 311 | 312 | public_functions.unRegisterMaterial = function( description ) { 313 | delete _materials[ description.id ]; 314 | }; 315 | 316 | public_functions.setFixedTimeStep = function( description ) { 317 | fixedTimeStep = description; 318 | }; 319 | 320 | public_functions.setGravity = function( description ) { 321 | _vec3_1.setX(description.x); 322 | _vec3_1.setY(description.y); 323 | _vec3_1.setZ(description.z); 324 | world.setGravity(_vec3_1); 325 | }; 326 | 327 | public_functions.addObject = function( description ) { 328 | 329 | var i, 330 | localInertia, shape, motionState, rbInfo, body; 331 | 332 | shape = createShape( description ); 333 | if (!shape) return 334 | // If there are children then this is a compound shape 335 | if ( description.children ) { 336 | var compound_shape = new Ammo.btCompoundShape, _child; 337 | compound_shape.addChildShape( _transform, shape ); 338 | 339 | for ( i = 0; i < description.children.length; i++ ) { 340 | _child = description.children[i]; 341 | 342 | var trans = new Ammo.btTransform; 343 | trans.setIdentity(); 344 | 345 | _vec3_1.setX(_child.position_offset.x); 346 | _vec3_1.setY(_child.position_offset.y); 347 | _vec3_1.setZ(_child.position_offset.z); 348 | trans.setOrigin(_vec3_1); 349 | 350 | _quat.setX(_child.rotation.x); 351 | _quat.setY(_child.rotation.y); 352 | _quat.setZ(_child.rotation.z); 353 | _quat.setW(_child.rotation.w); 354 | trans.setRotation(_quat); 355 | 356 | shape = createShape( description.children[i] ); 357 | compound_shape.addChildShape( trans, shape ); 358 | Ammo.destroy(trans); 359 | } 360 | 361 | shape = compound_shape; 362 | _compound_shapes[ description.id ] = shape; 363 | } 364 | _vec3_1.setX(0); 365 | _vec3_1.setY(0); 366 | _vec3_1.setZ(0); 367 | shape.calculateLocalInertia( description.mass, _vec3_1 ); 368 | 369 | _transform.setIdentity(); 370 | 371 | _vec3_2.setX(description.position.x); 372 | _vec3_2.setY(description.position.y); 373 | _vec3_2.setZ(description.position.z); 374 | _transform.setOrigin(_vec3_2); 375 | 376 | _quat.setX(description.rotation.x); 377 | _quat.setY(description.rotation.y); 378 | _quat.setZ(description.rotation.z); 379 | _quat.setW(description.rotation.w); 380 | _transform.setRotation(_quat); 381 | 382 | motionState = new Ammo.btDefaultMotionState( _transform ); // #TODO: btDefaultMotionState supports center of mass offset as second argument - implement 383 | rbInfo = new Ammo.btRigidBodyConstructionInfo( description.mass, motionState, shape, _vec3_1 ); 384 | 385 | if ( description.materialId !== undefined ) { 386 | rbInfo.set_m_friction( _materials[ description.materialId ].friction ); 387 | rbInfo.set_m_restitution( _materials[ description.materialId ].restitution ); 388 | } 389 | 390 | body = new Ammo.btRigidBody( rbInfo ); 391 | Ammo.destroy(rbInfo); 392 | 393 | if ( typeof description.collision_flags !== 'undefined' ) { 394 | body.setCollisionFlags( description.collision_flags ); 395 | } 396 | 397 | world.addRigidBody( body ); 398 | 399 | body.id = description.id; 400 | _objects[ body.id ] = body; 401 | _motion_states[ body.id ] = motionState; 402 | 403 | var ptr = body.a != undefined ? body.a : body.ptr; 404 | _objects_ammo[ptr] = body.id; 405 | _num_objects++; 406 | 407 | transferableMessage({ cmd: 'objectReady', params: body.id }); 408 | }; 409 | 410 | public_functions.addVehicle = function( description ) { 411 | var vehicle_tuning = new Ammo.btVehicleTuning(), 412 | vehicle; 413 | 414 | vehicle_tuning.set_m_suspensionStiffness( description.suspension_stiffness ); 415 | vehicle_tuning.set_m_suspensionCompression( description.suspension_compression ); 416 | vehicle_tuning.set_m_suspensionDamping( description.suspension_damping ); 417 | vehicle_tuning.set_m_maxSuspensionTravelCm( description.max_suspension_travel ); 418 | vehicle_tuning.set_m_maxSuspensionForce( description.max_suspension_force ); 419 | 420 | vehicle = new Ammo.btRaycastVehicle( vehicle_tuning, _objects[ description.rigidBody ], new Ammo.btDefaultVehicleRaycaster( world ) ); 421 | vehicle.tuning = vehicle_tuning; 422 | 423 | _objects[ description.rigidBody ].setActivationState( 4 ); 424 | vehicle.setCoordinateSystem( 0, 1, 2 ); 425 | 426 | world.addVehicle( vehicle ); 427 | _vehicles[ description.id ] = vehicle; 428 | }; 429 | public_functions.removeVehicle = function( description ) { 430 | delete _vehicles[ description.id ]; 431 | }; 432 | 433 | public_functions.addWheel = function( description ) { 434 | if ( _vehicles[description.id] !== undefined ) { 435 | var tuning = _vehicles[description.id].tuning; 436 | if ( description.tuning !== undefined ) { 437 | tuning = new Ammo.btVehicleTuning(); 438 | tuning.set_m_suspensionStiffness( description.tuning.suspension_stiffness ); 439 | tuning.set_m_suspensionCompression( description.tuning.suspension_compression ); 440 | tuning.set_m_suspensionDamping( description.tuning.suspension_damping ); 441 | tuning.set_m_maxSuspensionTravelCm( description.tuning.max_suspension_travel ); 442 | tuning.set_m_maxSuspensionForce( description.tuning.max_suspension_force ); 443 | } 444 | 445 | _vec3_1.setX(description.connection_point.x); 446 | _vec3_1.setY(description.connection_point.y); 447 | _vec3_1.setZ(description.connection_point.z); 448 | 449 | _vec3_2.setX(description.wheel_direction.x); 450 | _vec3_2.setY(description.wheel_direction.y); 451 | _vec3_2.setZ(description.wheel_direction.z); 452 | 453 | _vec3_3.setX(description.wheel_axle.x); 454 | _vec3_3.setY(description.wheel_axle.y); 455 | _vec3_3.setZ(description.wheel_axle.z); 456 | 457 | _vehicles[description.id].addWheel( 458 | _vec3_1, 459 | _vec3_2, 460 | _vec3_3, 461 | description.suspension_rest_length, 462 | description.wheel_radius, 463 | tuning, 464 | description.is_front_wheel 465 | ); 466 | } 467 | 468 | _num_wheels++; 469 | 470 | if ( SUPPORT_TRANSFERABLE ) { 471 | vehiclereport = new Float32Array(1 + _num_wheels * VEHICLEREPORT_ITEMSIZE); // message id & ( # of objects to report * # of values per object ) 472 | vehiclereport[0] = MESSAGE_TYPES.VEHICLEREPORT; 473 | } else { 474 | vehiclereport = [ MESSAGE_TYPES.VEHICLEREPORT ]; 475 | } 476 | }; 477 | 478 | public_functions.setSteering = function( details ) { 479 | if ( _vehicles[details.id] !== undefined ) { 480 | _vehicles[details.id].setSteeringValue( details.steering, details.wheel ); 481 | } 482 | }; 483 | public_functions.setBrake = function( details ) { 484 | if ( _vehicles[details.id] !== undefined ) { 485 | _vehicles[details.id].setBrake( details.brake, details.wheel ); 486 | } 487 | }; 488 | public_functions.applyEngineForce = function( details ) { 489 | if ( _vehicles[details.id] !== undefined ) { 490 | _vehicles[details.id].applyEngineForce( details.force, details.wheel ); 491 | } 492 | }; 493 | 494 | public_functions.removeObject = function( details ) { 495 | world.removeRigidBody( _objects[details.id] ); 496 | Ammo.destroy(_objects[details.id]); 497 | Ammo.destroy(_motion_states[details.id]); 498 | if (_compound_shapes[details.id]) Ammo.destroy(_compound_shapes[details.id]); 499 | if (_noncached_shapes[details.id]) Ammo.destroy(_noncached_shapes[details.id]); 500 | var ptr = _objects[details.id].a != undefined ? _objects[details.id].a : _objects[details.id].ptr; 501 | delete _objects_ammo[ptr]; 502 | delete _objects[details.id]; 503 | delete _motion_states[details.id]; 504 | if (_compound_shapes[details.id]) delete _compound_shapes[details.id]; 505 | if (_noncached_shapes[details.id]) delete _noncached_shapes[details.id]; 506 | _num_objects--; 507 | }; 508 | 509 | public_functions.updateTransform = function( details ) { 510 | _object = _objects[details.id]; 511 | _object.getMotionState().getWorldTransform( _transform ); 512 | 513 | if ( details.pos ) { 514 | _vec3_1.setX(details.pos.x); 515 | _vec3_1.setY(details.pos.y); 516 | _vec3_1.setZ(details.pos.z); 517 | _transform.setOrigin(_vec3_1); 518 | } 519 | 520 | if ( details.quat ) { 521 | _quat.setX(details.quat.x); 522 | _quat.setY(details.quat.y); 523 | _quat.setZ(details.quat.z); 524 | _quat.setW(details.quat.w); 525 | _transform.setRotation(_quat); 526 | } 527 | 528 | _object.setWorldTransform( _transform ); 529 | _object.activate(); 530 | }; 531 | 532 | public_functions.updateMass = function( details ) { 533 | // #TODO: changing a static object into dynamic is buggy 534 | _object = _objects[details.id]; 535 | 536 | // Per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?p=&f=9&t=3663#p13816 537 | world.removeRigidBody( _object ); 538 | 539 | _vec3_1.setX(0); 540 | _vec3_1.setY(0); 541 | _vec3_1.setZ(0); 542 | 543 | _object.setMassProps( details.mass, _vec3_1 ); 544 | world.addRigidBody( _object ); 545 | _object.activate(); 546 | }; 547 | 548 | public_functions.applyCentralImpulse = function ( details ) { 549 | 550 | _vec3_1.setX(details.x); 551 | _vec3_1.setY(details.y); 552 | _vec3_1.setZ(details.z); 553 | 554 | _objects[details.id].applyCentralImpulse(_vec3_1); 555 | _objects[details.id].activate(); 556 | }; 557 | 558 | public_functions.applyImpulse = function ( details ) { 559 | 560 | _vec3_1.setX(details.impulse_x); 561 | _vec3_1.setY(details.impulse_y); 562 | _vec3_1.setZ(details.impulse_z); 563 | 564 | _vec3_2.setX(details.x); 565 | _vec3_2.setY(details.y); 566 | _vec3_2.setZ(details.z); 567 | 568 | _objects[details.id].applyImpulse( 569 | _vec3_1, 570 | _vec3_2 571 | ); 572 | _objects[details.id].activate(); 573 | }; 574 | 575 | public_functions.applyTorque = function ( details ) { 576 | 577 | _vec3_1.setX(details.torque_x); 578 | _vec3_1.setY(details.torque_y); 579 | _vec3_1.setZ(details.torque_z); 580 | 581 | _objects[details.id].applyTorque( 582 | _vec3_1 583 | ); 584 | _objects[details.id].activate(); 585 | }; 586 | 587 | public_functions.applyCentralForce = function ( details ) { 588 | 589 | _vec3_1.setX(details.x); 590 | _vec3_1.setY(details.y); 591 | _vec3_1.setZ(details.z); 592 | 593 | _objects[details.id].applyCentralForce(_vec3_1); 594 | _objects[details.id].activate(); 595 | }; 596 | 597 | public_functions.applyForce = function ( details ) { 598 | 599 | _vec3_1.setX(details.force_x); 600 | _vec3_1.setY(details.force_y); 601 | _vec3_1.setZ(details.force_z); 602 | 603 | _vec3_2.setX(details.x); 604 | _vec3_2.setY(details.y); 605 | _vec3_2.setZ(details.z); 606 | 607 | _objects[details.id].applyForce( 608 | _vec3_1, 609 | _vec3_2 610 | ); 611 | _objects[details.id].activate(); 612 | }; 613 | 614 | public_functions.onSimulationResume = function( params ) { 615 | last_simulation_time = Date.now(); 616 | }; 617 | 618 | public_functions.setAngularVelocity = function ( details ) { 619 | 620 | _vec3_1.setX(details.x); 621 | _vec3_1.setY(details.y); 622 | _vec3_1.setZ(details.z); 623 | 624 | _objects[details.id].setAngularVelocity( 625 | _vec3_1 626 | ); 627 | _objects[details.id].activate(); 628 | }; 629 | 630 | public_functions.setLinearVelocity = function ( details ) { 631 | 632 | _vec3_1.setX(details.x); 633 | _vec3_1.setY(details.y); 634 | _vec3_1.setZ(details.z); 635 | 636 | _objects[details.id].setLinearVelocity( 637 | _vec3_1 638 | ); 639 | _objects[details.id].activate(); 640 | }; 641 | 642 | public_functions.setAngularFactor = function ( details ) { 643 | 644 | _vec3_1.setX(details.x); 645 | _vec3_1.setY(details.y); 646 | _vec3_1.setZ(details.z); 647 | 648 | _objects[details.id].setAngularFactor( 649 | _vec3_1 650 | ); 651 | }; 652 | 653 | public_functions.setLinearFactor = function ( details ) { 654 | 655 | _vec3_1.setX(details.x); 656 | _vec3_1.setY(details.y); 657 | _vec3_1.setZ(details.z); 658 | 659 | _objects[details.id].setLinearFactor( 660 | _vec3_1 661 | ); 662 | }; 663 | 664 | public_functions.setDamping = function ( details ) { 665 | _objects[details.id].setDamping( details.linear, details.angular ); 666 | }; 667 | 668 | public_functions.setCcdMotionThreshold = function ( details ) { 669 | _objects[details.id].setCcdMotionThreshold( details.threshold ); 670 | }; 671 | 672 | public_functions.setCcdSweptSphereRadius = function ( details ) { 673 | _objects[details.id].setCcdSweptSphereRadius( details.radius ); 674 | }; 675 | 676 | public_functions.addConstraint = function ( details ) { 677 | var constraint; 678 | 679 | switch ( details.type ) { 680 | 681 | case 'point': 682 | if ( details.objectb === undefined ) { 683 | 684 | _vec3_1.setX(details.positiona.x); 685 | _vec3_1.setY(details.positiona.y); 686 | _vec3_1.setZ(details.positiona.z); 687 | 688 | constraint = new Ammo.btPoint2PointConstraint( 689 | _objects[ details.objecta ], 690 | _vec3_1 691 | ); 692 | } else { 693 | 694 | _vec3_1.setX(details.positiona.x); 695 | _vec3_1.setY(details.positiona.y); 696 | _vec3_1.setZ(details.positiona.z); 697 | 698 | _vec3_2.setX(details.positionb.x); 699 | _vec3_2.setY(details.positionb.y); 700 | _vec3_2.setZ(details.positionb.z); 701 | 702 | constraint = new Ammo.btPoint2PointConstraint( 703 | _objects[ details.objecta ], 704 | _objects[ details.objectb ], 705 | _vec3_1, 706 | _vec3_2 707 | ); 708 | } 709 | break; 710 | 711 | case 'hinge': 712 | if ( details.objectb === undefined ) { 713 | 714 | _vec3_1.setX(details.positiona.x); 715 | _vec3_1.setY(details.positiona.y); 716 | _vec3_1.setZ(details.positiona.z); 717 | 718 | _vec3_2.setX(details.axis.x); 719 | _vec3_2.setY(details.axis.y); 720 | _vec3_2.setZ(details.axis.z); 721 | 722 | constraint = new Ammo.btHingeConstraint( 723 | _objects[ details.objecta ], 724 | _vec3_1, 725 | _vec3_2 726 | ); 727 | } else { 728 | 729 | _vec3_1.setX(details.positiona.x); 730 | _vec3_1.setY(details.positiona.y); 731 | _vec3_1.setZ(details.positiona.z); 732 | 733 | _vec3_2.setX(details.positionb.x); 734 | _vec3_2.setY(details.positionb.y); 735 | _vec3_2.setZ(details.positionb.z); 736 | 737 | _vec3_3.setX(details.axis.x); 738 | _vec3_3.setY(details.axis.y); 739 | _vec3_3.setZ(details.axis.z); 740 | 741 | constraint = new Ammo.btHingeConstraint( 742 | _objects[ details.objecta ], 743 | _objects[ details.objectb ], 744 | _vec3_1, 745 | _vec3_2, 746 | _vec3_3, 747 | _vec3_3 748 | ); 749 | } 750 | break; 751 | 752 | case 'slider': 753 | var transforma, transformb, rotation; 754 | 755 | transforma = new Ammo.btTransform(); 756 | 757 | _vec3_1.setX(details.positiona.x); 758 | _vec3_1.setY(details.positiona.y); 759 | _vec3_1.setZ(details.positiona.z); 760 | 761 | transforma.setOrigin(_vec3_1); 762 | 763 | var rotation = transforma.getRotation(); 764 | rotation.setEuler( details.axis.x, details.axis.y, details.axis.z ); 765 | transforma.setRotation( rotation ); 766 | 767 | if ( details.objectb ) { 768 | transformb = new Ammo.btTransform(); 769 | 770 | _vec3_2.setX(details.positionb.x); 771 | _vec3_2.setY(details.positionb.y); 772 | _vec3_2.setZ(details.positionb.z); 773 | 774 | transformb.setOrigin(_vec3_2); 775 | 776 | rotation = transformb.getRotation(); 777 | rotation.setEuler( details.axis.x, details.axis.y, details.axis.z ); 778 | transformb.setRotation( rotation ); 779 | 780 | constraint = new Ammo.btSliderConstraint( 781 | _objects[ details.objecta ], 782 | _objects[ details.objectb ], 783 | transforma, 784 | transformb, 785 | true 786 | ); 787 | } else { 788 | constraint = new Ammo.btSliderConstraint( 789 | _objects[ details.objecta ], 790 | transforma, 791 | true 792 | ); 793 | } 794 | 795 | Ammo.destroy(transforma); 796 | if (transformb != undefined) { 797 | Ammo.destroy(transformb); 798 | } 799 | break; 800 | 801 | case 'conetwist': 802 | var transforma, transformb; 803 | 804 | transforma = new Ammo.btTransform(); 805 | transforma.setIdentity(); 806 | 807 | transformb = new Ammo.btTransform(); 808 | transformb.setIdentity(); 809 | 810 | _vec3_1.setX(details.positiona.x); 811 | _vec3_1.setY(details.positiona.y); 812 | _vec3_1.setZ(details.positiona.z); 813 | 814 | _vec3_2.setX(details.positionb.x); 815 | _vec3_2.setY(details.positionb.y); 816 | _vec3_2.setZ(details.positionb.z); 817 | 818 | transforma.setOrigin(_vec3_1); 819 | transformb.setOrigin(_vec3_2); 820 | 821 | var rotation = transforma.getRotation(); 822 | rotation.setEulerZYX( -details.axisa.z, -details.axisa.y, -details.axisa.x ); 823 | transforma.setRotation( rotation ); 824 | 825 | rotation = transformb.getRotation(); 826 | rotation.setEulerZYX( -details.axisb.z, -details.axisb.y, -details.axisb.x ); 827 | transformb.setRotation( rotation ); 828 | 829 | constraint = new Ammo.btConeTwistConstraint( 830 | _objects[ details.objecta ], 831 | _objects[ details.objectb ], 832 | transforma, 833 | transformb 834 | ); 835 | 836 | constraint.setLimit( Math.PI, 0, Math.PI ); 837 | 838 | Ammo.destroy(transforma); 839 | Ammo.destroy(transformb); 840 | 841 | break; 842 | 843 | case 'dof': 844 | var transforma, transformb, rotation; 845 | 846 | transforma = new Ammo.btTransform(); 847 | transforma.setIdentity(); 848 | 849 | _vec3_1.setX(details.positiona.x); 850 | _vec3_1.setY(details.positiona.y); 851 | _vec3_1.setZ(details.positiona.z); 852 | 853 | transforma.setOrigin(_vec3_1 ); 854 | 855 | rotation = transforma.getRotation(); 856 | rotation.setEulerZYX( -details.axisa.z, -details.axisa.y, -details.axisa.x ); 857 | transforma.setRotation( rotation ); 858 | 859 | if ( details.objectb ) { 860 | transformb = new Ammo.btTransform(); 861 | transformb.setIdentity(); 862 | 863 | _vec3_2.setX(details.positionb.x); 864 | _vec3_2.setY(details.positionb.y); 865 | _vec3_2.setZ(details.positionb.z); 866 | 867 | transformb.setOrigin(_vec3_2); 868 | 869 | rotation = transformb.getRotation(); 870 | rotation.setEulerZYX( -details.axisb.z, -details.axisb.y, -details.axisb.x ); 871 | transformb.setRotation( rotation ); 872 | 873 | constraint = new Ammo.btGeneric6DofConstraint( 874 | _objects[ details.objecta ], 875 | _objects[ details.objectb ], 876 | transforma, 877 | transformb 878 | ); 879 | } else { 880 | constraint = new Ammo.btGeneric6DofConstraint( 881 | _objects[ details.objecta ], 882 | transforma 883 | ); 884 | } 885 | Ammo.destroy(transforma); 886 | if (transformb != undefined) { 887 | Ammo.destroy(transformb); 888 | } 889 | break; 890 | 891 | default: 892 | return; 893 | 894 | }; 895 | 896 | world.addConstraint( constraint ); 897 | 898 | constraint.enableFeedback(); 899 | _constraints[ details.id ] = constraint; 900 | _num_constraints++; 901 | 902 | if ( SUPPORT_TRANSFERABLE ) { 903 | constraintreport = new Float32Array(1 + _num_constraints * CONSTRAINTREPORT_ITEMSIZE); // message id & ( # of objects to report * # of values per object ) 904 | constraintreport[0] = MESSAGE_TYPES.CONSTRAINTREPORT; 905 | } else { 906 | constraintreport = [ MESSAGE_TYPES.CONSTRAINTREPORT ]; 907 | } 908 | }; 909 | 910 | public_functions.removeConstraint = function( details ) { 911 | var constraint = _constraints[ details.id ]; 912 | if ( constraint !== undefined ) { 913 | world.removeConstraint( constraint ); 914 | delete _constraints[ details.id ]; 915 | _num_constraints--; 916 | } 917 | }; 918 | 919 | public_functions.constraint_setBreakingImpulseThreshold = function( details ) { 920 | var constraint = _constraints[ details.id ]; 921 | if ( constraint !== undefind ) { 922 | constraint.setBreakingImpulseThreshold( details.threshold ); 923 | } 924 | }; 925 | 926 | public_functions.simulate = function simulate( params ) { 927 | if ( world ) { 928 | params = params || {}; 929 | 930 | if ( !params.timeStep ) { 931 | if ( last_simulation_time ) { 932 | params.timeStep = 0; 933 | while ( params.timeStep + last_simulation_duration <= fixedTimeStep ) { 934 | params.timeStep = ( Date.now() - last_simulation_time ) / 1000; // time since last simulation 935 | } 936 | } else { 937 | params.timeStep = fixedTimeStep; // handle first frame 938 | } 939 | } else { 940 | if ( params.timeStep < fixedTimeStep ) { 941 | params.timeStep = fixedTimeStep; 942 | } 943 | } 944 | 945 | params.maxSubSteps = params.maxSubSteps || Math.ceil( params.timeStep / fixedTimeStep ); // If maxSubSteps is not defined, keep the simulation fully up to date 946 | 947 | last_simulation_duration = Date.now(); 948 | world.stepSimulation( params.timeStep, params.maxSubSteps, fixedTimeStep ); 949 | 950 | reportVehicles(); 951 | reportCollisions(); 952 | reportConstraints(); 953 | reportWorld(); 954 | 955 | last_simulation_duration = ( Date.now() - last_simulation_duration ) / 1000; 956 | last_simulation_time = Date.now(); 957 | } 958 | }; 959 | 960 | 961 | // Constraint functions 962 | public_functions.hinge_setLimits = function( params ) { 963 | _constraints[ params.constraint ].setLimit( params.low, params.high, 0, params.bias_factor, params.relaxation_factor ); 964 | }; 965 | public_functions.hinge_enableAngularMotor = function( params ) { 966 | var constraint = _constraints[ params.constraint ]; 967 | constraint.enableAngularMotor( true, params.velocity, params.acceleration ); 968 | constraint.getRigidBodyA().activate(); 969 | if ( constraint.getRigidBodyB() ) { 970 | constraint.getRigidBodyB().activate(); 971 | } 972 | }; 973 | public_functions.hinge_disableMotor = function( params ) { 974 | _constraints[ params.constraint ].enableMotor( false ); 975 | if ( constraint.getRigidBodyB() ) { 976 | constraint.getRigidBodyB().activate(); 977 | } 978 | }; 979 | 980 | public_functions.slider_setLimits = function( params ) { 981 | var constraint = _constraints[ params.constraint ]; 982 | constraint.setLowerLinLimit( params.lin_lower || 0 ); 983 | constraint.setUpperLinLimit( params.lin_upper || 0 ); 984 | 985 | constraint.setLowerAngLimit( params.ang_lower || 0 ); 986 | constraint.setUpperAngLimit( params.ang_upper || 0 ); 987 | }; 988 | public_functions.slider_setRestitution = function( params ) { 989 | var constraint = _constraints[ params.constraint ]; 990 | constraint.setSoftnessLimLin( params.linear || 0 ); 991 | constraint.setSoftnessLimAng( params.angular || 0 ); 992 | }; 993 | public_functions.slider_enableLinearMotor = function( params ) { 994 | var constraint = _constraints[ params.constraint ]; 995 | constraint.setTargetLinMotorVelocity( params.velocity ); 996 | constraint.setMaxLinMotorForce( params.acceleration ); 997 | constraint.setPoweredLinMotor( true ); 998 | constraint.getRigidBodyA().activate(); 999 | if ( constraint.getRigidBodyB() ) { 1000 | constraint.getRigidBodyB().activate(); 1001 | } 1002 | }; 1003 | public_functions.slider_disableLinearMotor = function( params ) { 1004 | var constraint = _constraints[ params.constraint ]; 1005 | constraint.setPoweredLinMotor( false ); 1006 | if ( constraint.getRigidBodyB() ) { 1007 | constraint.getRigidBodyB().activate(); 1008 | } 1009 | }; 1010 | public_functions.slider_enableAngularMotor = function( params ) { 1011 | var constraint = _constraints[ params.constraint ]; 1012 | constraint.setTargetAngMotorVelocity( params.velocity ); 1013 | constraint.setMaxAngMotorForce( params.acceleration ); 1014 | constraint.setPoweredAngMotor( true ); 1015 | constraint.getRigidBodyA().activate(); 1016 | if ( constraint.getRigidBodyB() ) { 1017 | constraint.getRigidBodyB().activate(); 1018 | } 1019 | }; 1020 | public_functions.slider_disableAngularMotor = function( params ) { 1021 | var constraint = _constraints[ params.constraint ]; 1022 | constraint.setPoweredAngMotor( false ); 1023 | constraint.getRigidBodyA().activate(); 1024 | if ( constraint.getRigidBodyB() ) { 1025 | constraint.getRigidBodyB().activate(); 1026 | } 1027 | }; 1028 | 1029 | public_functions.conetwist_setLimit = function( params ) { 1030 | _constraints[ params.constraint ].setLimit( params.z, params.y, params.x ); // ZYX order 1031 | }; 1032 | public_functions.conetwist_enableMotor = function( params ) { 1033 | var constraint = _constraints[ params.constraint ]; 1034 | constraint.enableMotor( true ); 1035 | constraint.getRigidBodyA().activate(); 1036 | constraint.getRigidBodyB().activate(); 1037 | }; 1038 | public_functions.conetwist_setMaxMotorImpulse = function( params ) { 1039 | var constraint = _constraints[ params.constraint ]; 1040 | constraint.setMaxMotorImpulse( params.max_impulse ); 1041 | constraint.getRigidBodyA().activate(); 1042 | constraint.getRigidBodyB().activate(); 1043 | }; 1044 | public_functions.conetwist_setMotorTarget = function( params ) { 1045 | var constraint = _constraints[ params.constraint ]; 1046 | 1047 | _quat.setX(params.x); 1048 | _quat.setY(params.y); 1049 | _quat.setZ(params.z); 1050 | _quat.setW(params.w); 1051 | 1052 | constraint.setMotorTarget(_quat); 1053 | 1054 | constraint.getRigidBodyA().activate(); 1055 | constraint.getRigidBodyB().activate(); 1056 | }; 1057 | public_functions.conetwist_disableMotor = function( params ) { 1058 | var constraint = _constraints[ params.constraint ]; 1059 | constraint.enableMotor( false ); 1060 | constraint.getRigidBodyA().activate(); 1061 | constraint.getRigidBodyB().activate(); 1062 | }; 1063 | 1064 | public_functions.dof_setLinearLowerLimit = function( params ) { 1065 | var constraint = _constraints[ params.constraint ]; 1066 | 1067 | _vec3_1.setX(params.x); 1068 | _vec3_1.setY(params.y); 1069 | _vec3_1.setZ(params.z); 1070 | 1071 | constraint.setLinearLowerLimit(_vec3_1); 1072 | 1073 | constraint.getRigidBodyA().activate(); 1074 | if ( constraint.getRigidBodyB() ) { 1075 | constraint.getRigidBodyB().activate(); 1076 | } 1077 | }; 1078 | public_functions.dof_setLinearUpperLimit = function( params ) { 1079 | var constraint = _constraints[ params.constraint ]; 1080 | 1081 | _vec3_1.setX(params.x); 1082 | _vec3_1.setY(params.y); 1083 | _vec3_1.setZ(params.z); 1084 | 1085 | constraint.setLinearUpperLimit(_vec3_1); 1086 | 1087 | constraint.getRigidBodyA().activate(); 1088 | if ( constraint.getRigidBodyB() ) { 1089 | constraint.getRigidBodyB().activate(); 1090 | } 1091 | }; 1092 | public_functions.dof_setAngularLowerLimit = function( params ) { 1093 | var constraint = _constraints[ params.constraint ]; 1094 | 1095 | _vec3_1.setX(params.x); 1096 | _vec3_1.setY(params.y); 1097 | _vec3_1.setZ(params.z); 1098 | 1099 | constraint.setAngularLowerLimit(_vec3_1); 1100 | 1101 | constraint.getRigidBodyA().activate(); 1102 | if ( constraint.getRigidBodyB() ) { 1103 | constraint.getRigidBodyB().activate(); 1104 | } 1105 | }; 1106 | public_functions.dof_setAngularUpperLimit = function( params ) { 1107 | var constraint = _constraints[ params.constraint ]; 1108 | 1109 | _vec3_1.setX(params.x); 1110 | _vec3_1.setY(params.y); 1111 | _vec3_1.setZ(params.z); 1112 | 1113 | constraint.setAngularUpperLimit(_vec3_1); 1114 | 1115 | constraint.getRigidBodyA().activate(); 1116 | if ( constraint.getRigidBodyB() ) { 1117 | constraint.getRigidBodyB().activate(); 1118 | } 1119 | }; 1120 | public_functions.dof_enableAngularMotor = function( params ) { 1121 | var constraint = _constraints[ params.constraint ]; 1122 | 1123 | var motor = constraint.getRotationalLimitMotor( params.which ); 1124 | motor.set_m_enableMotor( true ); 1125 | 1126 | constraint.getRigidBodyA().activate(); 1127 | if ( constraint.getRigidBodyB() ) { 1128 | constraint.getRigidBodyB().activate(); 1129 | } 1130 | }; 1131 | public_functions.dof_configureAngularMotor = function( params ) { 1132 | var constraint = _constraints[ params.constraint ]; 1133 | 1134 | var motor = constraint.getRotationalLimitMotor( params.which ); 1135 | 1136 | motor.set_m_loLimit( params.low_angle ); 1137 | motor.set_m_hiLimit( params.high_angle ); 1138 | motor.set_m_targetVelocity( params.velocity ); 1139 | motor.set_m_maxMotorForce( params.max_force ); 1140 | 1141 | constraint.getRigidBodyA().activate(); 1142 | if ( constraint.getRigidBodyB() ) { 1143 | constraint.getRigidBodyB().activate(); 1144 | } 1145 | }; 1146 | public_functions.dof_disableAngularMotor = function( params ) { 1147 | var constraint = _constraints[ params.constraint ]; 1148 | 1149 | var motor = constraint.getRotationalLimitMotor( params.which ); 1150 | motor.set_m_enableMotor( false ); 1151 | 1152 | constraint.getRigidBodyA().activate(); 1153 | if ( constraint.getRigidBodyB() ) { 1154 | constraint.getRigidBodyB().activate(); 1155 | } 1156 | }; 1157 | 1158 | reportWorld = function() { 1159 | var index, object, 1160 | transform, origin, rotation, 1161 | offset = 0, 1162 | i = 0; 1163 | 1164 | if ( SUPPORT_TRANSFERABLE ) { 1165 | if ( worldreport.length < 2 + _num_objects * WORLDREPORT_ITEMSIZE ) { 1166 | worldreport = new Float32Array( 1167 | 2 + // message id & # objects in report 1168 | ( Math.ceil( _num_objects / REPORT_CHUNKSIZE ) * REPORT_CHUNKSIZE ) * WORLDREPORT_ITEMSIZE // # of values needed * item size 1169 | ); 1170 | worldreport[0] = MESSAGE_TYPES.WORLDREPORT; 1171 | } 1172 | } 1173 | 1174 | worldreport[1] = _num_objects; // record how many objects we're reporting on 1175 | 1176 | //for ( i = 0; i < worldreport[1]; i++ ) { 1177 | for ( index in _objects ) { 1178 | if ( _objects.hasOwnProperty( index ) ) { 1179 | object = _objects[index]; 1180 | 1181 | // #TODO: we can't use center of mass transform when center of mass can change, 1182 | // but getMotionState().getWorldTransform() screws up on objects that have been moved 1183 | //object.getMotionState().getWorldTransform( transform ); 1184 | transform = object.getCenterOfMassTransform(); 1185 | 1186 | origin = transform.getOrigin(); 1187 | rotation = transform.getRotation(); 1188 | 1189 | // add values to report 1190 | offset = 2 + (i++) * WORLDREPORT_ITEMSIZE; 1191 | 1192 | worldreport[ offset ] = object.id; 1193 | 1194 | worldreport[ offset + 1 ] = origin.x(); 1195 | worldreport[ offset + 2 ] = origin.y(); 1196 | worldreport[ offset + 3 ] = origin.z(); 1197 | 1198 | worldreport[ offset + 4 ] = rotation.x(); 1199 | worldreport[ offset + 5 ] = rotation.y(); 1200 | worldreport[ offset + 6 ] = rotation.z(); 1201 | worldreport[ offset + 7 ] = rotation.w(); 1202 | 1203 | _vector = object.getLinearVelocity(); 1204 | worldreport[ offset + 8 ] = _vector.x(); 1205 | worldreport[ offset + 9 ] = _vector.y(); 1206 | worldreport[ offset + 10 ] = _vector.z(); 1207 | 1208 | _vector = object.getAngularVelocity(); 1209 | worldreport[ offset + 11 ] = _vector.x(); 1210 | worldreport[ offset + 12 ] = _vector.y(); 1211 | worldreport[ offset + 13 ] = _vector.z(); 1212 | } 1213 | } 1214 | 1215 | 1216 | if ( SUPPORT_TRANSFERABLE ) { 1217 | transferableMessage( worldreport.buffer, [worldreport.buffer] ); 1218 | } else { 1219 | transferableMessage( worldreport ); 1220 | } 1221 | 1222 | }; 1223 | 1224 | reportCollisions = function() { 1225 | var i, offset, 1226 | dp = world.getDispatcher(), 1227 | num = dp.getNumManifolds(), 1228 | manifold, num_contacts, j, pt, 1229 | _collided = false; 1230 | 1231 | if ( SUPPORT_TRANSFERABLE ) { 1232 | if ( collisionreport.length < 2 + num * COLLISIONREPORT_ITEMSIZE ) { 1233 | collisionreport = new Float32Array( 1234 | 2 + // message id & # objects in report 1235 | ( Math.ceil( _num_objects / REPORT_CHUNKSIZE ) * REPORT_CHUNKSIZE ) * COLLISIONREPORT_ITEMSIZE // # of values needed * item size 1236 | ); 1237 | collisionreport[0] = MESSAGE_TYPES.COLLISIONREPORT; 1238 | } 1239 | } 1240 | 1241 | collisionreport[1] = 0; // how many collisions we're reporting on 1242 | 1243 | for ( i = 0; i < num; i++ ) { 1244 | manifold = dp.getManifoldByIndexInternal( i ); 1245 | 1246 | num_contacts = manifold.getNumContacts(); 1247 | if ( num_contacts === 0 ) { 1248 | continue; 1249 | } 1250 | 1251 | for ( j = 0; j < num_contacts; j++ ) { 1252 | pt = manifold.getContactPoint( j ); 1253 | //if ( pt.getDistance() < 0 ) { 1254 | offset = 2 + (collisionreport[1]++) * COLLISIONREPORT_ITEMSIZE; 1255 | collisionreport[ offset ] = _objects_ammo[ manifold.getBody0() ]; 1256 | collisionreport[ offset + 1 ] = _objects_ammo[ manifold.getBody1() ]; 1257 | 1258 | _vector = pt.get_m_normalWorldOnB(); 1259 | collisionreport[ offset + 2 ] = _vector.x(); 1260 | collisionreport[ offset + 3 ] = _vector.y(); 1261 | collisionreport[ offset + 4 ] = _vector.z(); 1262 | break; 1263 | //} 1264 | 1265 | transferableMessage( _objects_ammo ); 1266 | 1267 | } 1268 | } 1269 | 1270 | 1271 | if ( SUPPORT_TRANSFERABLE ) { 1272 | transferableMessage( collisionreport.buffer, [collisionreport.buffer] ); 1273 | } else { 1274 | transferableMessage( collisionreport ); 1275 | } 1276 | }; 1277 | 1278 | reportVehicles = function() { 1279 | var index, vehicle, 1280 | transform, origin, rotation, 1281 | offset = 0, 1282 | i = 0, j = 0; 1283 | 1284 | if ( SUPPORT_TRANSFERABLE ) { 1285 | if ( vehiclereport.length < 2 + _num_wheels * VEHICLEREPORT_ITEMSIZE ) { 1286 | vehiclereport = new Float32Array( 1287 | 2 + // message id & # objects in report 1288 | ( Math.ceil( _num_wheels / REPORT_CHUNKSIZE ) * REPORT_CHUNKSIZE ) * VEHICLEREPORT_ITEMSIZE // # of values needed * item size 1289 | ); 1290 | vehiclereport[0] = MESSAGE_TYPES.VEHICLEREPORT; 1291 | } 1292 | } 1293 | 1294 | for ( index in _vehicles ) { 1295 | if ( _vehicles.hasOwnProperty( index ) ) { 1296 | vehicle = _vehicles[index]; 1297 | 1298 | for ( j = 0; j < vehicle.getNumWheels(); j++ ) { 1299 | 1300 | //vehicle.updateWheelTransform( j, true ); 1301 | 1302 | //transform = vehicle.getWheelTransformWS( j ); 1303 | transform = vehicle.getWheelInfo( j ).get_m_worldTransform(); 1304 | 1305 | origin = transform.getOrigin(); 1306 | rotation = transform.getRotation(); 1307 | 1308 | // add values to report 1309 | offset = 1 + (i++) * VEHICLEREPORT_ITEMSIZE; 1310 | 1311 | vehiclereport[ offset ] = index; 1312 | vehiclereport[ offset + 1 ] = j; 1313 | 1314 | vehiclereport[ offset + 2 ] = origin.x(); 1315 | vehiclereport[ offset + 3 ] = origin.y(); 1316 | vehiclereport[ offset + 4 ] = origin.z(); 1317 | 1318 | vehiclereport[ offset + 5 ] = rotation.x(); 1319 | vehiclereport[ offset + 6 ] = rotation.y(); 1320 | vehiclereport[ offset + 7 ] = rotation.z(); 1321 | vehiclereport[ offset + 8 ] = rotation.w(); 1322 | 1323 | } 1324 | 1325 | } 1326 | } 1327 | 1328 | if ( j !== 0 ) { 1329 | if ( SUPPORT_TRANSFERABLE ) { 1330 | transferableMessage( vehiclereport.buffer, [vehiclereport.buffer] ); 1331 | } else { 1332 | transferableMessage( vehiclereport ); 1333 | } 1334 | } 1335 | }; 1336 | 1337 | reportConstraints = function() { 1338 | var index, constraint, 1339 | offset_body, 1340 | transform, origin, 1341 | offset = 0, 1342 | i = 0; 1343 | 1344 | if ( SUPPORT_TRANSFERABLE ) { 1345 | if ( constraintreport.length < 2 + _num_constraints * CONSTRAINTREPORT_ITEMSIZE ) { 1346 | constraintreport = new Float32Array( 1347 | 2 + // message id & # objects in report 1348 | ( Math.ceil( _num_constraints / REPORT_CHUNKSIZE ) * REPORT_CHUNKSIZE ) * CONSTRAINTREPORT_ITEMSIZE // # of values needed * item size 1349 | ); 1350 | constraintreport[0] = MESSAGE_TYPES.CONSTRAINTREPORT; 1351 | } 1352 | } 1353 | 1354 | for ( index in _constraints ) { 1355 | if ( _constraints.hasOwnProperty( index ) ) { 1356 | constraint = _constraints[index]; 1357 | offset_body = constraint.getRigidBodyA(); 1358 | transform = constraint.getFrameOffsetA(); 1359 | origin = transform.getOrigin(); 1360 | 1361 | // add values to report 1362 | offset = 1 + (i++) * CONSTRAINTREPORT_ITEMSIZE; 1363 | 1364 | constraintreport[ offset ] = index; 1365 | constraintreport[ offset + 1 ] = offset_body.id; 1366 | constraintreport[ offset + 2 ] = origin.getX(); 1367 | constraintreport[ offset + 3 ] = origin.getY(); 1368 | constraintreport[ offset + 4 ] = origin.getZ(); 1369 | constraintreport[ offset + 5 ] = constraint.getAppliedImpulse(); 1370 | } 1371 | } 1372 | 1373 | 1374 | if ( i !== 0 ) { 1375 | if ( SUPPORT_TRANSFERABLE ) { 1376 | transferableMessage( constraintreport.buffer, [constraintreport.buffer] ); 1377 | } else { 1378 | transferableMessage( constraintreport ); 1379 | } 1380 | } 1381 | 1382 | }; 1383 | 1384 | self.onmessage = function( event ) { 1385 | 1386 | if ( event.data instanceof Float32Array ) { 1387 | // transferable object 1388 | 1389 | switch ( event.data[0] ) { 1390 | case MESSAGE_TYPES.WORLDREPORT: 1391 | worldreport = new Float32Array( event.data ); 1392 | break; 1393 | 1394 | case MESSAGE_TYPES.COLLISIONREPORT: 1395 | collisionreport = new Float32Array( event.data ); 1396 | break; 1397 | 1398 | case MESSAGE_TYPES.VEHICLEREPORT: 1399 | vehiclereport = new Float32Array( event.data ); 1400 | break; 1401 | 1402 | case MESSAGE_TYPES.CONSTRAINTREPORT: 1403 | constraintreport = new Float32Array( event.data ); 1404 | break; 1405 | } 1406 | 1407 | return; 1408 | } 1409 | 1410 | if ( event.data.cmd && public_functions[event.data.cmd] ) { 1411 | //if ( event.data.params.id !== undefined && _objects[event.data.params.id] === undefined && event.data.cmd !== 'addObject' && event.data.cmd !== 'registerMaterial' ) return; 1412 | public_functions[event.data.cmd]( event.data.params ); 1413 | } 1414 | 1415 | }; 1416 | --------------------------------------------------------------------------------