GLUT的简洁OO封装

来源:互联网 发布:淘宝店铺增加粉丝数量 编辑:程序博客网 时间:2024/04/26 18:25

毕业设计用到了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\n", 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\n", 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)\n", 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\n", x, y);fflush(stdout);}// 鼠标移动void passiveMotionHandler(int x, int y){printf("passive motion to %d, %d\n", x, y);fflush(stdout);}void testTimer(int i){printf("Alarm %d\n", 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_Hclass 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;// ctorGlutApp();// 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 voidMotionFuncCallback(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 glutInitstatic int s_argc;static char** s_argv;char *m_title;// for glutSetWindowSizeint m_winWidth;int m_winHeight;// for windows positionint 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\n", 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\n", g_iLastWindow);}app->onInit();// register keyboard callbacksglutKeyboardFunc(GlutApp::KeyboardCallback);glutKeyboardUpFunc(GlutApp::KeyboardUpCallback);glutSpecialFunc(GlutApp::SpecialKeyboardCallback);glutSpecialUpFunc(GlutApp::SpecialKeyboardUpCallback);// register mouse callbacksglutMouseFunc(GlutApp::MouseFuncCallback);glutMotionFunc(GlutApp::MotionFuncCallback);glutPassiveMotionFunc(GlutApp::MousePassiveCallback);// register menus:registerMenus();// regitser windows resize callbackglutReshapeFunc(GlutApp::ReshapeCallback);// register render callbackglutDisplayFunc(GlutApp::DisplayCallback);// register timer callbacks:if (app->m_delay){glutTimerFunc(app->m_delay, GlutApp::TimerCallback, app->m_period);}// register idle callbackglutIdleFunc(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!\n");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>\n", key, key, x, y);}virtual void onSpecialKey( int key, int x, int y ) {printf("onSpecialKey: %d(%c), <%d, %d>\n", key, key, x, y);}virtual void onKeyDown( unsigned char key, int x, int y ) {printf("onKeyDown: %d(%c), <%d, %d>\n", key, key, x, y);}virtual void onKey( unsigned char key, int x, int y ) {printf("onKey: %d(%c), <%d, %d>\n", key, key, x, y);}virtual void onMouseMove( int x, int y ) {printf("onMouseMove: %d, %d\n", x, y);}virtual void onMousePress( int button, int state, int x, int y ) {printf("onMousePress: %d, %d, %d, %d\n", button, state, x, y);}virtual void onMousePressMove( int x,int y ) {printf("onMousePressMove: %d, %d\n", x, y);}virtual void onInit() {printf("OnInit\n");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\n", w, h);}virtual void onIdle() {}};void menu1() { printf("menu1 selected!\n"); }void menu2() { printf("menu2 selected!\n"); }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;}


1 0
原创粉丝点击