├── 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 | [](http://chandlerprall.github.io/Physijs/examples/body.html)
19 | [](http://chandlerprall.github.io/Physijs/examples/collisions.html)
20 | [](http://chandlerprall.github.io/Physijs/examples/compound.html)
21 | [](http://chandlerprall.github.io/Physijs/examples/shapes.html)
22 | [](http://chandlerprall.github.io/Physijs/examples/jenga.html)
23 | [](http://chandlerprall.github.io/Physijs/examples/constraints_car.html)
24 | [](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 | Stop adding shapes
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 | Stop adding shapes
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 |
--------------------------------------------------------------------------------