zoukankan      html  css  js  c++  java
  • GLUT的简洁OO封装

    毕业设计用到了OpenGL,由于不会用MFC和Win32API做窗口程序;自然选用了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?

    这里其实是两个问题。
    第一个问题,member function的函数签名上有this指针,不能直接传给glut*Func作为callback,怎么办?
    member function不行,很自然的想到static function;因为static function的函数签名上没有this指针。
    第二个问题,static function如何能够调用member function,且与之关联的对象(this指针)能够在运行时期(或者用户程序)决定?
    其一,static function调用member function自然要用到 static member。
    其二,可让member function修改这个static member。

    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;
    }
    


  • 相关阅读:
    几种常见的基于Lucene的开源搜索解决方案对比
    【例子】Bobobrowse:lucene分组统计扩展组件
    Solr 相似页面MoreLikeThis
    eclipse安装、基本使用、常用设置
    iPhone开发入门守则:ObjectiveC编码规范系列教程
    ios开发学习按钮(Button)效果源码分享
    ios开发学习音频声效(Audio)效果源码分享系列教程
    ios开发学习动画(Animation)效果源码分享系列教程1
    ios开发学习图表(Chart)效果源码分享系列教程
    局网满猿关不住,一波码农出墙来。
  • 原文地址:https://www.cnblogs.com/xusw/p/5205856.html
Copyright © 2011-2022 走看看