[phina.js]せっかくなので二大３Ｄライブラリをphina.jsでそれっぽく使ってみた

#はじめに
こんにちは。
3DはDirectX10で挫折した@minimoです。
趣味は2Dで[STG](http://9leap.net/games/2359)作る人なので、3Dは次元の違う世界の話と思ってます。
…が！せっかくphina.jsが３Ｄライブラリに対応した機能を持っているのなら使ってみたい！
という事で、俺的二大３Ｄライブラリ、[Three.js](http://threejs.org/)と[GLBoost](https://github.com/emadurandal/GLBoost)について、[phina.js](http://phinajs.com/)っぽく使ってみました。

現在、[phina.js](http://phinajs.com/)は開発中の為、３Dのサポートに関しては別途レイヤーを用意して、緩く対応という事になっております。
[phina.jsのGitter](https://gitter.im/phi-jp/phina.js)の方でもお話がありますが、phina.jsにおける３Ｄ対応に関してはどのように対応するか検討中です。
あくまでも、やってみた的な記事として捉えて頂けると幸いです。

[phina.js](http://phinajs.com/)の３Ｄ機能については@emadurandalさんと@daishi_hmrさんがエントリお書きになってますのでそちらも是非ご参照ください。

[国産ゲームライブラリ「phina.js」で3Dゲーム作ろうぜ](http://qiita.com/emadurandal/items/93223877936b5dc767f5)(emadurandalさん)
[せっかくだから俺も3Dやってみた](http://github.dev7.jp/b/2015/12/20/phinaadvcal20151220/#.Vnai76uJNXg.twitter)(daishi_hmrさん)

#Three.js編
まずは*Three.js*を使ってみます。
こちらは、@daishi_hmrさんが*tmlib.js*用に作成した[tm.hybrid](https://github.com/daishihmr/tm.hybrid)を~~パクって~~phina.js用に修正し、**phina.three**として使用しています。

[Three.js連携サンプル](http://minimo.github.io/phina.js_advent_20151221/three.js/)

正常に表示されたら、グラドリエル王女がぴょこぴょこしつつ回ってるのが見えると思います。
ちょっと、ソースを見てみましょう。

[main.js](https://rawgit.com/minimo/phina.js_advent_20151221/master/three.js/src/main.js)

```js
    //Three.js用レイヤー
    var layer = this.layer = phina.display.ThreeLayer({
      width: SCREEN_WIDTH,
      height: SCREEN_HEIGHT
    }).addChildTo(this);

```

基本的な部分は同じです。
ThreeLayerを作成し、シーンに追加しています。


```js
    //３Dメッシュをレイヤーに追加
    var mesh = phina.three.Mesh('gradriel').addChildTo(layer);
    mesh.update = function() {
        //ぐるぐる
        this.rotation.y+=0.02;
    }
    //Tweenerが使える
    mesh.tweener.clear()
      .to({scaleY: 0.9}, 500, "easeOutSine")
      .to({scaleY: 1}, 500, "easeOutElastic")
      .setLoop(true);
```
phinaのSpriteと同じ形でメタセコイアのモデルをアセットとして読み込み、Meshにアセット名をパラメータとして渡す事が出来ます。

```js
phina.main(function() {
  var app = phina.game.GameApp({
    assets: {
      image: {
        'hiyoko': 'assets/hiyoco_nomal_full.png',
      },
      mqo: {
        'gradriel': 'assets/gradriel_pose.mqo',
      },
    },
    startLabel: 'main',
    width: SCREEN_WIDTH,
    height: SCREEN_HEIGHT
  });

  app.run();
});
```
アセットはMQOとして、Imageと同じ形で指定して読み込んでます。



##どういう事をしてるのか

```js:phina.three.js
phina.define("phina.three.ThreeElement", {
  superClass: "phina.app.Element",
  init: function(threeObject) {
    this.superInit();
    this.threeObject = threeObject || new THREE.Object3D();
  },

...
    _accessor: {
      scaleX: {
        get: function() {
          return this.threeObject.scale.x;
        },
        set: function(v) {
          this.threeObject.scale.x = v;
        },
...

```
@daishi_hmrさんの[昨日の記事](http://github.dev7.jp/b/2015/12/20/phinaadvcal20151220/#.Vnai76uJNXg.twitter)と同様の形で、three.jsのオブジェクトをラップし、各プロパティに対しアクセッサを定義してあります。
これにより、Tweenerクラスを利用して、座標移動や回転、スケールの変更が出来るようになります。


#GLBoost編
お次は[GLBoost](https://github.com/emadurandal/GLBoost)です。
[GLBoost](https://github.com/emadurandal/GLBoost)とは、@emadrandalさんが開発中の新進気鋭のWebGLライブラリです。
玄人向けとの事ですが、裏を返せば自由度があるという事であり、開発者の@emadurandalさんがphina.jsの開発にも参加してると言う事で、個人的には非常に期待しております。
（玄人向けの部分は時間が経てばサンプルも出てくるでしょうし、ある程度は敷居も下がるのでは？とも思ってます（他力本願））


[GLBoost連携サンプル](http://minimo.github.io/phina.js_advent_20151221/glboost/)

MQOモデルの読み込み対応が間に合わなかったので、@emadurandalさんの[１９日の記事](http://qiita.com/emadurandal/private/93223877936b5dc767f5)のサンプルを元にしています。

オブジェクトに対してちゃんとTweenerが効いてるのが確認できると思います。
ただ、スケール変更がそれぞれのオブジェクトの原点ではなく、ワールド座標の原点に対してのスケール変更になっている感じです。
調査して修正したかったのですが、ちょっと間に合わなかったのでそのままです（そんなのばっかでごめんなさい＞＜）

ちょっと中身を見てましょう。

```js
    //GLBoost用レイヤー
    var layer = this.layer = phina.display.GLBoostLayer({
      width: SCREEN_WIDTH,
      height: SCREEN_HEIGHT
    }).addChildTo(this);

    //カメラ設定
    var lookat = {
      eye: new GLBoost.Vector4(0.0, 1.5, 10.0, 1),
      center: new GLBoost.Vector3(0.0, 1.5, 0.0),
      up: new GLBoost.Vector3(0.0, 1.0, 0.0)
    };
    var perspective = {
      fovy: 45.0,
      aspect: 1.0,
      zNear: 0.1,
      zFar: 1000.0
    };
    var camera = phina.glboost.Camera(lookat, perspective).addChildTo(layer);

    this.createObject();
    layer.prepareForRender();
```

ここらへんは、@emadurandalさんのサンプルとほぼ同じですね。
GLBoostLayerをシーンに追加して、カメラも追加しています。

```js
  createObject: function() {
    var material = new GLBoost.ClassicMaterial(this.layer.canvas);
    material.shader = new GLBoost.PhongShader(this.layer.canvas);

    var geometry = new GLBoost.Cube(
      new GLBoost.Vector3(20,20,20),
      new GLBoost.Vector4(Math.random()*1,Math.random()*1,Math.random()*1,1),
      this.layer.canvas);

    for (var i = 0; i < 2000; i ++) {
      var object = phina.glboost.Mesh({
        geometry: geometry,
        material: material
      });
      object.translate = new GLBoost.Vector3(Math.randint(0, 800) - 400, Math.randint(0, 800) - 400, Math.randint(0, 800) - 400);
      object.rotate = new GLBoost.Vector3(Math.randfloat(0, 2)*Math.PI, Math.randfloat(0, 2)*Math.PI, Math.randfloat(0, 2)*Math.PI);
      object.scale = new GLBoost.Vector3(Math.randfloat(0.5, 1.5), Math.randfloat(0.5, 1.5), Math.randfloat(0.5, 1.5));
      object.dirty = true;

      if (i%3 == 0) {
        //phina.jsのオブジェクトと同様にtweenerが使える
        object.tweener.clear()
          .to({scaleX: 3}, 500, "easeOutSine")
          .to({scaleX: 1}, 500, "easeOutElastic")
          .setLoop(true);
      }
      object.addChildTo(this.layer);
    }   
  },
});
```
オブジェクトの生成は、分かり易くする為に別関数に切り出しました。

```js
var object = phina.glboost.Mesh({
  geometry: geometry,
  material: material
});
```
違うのはこの部分ですね。
GLBoostのMeshをラップして、ジオメトリとマテリアルを元に生成出来る様にしてあります。
連携の手法については、Three.jsと同様の為、割愛致します。
positionがtranslateだったり、ratationがrotateだったりと、機能が同じでも名前が違うプロパティがあるので注意が必要です。
もし、本格的にこの手法で連携を進める場合、こういった細かい差異が吸収出来る様に設計する必要があると思われます。

#まとめ
人様のコードを切った貼ったのヤクザ仕事ばかりで非常に恐縮ですが、以上で本エントリは終了となります。
冒頭の繰り返しになりますが、あくまでも勝手に作ってみたよ！という事で、今後phina.jsに組み込まれるというものではありません。
私感ではありますが、この緩い対応のまま、各WebGLのライブラリで使用されるクラスをラップして、phina.jsの機能を扱えるようにする、というのが一番直感的で早道かなという気がします。
[phina.js](http://phinajs.com/)にはTweener等、使いこなせば超強力な機能が沢山ありますので、２Ｄゲームを作る感覚で３Ｄのゲームが作れる様になると良いですね。

[phina.js](http://phinajs.com/)も[GLBoost](https://github.com/emadurandal/GLBoost)も開発が始まったばかりで、この先も発展が期待できるライブラリです。
今後も地味に開発に参加しつつ、発展に寄与する事が出来ればと思ってます。

と、固い締めをした所でさようなら。

おまけ
[phina.jsでMMD再生してみたよ](http://minimo.github.io/phina.mmd.js)（音が出ます）
takahiroxさんのThree.js用mmdローダーを使用しております。