GUIと2D/3Dグラフィックス

前節で、VCSSLの文法・仕様に関する説明はほぼ終わりました。即席ガイドとしてはここで終わるべきかもしれません。 しかしここで切るとまさに「Cでいいではないか」という内容だけになってしまうので、 今回は標準ライブラリから、GUIと2D/3Dグラフィックスを選んで簡単にまとめてみます。

なお、ここで扱う機能については、Vnanoでは全て削られており、VCSSLでのみ使用できます。
- 目次 -

GUI

まず、VCSSLでGUIを扱うサンプルコードは以下のようになります:

GUISample.vcssl

実行すると、テキストフィールドとボタンが並ぶウィンドウが表示され、ボタンをクリックすると、テキストフィールドの入力内容を表示します。

実行結果の図
実行結果

上のコードで、new〜関数はGUI部品を生成する関数です。戻り値はintですが、これは各GUI部品を区別するためのID番号が返されます。それをint型変数に格納しています:

GUISampleNewWindow.vcssl

ID番号は、早い話が処理系内部でのリソースの参照みたいなものですが、VCSSLでは参照型やポインタの仕組みが無いので、3Dなどでもシステムリソースの参照は基本的に “むき出し” のintで扱います。 (これについては静的型付けの利点を放棄しているわけで、まずかったと思っています。将来的には互換を保ちつつ、型検査が効くような、何らかの対策を取るかもしれません。)

ところで実装面の話ですが、実はこういったnew〜関数の実装には、現状であまり好ましくない挙動が残っています。 それはID番号が「 0から順に割り振られる 」という事です。 そしてint型のデフォルト値も「 0 」です。つまり初期化を忘れたID格納変数でも、0番のGUI部品を参照している状態と見なせるため、初期化を忘れるとデバッグの難しいバグを招きます。 これを防ぐため、かなり後付けで強引な仕様ですが、intをNULLで初期化できます:

GUISampleNULL.vcssl

intにNULLを代入すると、「 システムリソースのID番号で絶対に割り振られない値 」が代入されます。 IDを格納するのを忘れて使っても実行時エラーで落ちます。 普通にnew〜関数が1以上を返すように実装を変えれば済むのですが、現状は互換問題から上のような解決策になっています。

続いてイベントハンドラを見てみます:

GUISampleEvent.vcssl

イベントハンドラとは、ボタンのクリックなど、外部からの操作があった際に、それに対応した処理を行わせるためのもので、VCSSLでは上のような普通の関数です。

しかし上の関数をボタンに紐づけるような記述は、コード中のどこにもありません。 実はVCSSLでは、「onButtonClick」という名称(と適切なシグネチャ)の関数は、自動でボタンのイベントハンドラと見なされ、全てのボタンに紐づけられます。 つまり、どのボタンを押しても上の関数が呼ばれます。

では複数のボタンがある場合はどう区別するのかというと、引数idに、押されたボタンのIDが渡されるので、それで区別します:

GUISampleEvent2.vcssl

この方式は、ボタンが多いと面倒ですが、少ない場合は手短に書けます。VCSSLは後者優先です。

さて、ここで扱ったのはテキストフィールドとボタンだけですが、概ねVCSSLでGUIをどう扱うかという雰囲気はまとめられたと思います。より詳しくは、以下の資料をご参照ください。

2D グラフィックス

続いて2Dグラフィックスです。まずはサンプルコードです:

Graphics2DSample.vcssl

実行すると、以下のような画面が表示されます。

実行結果の図
実行結果

上のコードは説明を手短に済ませるために2DCG用フレームワークを使っています。 「 import graphics2d.Graphics2DFramework; 」で読み込んでいるのがそれで、標準で使えます。 何をやってくれるかというと、ウィンドウや描画エンジンを生成したり、アニメーション用のループを回したり、フレームレートの調整を行ったりしてくれます。 このフレームワークは全てVCSSLで書いてあり、処理系のlib/graphics2dディレクトリの中にあるので、実装を見たい場合はそちらを参照してください。 フレームワークを使わず、これらの処理をゼロから実装する事も可能です。

上のコードで onStart 関数は、起動後に1度だけフレームワークから呼ばれ、初期化処理を記述します。 onPaint 関数は毎秒数十回呼ばれ、画面の描画処理などを記述します。 時刻のカウンタ変数を作っておいて、毎回変化する内容を記述すれば、そのままアニメーション描画もできます。

さて、肝心の描画部分を説明します:

Graphics2DPaint.vcssl

まず onPaint 関数の引数rendですが、これは描画エンジンのIDが渡されます。G UI部品を生成するnew〜関数を思い出してください。あれと同じで、描画エンジンにもint型のIDが割り振られます (本来は描画エンジンもnewGraphics2DRenderer関数で、必要なら自分で何個でも生成できますが、今回はフレームワーク裏でやってくれていて、それが引数で渡されているわけです)。

続いて描画を行う関数です。 これらは第一引数に、描画エンジンのIDを指定します。 これは、例えば描画エンジンを2個生成して合成するような場合などに、どちらの描画エンジンへの命令かを区別するためです。 setDrawColor関数は描画色を設定する関数で、0〜255の範囲でRGBA(赤,緑,青,不透明度)値を指定します。 drawRectangle関数は四角形を描画する関数で、引数は「描画エンジンID, X, Y, 幅, 高さ, 塗りつぶしの有無」です。

最後に、「S」キーを押すと画像を保存するようにしてみましょう。上のコードの末尾に追記します:

Graphics2DSave.vcssl

onKeyDownはキー入力のイベントハンドラです。 画像の保存にはGraphicsライブラリexportGraphics関数を使用しています。 この関数は第一引数に「グラフィックスデータID」という、つまるところ描画用バッファ(オフスクリーンバッファ)の参照を渡す必要があるのですが、 フレームワークがgetGraphicsという関数の戻り値で返してくれるので、それをそのまま渡します。

2Dグラフィックスの使い方は大体こんな調子です。より詳しくは、以下の資料をご参照ください。

3D グラフィックス

最後に3Dグラフィックスです。サンプルコードは以下の通りです:

Graphics3DSample.vcssl

実行すると、以下のような画面が表示されます。

実行結果の図
実行結果

2Dの場合同様、3Dでも簡潔に済ませるためにフレームワークを使用しました。 このフレームワークも標準で使えるもので、基本的に2D/3Dで共通設計になっています。なので、概ね同じような感覚で使用できます。

onStart 関数も2Dの場合と同様、起動時にフレームワーク側から1度だけ呼ばれる関数で、ここで初期化処理を行います。 引数には描画エンジンのIDが渡されます。このIDは立体の配置登録に使います。

Graphics3DInitialize.vcssl

newAxisModel関数は、座標軸の形をしたモデルを生成する関数で、戻り値はモデルに固有のIDが返されます。 自分でポリゴンを定義してモデルを自作する事もできますが、単純なものは最初から用意されています。続いてmountModel関数で、それを描画エンジンに配置登録しています。

3Dでは2Dのように直接的に描画を行っていくのではなく、上の通りモデルやポリゴンなどの立体オブジェクトを生成し、それを描画エンジンに配置登録するという形が基本になります。 あとは描画エンジンが勝手に座標変換やシェーディングをした上でレンダリングしてくれます。 ウィンドウへの表示などやアニメーションループ、フレームレート制御などもフレームワークがやってくれます。

モデルの色設定や回転・移動などを行う関数は、第一引数にモデルのIDを渡します:

Graphics3DSetModel.vcssl

場のシミュレーションなどでは、箱型のような立体モデルよりも、四角ポリゴンを直接描いて動かしたい場合もあるでしょう。 実際に四角ポリゴンをアニメーションさせるコードを例示しておきます。

Graphics3DAnimation.vcssl

実行すると、四角ポリゴンの頂点の1つ (x0,y0,z0) が、X-Y平面内で円を描くようにアニメーションします。

実行結果の図
実行結果

上のコードでは、四角ポリゴンの頂点をsetPolygonVertex関数で書き換えた後、描画エンジンへの再登録などは行っていません。 この通り、描画エンジンに一度登録した立体オブジェクトは、移動や変形などが自動で反映されます。

なお、VCSSLの3D機能の実装は、現時点でZソート形式/フラットシェーディングのみです。 Zバッファ形式や、各種の補完シェーディング、テクスチャマッピングなどには対応していません。 早い話がベタ塗りのカクカクで、ポリゴン同士が重なるような場合には前後関係を正しく描画できません。 これはプラットフォームやハードウェアへの依存性を下げるため、GPUを使わないソフトウェアレンダリング式の3D描画エンジンをVCSSL処理系に内蔵しているためで、あまり大がかりなものを載せられないという点があります。 基本的にCPU処理が大きなウェイトを占めるため、GPUを強化してもあまり効果はありません。 パフォーマンスとしては概ね数十万ポリゴン/秒程度です。 シミュレーションの可視化などはある程度こなせると思いますが、ポリゴン数の多い美麗なグラフィックの3Dゲームを作ろうと思っても困難です。 このあたりの限界については、あらかじめご了承ください。

さて最後ですが、画像の保存は2Dと同様、末尾に以下のようなコードを追記してください:

3Dグラフィックスの使い方は大体こんな調子です。より詳しくは、以下の資料をご参照ください。

Graphics2DSave.vcssl

onKeyDownはキー入力のイベントハンドラです。 画像の保存にはGraphicsライブラリexportGraphics関数を使用しています。 この関数は第一引数に「グラフィックスリソースID」という、つまるところレンダリングバッファの参照を渡す必要があるのですが、 フレームワークがgetGraphicsという関数の戻り値で返してくれるので、それをそのまま渡します。

3Dグラフィックスの使い方は大体こんな調子です。より詳しくは、以下の資料をご参照ください。

このガイドは次回で最終回です。次回は、VCSSLから他言語のコードを実行する方法についてまとめます。