3
1

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-12-06

(これはGrimoirejsアドベントカレンダー6日目の記事です。)

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

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

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

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

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

GomlNode

grimoireJSを使うときはかなり高頻度で使う.gomlですが、たとえばこんな感じです。(公式チュートリアル参照)

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はこのツリーのノードとなるオブジェクトです。
ノードは名前ごとにいくつかの属性を持っていて、それを指定することで動作を変化させることができます。
つまり、gomlgeometryscenecamerameshなどがここで登場しているGomlNodeですね。
基本的にgomlのタグとそのまま対応しているので、とくに難しくないと思います。
ただし、全てのタグがノードなわけではありません
Componentも混ざってます。どこで見分けるんでしょうか?

Component

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

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やノードをプラグインとして追加するときのコードみて見ましょう(公式チュートリアル参照)

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");
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.gomlrotateタグを使ってますが、このコンポーネントはindex.jsgr.registerNode()によってあたらしく定義されたノードです。その直前ではRotateコンポーネントも定義しています。

APIの詳細はここを見ていただくとして、やってることはざっくりいって
meshノードにRotateコンポーネントを追加した、rotateという新しいノードの定義です。
これによってgomlでrotateタグが利用できるようになります。

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

GrimoireJS-Core

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

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

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

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

などという構造的な基盤としての機能しか持っていません。
レンダリングなどの基本的な機能の実装は、grimoirejs-fundamentalというプラグインに分離されています。

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

まとめ

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

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

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

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

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

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1