GLUTのシンプルOOパッケージ

14044 ワード

卒業の設計はOpenGLを使って、MFCとWin 32 APIでウィンドウのプログラムをすることができないためです;GLUTは自然に選びました.GLUTはとても使いやすくて、毎回Initを書いて、callbackを登録して、少し気持ちが悪くて、そこで彼に簡単なOOパッケージをしました.ここに記録して、もし同級生が興味があればダウンロードすることができます.
GLUTアプリケーション
GLUTを直接使用するプログラムは次のとおりです.
#include <GL/glut.h>
#include <stdio.h>

void display() 
{
// OpenGL commands
}

//     (       ,ESC   )
void keyboardHander(unsigned char ch, int x, int y)
{
	printf("key %d(%c) x: %d, y: %d
", ch, ch, x, y); fflush(stdout); } // void specialKeyHandler(int key, int x, int y) { printf("special key"); switch(key) { case GLUT_KEY_UP: printf("%d(%s) ", key, "GLUT_KEY_UP"); break; case GLUT_KEY_DOWN: printf("%d(%s) ", key, "GLUT_KEY_DOWN"); break; case GLUT_KEY_LEFT: printf("%d(%s) ", key, "GLUT_KEY_LEFT"); break; case GLUT_KEY_RIGHT: printf("%d(%s) ", key, "GLUT_KEY_RIGHT"); break; default: printf("%d(%s) ", key, "Other Special keys"); } printf("x: %d, y: %d
", x, y); fflush(stdout); } // void mouseHandler(int button, int state, int x, int y) { printf("mouse pos: (%3d, %3d) button: %s(%d), state: %s(%d)
", x, y, GLUT_LEFT_BUTTON == button ? "GLUT_LEFT_BUTTON" : GLUT_RIGHT_BUTTON == button ? "GLUT_RIGHT_BUTTON" : GLUT_MIDDLE_BUTTON == button ? "GLUT_MIDDLE_BUTTON" : "UNKOW" , button, GLUT_UP == state ? "GLUT_UP" : GLUT_DOWN == state ? "GLUT_DOWN" : "UNKNOW" , state ); fflush(stdout); } // void motionHandler(int x, int y) { printf("motion to %d, %d
", x, y); fflush(stdout); } // void passiveMotionHandler(int x, int y) { printf("passive motion to %d, %d
", x, y); fflush(stdout); } void testTimer(int i) { printf("Alarm %d
", i); fflush(stdout); if( i < 5 ) glutTimerFunc(1000, testTimer, i+1); } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowSize(400, 300); glutInitWindowPosition(100, 100); glutCreateWindow("Getting started with OpenGL 4.3"); glutDisplayFunc(display); glutKeyboardFunc(keyboardHander); // ( ) glutSpecialFunc(specialKeyHandler); // glutMouseFunc(mouseHandler); // glutMotionFunc(motionHandler); // glutPassiveMotionFunc(passiveMotionHandler); // glutTimerFunc(1000, testTimer, 1); // glutMainLoop(); // start main loop. return 0; }
は使いやすいですが、毎回Initとcallbackの登録を書いています.
そこで、それをベースクラスにカプセル化して、毎回継承できるかどうか考えました(Java、C#のフォームプログラムに似ています).
つまり、各メンバー関数は、onKey、onMouseなどのイベントの応答に対応します.私たちのプログラムは次のようにしてほしいです.
int main(int argc, char **argv)
{
	GlutApp::initGlut(argc, argv);
	GlutApp* app = new DemoApp();

	app->setTitle("a demo app");
	app->setWindowsSize(800, 600);

	app->run();
	delete app;
	return 0;
}

Member functionはどのようにCallbackとして機能しますか?
ここは実は2つの問題です.
1つ目の質問ですが、member functionの関数署名にthisポインタがあり、glut*Funcにcallbackとして直接伝えることはできません.どうすればいいですか?
member functionはだめで、自然にstatic functionを思い浮かべます.static functionの関数署名にthisポインタがないためです.
2つ目の問題は、static functionがどのようにmember functionを呼び出し、それに関連付けられたオブジェクト(thisポインタ)が実行時期(またはユーザープログラム)で決定できるかということです.
1つは、static function呼び出しmember functionがstatic memberに自然に使用されることである.
二つ目は、このstatic memberをmember functionに修正させることです.
GlutApp
上記の分析があれば、GlutAppクラスは簡単に書くことができます.
GlutApp.h
#ifndef GLUT_APP_H
#define GLUT_APP_H

class GlutApp 
{
public:
	typedef void (*MenuFuncPtr)(void);

	struct MenuEntry 
	{
		int id;
		const char* str;
		MenuFuncPtr fun;
	};

	//    App     ,      
	static GlutApp* s_pCurrentApp;

	//           
	static const int MAX_MENU = 32;

	// ctor
	GlutApp();

	// getter and setters:
	static void initGlut(int argc, char** argv) { s_argc = argc; s_argv = argv; }

	void setDisplayMode(unsigned int mode) { m_displayMode = mode; }

	void setWindowsSize(int w, int h) { m_winWidth = w; m_winHeight = h; }

	int getWindowWidth() { return m_winWidth; }

	int getWindowHeight() { return m_winHeight; }

	void setWindowsPos(int x, int y) { m_winPosX = x; m_winPosY = y; }

	void setTitle(char *title) { m_title = title; }

	void run();

	void addRightMenu(const char *str, MenuFuncPtr fun);

	//    
	virtual void onInit(){}

	//////////////////////////////////////////////////////////////////////////
	// GLUT delegate callbacks:

	//     
	virtual void onIdle(){}

	//     (OpenGL    )
	virtual void onDisplay() = 0; //       ;       

	//       
	virtual void onResize(int w, int h){}

	//////////////////////////////////////////////////////////////////////////
	//          :

	//     (     ,ESC)
	virtual void onKey(unsigned char key, int x, int y){}

	//        
	virtual void onKeyDown(unsigned char key, int x, int y) {}

	//     (        )
	virtual void onSpecialKey(int key, int x, int y){}

	//       
	virtual void onSpecialKeyDown(int key, int x, int y){}

	//////////////////////////////////////////////////////////////////////////
	//          :

	//     
	//! @param button: The button parameter is one of GLUT LEFT BUTTON, GLUT MIDDLE BUTTON, or GLUT RIGHT BUTTON.
	//! @param state: The state parameter is either GLUT UP or GLUT DOWN indicating 
	//                 whether the callback was due to a release or press respectively.
	virtual void onMousePress(int button, int state, int x, int y){}

	//     
	virtual void onMouseMove(int x, int y){}

	//     
	virtual void onMousePressMove(int x,int y){}

	//////////////////////////////////////////////////////////////////////////
	//         :
	virtual void onTimer() {}

	void setTimer(int delay, int period = 0);

protected:
	void registerMenus();

	// actual GLUT callback functions:
	static void KeyboardCallback(unsigned char key, int x, int y);

	static void KeyboardUpCallback(unsigned char key, int x, int y);

	static void SpecialKeyboardCallback(int key, int x, int y);

	static void SpecialKeyboardUpCallback(int key, int x, int y);

	static void ReshapeCallback(int w, int h);

	static void IdleCallback();

	static void MouseFuncCallback(int button, int state, int x, int y);

	static void	MotionFuncCallback(int x,int y);

	static void MousePassiveCallback(int x, int y);

	static void DisplayCallback();

	static void MenuCallback(int menuId);
	
	static void TimerCallback(int period);
private:
	unsigned int m_displayMode;

	// for glutInit
	static int s_argc;
	static char** s_argv;

	char *m_title;

	// for glutSetWindowSize
	int m_winWidth;
	int m_winHeight;

	// for windows position
	int m_winPosX;
	int m_winPosY;

	// for menus:
	int       m_menuCount;
	MenuEntry m_menuEntry[MAX_MENU];

	// for timer:
	int m_delay;
	int m_period;
};

#endif // GLUT_APP_H

GlutApp.cpp
#include <gl/glut.h>
#include <assert.h>
#include <stdio.h>

#include "GlutApp.h"

int GlutApp::s_argc = 0;

char** GlutApp::s_argv = 0;

GlutApp* GlutApp::s_pCurrentApp = 0;

int g_iLastWindow = 0;

void GlutApp::run()
{
	GlutApp* lastApp = GlutApp::s_pCurrentApp;
	GlutApp::s_pCurrentApp = this;

	GlutApp* app = GlutApp::s_pCurrentApp;
	assert(app);

	int screenW = glutGet(GLUT_SCREEN_WIDTH);
	int screenH = glutGet(GLUT_SCREEN_HEIGHT);

	if (!app->m_winWidth)
	{
		app->m_winWidth = screenW / 2;
		app->m_winHeight = screenH / 2;
	}
	
	if (!app->m_winPosX)
	{
		app->m_winPosX = (screenW - app->m_winWidth) / 2;
		app->m_winPosY = (screenH - app->m_winHeight) / 2;
	}

	if (!lastApp) // first time calling Glut::run().
	{
		// glutInit that should only be called exactly once in a GLUT program.
		glutInit(&this->s_argc, this->s_argv);

		glutInitDisplayMode(this->m_displayMode);
		glutInitWindowPosition(app->m_winPosX, app->m_winPosY);
		glutInitWindowSize(app->m_winWidth, app->m_winHeight);

		glutCreateWindow(app->m_title);
		g_iLastWindow = glutGetWindow();
		// printf("create window: %d
", g_iLastWindow); } else { glutDestroyWindow(g_iLastWindow); glutInitDisplayMode(this->m_displayMode); glutInitWindowPosition(app->m_winPosX, app->m_winPosY); glutInitWindowSize(app->m_winWidth, app->m_winHeight); glutCreateWindow(app->m_title); g_iLastWindow = glutGetWindow(); // printf("create window: %d
", g_iLastWindow); } app->onInit(); // register keyboard callbacks glutKeyboardFunc(GlutApp::KeyboardCallback); glutKeyboardUpFunc(GlutApp::KeyboardUpCallback); glutSpecialFunc(GlutApp::SpecialKeyboardCallback); glutSpecialUpFunc(GlutApp::SpecialKeyboardUpCallback); // register mouse callbacks glutMouseFunc(GlutApp::MouseFuncCallback); glutMotionFunc(GlutApp::MotionFuncCallback); glutPassiveMotionFunc(GlutApp::MousePassiveCallback); // register menus: registerMenus(); // regitser windows resize callback glutReshapeFunc(GlutApp::ReshapeCallback); // register render callback glutDisplayFunc(GlutApp::DisplayCallback); // register timer callbacks: if (app->m_delay) { glutTimerFunc(app->m_delay, GlutApp::TimerCallback, app->m_period); } // register idle callback glutIdleFunc(GlutApp::IdleCallback); GlutApp::IdleCallback(); glutMainLoop(); } GlutApp::GlutApp() { m_displayMode = GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL; m_menuCount = 0; m_delay = 0; m_period = 0; m_winPosX = 0; m_winPosY = 0; m_winWidth = 0; m_winHeight = 0; } void GlutApp::KeyboardCallback( unsigned char key, int x, int y ) { GlutApp::s_pCurrentApp->onKey(key,x,y); } void GlutApp::KeyboardUpCallback( unsigned char key, int x, int y ) { GlutApp::s_pCurrentApp->onKeyDown(key,x,y); } void GlutApp::SpecialKeyboardCallback( int key, int x, int y ) { GlutApp::s_pCurrentApp->onSpecialKey(key,x,y); } void GlutApp::SpecialKeyboardUpCallback( int key, int x, int y ) { GlutApp::s_pCurrentApp->onSpecialKeyDown(key,x,y); } void GlutApp::ReshapeCallback( int w, int h ) { GlutApp::s_pCurrentApp->setWindowsSize(w, h); GlutApp::s_pCurrentApp->onResize(w,h); } void GlutApp::IdleCallback() { GlutApp::s_pCurrentApp->onIdle(); } void GlutApp::MouseFuncCallback( int button, int state, int x, int y ) { GlutApp::s_pCurrentApp->onMousePress(button,state,x,y); } void GlutApp::MotionFuncCallback( int x,int y ) { GlutApp::s_pCurrentApp->onMousePressMove(x,y); } void GlutApp::MousePassiveCallback( int x, int y ) { GlutApp::s_pCurrentApp->onMouseMove(x, y); } void GlutApp::DisplayCallback( void ) { GlutApp::s_pCurrentApp->onDisplay(); } void GlutApp::addRightMenu( const char *str, MenuFuncPtr fun ) { m_menuEntry[m_menuCount].id = m_menuCount; m_menuEntry[m_menuCount].str = str; m_menuEntry[m_menuCount].fun = fun; m_menuCount++; } void GlutApp::registerMenus() { if (m_menuCount > 0) { glutCreateMenu(GlutApp::MenuCallback); for (int i=0; i<m_menuCount; ++i) { glutAddMenuEntry(m_menuEntry[i].str, m_menuEntry[i].id); } glutAttachMenu(GLUT_RIGHT_BUTTON); } } void GlutApp::MenuCallback( int menuId ) { for (int i=0; i<GlutApp::s_pCurrentApp->m_menuCount; ++i) { if (menuId == GlutApp::s_pCurrentApp->m_menuEntry[i].id) { GlutApp::s_pCurrentApp->m_menuEntry[i].fun(); } } } void GlutApp::setTimer( int delay, int period ) { this->m_delay = delay; this->m_period = period; } void GlutApp::TimerCallback( int period ) { // printf("Timer Alarm!
"); GlutApp::s_pCurrentApp->onTimer(); if (period) { glutTimerFunc(period, GlutApp::TimerCallback, period); } }

ひとつのDemo
「偉大な三角形」は、Hello worldがプログラミング言語のチュートリアルで有名です.
#include <windows.h> //        Windows SDK    OpenGL,    <gl/gl.h>  
#include <gl/gl.h>
#include <gl/glut.h>
#include <stdio.h>

#include "GlutApp.h"

class TestApp : public GlutApp
{
	virtual void onSpecialKeyDown( int key, int x, int y ) 
	{
		printf("onKeyDown: %d(%c), <%d, %d>
", key, key, x, y); } virtual void onSpecialKey( int key, int x, int y ) { printf("onSpecialKey: %d(%c), <%d, %d>
", key, key, x, y); } virtual void onKeyDown( unsigned char key, int x, int y ) { printf("onKeyDown: %d(%c), <%d, %d>
", key, key, x, y); } virtual void onKey( unsigned char key, int x, int y ) { printf("onKey: %d(%c), <%d, %d>
", key, key, x, y); } virtual void onMouseMove( int x, int y ) { printf("onMouseMove: %d, %d
", x, y); } virtual void onMousePress( int button, int state, int x, int y ) { printf("onMousePress: %d, %d, %d, %d
", button, state, x, y); } virtual void onMousePressMove( int x,int y ) { printf("onMousePressMove: %d, %d
", x, y); } virtual void onInit() { printf("OnInit
"); glClearColor(0.0, 0.0, 0.0, 0.0); glShadeModel(GL_FLAT); } virtual void onDisplay() { glClear(GL_COLOR_BUFFER_BIT); // glPolygonMode(GL_FRONT, GL_LINE); glBegin(GL_TRIANGLES); glColor3f(1, 0, 0); glVertex2f(-1, -1); glVertex2f(1, -1); glVertex2f(0, 1); glEnd(); glFlush(); glutSwapBuffers(); } virtual void onResize( int w, int h ) { printf("resize window: %d, %d
", w, h); } virtual void onIdle() { } }; void menu1() { printf("menu1 selected!
"); } void menu2() { printf("menu2 selected!
"); } void fullScreen() { glutFullScreen(); } void exitApp() { exit(0); } int main(int argc, char **argv) { TestApp test; test.initGlut(argc, argv); test.setTitle("AppTest"); test.setWindowsSize(640, 480); test.setDisplayMode(GLUT_RGBA | GLUT_SINGLE); test.addRightMenu("menu1", menu1); test.addRightMenu("menu2", menu2); test.addRightMenu("full screen", fullScreen); test.addRightMenu("exit", exitApp); test.run(); return 0; }