GrimoireJSのノードとコンポーネントの話

（これは[Grimoirejsアドベントカレンダー6日目](http://qiita.com/advent-calendar/2016/grimoire)の記事です。）

みなさん、**[GrimoireJS](https://grimoire.gl/)**使ってますか？
ご存知の通り、GrimoireJSはweb3Dをとてもかんたんに扱うための便利なフレームワークです。
内部に独自レンダラ実装を持つくらいにはゴリゴリとwebglを使っているので、さぞ中身は難解なレンダリングパイプラインや解読不能なglslで満ち満ちているような気がしますね。実際webglは激しい環境依存に**深い闇**を垣間見るのですが、実はGrimoireJSの基本構造にwebglのコードは**1行すらも含まれていません**。

GrimoireJSには**GomlNode**と**Component**の組み合わせで構成されるプラグインの仕組みが用意されていて、webglを含むコードは公式のものでさえこの上に乗る**プラグインに過ぎない**からです。

この仕組みによってGrimoireJSは高い拡張性と、用途に合わせた柔軟性を実現していて、拡張機能を追加もとても簡単にできます。

> で、そもそもGomlNodeとかComponentってなんなの？

ということで、今回はこのあたりの構造の話をしていきたいと思います。


# GomlNode
grimoireJSを使うときはかなり高頻度で使う`.goml`ですが、たとえばこんな感じです。（[公式チュートリアル](https://grimoire.gl/tutorial/04-handle-component.html)参照）

```xml:index.goml
<goml width="fit" height="fit">
    <geometry name="cylinder" type="cylinder" />
    <scene>
        <camera class="camera" near="0.01" far="40.0" aspect="1.5" fovy="45d" position="0,0,20">
            <camera.components>
                <MouseCameraControl/>
            </camera.components>
        </camera>
        <mesh geometry="cylinder" position="0,0,0" scale="3,3,3" color="#6a5acd" targetBuffer="wireframe"/>
    </scene>
</goml>
```
GrimoireJSはこの`goml`をパースしてツリー構造として扱います。
GomlNodeはこのツリーのノードとなるオブジェクトです。
ノードは名前ごとにいくつかの属性を持っていて、それを指定することで動作を変化させることができます。
つまり、**`goml`、`geometry`、`scene`、`camera`、`mesh`**などがここで登場しているGomlNodeですね。
基本的にgomlのタグとそのまま対応しているので、とくに難しくないと思います。
ただし、**全てのタグがノードなわけではありません**。
Componentも混ざってます。どこで見分けるんでしょうか？

# Component
先ほどのコードをよく見ると、

```xml:index.goml
<camera class="camera" near="0.01" far="40.0" aspect="1.5" fovy="45d" position="0,0,20">
    <camera.components>
        <MouseCameraControl/>
    </camera.components>
</camera>
```
という部分がありますね。
`<camera.components>`タグは特別で、**GomlNodeではありません**。
名前的にもそのままですが、**`camera`ノードにコンポーネントを追加**するための記法です。

> gomlのタグの中に、`[ノード名].components`という名の子要素を書き、その中にコンポーネント名をタグとして書くことで、**ノードにコンポーネントを追加していくことができます。**

このようにコンポーネントを追加していくことで、どんどん機能を拡張していけるわけですね！

# で、結局何者なの？
何者なのか説明するといっておきながらまだ使い方しか説明してないのは僕の文章力が足りなかったからです。ごめんなさい。
とりあえず、**自作のComponentやノードをプラグインとして追加**するときのコードみて見ましょう([公式チュートリアル](https://grimoire.gl/tutorial/10-create-node.html)参照)

```js:index.js
gr.registerComponent('Rotate', {
  attributes: {
    speed: {
      defaultValue: '1',
      converter: 'Number',
    },
  },
  $mount: function() {
    this.phi = 0;
  },
  $update: function() {
    this.phi += this.getValue('speed');
    this.node.setAttribute('rotation', this.phi + ',' + this.phi + ',' + this.phi);
  },
});

gr.registerNode("rotate", ["Rotate"], {}, "mesh");
```

```xml:index.goml
<goml width="fit" height="fit">
    <scene>
        <camera class="camera" near="0.01" far="40.0" aspect="1.5" fovy="45d" position="0,0,20"/>
        <rotate geometry="cube" position="0,0,0" color="#0000FF" speed="1" />
    </scene>
</goml>

```
見やすいように若干変更してますがほぼそのままです。
実行してみるとキューブが回転してますね。
`index.goml`で`rotate`タグを使ってますが、このコンポーネントは`index.js`で`gr.registerNode()`によって**あたらしく定義**されたノードです。その直前では`Rotate`コンポーネントも定義しています。

APIの詳細は[ここ](https://grimoire.gl/api/#registerNode)を見ていただくとして、やってることはざっくりいって
**`mesh`ノードに`Rotate`コンポーネントを追加した、`rotate`という新しいノード**の定義です。
これによってgomlで`rotate`タグが利用できるようになります。

実は、GrimoireJSの**全ての**ノードとコンポーネントは、このサンプルと同じように定義されています。
`rotate`は`mesh`を拡張していますが、`mesh`も同様に他のノードの拡張であり、最後は空のノードにたどり着きます。
空のノードにはツリーの節点である以上の機能はないので、結局ノードの持つ機能は、**すべてコンポーネントに実装されている**ということがわかります。


# GrimoireJS-Core
ところで、GrimoireJSを利用するときには大抵、まず公式から[バンドリング済みの.js](https://grimoire.gl/guide/download.html)をダウンロードしてきてhtmlに埋め込むと思います。公式ページに載っているチュートリアルのとおりですね。
これだけでhtml上の.gomlファイルを埋め込んだ位置にcanvasが生成され、gomlで定義された通りの3d空間がレンダリングされます。簡単ですね！

簡単すぎて感動するのですが、
**これはGrimoireJS本体の機能ではありません。**

実はGrimoireJSの本体は**GrimoireJS-Core**と呼ばれ、ざっくり書くと

+ gomlをパースして**GomlNode**ツリーを構成
+ GomlNodeが保持する**Component**の管理
+ Component間の**メッセージ**の伝達

などという構造的な基盤としての機能しか持っていません。
レンダリングなどの基本的な機能の実装は、[**grimoirejs-fundamental**](https://github.com/GrimoireGL/grimoirejs-fundamental)というプラグインに分離されています。

ということで、GrimoireJSのプラグイン機能は強力です。大抵なんでもできます。

# まとめ
コンポーネントは、**モジュール化した機能の実装**
ノードは、**コンポーネントを入れる箱**
という感じでしょうかね？

機能を追加したいときは、

+ コンポーネントとして実装して、
+ 必要であればノードしてタグを登録

しましょう。あとはgomlを書くだけ！

次回はGrimoireJSのインタフェースの使い方について書く予定です！