OB3D クイックスタート

この章の目的は、Jitter によるシンプルなOpenGL オブジェクトを開発する方法に関して、簡単で高レベルな概要を示すことです。このシンプルな OpenGL オブジェクトは、名前をつけられたレンダリングコンテキスト内でジオメトリを描画するもので、OB3D と呼ばれます。説明を行なうにあたっては、SDK にある jit.gl.simple をサンプルとして使用します。ディスプレイリストやテクステャなどのリソースを取り扱うオブジェクト、マトリックス入出力のサポートを行なうオブジェクト、OpenGL のステートに対してより綿密なアクセスを必要とするオブジェクトなどの開発方法に関する詳細は、次の章以降で説明します。

この章は、Max パッチャー内で使用される Jitter の OpenGL オブジェクトセットを熟知していることを前提に書かれています。これについては、Jitterチュートリアルや、このドキュメントでこれまで説明してきた Jitter オブジェクトモデル や Max ラッパークラスの章で述べられています。

OB3D Jitter クラスの定義

一般的に、Jitter OB3Dは、OB3D に共通するアトリビュートとメソッドのすべて、あるいは大部分を持つように定義されます。共通のアトリビュートやメソッドについては、Jitter HTML オブジェクトリファレンスの Group-OB3D セクションで述べられていますが、その中には、レンダリング先(デスティネーション)の名前、オブジェクト名、色、ライティング、テクスチャ、モデルビュー変換、デプスバッファ、ポリゴンモードなどの、様々な共通のタスクをセットするものが含まれています。あなたの Jitter クラスにこれらの共通するアトリビュートとメソッドを追加するためには、クラス定義の中でjit_ob3d_setup 関数を呼び出します。この呼び出しは jit_class_newの呼び出しの後、通常、他のメソッドやアトリビュートを定義する前に行ないます。OB3D の使用のために、Jitter はあなたのオブジェクトに関する追加情報を保存しておく必要があります。この情報は、あなたのオブジェクトのオブジェクト構造体の中のポインタに保存されます。これは通常 ob3dという名前を持ち、その内容は見えないようになっています。OB3D データポインタのバイトオフセットは jit_ob3d_setupの中へ渡されます。jit_ob3d_setupによって追加されるすべてのアトリビュート、メソッドの初期値は、次のようなフラグを使ってオーバーライドすることができます。

#define JIT_OB3D_NO_ROTATION_SCALE1 << 0 #define JIT_OB3D_NO_POLY_VARS 1 << 1 #define JIT_OB3D_NO_BLEND 1 << 2 #define JIT_OB3D_NO_TEXTURE 1 << 3 #define JIT_OB3D_NO_MATRIXOUTPUT 1 << 4 #define JIT_OB3D_AUTO_ONLY 1 << 5 #define JIT_OB3D_DOES_UI 1 << 6 #define JIT_OB3D_NO_DEPTH 1 << 7 #define JIT_OB3D_NO_ANTIALIAS 1 << 8 #define JIT_OB3D_NO_FOG 1 << 9 #define JIT_OB3D_NO_LIGHTING_MATERIAL 1 << 10 #define JIT_OB3D_HAS_LIGHTS 1 << 11 #define JIT_OB3D_HAS_CAMERA 1 << 12 #define JIT_OB3D_IS_RENDERER 1 << 13 #define JIT_OB3D_NO_COLOR 1 << 14

jit_ob3d_setupによって追加されるアトリビュートとメソッドの他に、あなたのクラスでは、シンボルob3d_drawにバインドされた、プライベートな、型を持たないメソッドを定義する必要があります。あなたのオブジェクトによるすべての描画は、このメソッドで行なわれます。このメソッドは、OB3D 標準のメソッドである draw、および drawraw メソッドによって呼び出されます。OB3D の draw メソッドは、プライベートなob3d_drawメソッドを呼び出す前に、共通な OB3D アトリビュートに関連した OpenGL のすべてのステートを設定します。drawrawメソッドは、プライベートな ob3d_draw メソッドを呼び出す前に、コンテキストの設定だけを行ないます。OB3Dは jit.gl.sketchdrawobject コマンド内で使用する名前付けをサポートするため、あなたのオブジェクトでは、プライベートで型を持たない " register " メソッドを追加し、jit_object_register 関数と結びつけなければなりません。例として、jit.gl.simple SDK プロジェクトを見てみましょう。

t_jit_err jit_gl_simple_init(void) { long ob3d_flags = JIT_OB3D_NO_MATRIXOUTPUT; // マトリックスは出力しま // せん。 void *ob3d; _jit_gl_simple_class = jit_class_new("jit_gl_simple", (method)jit_gl_simple_new, (method)jit_gl_simple_free, sizeof(t_jit_gl_simple),0L); // フラグで指定された3d オブジェクトのためのオブジェクトエクステンションをセット ob3d = jit_ob3d_setup(_jit_gl_simple_class, calcoffset(t_jit_gl_simple, ob3d), ob3d_flags); // OB3D draw メソッドの定義。jit.gl.render によって自動的に呼び出されるか、あ // るいはbang を受信した場合に ob3d から呼び出されます。私たちのdraw 設定は、 // OpenGL のステートの初期化を行なうために、ob3d の中で前もって実行されている必要 // があります。そのため、このメソッドは A_CANT 型になっています。 jit_class_addmethod(_jit_gl_simple_class, (method)jit_gl_simple_draw, "ob3d_draw", A_CANT, 0L); // dest_closing dest_changed メソッドを定義します。これらのメソッドは、デス // ティネーションのコンテキストが閉じられるか変更された場合に、jit.gl.render によ // って呼び出されます。コンテキストの変更の例としては、ユーザがあるモニタから別のモニタ // へウィンドウを移動させた場合があります。オブジェクトがOpenGL マシンに保持している // すべてのリソース(例えば、テクスチャ、ディスプレイリスト、頂点シェーダなど)はコン // テキストが閉じられる際に開放する必要があります。また、コンテキストが変更された場合 // には、再構成しなければなりません。このオブジェクトでは、これらの関数は何も実行せず、 // 省略することが可能です。 jit_class_addmethod(_jit_gl_simple_class, (method)jit_gl_simple_dest_closing, "dest_closing", A_CANT, 0L); jit_class_addmethod(_jit_gl_simple_class, (method)jit_gl_simple_dest_changed, "dest_changed", A_CANT, 0L); // ob3d での使用のために、登録しなければなりません。 jit_class_addmethod(_jit_gl_simple_class, (method)jit_object_register, "register",A_CANT, 0L); jit_class_register(_jit_gl_simple_class); return JIT_ERR_NONE; }

Jitter クラスのコンストラクタ/デストラクタ

あなたの OB3D Jitter クラスのコンストラクタには、最初の引数としてレンダリングを行なうデスティネーションの名前を渡さなければなりません。jit_ob3d_new 関数は、OB3D データポインタを初期化し、これをレンダリングデスティネーションと結び付けるために、デスティネーションの名前を引数として与えて呼び出さなければなりません。デストラクタでは、 jit_ob3d_free によって、この OB3D データポインタを開放しなければなりません。例として、jit.gl.simple のコンストラクタとデストラクタの例を次に示します。

t_jit_gl_simple *jit_gl_simple_new(t_symbol *dest_name) { t_jit_gl_simple *x; // jitter オブジェクトを作ります。 if (x = (t_jit_gl_simple *)jit_object_alloc(_jit_gl_simple_class)) { // ob3d を生成し、関連付けます。 jit_ob3d_new(x, dest_name); } else { x = NULL; } return x; } void jit_gl_simple_free(t_jit_gl_simple *x) { // ob3d データを開放します。 jit_ob3d_free(x); }

OB3D の draw メソッド

ob3d_draw シンボルにバインドされたあなたの OB3D draw メソッドでは、すべての描画コードを記述します。オブジェクトの automatic および enabled アトリビュートがオンになっている場合、関連づけた jit.render オブジェクトが bang を受信すると自動的にこのメソッドが呼び出されます。この設定はデフォルトではオンになっています。このメソッドは、あなたのMax ラッパーオブジェクトが bang を受信した場合、あるいは draw もしくは drawraw メッセージを受信した場合にも呼び出されます。drawraw メッセージの場合を除き、すべての標準的なOB3Dオブジェクトのステート、ob3d_draw メソッドが呼び出される前に設定されています。そのため、オブジェクトが特に必要とするのでなければ、モデルビュー変換、色、ライティングのプロパティ、テクスチャ情報などの設定を行なう必要はありません。 jit.gl.simple からの次の例は、シンプルな四辺形を描画するものです。

t_jit_err jit_gl_simple_draw(t_jit_gl_simple *x) { t_jit_err result = JIT_ERR_NONE; // OpenGL ジオメトリの描画を行ないます。 glBegin(GL_QUADS); glVertex3f(-1,-1,0); glVertex3f(-1,1,0); glVertex3f(1,1,0); glVertex3f(1,-1,0); glEnd(); return result; }

このサンプルは、標準のOpenGL 呼び出しによってジオメトリを描画する最小限のオブジェクトを示すだけのもので、テクスチャ情報や頂点の法線の指定は行なわれていません。しかし、すべての標準OpenGL 呼び出しは ob3d_draw メソッドの中で実行されなければなりません。この例では、jit_ob3d_draw_chunk によって行なわれるマトリックスの出力についても示されていません。これに関しては次章の「OB3Dの詳細」で述べます。

OB3D Max ラッパークラスの定義

OB3D では、Max ラッパークラスは、MOP に比べあまり特別な動作を必要としません。Max ラッパークラスの定義では、標準の描画メソッドを追加するためのmax_ob3d_setup関数の呼び出しを追加し、独自の assist メソッドを定義したい場合を除いて max_jit_ob3d_assist 関数を assist メソッドとして追加する必要があるだけです。それ以外は、Max ラッパークラスの章で示したような、標準的な Jitter クラスのラッピングと同様な処理を行ないます。

void main(void) { void *classex, *jitclass; // Jitter クラスの初期化 jit_gl_simple_init(); // Max クラスの生成 setup((t_messlist **)&max_jit_gl_simple_class, (method)max_jit_gl_simple_new, (method)max_jit_gl_simple_free, (short)sizeof(t_max_jit_gl_simple), 0L, A_GIMME, 0); // 私たちのオブジェクトに関する追加情報を保持するためにバイトオフセットを指定します。 classex = max_jit_classex_setup(calcoffset(t_max_jit_gl_simple, obex)); // クラスレジストリから Jitter クラスを探します。 jitclass = jit_class_findbyname(gensym("jit_gl_simple")); // Jitter オブジェクトの標準メソッドで Jitter クラスをラップします。 max_jit_classex_standard_wrap(classex, jitclass, 0); // 標準 ob3d アシストメソッドを使用します。 addmess((method)max_jit_ob3d_assist, "assist", A_CANT,0); // 3d 描画のためのメソッドを追加します。 max_ob3d_setup(); }

Max クラスのコンストラクタ/デストラクタ

あなたの Max クラスのコンストラクタは、標準の Max ラッパーのコンストラクタと同様でなければなりませんが、ぜひ覚えておく必要がある相違点があります。それは、Jitter の OB3D コンストラクタに知らせるために、レンダリングデスティネーションを通常の第1の引数として渡さなければならない点、および、オブジェクトの OB3D データに結び付けられたマトリックス出力のための第2のアウトレットを作らなければならない点です。デストラクタには、OB3D の使用によって追加すべきものはなにもありません。例として、jit.gl.simple の Max クラスのコンストラクタとデストラクタを示しておきます。

void *max_jit_gl_simple_new(t_symbol *s, long argc, t_atom *argv) { t_max_jit_gl_simple *x; void *jit_ob; long attrstart; t_symbol *dest_name_sym = _jit_sym_nothing; if (x = (t_max_jit_gl_simple *) max_jit_obex_new( max_jit_gl_simple_class, gensym("jit_gl_simple"))) { // 通常の第1の引数を取得。デスティネーション名です。 attrstart = max_jit_attr_args_offset(argc,argv); if (attrstart&&argv) { jit_atom_arg_getsym(&dest_name_sym, 0, attrstart, argv); } // Jitter オブジェクトを dest_name という引数を使ってインスタンス化します。 if (jit_ob = jit_object_new( gensym("jit_gl_simple"), dest_name_sym)) { // 内部の Jitter オブジェクトのインスタンスを設定します。 max_jit_obex_jitob_set(x, jit_ob); // 汎用のアウトレット(右端)を追加します。 max_jit_obex_dumpout_set(x, outlet_new(x,NULL)); // アトリビュートアーギュメントの処理 max_jit_attr_args(x, argc, argv); // jitter オブジェクトの ob3d を新しいアウトレットに接続します。 // このアウトレットは matrixoutput モードで使用します。 max_jit_ob3d_attach(x, jit_ob, outlet_new(x, "jit_matrix")); } else { error("jit.gl.simple: could not allocate object"); freeobject((t_object *)x); x = NULL; } } return (x); } void max_jit_gl_simple_free(t_max_jit_gl_simple *x) { // 内部の Jitter オブジェクトのインスタンスを探し、開放します。 jit_object_free(max_jit_obex_jitob_get(x)); // obex エントリに関連付けられたリソースを開放します。 max_jit_obex_free(x); }