オブジェクトをマウスで配置する手法の紹介です。
一見難しそうですが、大事なのは以下の2つです。
1.マウスで指定したウインドウ座標をOpenGL座標に変換
2.そのOpenGL座標にオブジェクトを描画
この2つができれば、あとはその座標を使って点を書いたり、
ファイルから読んだ3Dオブジェクトを配置することも簡単です。
[ ソース ]
ここではWM_LBUTTONDOWNイベント内での処理を考えるものとします。
POINTS lbuttonDownPoint; //ウインドウ座標
//ウインドウ座標が変換された時のOpenGL座標
double ox;
double oy;
double oz;
HDC WinDC;
//左クリックしたウインドウ座標を取得。ウインドウプロシージャの引数のLPARAMから取得します。
lbuttonDownPoint.x = LOWORD(lParam);
lbuttonDownPoint.y = HIWORD(lParam);
//デバイスコンテキストを取得し、OpenGLと接続します。
WinDC = GetDC(hwnd); //ウインドウプロシージャの引数であるウインドウハンドルを指定してデバイスコンテキストを取得。
wglMakeCurrent(WinDC, hRC); //hRCはWM_CREATEで作成し、グローバル変数であるとする(前項「wglを使う」を参照)
GLint viewport[4]; //ビューポートのデータ
//モデルビュー行列、射影行列を格納する配列
GLdouble modelviewMatrix[16];
GLdouble projectionMatrix[16];
GLint glY; //OpenGLでのY座標
float depth; //デプス値
//ビューポート取得
glGetIntegerv(GL_VIEWPORT, viewport);
//現在設定されている行列情報を取得する
glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix); //投影変換行列
glGetDoublev(GL_MODELVIEW_MATRIX, modelviewMatrix); //幾何変換(モデルビュー)行列
//viewport[3]はウインドウの高さ。GLの座標系はウインドウで言うと左下が原点で0から始まるので、それに対応させる。
glY = viewport[3] - _windowY;
glZ = depth; //デプスを与える(好きな数値を与えてよい)
gluUnProject((GLdouble)lbuttonDownPoint.x, (GLdouble)glY, (GLdouble)depth, //ウインドウ座標と指定したデプス値を与える
modelviewMatrix, projectionMatrix, viewport, //モデルビュー行列、投影行列、ビューポートを与える
&ox, &oy, &oz) //ここにOpenGL座標に変換されたデータが格納される
[ 解説 ]
LPARAMからウインドウ座標を取得します。上位WORDにx座標、下位WORDにy座標が入ってます。
デバイスコンテキストを取得し、OpenGLとデバイスコンテキストを接続させます。
次にビューポートを取得します。
ビューポートはGLint型の4つの配列として宣言します。
インデックスの[3]にはウインドウの高さが格納されているので、
「ウインドウの高さ - 左上を原点と考えた時のマウスをクリックしたy座標」
という計算をして、ウインドウ座標の原点を左下隅と考えた時のウインドウy座標を取得します。
(gluUnProject()では、ウインドウの左下を原点と考えて処理されるためです。通常、ウインドウ座標は左上が原点と考えられています)
その次は投影変換行列とモデルビュー行列を取得しています。
格納する配列は16個の配列を宣言しないといけません。
他のWMイベントでもウインドウ座標をOpenGL座標にすることがあると思います。
その場合はこの2つの配列をグローバル変数で宣言し、WM_PAINT内でOpenGLの描画をした後に取得しておくと便利です。
depthにはデプス値が入ります。floatかdoubleで好きに指定してよいです。
BrightObjectでは「depth = 0.887」としています。
gluUnProject()でウインドウ座標をOpenGL座標に変換します。
最初の3つの引数には「左下を原点と考えた時のウインドウ座標のx座標とy座標、指定したデプス値」を指定します。
その次からの3つの引数には取得した「モデルビュー行列、投影行列、ビューポート」を指定します。
最後の3つに変換されたOpenGL上の座標が格納されます。
基本的なことですが、最後の3つの引数はアドレス渡しをしないと値が格納されませんので注意。
これでウインドウ座標からOpenGL座標が取得できました。
この座標に点を書いたりするのはもちろん、3Dオブジェクトファイルから読み込んだデータをこの位置に配置することもすぐにできそうですね。
関連:ウインドウ座標をワールド座標へ変換する
|