Hatena::Groupjavascript

JavaScriptで遊ぶよ

 | 

2011-12-15

ammo.jsとbullet.jsを使ってみた(bullet.js編)

09:46

JavaScript Advent Calendar 2011 WebGL駅伝16日目です。

14日目はammo.jsを試しました。比較しながらお読みください。ちなみに15日目は@Roxigaさんによる自作ライブラリPsycoVision3Dの紹介でした。

bullet.jsは↓のデモのHTMLの中に直接書かれています。もう一つ前のデモもありましたが、その中のコードとは微妙に別物でした。

この中でVecmathとBulletという2つの名前空間を定義しています。VecmathはBulletに渡すためのベクトルや行列のクラス群です。


それではammo.jsでやったのと同じサンプルを使って説明していきます。

f:id:edvakf:20111216094643p:image:w700


物理世界

今日はまず物理世界を作るところからやります。

function initPhysicsWorld() {
  var gravity = new Vecmath.Vec3(0, -200, 0);

  var collisionConfiguration = new Bullet.CollisionConfiguration();
  var dispatcher = new Bullet.Dispatcher(collisionConfiguration);

  var worldAabbMin = new Vecmath.Vec3(-1500, -1500, -1500);
  var worldAabbMax = new Vecmath.Vec3(1500, 1500, 1500);

  var overlappingPairCache = new Bullet.BroadphaseInterface(
                worldAabbMin, worldAabbMax, 0xfffe, 0xffff, 16384, null);

  var solver = new Bullet.ConstraintSolver();
  dynamicsWorld = new Bullet.CollisionWorld(
    dispatcher, overlappingPairCache, solver, collisionConfiguration);
  dynamicsWorld.setGravity(gravity);

  return dynamicsWorld;
}

ammo.js版↓と見比べてみます。

function initPhysicsWorld() {
  var gravity = new Ammo.btVector3(0, -200, 0);

  var collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
  var dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
  var overlappingPairCache = new Ammo.btDbvtBroadphase();
  var solver = new Ammo.btSequentialImpulseConstraintSolver();
  var dynamicsWorld = new Ammo.btDiscreteDynamicsWorld(
      dispatcher, overlappingPairCache, solver, collisionConfiguration);
  dynamicsWorld.setGravity(gravity);

  return dynamicsWorld;
}

ammo.jsのAmmo.btDbvtBroadphaseということろがbullet.jsではBullet.BroadphaseInterfaceになって、何やらworldAabbMinとかworldAabbMaxというのが増えてます。引数の意味はよく知りません。

どちらも同じbtDbvtBroadphaseに相当するようですが、bullet.jsのほうはコンストラクタの後にcreateProxyメソッドを呼んでるのに相当する処理をBullet.BroadphaseInterfaceだけで行なっているようです。

ちなみにBroadphaseというのは、近くにある剛体同士をざっくりとグループ分けする段階です。厳密な衝突判定はその後で行います。overlappingPairCacheは近くにある剛体同士のペアのキャッシュだったのですね。ってなことは今日知りました。


他のやつも名前が微妙に違ったりしてますね。jBulletの影響なんでしょうか。


剛体

bullet.jsはこんな感じです。

Box.prototype.initBulletObj = function(m) {
  var s = this.s;
  var shape = new Bullet.BoxShape(new Vecmath.Vec3(s / 2, s / 2, s / 2));
  var startTransform = new Bullet.Transform();
  startTransform.setIdentity();
  startTransform.origin.set3(this.x, this.y, this.z);

  var localInertia = new Vecmath.Vec3(0, 0, 0);
  var motionState = new Bullet.MotionState(startTransform);
  var rbInfo = new Bullet.RigidBodyConstructionInfo(m, motionState, shape, localInertia);
  rbInfo.restitution = 1;
  var body = new Bullet.RigidBody(rbInfo);

  this.bulletObj = body;
};

このへんはammo.jsのやり方↓とほとんど変わりません。ただ、newしても自分で解放しなくてもいいというのはかなりラクです。

Box.prototype.initBulletObj = function(m) {
  var s = this.s;
  var tmpVec = new Ammo.btVector3(s / 2, s / 2, s / 2);
  var shape = new Ammo.btBoxShape(tmpVec);
  Ammo.destroy(tmpVec);
  var startTransform = new Ammo.btTransform();
  startTransform.setIdentity();
  tmpVec = new Ammo.btVector3(this.x, this.y, this.z);
  startTransform.setOrigin(tmpVec);
  Ammo.destroy(tmpVec);

  var localInertia = new Ammo.btVector3(0, 0, 0);
  var motionState = new Ammo.btDefaultMotionState(startTransform);
  var rbInfo = new Ammo.btRigidBodyConstructionInfo(m, motionState, shape, localInertia);
  rbInfo.set_m_restitution(1);
  var body = new Ammo.btRigidBody(rbInfo);

  Ammo.destroy(startTransform);
  //Ammo.destroy(shape)
  Ammo.destroy(localInertia);
  //Ammo.destroy(motionState);
  Ammo.destroy(rbInfo);

  this.bulletObj = body;
};

moveメソッドもほとんど同じですが、bullet.jsのほうはgetMotionStateがありません。これもjBulletの作法なんでしょうか?

//bullet.js
  this.bulletObj.getWorldTransform(this._trans);
  this.x = this.threeObj.position.x = this._trans.origin.x;
  this.y = this.threeObj.position.y = this._trans.origin.y;
  this.z = this.threeObj.position.z = this._trans.origin.z;

//ammo.js
  this.bulletObj.getMotionState().getWorldTransform(this._trans);
  var origin = this._trans.getOrigin();
  this.x = this.threeObj.position.x = origin.x();
  this.y = this.threeObj.position.y = origin.y();
  this.z = this.threeObj.position.z = origin.z();

このように、ところどころ違うところがあります。


結果

お試しあれ。


気づいたいくつか。

まず、ammo.jsに比べるとまだまだ圧倒的に貧弱です。実はbtSphereShape相当のものが無くて、仕方なくBullet.CapsuleShapeで代用しています。他にもremoveRigidBodyが無いので画面外のボールを取り除くことが出来なかったり。

そして、ボールが近づきすぎるとエラーが出ます。

f:id:edvakf:20111216073202p:image

上のデモはたぶん3分もたたずに止まっちゃうと思います。ランダムですが。

早い話が、使える品質になるまではまだまだかかりそうでした。というかメンテされてるわけでもなさそうですし、自分でやる以外には期待できません。まああれだけのコードを移植したpl4n3さんがすごいのは間違いありませんが。

JSのGCの恩恵に預かれるので書き心地はammo.jsに比べてだいぶラクなんですけどね。


それから、dynamicsWorld.stepSimulation1はC++では1引数版と3引数版(と2引数版)がオーバーロードされていますが、bullet.jsではstepSimulation1stepSimulation3というふうに分かれていました。

そんなところでした。

トラックバック - http://javascript.g.hatena.ne.jp/edvakf/20111215
 |