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_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\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 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!\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;
}
转自:http://www.myexception.cn/program/1678902.html