前回GLSamplerの出力を見ましたが、今回から少し解説します。
前に書きましたが、OpenGLのツールキットであるGLUTは基本的な使い方の仕様があり、C言語で記述したコンソールウィンドウからグラフィック出力ウィンドウを作成し、それに出力するので、ウィンドウプログラミングに慣れた方からみると「古臭い」印象を受けます。その為、コンソールウィンドウを使わずにウィンドウからGLUTの出力を制御、ウィンドウに出力したい、と感じさせます。
これについて色々と試しましたが、GLUTのビルトイン関数には専用の出力ウィンドウでなければ受け付けないものもあり、アプローチとしては、
(1)直接ユーザーウィンドウにグラフィックを出力し、そのウィンドウで描画の制御を行う。(注1)
(2)GLUTの出力制御ウィンドウを作成し、グラフィック出力ウィンドウの作成、描画の制御を行う(注2)。
となるようです。
注1:但し、これではGLUTにビルトインされたTeapot等18の基本プリミティブが使えません。
注2:グラフィック出力は大きくして見たいので、別に生成されGLUTのグラフィック専用ウィンドウでも余り違和感はありません。但し、このグラフィック専用ウィンドウのハンドルを取得して、SetWindowLong()関数を使ってウィンドウスタイルのWM_OVERLAPPEDWINDOW(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)を外し、WS_CHILDを入れるとコントロールに変えることが出来ますね。
今回のGLSamplerは(描画の制御はしていませんが)(1)に相当し、後に紹介するGlut_BCCは(2)に相当します。
では、GLSamplerの解説です。
【リソース】
メニュー、バージョンダイアログ、アイコンだけのシンプルなものです。
//-----------------------------------------
// BCCForm Ver 2.41
// An Easy Resource Editor for BCC
// Copyright (c) February 2002 by ysama
//-----------------------------------------
#include "ResGLSampler.h" //解説:リソース定義ファイルResGLSampler.hは省略します。
//-------------------------
// メニュー(IDM_MENU)
//-------------------------
IDM_MENU MENU DISCARDABLE
{
POPUP "ファイル(&F)"
{
MENUITEM "終了(&X)", IDM_EXIT
}
POPUP "表示(&D)"
{
MENUITEM "2DSample1", IDM_2D01 //解説:2Dのサンプル5つ分です。
MENUITEM "2DSample2", IDM_2D02
MENUITEM "2DSample3", IDM_2D03
MENUITEM "2DSample4", IDM_2D04, GRAYED //追加可能
MENUITEM "2DSample5", IDM_2D05, GRAYED //追加可能
MENUITEM SEPARATOR
MENUITEM "3DSample1", IDM_3D01 //解説:3Dのサンプル5つ分です。
MENUITEM "3DSample2", IDM_3D02
MENUITEM "3DSample3", IDM_3D03
MENUITEM "3DSample4", IDM_3D04, GRAYED //追加可能
MENUITEM "3DSample5", IDM_3D05, GRAYED //追加可能
}
POPUP "ヘルプ(&H)"
{
MENUITEM "バージョン情報(&V)", IDM_VERSION
}
}
//----------------------------------
// ダイアログ (IDD_VERSION)
//----------------------------------
IDD_VERSION DIALOG DISCARDABLE 0, 0, 160, 40
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | DS_MODALFRAME | DS_3DLOOK | DS_CENTER
CAPTION "バージョン情報"
FONT 9, "Times New Roman"
{
CONTROL IDI_ICON, 0, "STATIC", SS_SUNKEN | SS_ICON | WS_CHILD | WS_VISIBLE, 12, 10, 32, 32
CONTROL "GLSampler Version 1.0\nCopyright 2022 by Ysama", 0, "STATIC", SS_CENTER | SS_SUNKEN | WS_CHILD | WS_VISIBLE, 42, 8, 80, 24
CONTROL "OK", IDOK, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 130, 14, 20, 12
}
//--------------------------
// イメージ(IDI_ICON)
//--------------------------
IDI_ICON ICON DISCARDABLE "Icon.ico"
【ウィンドウ関連プログラム部分】
<GLSamplerProc.h>
BCCSkeltonの"*Proc.h"ファイルと同じく、コントロールやメッセージの割り込み部分を記述tしています。
/////////////////////////////////////
// GLProcedure.h
//注:アプリケーションが処理した場合
// TRUEを返すと、コールバック関数
// は0を返し、FALSEを返すとDef-
// WindowProcを呼びます。
/////////////////////////////////////
//メッセージ処理関数定義
/////////////////////////
//WM_CREATE処理
bool OnCreate(HWND hWnd) {
//解説:以下はOpenGL.hの関数やGLUTの関数で別途解説します。
GetRC(hWnd); //レンダリングコンテキストを取得してhDCと連携させる
glutInit(&__argc, __argv); //GLUTを初期化する(__argcと__argvはWinMainで使用可)
glShadeModel(GL_SMOOTH); //既定値の滑らかな網かけ(他の選択肢としてフラットシェーディング-GL_FLATがある)
glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA); // 表示モード設定
return TRUE;
}
//WM_LBUTTONDOWN処理
bool OnLButtonDown(HWND hWnd) {
g_Stop = !g_Stop; //一時停止フラグ(解説:便宜上OpenGL.hファイルで定義しています。)
return TRUE;
}
//WM_SIZE処理
bool OnSize(LPARAM lParam) {
if(IsGL()) { //OpenGLを使っていてRCを取得している時のみ処理する
SizeChanged(lParam);
return TRUE;
} //解説:IsGL()、SizeChanged()共にOpenGL.hで定義されています。
else
return FALSE;
}
//WM_CLOSE処理
bool OnClose(HWND hWnd) {
if(MessageBox(hWnd, "終了してもよいですか", "終了確認",
MB_YESNO | MB_ICONQUESTION) == IDYES)
return TRUE;
else
return FALSE;
}
//WM_DESTROY処理
bool OnDestroy() {
if(IsGL())
ReleaseRC(); //レンダリングコンテキストを開放する(解説:OpenGL.hで定義しています。)
return TRUE;
}
<GLSampler.cpp>
///////////////////////////
// OpenGL Sampler Program
///////////////////////////
//インクルードファイル
#define STRICT
#include <windows.h>
#include "ResGLSampler.h"
#include "OpenGL.h" //OpenGL用初期化、表示、終了関数等
#include "DrawFunctions.h" //OpenGLによる描画関数を記述したファイル
#include "GLSamplerProc.h" //ウィンドウ割り込み処理関数
//解説:OpenGL.hとDrawFunctions.hが、GLUTと描画関連の関数を定義しています。
#pragma comment(lib, "freeglut") //若しくはコンパイラーの-Lオプションを使う
//外部変数
char szClassName[] = "GL_Sampler";
char szTitleName[] = "GL Sampler";
///////////////////////////////////////
//バージョンダイアログコールバック関数
///////////////////////////////////////
BOOL CALLBACK VerDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
bool Done = FALSE;
switch(msg){
case WM_COMMAND:
if(LOWORD(wParam) == IDOK) {
SendMessage(hWnd, WM_CLOSE, 0, 0);
Done == TRUE;
}
break;
case WM_SYSCOMMAND:
if(wParam == SC_CLOSE) {
SendMessage(hWnd, WM_CLOSE, 0, 0);
Done == TRUE;
}
break;
case WM_CLOSE:
EndDialog(hWnd, 0);
Done == TRUE;
break;
}
return Done;
}
/////////////////////////////
//ウィンドウコールバック関数
/////////////////////////////
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
//解説:以下はほぼBCCSkeltonがマクロで実装しているような内容です。
bool Done = FALSE;
switch(msg){
case WM_COMMAND: //メニュー処理
switch(LOWORD(wParam)) {
case IDM_2D01:
case IDM_2D02:
case IDM_2D03:
case IDM_2D04:
case IDM_2D05:
case IDM_3D01:
case IDM_3D02:
case IDM_3D03:
case IDM_3D04:
case IDM_3D05:
Done = DrawMenu(LOWORD(wParam));
break;
case IDM_VERSION: //ダイアログ処理
DialogBox((HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), "IDD_VERSION", hWnd, VerDlgProc);
Done = TRUE;
break;
case IDM_EXIT: //終了処理
SendMessage(hWnd, WM_CLOSE, NULL, NULL);
Done = TRUE;
break;
}
break;
case WM_CREATE: //ウィンドウ生成時処理
Done = OnCreate(hWnd);
break;
case WM_LBUTTONDOWN: //マウス左ボタン押下時処理
Done = OnLButtonDown(hWnd);
break;
case WM_SIZE: //ウィンドウサイズ変更時処理
Done = OnSize(lParam);
break;
case WM_CLOSE: //ウィンドウ終了時処理
Done = OnClose(hWnd);
if(Done) {
DestroyWindow(hWnd);
return TRUE;
}
else
return FALSE;
break;
case WM_DESTROY: //ウィンドウ消滅時処理
Done = OnDestroy();
PostQuitMessage(0);
break;
default:
break;
}
if(Done)
return 0L;
else
return(DefWindowProc(hWnd, msg, wParam, lParam));
}
///////////////////////////
//ウィンドウ・クラスの登録
///////////////////////////
//解説:BCCSkeltonの"Init(...)"関数に相当します。
BOOL InitApp(HINSTANCE hInstance) {
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX); //この構造体のサイズ
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc; //プロシージャ名
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance; //インスタンス
wc.hIcon = (HICON)LoadImage(hInstance, "IDI_ICON", IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR); //LoadIcon関数は旧いので。
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "IDM_MENU"; //メニュー名
wc.lpszClassName = (LPCSTR)szClassName;
wc.hIconSm = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); //"IDI_ICON"で指定すると読み込めない
return (RegisterClassEx(&wc));
}
///////////////////
//ウィンドウの生成
///////////////////
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) {
//解説:BCCSkeltonの"Create(...)"関数に相当します。
HWND hWnd = CreateWindowEx(
NULL, //dwExStyle
szClassName, //ウィンドウクラス名
szTitleName, //タイトルバーにこの名前が表示されます
WS_OVERLAPPEDWINDOW, //ウィンドウスタイル
CW_USEDEFAULT, //X座標
CW_USEDEFAULT, //Y座標
640, //幅
640, //高さ
NULL, //親ウィンドウのハンドル、親を作るときはNULL
NULL, //メニューハンドル、クラスメニューを使うときはNULL
hInstance, //インスタンスハンドル
NULL);
if(!hWnd)
return FALSE;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
///////////////////
//ウィンドウループ
///////////////////
WPARAM Loop() {
//解説:BCCSkeltonの"Loop()"関数に相当しますが、メッセージがない(アイドリング)時に処理をさせる赤字部分はGLUTに特有の処理です。
MSG msg;
//ウィンドウメッセージループ
while(GetMessage(&msg, NULL, 0, 0) > 0) { //エラーの場合-1が返る
TranslateMessage(&msg);
DispatchMessage(&msg);
if(IsGL()) { //IsGLが真になると次のメッセージ処理からOpenGLのループ処理になる
//アイドル時に描画させるメッセージループ
while(msg.message != WM_QUIT) {
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//処理メッセージが無ければ描画する
else {
if(!g_Stop) //一時停止フラグ
DrawObjects(g_DrawMenuNo);
}
}
break; //GLLoopが終了するとこのループも抜ける
}
}
return msg.wParam;
}
////////////////////////
//ウィンドウズメイン関数
////////////////////////
int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow) {
if(!hPrevInst) {
if(!InitApp(hCurInst)) //ウィンドウ・クラスの登録
return FALSE;
}
if(!InitInstance(hCurInst, nCmdShow)) { //ウィンドウの生成
return FALSE;
}
return Loop();
}
基本的にほぼC++とWin32SKDのウィンドウプログラミングの教本に出てくるような中身ですが、赤字部分がGLUTの使用に特有と言えます。
次回はユーザーウィンドウで直接GLUTを使う際の中核となるOpenGL.hとDrawFunctions.hについて解説してみようと思います。