- マウスでOpenGL空間上のオブジェクトを選択する -

次の図のように、オブジェクトを選択する時の手法を紹介します。
(赤い部分が選択されているオブジェクトです)



[ ソース ]
WM_MOUSEMOVEイベント内にて

	//マウスのウインドウ座標を取得
	mousePickPoint.x = LOWORD(lParam);
	mousePickPoint.y = HIWORD(lParam);


WM_PAINTイベント内にて

	//現在のビューポート取得
	glGetIntegerv(GL_VIEWPORT, viewport);

	//セレクション用バッファをOpenGLへ渡す
	glSelectBuffer(SELECT_BUF_COUNT, selectionBuff);	//selectionBuffは要素数100のグローバル配列として宣言している

	//OpenGL描画モードをセレクションモードにする
	(void)glRenderMode(GL_SELECT);

	//投影変換行列を操作対象とし、現在の投影変換行列をスタックへ入れる
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();		//レンダリングモードの現在の射影変換を保持するために行列スタックにプッシュ(簡単に言えばカメラ位置の保存です)

	//投影行列を初期化
	glLoadIdentity();

	//マウスピッキングの範囲の指定
	gluPickMatrix(mousePickPoint.x,	//マウスの座標
			  viewport[3] - mousePickPoint.y,	//ウインドウの左下を原点としたウインドウy座標与える
			  1,	//マウス座標を中心としたセレクション範囲(単位:Pixcel)
			  1,	//つまり、縦1ピクセル×横1ピクセルの範囲でセレクションを感知する
			  viewport);	//取得した現在のビューポート

	init();		//OpenGLの描画をするための初期化関数を呼ぶ(→「OpenGLで描画をする前の初期化」参照)

	OpenGLDrawing()	//ここで、マウスで選択させたいオブジェクトのみを描画する。
			//オブジェクトの描画の前にはglPushName(最初のオブジェクトの描画時)、glLoadName(2つ目以降のオブジェクト描画時)で、
			//オブジェクトに対して名前をつけてから描画する。名前は描画するオブジェクト毎に違う名前をつけるとよい。

	//レンダリングモードに戻す。このときにマウスとオブジェクトが重なっていると、ヒット数を返すので取得
	hits = glRenderMode(GL_RENDER);

	//ヒットしている場合、1以上が取得できる(今回は1つしかヒットしないものと仮定します)
	if(hits > 0)
	{
		nameNum = selectionBuff[3];	//一番最初にヒットしたオブジェクトにつけた名前が取得できる
		float f1, f2;

		f1 = (float)selectionBuff[1] / 0xFFFFFFFF;	//描画されたオブジェクトの中で一番小さいデプス値を取得
		f2 = (float)selectionBuff[2] / 0xFFFFFFFF;	//				  大きいデプス値を取得
		nameDepth = (f1 + f2) / 2.0;	//中間のデプス値を取得する
	}

	//普通に描画する
	init();		//OpenGLの描画をするための初期化関数を再度呼ぶ(投影変換行列も初期化)
	OpenGLDrawing()	//OpenGL描画処理
	glPopMatrix();	//セレクションモードで使った投影変換行列を取り出し、
			//レンダリングモードで使ってた投影変換が適用される(セレクション前に戻るということ)

[ 解説 ]
上記ソースとコメント見れば大体わかると思います。

セレクションの仕組みとしては
「セレクションモードに切り替え→マウスピック関数の呼び出し→セレクト対象のオブジェクトのみ描画→
ヒットしたオブジェクトの名前を取得→レンダリングモードに戻す→ヒットした名前のオブジェクトに対して色を変える等の選択時の処理」
のような流れです。

投影変換行列の初期化については初期化関数内で初期化せず、別のところで行列をPushしつつ初期化しています。
(init()にまとめればいいことなので、わざわざ別に行う必要はないですけどね)
Pushするのはレンダリングモードに戻ったときに元の投影変換にする為です
(オブジェクト選択したらカメラ位置変わるのはちょっと違和感がありますね)

レンダリングモードに戻るときにマウスとオブジェクトが重なった(ヒットした)数を取得します。
一番最初にヒットしたオブジェクトの名前を取得し、オブジェクトの中間デプス値を作成しています。
このデプス値はオブジェクトをマウスで動かす時などに使えます
(詳しくは「OpenGL空間上のオブジェクトをマウスドラッグで移動させる」にて)

selectionBuffにはヒットしたオブジェクトの情報が格納されます。
(詳しくは次項の「マウスで視点から一番近いオブジェクトを選択する」にて)

あとは通常通りに描画します。OpenGLの描画時にヒットしたオブジェクトの名前を使ってオブジェクトを判別すれば
たとえば、選択したオブジェクトの色を変える処理も簡単にできそうですね。

最後に投影変換行列をセレクション前に戻すことで、自然なオブジェクト選択処理が可能になります。



inserted by FC2 system