1 /**
  2  * @fileOverview
  3  * physics.gl.enchant.js
  4  * @version 0.3.6
  5  * @require enchant.js v0.4.5+
  6  * @require gl.enchant.js v0.3.6+
  7  * @require primitive.gl.enchant.js v0.3.5+
  8  * @require gl-matrix.js 1.3.7+
  9  * @author UEI Corporation
 10  *
 11  * @description
 12  * ammo.jsを使用している物理演算ライブラリ.
 13  * gl.enchant.jsで物理演算によって動作するオブジェクトを使えるようにする.
 14  * @detail
 15  * ammo.js:
 16  * https://github.com/kripken/ammo.js
 17  */
 18 if (typeof Ammo === 'undefined') {
 19     throw new Error('physics.gl.enchant.js must be loaded after ammo.js');
 20 }
 21 if (enchant.gl !== undefined && enchant.gl.primitive !== undefined) {
 22     (function() {
 23         /**
 24          * namespace object
 25          * @type {Object}
 26          */
 27         enchant.gl.physics = {};
 28         /**
 29          * @scope enchant.gl.physics.World.prototype
 30          */
 31         enchant.gl.physics.World = enchant.Class.create({
 32             /**
 33              * 物理演算が適用される世界.
 34              * ここに剛体オブジェクトを追加し, 時間を進めることで物理演算が実行される.
 35              * @see enchant.gl.physics.PhyScene3D
 36              * @constructs
 37              */
 38             initialize: function() {
 39                 var g = new Ammo.btVector3(0, -10, 0);
 40                 var collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
 41                 var dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
 42                 var pairCache = new Ammo.btDbvtBroadphase();
 43                 var constraintSolver = new Ammo.btSequentialImpulseConstraintSolver();
 44 
 45                 this._dynamicsWorld = new Ammo.btDiscreteDynamicsWorld(
 46                     dispatcher, pairCache, constraintSolver, collisionConfiguration);
 47 
 48                 this._dynamicsWorld.setGravity(g);
 49 
 50                 Ammo.destroy(g);
 51             },
 52             /**
 53              * Worldの重力を設定する.
 54              * @param {Number} gx x軸方向の重力.
 55              * @param {Number} gy y軸方向の重力.
 56              * @param {Number} gz z軸方向の重力.
 57              */
 58             setGravity: function(gx, gy, gz) {
 59                 var g = new Ammo.btVector3(gx, gy, gz);
 60                 this._dynamicsWorld.setGravity(g);
 61                 Ammo.destroy(g);
 62             },
 63             /**
 64              * Worldの時間を進める.
 65              * timeStepがfixedTimeStepより大きい場合, maxSubStepで指定した回数まで続けて時間を進める.
 66              * @param {Number} timeStep 進めたい時間.単位は秒.
 67              * @param {Number} maxSubSteps シミュレーションの最大追加回数.
 68              * @param {Number} fixedTimeStep 基本となる時間. デフォルト値は1/60.
 69              * @return {Number} subStepsNum
 70              */
 71             stepSimulation: function(timeStep, maxSubSteps, fixedTimeStep) {
 72                 return this._dynamicsWorld.stepSimulation(timeStep, maxSubSteps, fixedTimeStep);
 73             },
 74             /**
 75              * Worldに剛体を追加する.
 76              * @param {enchant.gl.physics.Rigid} Rigid 追加する剛体オブジェクト.
 77              */
 78             addRigid: function(rigid) {
 79                 this._dynamicsWorld.addRigidBody(rigid.rigidBody);
 80                 rigid.world = this;
 81             },
 82             /**
 83              * Worldから剛体を削除する.
 84              * @param {enchant.gl.physics.Rigid} Rigid 削除する剛体オブジェクト.
 85              */
 86             removeRigid: function(rigid) {
 87                 this._dynamicsWorld.removeRigidBody(rigid.rigidBody);
 88                 rigid.world = null;
 89             },
 90             /**
 91              * Rigid同士が衝突しているかを判定する.
 92              * @param {enchant.gl.physics.Rigid} rigid1 判定するRigid1.
 93              * @param {enchant.gl.physics.Rigid} rigid2 判定するRigid2.
 94              * @return {Boolean} bool 衝突の有無.
 95              */
 96             contactPairTest: function(rigid1, rigid2) {
 97                 var callback = new Ammo.ConcreteContactResultCallback();
 98                 var result = false;
 99                 Ammo.customizeVTable(callback, [
100                     {
101                         original: Ammo.ConcreteContactResultCallback.prototype.addSingleResult,
102                         replacement: function(tp, cp, colObj0, partid0, index0, colObj1, partid1, index1) {
103                             result = true;
104                         }
105                     }
106                 ]);
107                 this._dynamicsWorld.contactPairTest(rigid1.rigidBody, rigid2.rigidBody, callback);
108                 Ammo.destroy(callback);
109                 return result;
110             }
111         });
112 
113         /**
114          * @scope enchant.gl.physics.Rigid.prototype
115          */
116         enchant.gl.physics.Rigid = enchant.Class.create({
117             /**
118              * 剛体オブジェクト.
119              * Worldに追加して使用する.
120              * @param shape Ammo.btCollisionShapeオブジェクト.
121              * @param {Number} mass 剛体の質量.
122              * @param {Number} linearDamping 剛体の線形速度の減衰率.
123              * @param {Number} angularDamping 剛体の角速度の減衰率.
124              * @see enchant.gl.physics.RigidBox
125              * @see enchant.gl.physics.RigidCube
126              * @see enchant.gl.physics.RigidSphere
127              * @see enchant.gl.physics.RigidCylinder
128              * @see enchant.gl.physics.RigidCapsule
129              * @see enchant.gl.physics.RigidPlane
130              * @see enchant.gl.physics.RigidContainer
131              * @constructs
132              */
133             initialize: function(shape, mass, lDamp, aDamp) {
134                 if (typeof shape === 'undefined') {
135                     shape = new Ammo.btBoxShape(1);
136                 }
137                 if (typeof mass === 'undefined') {
138                     mass = 1;
139                 }
140 
141                 var localInertia = new Ammo.btVector3(0, 0, 0);
142                 shape.calculateLocalInertia(mass, localInertia);
143 
144                 var transform = new Ammo.btTransform();
145                 transform.setIdentity();
146 
147                 var motionState = new Ammo.btDefaultMotionState(transform);
148                 var rigidBodyInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, shape, localInertia);
149                 rigidBodyInfo.set_m_restitution(0.1);
150                 rigidBodyInfo.set_m_friction(3.0);
151 
152                 if (typeof lDamp !== 'undefined') {
153                     rigidBodyInfo.set_m_linearDamping(lDamp);
154                 }
155                 if (typeof aDamp !== 'undefined') {
156                     rigidBodyInfo.set_m_angularDamping(aDamp);
157                 }
158 
159                 this.shape = shape;
160                 /**
161                  * Rigidが所属するWorld
162                  */
163                 this.world = null;
164 
165                 /**
166                  * Ammoの剛体オブジェクト
167                  */
168                 this.rigidBody = new Ammo.btRigidBody(rigidBodyInfo);
169                 var p = Ammo.getPointer(this.rigidBody);
170                 enchant.gl.physics.Rigid._refs[p] = this;
171 
172                 Ammo.destroy(transform);
173                 Ammo.destroy(localInertia);
174                 Ammo.destroy(rigidBodyInfo);
175 
176                 this._x = 0;
177                 this._y = 0;
178                 this._z = 0;
179                 this._scaleX = 1;
180                 this._scaleY = 1;
181                 this._scaleZ = 1;
182                 this._mass = mass;
183                 this._restitution = 0.3;
184                 this._friction = 0.3;
185             },
186             /**
187              * Rigidを拡大縮小する.
188              * Worldでの現在の拡大率から, 各軸に対して指定された倍率分だけ拡大縮小をする.
189              * @param {Number} x x軸方向の拡大率.
190              * @param {Number} y y軸方向の拡大率.
191              * @param {Number} z z軸方向の拡大率.
192              */
193             scale: function(x, y, z) {
194                 this.activate();
195                 this._scaleX *= x;
196                 this._scaleY *= y;
197                 this._scaleZ *= z;
198                 var sv = new Ammo.btVector3(this._scaleX, this._scaleY, this._scaleZ);
199                 this.shape.setLocalScaling(sv);
200                 Ammo.destroy(sv);
201             },
202             _scaleAxis: function(axis, scale) {
203                 axis.toUpperCase();
204                 this['_scale' + axis] = scale;
205                 var sv = new Ammo.btVector3(this._scaleX, this._scaleY, this._scaleZ);
206                 this.shape.setLocalScaling(sv);
207                 Ammo.destroy(sv);
208             },
209             /**
210              * Rigidを平行移動する.
211              * Worldでの現在の位置から, 各軸に対して指定された分だけ平行移動をする.
212              * @param {Number} x x軸方向の平行移動量.
213              * @param {Number} y y軸方向の平行移動量.
214              * @param {Number} z z軸方向の平行移動量.
215              */
216             translate: function(x, y, z) {
217                 this.activate();
218                 var vec = new Ammo.btVector3(x, y, z);
219                 this.rigidBody.translate(vec);
220                 Ammo.destroy(vec);
221                 this._x += x;
222                 this._y += y;
223                 this._z += z;
224             },
225             _translateAxis: function(axis, n) {
226                 this.activate();
227                 var x = 0;
228                 var y = 0;
229                 var z = 0;
230                 if (axis === 'x') {
231                     x = n - this._x;
232                     this._x = n;
233                 } else if (axis === 'y') {
234                     y = n - this._y;
235                     this._y = n;
236                 } else if (axis === 'z') {
237                     z = n - this._z;
238                     this._z = n;
239                 }
240                 var vec = new Ammo.btVector3(x, y, z);
241                 this.rigidBody.translate(vec);
242                 Ammo.destroy(vec);
243             },
244             /**
245              * クォータニオンで表した姿勢をRigidにセットする.
246              * @param {enchant.gl.Quat} quat
247              */
248             rotationSet: function(quat) {
249                 var qq = quat._quat;
250                 var q = new Ammo.btQuaternion(qq[0], qq[1], qq[2], qq[3]);
251                 var t = this._getTransform();
252                 t.setRotation(q);
253                 this.rigidBody.setWorldTransform(t);
254                 Ammo.destroy(q);
255                 Ammo.destroy(t);
256             },
257             /**
258              * クォータニオンで表した回転をRigidに適用する.
259              * @param {enchant.gl.Quat} quat
260              */
261             rotationApply: function(quat) {
262                 var quat1 = quat._quat;
263                 var t = this._getTransform();
264                 var qq = t.getRotation();
265                 var quat2 = quat4.create([qq.x(), qq.y(), qq.z(), qq.w()]);
266                 quat4.multiply(quat2, quat1, quat2);
267                 var q = new Ammo.btQuaternion(quat2[0], quat2[1], quat2[2], quat2[3]);
268                 t.setRotation(q);
269                 this.rigidBody.setWorldTransform(t);
270                 Ammo.destroy(q);
271                 Ammo.destroy(t);
272             },
273             /**
274              * Rigidを止める.
275              */
276             clearForces: function() {
277                 var vec0 = new Ammo.btVector3(0, 0, 0);
278                 this.rigidBody.setLinearVelocity(vec0);
279                 this.rigidBody.setAngularVelocity(vec0);
280                 this.rigidBody.clearForces();
281                 Ammo.destroy(vec0);
282             },
283             /**
284              * 他のRigidとの衝突判定.
285              * @param {enchant.gl.physics.Rigid} rigid 判定するRigid.
286              * @return {Boolean} bool 衝突の有無.
287              */
288             contactTest: function(rigid) {
289                 if (this.world && rigid.world &&
290                     this.world === rigid.world) {
291                     return this.world.contactPairTest(this, rigid);
292                 } else {
293                     return false;
294                 }
295             },
296             /**
297              * Rigidを有効化する.
298              * @param {Boolean} force 強制的に有効化する.
299              */
300             activate: function(force) {
301                 this.rigidBody.activate(force);
302             },
303             /**
304              * Rigidに力を加える.
305              * 力はRigidの中心に加えられる.
306              * @param {Number} powerX x軸方向の力.
307              * @param {Number} powerY y軸方向の力.
308              * @param {Number} powerZ z軸方向の力.
309              */
310             applyCentralImpulse: function(powx, powy, powz) {
311                 var powv = new Ammo.btVector3(powx, powy, powz);
312                 this.activate();
313                 this.rigidBody.applyCentralImpulse(powv);
314                 Ammo.destroy(powv);
315             },
316             /**
317              * Rigidに力を加える.
318              * 力は指定した位置に加えられる.
319              * @param {Number} powerX x軸方向の力.
320              * @param {Number} powerY y軸方向の力.
321              * @param {Number} powerZ z軸方向の力.
322              * @param {Number} positonX 力を加える位置のx座標.
323              * @param {Number} positonY 力を加える位置のy座標.
324              * @param {Number} positonZ 力を加える位置のz座標.
325              */
326             applyImpulse: function(powx, powy, powz, posx, posy, posz) {
327                 var powv = new Ammo.btVector3(powx, powy, powz);
328                 var posv = new Ammo.btVector3(posx, posy, posz);
329                 this.activate();
330                 this.rigidBody.applyImpulse(powv, posv);
331                 Ammo.destroy(powv);
332                 Ammo.destroy(posv);
333             },
334             _getTransform: function() {
335                 return this.rigidBody.getWorldTransform();
336             },
337             /**
338              * Rigidをユーザが動かすためのオブジェクトとして設定する.
339              */
340             kinematize: function() {
341                 var flag = this.rigidBody.getCollisionFlags();
342                 this.rigidBody.setCollisionFlags(flag | 2);
343                 this.rigidBody.setActivationState(4);
344             },
345             /**
346              * Rigidの反発係数.
347              * @type Number
348              */
349             restitution: {
350                 get: function() {
351                     return this._restitution;
352                 },
353                 set: function(n) {
354                     this._restitution = n;
355                     this.rigidBody.setRestitution(n);
356                 }
357             },
358             /**
359              * Rigidの摩擦係数.
360              * @type Number
361              */
362             friction: {
363                 get: function() {
364                     return this._friction;
365                 },
366                 set: function(n) {
367                     this._friction = n;
368                     this.rigidBody.setFriction(n);
369                 }
370             }
371         });
372         enchant.gl.physics.Rigid._refs = {};
373 
374         /**
375          * @scope enchant.gl.physics.RigidBox.prototype
376          */
377         enchant.gl.physics.RigidBox = enchant.Class.create(enchant.gl.physics.Rigid, {
378             /**
379              * 直方体型の剛体オブジェクト.
380              * @param {Number} scaleX 直方体の中心からx軸に垂直な面までの距離.
381              * @param {Number} scaleY 直方体の中心からy軸に垂直な面までの距離.
382              * @param {Number} scaleZ 直方体の中心からz軸に垂直な面までの距離.
383              * @param {Number} mass 剛体の質量.
384              * @see enchant.gl.physics.Rigid
385              * @see enchant.gl.physics.PhyBox
386              * @constructs
387              * @extends enchant.gl.physics.Rigid
388              */
389             initialize: function(sx, sy, sz, mass) {
390                 var scale = new Ammo.btVector3(sx, sy, sz);
391                 var shape = new Ammo.btBoxShape(scale);
392                 enchant.gl.physics.Rigid.call(this, shape, mass);
393                 Ammo.destroy(scale);
394             }
395         });
396         /**
397          * @scope enchant.gl.physics.RigidCube.prototype
398          */
399         enchant.gl.physics.RigidCube = enchant.Class.create(enchant.gl.physics.RigidBox, {
400             /**
401              * 立方体型の剛体オブジェクト.
402              * @param {Number} scale 箱の中心から面までの距離.
403              * @param {Number} mass 剛体の質量.
404              * @see enchant.gl.physics.PhyCube
405              * @constructs
406              * @see enchant.gl.physics.Rigid
407              * @extends enchant.gl.physics.PhyCube
408              */
409             initialize: function(scale, mass) {
410                 enchant.gl.physics.RigidBox.call(this, scale, scale, scale, mass);
411             }
412         });
413 
414         /**
415          * @scope enchant.gl.physics.RigidSphere.prototype
416          */
417         enchant.gl.physics.RigidSphere = enchant.Class.create(enchant.gl.physics.Rigid, {
418             /**
419              * 球体型の剛体オブジェクト.
420              * @param {Number} radius 球体の半径.
421              * @param {Number} mass 剛体の質量.
422              * @see enchant.gl.physics.Rigid
423              * @see enchant.gl.physics.PhySphere
424              * @constructs
425              * @extends enchant.gl.physics.Rigid
426              */
427             initialize: function(s, mass, lDamp, aDamp) {
428                 var shape = new Ammo.btSphereShape(s);
429                 enchant.gl.physics.Rigid.call(this, shape, mass, lDamp, aDamp);
430             }
431         });
432 
433         /**
434          * @scope enchant.gl.physics.RigidCylinder.prototype
435          */
436         enchant.gl.physics.RigidCylinder = enchant.Class.create(enchant.gl.physics.Rigid, {
437             /**
438              * 円柱型の剛体オブジェクト.
439              * @param {Number} radius 円柱の半径.
440              * @param {Number} height 円柱の高さ.
441              * @param {Number} mass 剛体の質量.
442              * @see enchant.gl.physics.Rigid
443              * @see enchant.gl.physics.PhyCylinder
444              * @constructs
445              * @extends enchant.gl.physics.Rigid
446              */
447             initialize: function(r, h, mass) {
448                 var scale = new Ammo.btVector3(r, h, r);
449                 var shape = new Ammo.btCylinderShape(scale);
450                 enchant.gl.physics.Rigid.call(this, shape, mass);
451                 Ammo.destroy(scale);
452             }
453         });
454 
455         /**
456          * @scope enchant.gl.physics.RigidCapsule.prototype
457          */
458         enchant.gl.physics.RigidCapsule = enchant.Class.create(enchant.gl.physics.Rigid, {
459             /**
460              * カプセル型の剛体オブジェクト.
461              * y軸に沿う円柱の両端に半球をつけた形状.
462              * @param {Number} radius 半球体の半径.
463              * @param {Number} height 円柱の高さの半分.
464              * @param {Number} mass 剛体の質量.
465              * @see enchant.gl.physics.Rigid
466              * @see enchant.gl.physics.PhyCapsule
467              * @constructs
468              * @extends enchant.gl.physics.Rigid
469              */
470             initialize: function(r, h, mass) {
471                 var shape = new Ammo.btCapsuleShape(r, h);
472                 enchant.gl.physics.Rigid.call(this, shape, mass);
473             }
474         });
475 
476         /**
477          * @scope enchant.gl.physics.RigidPlane.prototype
478          */
479         enchant.gl.physics.RigidPlane = enchant.Class.create(enchant.gl.physics.Rigid, {
480             /**
481              * 無限平面型の剛体オブジェクト.
482              * @param {Number} NormalX 平面の法線ベクトルのx成分.
483              * @param {Number} NormalY 平面の法線ベクトルのy成分.
484              * @param {Number} NormalZ 平面の法線ベクトルのz成分.
485              * @see enchant.gl.physics.Rigid
486              * @see enchant.gl.physics.PhyPlane
487              * @constructs
488              * @extends enchant.gl.physics.Rigid
489              */
490             initialize: function(nx, ny, nz, distance) {
491                 var normal = new Ammo.btVector3(nx, ny, nz);
492                 var shape = new Ammo.btStaticPlaneShape(normal, distance);
493                 enchant.gl.physics.Rigid.call(this, shape, 0);
494                 Ammo.destroy(normal);
495             }
496         });
497 
498         /**
499          * @scope enchant.gl.physics.RigidContainer.prototype
500          */
501         enchant.gl.physics.RigidContainer = enchant.Class.create(enchant.gl.physics.Rigid, {
502             /**
503              * 枡型の剛体オブジェクト.
504              * @param {Number} scale 枡の中心から枠までの距離.
505              * @param {Number} mass 剛体の質量.
506              * @see enchant.gl.physics.Rigid
507              * @see enchant.gl.physics.PhyContainer
508              * @constructs
509              * @extends enchant.gl.physics.Rigid
510              */
511             initialize: function(s, mass) {
512                 var shape = new Ammo.btCompoundShape(s);
513                 var addWall = function(sx, sy, sz, px, py, pz) {
514                     var sc = new Ammo.btVector3(sx, sy, sz);
515                     var tr = new Ammo.btTransform();
516                     tr.setIdentity();
517                     var or = new Ammo.btVector3(px, py, pz);
518                     tr.setOrigin(or);
519                     var shp = new Ammo.btBoxShape(sc);
520                     shape.addChildShape(tr, shp);
521                     Ammo.destroy(sc);
522                     Ammo.destroy(or);
523                     Ammo.destroy(tr);
524                 };
525                 addWall(s, s / 8, s, 0, s / 8 - s, 0);
526                 addWall(s - s / 8, s - s / 8 - s / 8, s / 8, s / 8, 0, s / 8 - s);
527                 addWall(s - s / 8, s - s / 8 - s / 8, s / 8, -s / 8, 0, s - s / 8);
528                 addWall(s / 8, s - s / 8 - s / 8, s - s / 8, s / 8 - s, 0, -s / 8);
529                 addWall(s / 8, s - s / 8 - s / 8, s - s / 8, s - s / 8, 0, s / 8);
530                 enchant.gl.physics.Rigid.call(this, shape, mass);
531             }
532         });
533 
534         /**
535          * @scope enchant.gl.physics.PhyScene3D.prototype
536          */
537         enchant.gl.physics.PhyScene3D = enchant.Class.create(enchant.gl.Scene3D, {
538             /**
539              * Worldを持つScene3D.
540              * 時間を進めることで, addChildされたSprite3Dに物理演算が適用される.
541              * @see enchant.gl.physics.World
542              * @constructs
543              * @extends enchant.gl.Scene3D
544              */
545             initialize: function() {
546                 enchant.gl.Scene3D.call(this);
547                 var core = enchant.Core.instance;
548                 this.world = new enchant.gl.physics.World();
549                 this.isPlaying = false;
550                 this.timeStep = 1 / core.fps;
551                 this.maxSubSteps = 1;
552                 this.fixedTimeStep = 1 / 60;
553                 var that = this;
554                 this._stepping = function() {
555                     that.stepSimulation(that.timeStep, that.maxSubSteps, that.fixedTimeStep);
556                 };
557             },
558             /**
559              * 子Sprite3Dを追加する.
560              * PhySprite3Dを追加した場合, PhySprite3Dが持つ剛体オブジェクトがPhyScene3Dが持つWorldに追加される.
561              * @param {enchant.gl.Sprite3D|enchant.gl.physics.PhySprite3D} Sprite3D 追加する子Sprite3D.
562              */
563             addChild: function(sprite) {
564                 enchant.gl.Scene3D.prototype.addChild.call(this, sprite);
565                 if (sprite instanceof enchant.gl.physics.PhySprite3D) {
566                     this.world.addRigid(sprite.rigid);
567                 }
568             },
569             /**
570              * 指定された子Sprite3Dを削除する.
571              * PhySprite3Dを指定した場合, PhySprite3Dが持つ剛体オブジェクトがPhyScene3Dが持つWorldから削除される.
572              * @param {enchant.gl.Sprite3D|enchant.gl.physics.PhySprite3D} Sprite3D 追加する子Sprite3D.
573              */
574             removeChild: function(sprite) {
575                 enchant.gl.Scene3D.prototype.removeChild.call(this, sprite);
576                 if (sprite instanceof enchant.gl.physics.PhySprite3D) {
577                     this.world.removeRigid(sprite.rigid);
578                 }
579             },
580             /**
581              * PhyScene3Dが持つWorldの重力を設定する。
582              * @param {Number} gx x軸方向の重力
583              * @param {Number} gy y軸方向の重力
584              * @param {Number} gz z軸方向の重力
585              */
586             setGravity: function(x, y, z) {
587                 this.world.setGravity(x, y, z);
588             },
589             /**
590              * PhySprite3Dが持つWorldの時間を進める.
591              * @param {Number} timeStep 進めたい時間.単位は秒.
592              * @param {Number} maxSubSteps
593              * @param {Number} fixedTimeStep
594              */
595             stepSimulation: function(timeStep, maxSubSteps, fixedTimeStep) {
596                 var subStep = this.world.stepSimulation(timeStep, maxSubSteps, fixedTimeStep);
597                 var e = new enchant.Event('timestep');
598                 e.timeStep = timeStep;
599                 e.subStep = subStep;
600                 this.dispatchEvent(e);
601                 for (var i = 0, l = this.childNodes.length; i < l; i++) {
602                     if (this.childNodes[i] instanceof enchant.gl.physics.PhySprite3D) {
603                         this.childNodes[i].dispatchEvent(e);
604                     }
605                 }
606             },
607             /**
608              * Worldの時間の進行を始める.
609              * enterframeごとにstepSimulationが自動で呼び出される.
610              */
611             play: function() {
612                 var core = enchant.Core.instance;
613                 if (!this.isPlaying) {
614                     this.isPlaying = true;
615                     core.addEventListener('enterframe', this._stepping);
616                 }
617             },
618             /**
619              * Worldの時間の進行を止める.
620              */
621             stop: function() {
622                 var core = enchant.Core.instance;
623                 this.isPlaying = false;
624                 core.removeEventListener('enterframe', this._stepping);
625             }
626         });
627 
628         /**
629          * @scope enchant.gl.physics.PhySprite3D.prototype
630          */
631         enchant.gl.physics.PhySprite3D = enchant.Class.create(enchant.gl.Sprite3D, {
632             /**
633              * 物理スプライト.
634              * PhySprite3Dに追加すると, stepSimulationを呼び出した際に物理演算が適用される.
635              * @param {enchant.gl.physics.Rigid} rigid
636              * @see enchant.gl.physics.PhyBox
637              * @see enchant.gl.physics.PhyCube
638              * @see enchant.gl.physics.PhySphere
639              * @see enchant.gl.physics.PhyCylinder
640              * @see enchant.gl.physics.PhyCapsule
641              * @see enchant.gl.physics.PhyPlane
642              * @see enchant.gl.physics.PhyContainer
643              * @constructs
644              */
645             initialize: function(rigid) {
646                 enchant.gl.Sprite3D.call(this);
647                 this.rigid = rigid;
648 
649                 this.addEventListener('timestep', function() {
650                     var t = this.rigid._getTransform();
651                     var o = t.getOrigin();
652                     var q = t.getRotation();
653                     this._x = this.rigid._x = o.x();
654                     this._y = this.rigid._y = o.y();
655                     this._z = this.rigid._z = o.z();
656                     this._changedTranslation = true;
657                     var a = [ q.x(), q.y(), q.z(), q.w() ];
658                     var quat = quat4.create(a);
659                     quat4.toMat4(quat, this.rotation);
660                     Ammo.destroy(t);
661                 });
662             },
663             /**
664              * PhySprite3Dを拡大縮小する.
665              * 表示上の拡大率とWorldでの拡大率が同時に変更される.
666              * @param {Number} x x軸方向の拡大率.
667              * @param {Number} y y軸方向の拡大率.
668              * @param {Number} z z軸方向の拡大率.
669              */
670             scale: function(x, y, z) {
671                 enchant.gl.Sprite3D.prototype.scale.call(this, x, y, z);
672                 this.rigid.scale(x, y, z);
673             },
674             /**
675              * PhySprite3Dを平行移動する.
676              * 表示上の位置とWorldでの位置が変更される.
677              * @param {Number} x x軸方向の平行移動量.
678              * @param {Number} y y軸方向の平行移動量.
679              * @param {Number} z z軸方向の平行移動量.
680              */
681             translate: function(x, y, z) {
682                 enchant.gl.Sprite3D.prototype.translate.call(this, x, y, z);
683                 this.rigid.translate(x, y, z);
684             },
685             /**
686              * 回転行列にクォータニオンから得られる回転行列をセットする.
687              * Worldでの姿勢も変更される.
688              * @param {enchant.gl.Quat} quat
689              */
690             rotationSet: function(quat) {
691                 enchant.gl.Sprite3D.prototype.rotationSet.call(this, quat);
692                 this.rigid.rotationSet(quat);
693             },
694             /**
695              * 回転行列にクォータニオンから得られる回転行列を適用する.
696              * Worldでの姿勢も変更される.
697              * @param {enchant.gl.Quat} quat
698              */
699             rotationApply: function(quat) {
700                 enchant.gl.Sprite3D.prototype.rotationApply.call(this, quat);
701                 this.rigid.rotationApply(quat);
702             },
703             /**
704              * Rigidを止める.
705              * Stops Rigid object.
706              */
707             clearForces: function() {
708                 this.rigid.clearForces();
709             },
710             /**
711              * 他のPhySprite3Dとの衝突判定.
712              * @param {enchant.gl.physics.PhySprite3D} sprite 判定するPhySprite3D.
713              * @return {Boolean} bool 衝突の有無.
714              */
715             contactTest: function(sprite) {
716                 return this.rigid.contactTest(sprite.rigid);
717             },
718             /**
719              * 剛体に力を加える.
720              * 力は剛体の中心に加えられる.
721              * @param {Number} powerX x軸方向の力.
722              * @param {Number} powerY y軸方向の力.
723              * @param {Number} powerZ z軸方向の力.
724              * @see enchant.gl.physics.Rigid#applyCentralImpulse
725              */
726             applyCentralImpulse: function(powx, powy, powz) {
727                 this.rigid.applyCentralImpulse(powx, powy, powz);
728             },
729             /**
730              * 剛体に力を加える.
731              * 力は指定した位置に加えられる.
732              * @param {Number} powerX x軸方向の力.
733              * @param {Number} powerY y軸方向の力.
734              * @param {Number} powerZ z軸方向の力.
735              * @param {Number} positonX 力を加える位置のx座標.
736              * @param {Number} positonY 力を加える位置のy座標.
737              * @param {Number} positonZ 力を加える位置のz座標.
738              * @see enchant.gl.physics.Rigid#applyImpulse
739              */
740             applyImpulse: function(powx, powy, powz, posx, posy, posz) {
741                 this.rigid.applyImpulse(powx, powy, powz, posx, posy, posz);
742             },
743             /**
744              * PhySprite3Dをユーザが動かすためのオブジェクトとして設定する.
745              */
746             kinematize: function() {
747                 this.rigid.kinematize();
748             },
749             /**
750              * PhySprite3Dの反発係数.
751              * @type Number
752              * @see enchant.gl.physics.Rigid#restitution
753              */
754             restitution: {
755                 get: function() {
756                     return this.rigid._restitution;
757                 },
758                 set: function(n) {
759                     this.rigid._restitution = n;
760                     this.rigid.rigidBody.setRestitution(n);
761                 }
762             },
763             /**
764              * PhySprite3Dの摩擦係数.
765              * @type Number
766              * @see enchant.gl.physics.Rigid#friction
767              */
768             friction: {
769                 get: function() {
770                     return this.rigid._friction;
771                 },
772                 set: function(n) {
773                     this.rigid._friction = n;
774                     this.rigid.rigidBody.setFriction(n);
775                 }
776             }
777         });
778         'x y z'.split(' ').forEach(function(prop) {
779             Object.defineProperty(enchant.gl.physics.PhySprite3D.prototype, prop, {
780                 get: function() {
781                     return this['_' + prop];
782                 },
783                 set: function(n) {
784                     this['_' + prop] = n;
785                     this._changedTranslation = true;
786                     this.rigid._translateAxis(prop, n);
787                 }
788             });
789         });
790         'scaleX scaleY scaleZ'.split(' ').forEach(function(prop) {
791             Object.defineProperty(enchant.gl.physics.PhySprite3D.prototype, prop, {
792                 get: function() {
793                     return this['_' + prop];
794                 },
795                 set: function(scale) {
796                     this['_' + prop] = scale;
797                     this._changedScale = true;
798                     this.rigid._scaleAxis(prop, scale);
799                 }
800             });
801         });
802 
803         /**
804          * @scope enchant.gl.physics.PhyBox.prototype
805          */
806         enchant.gl.physics.PhyBox = enchant.Class.create(enchant.gl.physics.PhySprite3D, {
807             /**
808              * 直方体型のPhySprite3D.
809              * PhySprite3Dに追加すると, stepSimulationを呼び出した際に物理演算が適用される.
810              * @param {Number} scaleX 直方体の中心からx軸に垂直な面までの距離.
811              * @param {Number} scaleY 直方体の中心からy軸に垂直な面までの距離.
812              * @param {Number} scaleZ 直方体の中心からz軸に垂直な面までの距離.
813              * @param {Number} mass 剛体の質量.
814              * @see enchant.gl.physics.PhySprite3D
815              * @see enchant.gl.physics.PhyScene3D
816              * @constructs
817              * @extends enchant.gl.physics.PhySprite3D
818              */
819             initialize: function(sx, sy, sz, mass) {
820                 var rigid = new enchant.gl.physics.RigidBox(sx, sy, sz, mass);
821                 enchant.gl.physics.PhySprite3D.call(this, rigid);
822                 this.mesh = enchant.gl.Mesh.createBox(sx, sy, sz);
823             }
824         });
825 
826         /**
827          * @scope enchant.gl.physics.PhyCube.prototype
828          */
829         enchant.gl.physics.PhyCube = enchant.Class.create(enchant.gl.physics.PhyBox, {
830             /**
831              * 立方体型のPhySprite3D.
832              * PhySprite3Dに追加すると, stepSimulationを呼び出した際に物理演算が適用される.
833              * @param {Number} scale 箱の中心から面までの距離.
834              * @param {Number} mass 剛体の質量.
835              * @see enchant.gl.physics.PhySprite3D
836              * @see enchant.gl.physics.PhyScene3D
837              * @constructs
838              * @extends enchant.gl.physics.PhyBox
839              */
840             initialize: function(s, mass) {
841                 var rigid = new enchant.gl.physics.RigidBox(s, s, s, mass);
842                 enchant.gl.physics.PhySprite3D.call(this, rigid);
843                 this.mesh = enchant.gl.Mesh.createBox(s, s, s);
844             }
845         });
846 
847         /**
848          * @scope enchant.gl.physics.PhySphere.prototype
849          */
850         enchant.gl.physics.PhySphere = enchant.Class.create(enchant.gl.physics.PhySprite3D, {
851             /**
852              * 球体型のPhySprite3D.
853              * PhySprite3Dに追加すると, stepSimulationを呼び出した際に物理演算が適用される.
854              * @param {Number} radius 球体の半径.
855              * @param {Number} mass 剛体の質量.
856              * @see enchant.gl.physics.PhySprite3D
857              * @see enchant.gl.physics.PhyScene3D
858              * @constructs
859              * @extends enchant.gl.physics.PhySprite3D
860              */
861             initialize: function(r, mass, lDamp, aDamp) {
862                 if (typeof lDamp === 'undefined') {
863                     lDamp = 0.05;
864                 }
865                 if (typeof aDamp === 'undefined') {
866                     aDamp = 0.05;
867                 }
868                 var rigid = new enchant.gl.physics.RigidSphere(r, mass, lDamp, aDamp);
869                 enchant.gl.physics.PhySprite3D.call(this, rigid);
870                 this.mesh = enchant.gl.Mesh.createSphere(r);
871                 this.addEventListener('timestep', function(e) {
872                     this.rigid.rigidBody.applyDamping(e.timeStep);
873                 });
874             }
875         });
876 
877         /**
878          * @scope enchant.gl.physics.PhyCylinder.prototype
879          */
880         enchant.gl.physics.PhyCylinder = enchant.Class.create(enchant.gl.physics.PhySprite3D, {
881             /**
882              * 円柱型のPhySprite3D.
883              * PhySprite3Dに追加すると, stepSimulationを呼び出した際に物理演算が適用される.
884              * @param {Number} radius 円柱の半径.
885              * @param {Number} height 円柱の高さ.
886              * @param {Number} mass 剛体の質量.
887              * @see enchant.gl.physics.PhySprite3D
888              * @see enchant.gl.physics.PhyScene3D
889              * @constructs
890              * @extends enchant.gl.physics.PhySprite3D
891              */
892             initialize: function(r, h, mass) {
893                 var rigid = new enchant.gl.physics.RigidCylinder(r, h, mass);
894                 enchant.gl.physics.PhySprite3D.call(this, rigid);
895                 this.mesh = enchant.gl.Mesh.createCylinder(r, h);
896             }
897         });
898 
899         /**
900          * @scope enchant.gl.physics.PhyCapsule.prototype
901          */
902         enchant.gl.physics.PhyCapsule = enchant.Class.create(enchant.gl.physics.PhySprite3D, {
903             /**
904              * カプセル型のPhySprite3D.
905              * y軸に沿う円柱の両端に半球をつけた形状.
906              * PhySprite3Dに追加すると, stepSimulationを呼び出した際に物理演算が適用される.
907              * @param {Number} radius 半球体の半径.
908              * @param {Number} height 円柱の高さの半分.
909              * @param {Number} mass 剛体の質量.
910              * @see enchant.gl.physics.PhySprite3D
911              * @see enchant.gl.physics.PhyScene3D
912              * @constructs
913              * @extends enchant.gl.physics.PhySprite3D
914              */
915             initialize: function(r, h, mass) {
916                 var rigid = new enchant.gl.physics.RigidCapsule(r, h, mass);
917                 enchant.gl.physics.PhySprite3D.call(this, rigid);
918                 this.mesh = enchant.gl.Mesh.createCylinder(r, h);
919                 this.mesh._join(enchant.gl.Mesh.createSphere(r), 0, h, 0);
920                 this.mesh._join(enchant.gl.Mesh.createSphere(r), 0, -h, 0);
921             }
922         });
923 
924         /**
925          * @scope enchant.gl.physics.PhyPlane.prototype
926          */
927         enchant.gl.physics.PhyPlane = enchant.Class.create(enchant.gl.physics.PhySprite3D, {
928             /**
929              * 無限平面型のPhySprite3D.
930              * PhySprite3Dに追加すると, stepSimulationを呼び出した際に物理演算が適用される.
931              * @param {Number} NormalX 平面の法線ベクトルのx成分.
932              * @param {Number} NormalY 平面の法線ベクトルのy成分.
933              * @param {Number} NormalZ 平面の法線ベクトルのz成分.
934              * @see enchant.gl.physics.PhySprite3D
935              * @see enchant.gl.physics.PhyScene3D
936              * @constructs
937              * @extends enchant.gl.physics.PhySprite3D
938              */
939             initialize: function(nx, ny, nz, dist, scale) {
940                 if (!scale) {
941                     scale = 50;
942                 }
943 
944                 var rigid = new enchant.gl.physics.RigidPlane(nx, ny, nz, dist);
945                 enchant.gl.physics.PhySprite3D.call(this, rigid);
946                 this.mesh = enchant.gl.Mesh.createPlaneXZ(scale);
947                 var up = vec3.create([0, 1, 0]);
948                 var norm = vec3.create([nx, ny, nz]);
949                 var axis = vec3.create();
950                 vec3.cross(up, norm, axis);
951                 var rad = Math.acos(vec3.dot(up, norm) / (vec3.length(up) * vec3.length(norm)));
952                 var q = new enchant.gl.Quat(axis[0], axis[1], axis[2], rad);
953                 var vertices = [];
954                 for (var i = 0, l = this.mesh.vertices.length; i < l; i += 3) {
955                     var x = this.mesh.vertices[i];
956                     var y = this.mesh.vertices[i + 1];
957                     var z = this.mesh.vertices[i + 2];
958                     var arr = q.multiplyVec3([x, y, z]);
959                     vertices.push(arr[0] + nx * dist);
960                     vertices.push(arr[1] + ny * dist);
961                     vertices.push(arr[2] + nz * dist);
962                 }
963                 this.mesh.vertices = vertices;
964             }
965         });
966 
967         /**
968          * @scope enchant.gl.physics.PhyContainer.prototype
969          */
970         enchant.gl.physics.PhyContainer = enchant.Class.create(enchant.gl.physics.PhySprite3D, {
971             /**
972              * 枡型のPhySprite3D.
973              * PhySprite3Dに追加すると, stepSimulationを呼び出した際に物理演算が適用される.
974              * @param {Number} scale 枡の中心から枠までの距離.
975              * @param {Number} mass 剛体の質量.
976              * @see enchant.gl.physics.PhySprite3D
977              * @see enchant.gl.physics.PhyScene3D
978              * @constructs
979              * @extends enchant.gl.physics.PhySprite3D
980              */
981             initialize: function(scale, mass) {
982                 var s = scale;
983                 var rigid = new enchant.gl.physics.RigidContainer(s, mass);
984                 enchant.gl.physics.PhySprite3D.call(this, rigid);
985                 var that = this;
986                 this.mesh = new enchant.gl.Mesh();
987                 var addWall = function(sx, sy, sz, px, py, pz) {
988                     that.mesh._join(enchant.gl.Mesh.createBox(sx, sy, sz), px, py, pz);
989                 };
990                 addWall(s, s / 8, s, 0, s / 8 - s, 0);
991                 addWall(s - s / 8, s - s / 8 - s / 8, s / 8, s / 8, 0, s / 8 - s);
992                 addWall(s - s / 8, s - s / 8 - s / 8, s / 8, -s / 8, 0, s - s / 8);
993                 addWall(s / 8, s - s / 8 - s / 8, s - s / 8, s / 8 - s, 0, -s / 8);
994                 addWall(s / 8, s - s / 8 - s / 8, s - s / 8, s - s / 8, 0, s / 8);
995             }
996 
997         });
998     }());
999 }
1000