GLUT Tutorials 9: GLUT鼠标
博客转自:https://www.cnblogs.com/yangxi/archive/2011/09/16/2178499.html
在前几节,我们看了怎么使用GLUT的keyboard函数,来增加一个OpenGL程序的交互性。现在,是时候研究下鼠标了。GLUT的鼠标接口提供一些列的选项来增加鼠标的交互性。也就是检测鼠标单击,和鼠标移动。
检测鼠标Clicks
和键盘处理一样,GLUT为你的注册函数(也就是处理鼠标clicks事件的函数)提供了一个方法。函数glutMouseFunc,这个函数一般在程序初始化阶段被调用。函数原型如下:
void glutMouseFunc(void(*func)(int button,int state,int x,int y));
GLUT_LEFT_BUTTON
GLUT_MIDDLE_BUTTON
GLUT_RIGHT_BUTTON
第二个参数表明,函数被调用发生时,鼠标的状态,也就是是被按下,或松开,可能取值如下:
GLUT_DOWN
GLUT_UP
当函数被调用时,state的值是GLUT_DOWN,那么程序可能会假定将会有个GLUT_UP事件,甚至鼠标移动到窗口外面,也如此。然而,如果程序调用glutMouseFunc传递NULL作为参数,那么GLUT将不会改变鼠标的状态。
void glutMotionFunc(void(*func)(int x,int y)); void glutPassiveMotionFunc(void (*func)(int x,int y));
GLUT还能检测鼠标鼠标离开,进入窗口区域。一个回调函数可以被定义去处理这两个事件。GLUT里,调用这个函数的是glutEntryFunc,函数原型如下:
void glutEntryFunc(void(*func)(int state));
GLUT_LEFT
GLUT_ENTERED
表明,是离开,还是进入窗口。
整合全部功能到程序内
首先我们要做的是在GLUT里定义哪些函数将负责处理鼠标事件。因此我们将重写我们的main函数,让它包含所有必须的回调注册函数。我们将在程序里描述其他一些教程里没说清楚的地方。
#include <gl/glut.h> void main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA); glutInitWindowPosition(100, 100); glutInitWindowSize(320, 320); glutCreateWindow("GLUT Tutorial Cursor"); glutDisplayFunc(renderScene); glutIdleFunc(renderScene); glutReshapeFunc(reShape); //adding here the mouse processing callbacks glutMouseFunc(processMouse); glutMotionFunc(processMouseActiveMotion); glutPassiveMotionFunc(processMousePassiveMotion); glutEntryFunc(processMouseEntry); glutMainLoop(); }
OK,现在做点有趣的。我们将定义那些将做一些不可思议事件的回调函数。当一个鼠标键和alt键都被按下,我们将改变三角形的颜色。鼠标左键使三角形变成红色,中间的将三角形变成绿色,鼠标右键将三角形变成蓝色。函数如下:
void processMouse(int button, int state, int x, int y) { specialKey = glutGetModifiers(); // 当鼠标键和alt键都被按下 if ((state == GLUT_DOWN) && (specialKey == GLUT_ACTIVE_ALT)) { // set the color to pure red for the left button if (button == GLUT_LEFT_BUTTON) { red = 1.0; green = 0.0; blue = 0.0; } // set the color to pure green for the middle button else if (button == GLUT_MIDDLE_BUTTON) { red = 0.0; green = 1.0; blue = 0.0; } // set the color to pure blue for the right button else { red = 0.0; green = 0.0; blue = 1.0; } } }
接下来有一个精细的颜色拾取方法。当一个鼠标键被按下,但alt键被被按下。我们把blue设为0.0,并且让red和green分量的值取决于鼠标在窗口中的位置。。函数如下:
void processMouseActiveMotion(int x, int y) { // the ALT key was used in the previous function if (specialKey != GLUT_ACTIVE_ALT) { // setting red to be relative to the mouse // position inside the window if (x < 0) red = 0.0; else if (x > width) red = 1.0; else red = ((float) x)/height; // setting green to be relative to the mouse // position inside the window if (y < 0) green = 0.0; else if (y > width) green = 1.0; else green = ((float) y)/height; // removing the blue component. blue = 0.0; } }
下面给passive motion添加一些动作。当shift键被按下,鼠标将在x轴上有一个旋转。我们不得不修改renderScene函数。函数如下:
float angleX = 0.0; void renderScene(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glRotatef(angle,0.0,1.0,0.0); // This is the line we added for the // rotation on the X axis; glRotatef(angleX,1.0,0.0,0.0); glColor3f(red,green,blue); glBegin(GL_TRIANGLES); glVertex3f(-0.5,-0.5,0.0); glVertex3f(0.5,0.0,0.0); glVertex3f(0.0,0.5,0.0); glEnd(); glPopMatrix(); angle++; glutSwapBuffers(); }
现在我们的有个函数处理passive motion事件。函数将改变angleX的值。
void processMousePassiveMotion(int x, int y) { // User must press the SHIFT key to change the // rotation in the X axis if (specialKey != GLUT_ACTIVE_SHIFT) { // setting the angle to be relative to the mouse // position inside the window if (x < 0) angleX = 0.0; else if (x > width) angleX = 180.0; else angleX = 180.0 * ((float) x)/height; } }
最后鼠标离开窗口将使动画停止,为了做到这,我们也需要改变函数renderScene。
// initially define the increase of the angle by 1.0; float deltaAngle = 1.0; void renderScene(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glRotatef(angle,0.0,1.0,0.0); glRotatef(angleX,1.0,0.0,0.0); glColor3f(red,green,blue); glBegin(GL_TRIANGLES); glVertex3f(-0.5,-0.5,0.0); glVertex3f(0.5,0.0,0.0); glVertex3f(0.0,0.5,0.0); glEnd(); glPopMatrix(); // this is the new line // previously it was: angle++; angle+=deltaAngle; glutSwapBuffers(); }
processMouseEntry是最后一个函数。注意,这个在微软操作系统下可能工作的不是很好。
void processMouseEntry(int state) { if (state == GLUT_LEFT) deltaAngle = 0.0; else deltaAngle = 1.0; }
显示效果如下
#include <stdio.h> #include <gl/glut.h> int specialKey = 0; static float angle = 0.0, angleX = 0.0, ratio; // initially define the increase of the angle by 1.0; float deltaAngle = 0.05; //windows size int width = 320, height = 320; //所有的变量被初始化为1,表明三角形最开始是白色的。 float red = 1.0, blue = 1.0, green = 1.0; void processMouse(int button, int state, int x, int y) { specialKey = glutGetModifiers(); // 当鼠标键和alt键都被按下 if ((state == GLUT_DOWN) && (specialKey == GLUT_ACTIVE_ALT)) { // set the color to pure red for the left button if (button == GLUT_LEFT_BUTTON) { red = 1.0; green = 0.0; blue = 0.0; } // set the color to pure green for the middle button else if (button == GLUT_MIDDLE_BUTTON) { red = 0.0; green = 1.0; blue = 0.0; } // set the color to pure blue for the right button else { red = 0.0; green = 0.0; blue = 1.0; } } } void processMousePassiveMotion(int x, int y) { // User must press the SHIFT key to change the // rotation in the X axis if (specialKey != GLUT_ACTIVE_SHIFT) { // setting the angle to be relative to the mouse // position inside the window if (x < 0) angleX = 0.0; else if (x > width) angleX = 180.0; else angleX = 180.0 * ((float)x) / height; } } void processMouseActiveMotion(int x, int y) { // the ctrl key was used in the previous function if (specialKey != GLUT_ACTIVE_ALT) { // setting red to be relative to the mouse // position inside the window if (x < 0) red = 0.0; else if (x > width) red = 1.0; else red = ((float)x) / height; // setting green to be relative to the mouse // position inside the window if (y < 0) green = 0.0; else if (y > width) green = 1.0; else green = ((float)y) / height; // removing the blue component. blue = 0.0; } } void renderScene(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glRotatef(angle, 0.0, 1.0, 0.0); glRotatef(angleX, 1.0, 0.0, 0.0); glColor3f(red, green, blue); glBegin(GL_TRIANGLES); glVertex3f(-0.5, -0.5, 0.0); glVertex3f(0.5, 0.0, 0.0); glVertex3f(0.0, 0.5, 0.0); glEnd(); glPopMatrix(); // this is the new line // previously it was: angle++; angle += deltaAngle; glutSwapBuffers(); } void processMouseEntry(int state) { if (state == GLUT_LEFT) deltaAngle = 0.0; else deltaAngle = 0.05; } void reShape(int w, int h) { // 防止被0除. if (h == 0) h = 1; width = w; height = h; ratio = 1.0f * w / h; //Reset the coordinate system before modifying glMatrixMode(GL_PROJECTION); glLoadIdentity(); //设置视口为整个窗口大小 glViewport(0, 0, width, height); //设置可视空间 gluPerspective(45, ratio, 1, 1000); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, -1.0, 0.0f, 1.0f, 0.0f); } void main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA); glutInitWindowPosition(100, 100); glutInitWindowSize(320, 320); glutCreateWindow("GLUT Tutorial Cursor"); glutDisplayFunc(renderScene); glutIdleFunc(renderScene); glutReshapeFunc(reShape); //adding here the mouse processing callbacks glutMouseFunc(processMouse); glutMotionFunc(processMouseActiveMotion); glutPassiveMotionFunc(processMousePassiveMotion); glutEntryFunc(processMouseEntry); glutMainLoop(); }