Processingでシーングラフを実現する(1)
花粉、絶好調ですね。遺伝子組み換え杉ってその後どうなったのでしょうか。
さて、今回はProcessingを使って簡単なシーングラフを作成してみたいと思います。
シーングラフについて
シーングラフ(Scene graph)は3Dグラフィックスなどでよく使われる、描画要素を管理するためのデータ構造です。各要素の位置関係をツリー構造で表現することにより、「Aが動いたらBも一緒に動く」のような要素間の位置の連動を簡単に実現することができます。
アニメーションなどを行う際に非常に便利な機能なのですが、残念ながらProcessingではシーングラフが提供されていないため、自前で簡単なものを作成してみることにします。
今回実現する機能
通常のシーングラフは、描画要素の位置だけでなく、光源、カメラ、エフェクト等、様々な属性を管理するのが一般的ですが、今回は実装をシンプルにするため、以下の機能にしぼって実現します。
- 対象とする描画要素(ノード)は(x, y, z)の座標をもつ3次元要素とする
- 親ノードを移動させると子供ノードも同様に移動する
- 親ノードを回転させると子供ノードも同様に回転する
- 親ノードを拡大/縮小すると子供ノードも同様に拡大/縮小する
- 各ノードの描画処理はノードのdraw関数をオーバーライドして記述する
Processingには座標系の保存と復元を行うpushMarix
、popMatrix
命令がありますので、これらを使うことで比較的簡単にシーングラフが作成できます。
作成したシーングラフ用クラス
作成したシーングラフ用クラスはこちらです。
class Node { PVector pos = new PVector(); PVector rot = new PVector(); PVector scl = new PVector(1.0f, 1.0f, 1.0f); ArrayList<Node> childNodes = new ArrayList<Node>(); void draw() {} // 派生クラスでオーバーライドして描画処理を記述する void doDraw() { // ノードに実際に描画を行わせるメソッド pushMatrix(); translate(pos.x, pos.y, pos.z); rotateX(rot.x); rotateY(rot.y); rotateZ(rot.z); scale(scl.x, scl.y, scl.z); for (Node node: childNodes) { node.doDraw(); } draw(); popMatrix(); } };
このNode
クラスの派生クラスを作成し、draw
メソッドをオーバーライドすることで各描画要素を作成します。各Node
の親子化は少々乱暴ですがArrayList型のchildNodes
を直接操作することで行います。
簡単なサンプルでの動作確認
では実際に簡単なサンプルを作成して動かしてみます。
class Plane extends Node { // 平面を描画するノード void draw() { fill(128, 255, 128); rect(-50, -50, 100, 100); } } Node root = new Node(); Plane plane = new Plane(); void setup() { size(300, 300, OPENGL); plane.pos.set(150.0f, 150.0f, 0.0f); root.childNodes.add(plane); // rootノードの子供としてplaneノードを登録 } void draw() { background(0); plane.pos.x = 150.0f + sin(frameCount * 0.02f) * 100.0f; plane.rot.y += 0.05f; plane.scl.y = sin(frameCount * 0.03f) * 0.5f + 1.5f; root.doDraw(); }
無事動作が確認できました。でも、これだけではシーングラフのありがたみが伝わらないと思いますので、次回はもう少し複雑なサンプルを作成してみたいと思います。