- マウスで視点から一番近いオブジェクトを選択する -
前回は1つのオブジェクトを選択する方法を紹介しました。
今回は複数オブジェクトがヒットした時に、一番手前のオブジェクトを選択する方法を紹介します。

前回のソースで以下の部分がありました。

	//ヒットしている場合、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;	//中間のデプス値を取得する
	}

ここでやってる処理を次のような処理に変更します。

	if(hits > 0)
	{	//1つ以上ヒットしている場合
		if(hits > 1)
		{
			//配列内において、1番最初のネームと、そのネームの最小デプスを取得
			float f1, f2;
			f1 = (float)selectionBuff[1] / 0xFFFFFFFF;
			nameNum = selectionBuff[3];

			//一番手前面を選択するループ
			for(int i = 1; i < hits; i++)
			{
				f2 = (float)selectionBuff[i*4+1] / 0xFFFFFFFF;
				if(f2 < f1)	//f1より小さいデプスを見つけた場合
				{
					f1 = (float)selectionBuff[i*4+1] / 0xFFFFFFFF;	//そのデプスをf1へ
					nameNum = selectionBuff[i*4+3];	//そのデプスを持つネームを取得
				}
			}
		}
	}


selectionBuff[]には「つけた名前の階層の深さ、デプス最小値、デプス最大値、つけた名前…」の順にデータが格納されます。
(「glSelectBuffer(SELECT_BUF_COUNT, selectionBuff)」のように取得するとこのように格納されます)
BrightObjectでは名前に階層をつけてないので階層の深さは使用しません。また、デプス最大値も使いません。


[ 解説 ]
1つ以上ヒットした場合、まずは一番最初に出てくるデプス最小値(f1)とオブジェクトにつけた名前を取得しておきます。
for文で次のデプス最小値を取得します(f2)。f2とf1を比較し、f1より小さいデプス値が見つかったら
そのデプス値をf1に代入します。このときにオブジェクトについている名前も取得します。

この繰り返しをすることで、ヒットしたオブジェクトの中のデプス最小値と
そのデプス最小値を持つオブジェクトの名前を取得することができます。


ちょっと例をあげてみます。



たとえば黄色い矢印の部分をを選択したとします。
selectionBuff[]には以下のようにデータが入ります。



selectionBuff[]は、「つけた名前の階層の深さ、デプス最小値、デプス最大値、つけた名前…」の順に前から並んでます。
まず一番最初のデプス値を取得します。それが、「f1 = (float)selectionBuff[1] / 0xFFFFFFFF」の部分です。
デプス値は本来float型なのでint型から変換してやる必要があります。
次にオブジェクトにつけた名前を取得します。「nameNum = selectionBuff[3]」の部分です。

そしてfor文です。for文では一番小さいデプスとそのオブジェクトの名前を検索します。
一番手前にある面ということは、デプスが一番小さい値です。(視点から見て手前の方がデプス値が小さい)

GL_SELECTモードでオブジェクトを描画するときにglTranslatef()やglRotatef()で移動・回転をさせれば
初期の視点位置では一番奥の面でも、glRotatef()で奥の面が手前になるように回転(例えばY軸周り180度回転など)させると、
当然、元「奥の面」が視点の目の前にきますよね。
その状態でヒットさせるとデプス値が小さいのは初期視点位置では一番奥だった面、ということになります。
なので、単純に一番小さいデプス値を取得すればいいわけです。


ヒットは2つします。青い箱の手前の面と一番奥にある面です。
「ん?オブジェクトはヒットしないの?」 はい、ヒットしません。
なぜなら、GL_SELECTモードでオブジェクトを描画するとき、
内部的にはセレクションする対象のオブジェクトしか描画していない為です。

この仕組みを使って、セレクトしたいオブジェクトの描画を場面ごとに変えれば
「この処理の時はヒットするが、この処理の時はヒットしないように」のようなものもできます。


取得したオブジェクトの名前を使えば選択されているオブジェクトの判断が可能ですね。
色んなことができそうです。

また、デプス最小値・最大値を使ってマウスでオブジェクトを移動させるときに自然な移動が可能になります。
(次項「OpenGL空間上のオブジェクトをマウスドラッグで移動させる」にて)


inserted by FC2 system