JMPの3Dシーンでは、行列スタックを使って、現在の変換を継続してトラッキングします。スタックは、単位行列に初期化されます。そして、移動、回転、または拡大や縮小のコマンドが指定されるたびに、スタックの最上部の行列が変更されます。
注: 多くのOpenGL実装とは異なり、JMPでは転置行列は使いません。
次のプログラム例では、Push MatrixとPop Matrixを使って「toy top」の各部分の位置を決めています。各部分を指定した後には、原点に戻っています。この方法を使うと、Translateコマンドを2回使って元に戻すよりも速く処理できます。
図13.15 行列スタックを使った描画
toyTop = Scene Display List();
toyTop << Push Matrix;
toyTop << Translate( 0, 0, .1 );
toyTop << Color( 1, 0, 0 ); // red
// baseRadius、topRadius、height、slices、stacksを指定する
toyTop << Cylinder( 1, .2, .2, 25, 5 );
toyTop << Pop Matrix;
toyTop << Push Matrix;
toyTop << Translate( 0, 0, -.1 );
toyTop << Rotate( 180, 1, 0, 0 );
toyTop << Color( 0, 1, 0 ); // 緑
toyTop << Cylinder( 1, .2, .2, 25, 5 );
toyTop << Pop Matrix;
toyTop << Color( 0, 0, 1 ); // 青
toyTop << Sphere( .5, 30, 30 ); // radius、slices、stacksを指定する
toyTop << Color( 1, 1, 0 ); // 黄色
// innerRadius、outerRadius、slices、rings、startAngle、sweepAngleを指定する
toyTop << Partial Disk( 1, 1.2, 25, 2, 0, 270 );
toyTop << Push Matrix;
toyTop << Translate( 0, 0, -.1 );
toyTop << Color( 1, 0, 1 ); // マゼンタ
toyTop << Cylinder( 1, 1, .2, 25, 3 );
// baseRadius、topRadius、height、slices、stacksを指定する
toyTop << Pop Matrix;
toyTop << Push Matrix;
toyTop << Rotate( 90, 1, 0, 0 );
toyTop << Translate( 0, .5, 0 );
toyTop << Color( 0, 1, 1 ); // シアン
toyTop << Text( "center", "baseline", .2, "Toy Top" );
toyTop << Pop Matrix;
scene = Scene Box( 600, 600 ); // OpenGLシーンを保持するScene Boxを作成する
New Window( "例3", scene ); // シーンをウィンドウに配置する
scene << Perspective( 45, 3, 7 );
scene << Translate( 0.0, 0.0, -4.5 );
scene << Rotate( -85, 1, 0, 0 );
scene << Rotate( 65, 0, 0, 1 );
scene << Call List( toyTop );
scene << Update; // ディスプレイボックスを更新する
スタック上の現在の行列を変更したい場合は、Load Matrixコマンドを使います。
Load Matrix(m)
mは、現在の行列スタックにロードされる4行4列のJMP行列です。
同様なコマンドにMult Matrixがあります。
Mult Matrix(m)
Mult Matrixコマンドを実行すると、現在の行列スタックの最上部の行列にmが乗算されます。
次に、簡単なコマンドを実行する行列の例を示します。
移動
[1 0 0 x
0 1 0 y
0 0 1 z
0 0 0 1]
次の回転用行列に対する説明において、cはcos(a)、sはsin(a)です(ここで、aは回転の角度です)。
X軸を中心にして回転
[1 0 0 0
0 c -s 0
0 -s c 0
0 0 0 1]
Y軸を中心にして回転
[c 0 s 0
0 1 0 0
-s 0 c 0
0 0 0 1]
Z軸を中心にして回転
[c -s 0 0
s c 0 0
0 0 1 0
0 0 0 1]
例として、表示リストを移動して回転する2通りの方法を示します。初めの例は行列を用いており、2番目の例はTranslateとRotateを用いています。初めの例は、2番目の例と反対の方向に移動します。
// 行列を使った方法
gl << Push Matrix;
xt = Identity( 4 ); // これを0.75だけ左方向に移動する
xt[1, 4] = -.75;
xr = Identity( 4 ); // これを回転する。cosは度ではなくラジアンで指定する
xr[2, 2] = Cos( 3.14159 * a / 180 );
xr[2, 3] = -Sin( 3.14159 * a / 180 );
xr[3, 2] = Sin( 3.14159 * a / 180 );
xr[3, 3] = Cos( 3.14159 * a / 180 );
yr = Identity( 4 );
yr[1, 1] = Cos( 3.14159 * a / 180 );
yr[1, 3] = Sin( 3.14159 * a / 180 );
yr[3, 1] = -Sin( 3.14159 * a / 180 );
yr[3, 3] = Cos( 3.14159 * a / 180 );
zr = Identity( 4 );
zr[1, 1] = Cos( 3.14159 * a / 180 );
zr[1, 2] = -Sin( 3.14159 * a / 180 );
zr[2, 1] = Sin( 3.14159 * a / 180 );
zr[2, 2] = Cos( 3.14159 * a / 180 );
// 行列では乗算の順序が重要
gl << Mult Matrix( xt * xr * yr * zr );
gl << Arcball( dl, 1 );
gl << Pop Matrix;
// 関数を使った方法
gl << Push Matrix;
gl << Translate( .75, 0, 0 ); // これを0.75だけ右方向に移動する
gl << Rotate( a, 1, 0, 0 ); // これを度単位で回転させる
gl << Rotate( a, 0, 1, 0 ); // ここでは演算子の順序も重要
gl << Rotate( a, 0, 0, 1 );
gl << Arcball( dl, 1 );
gl << Pop Matrix;
行列は、表示リストの描画中にだけ保持されているので、以前の変換行列を遡って読み取ることはできません。以前の変換行列を知る必要がある場合には、JSL変数としてその行列を保持しておき、Load Matrixを使ってその行列をスタック上に置きます。