OpenGL 使用 GLUT 处理鼠标输入
OpenGL 二维图形学教程系列
此系列翻译自:OpenGL Tutorial An Introduction on OpenGL with 2D Graphics
- OpenGl 环境搭建与介绍
- OpenGl 顶点,图元以及颜色
- OpenGL 裁剪区域与视口
- OpenGl 平移和旋转
- OpenGl 动画
- OpenGL 使用 GLUT 处理键盘输入
- OpenGL 使用 GLUT 处理鼠标输入
8. 使用 GLUT 处理鼠标输入
与处理键盘输入相似,我们可以分别为鼠标点击和鼠标移动事件注册回调函数进行处理。
glutMouseFunc:注册处理鼠标点击的回调函数。
void glutMouseFunc(void (*func)(int button, int state, int x, int y) // (x, y) is the mouse-click location. // button: GLUT_LEFT_BUTTON, GLUT_RIGHT_BUTTON, GLUT_MIDDLE_BUTTON // state: GLUT_UP, GLUT_DOWN
glutMotionFunc:注册处理鼠标移动(当鼠标被点击且移动时)的回调函数。
void glutMotionFunc(void (*func)(int x, int y) // where (x, y) is the mouse location in Window's coordinates
8.1 例子 10:鼠标控制(GL10MouseControl.cpp)
对于之前的弹跳小球程序,现在你可以使用鼠标左键暂停小球的移动,并使用鼠标右键恢复其移动。
/*
* GL10MouseControl.cpp: A mouse-controlled bouncing ball
*/
#include <windows.h> // for MS Windows
#include <GL/glut.h> // GLUT, include glu.h and gl.h
#include <Math.h> // Needed for sin, cos
#define PI 3.14159265f
// Global variables
char title[] = "Full-Screen & Windowed Mode"; // Windowed mode's title
int windowWidth = 640; // Windowed mode's width
int windowHeight = 480; // Windowed mode's height
int windowPosX = 50; // Windowed mode's top-left corner x
int windowPosY = 50; // Windowed mode's top-left corner y
GLfloat ballRadius = 0.5f; // Radius of the bouncing ball
GLfloat ballX = 0.0f; // Ball's center (x, y) position
GLfloat ballY = 0.0f;
GLfloat ballXMax, ballXMin, ballYMax, ballYMin; // Ball's center (x, y) bounds
GLfloat xSpeed = 0.02f; // Ball's speed in x and y directions
GLfloat ySpeed = 0.007f;
int refreshMillis = 30; // Refresh period in milliseconds
// Projection clipping area
GLdouble clipAreaXLeft, clipAreaXRight, clipAreaYBottom, clipAreaYTop;
bool fullScreenMode = true; // Full-screen or windowed mode?
bool paused = false; // Movement paused or resumed
GLfloat xSpeedSaved, ySpeedSaved; // To support resume
/* Initialize OpenGL Graphics */
void initGL() {
glClearColor(0.0, 0.0, 0.0, 1.0); // Set background (clear) color to black
}
/* Callback handler for window re-paint event */
void display() {
glClear(GL_COLOR_BUFFER_BIT); // Clear the color buffer
glMatrixMode(GL_MODELVIEW); // To operate on the model-view matrix
glLoadIdentity(); // Reset model-view matrix
glTranslatef(ballX, ballY, 0.0f); // Translate to (xPos, yPos)
// Use triangular segments to form a circle
glBegin(GL_TRIANGLE_FAN);
glColor3f(0.0f, 0.0f, 1.0f); // Blue
glVertex2f(0.0f, 0.0f); // Center of circle
int numSegments = 100;
GLfloat angle;
for (int i = 0; i <= numSegments; i++) { // Last vertex same as first vertex
angle = i * 2.0f * PI / numSegments; // 360 deg for all segments
glVertex2f(cos(angle) * ballRadius, sin(angle) * ballRadius);
}
glEnd();
glutSwapBuffers(); // Swap front and back buffers (of double buffered mode)
// Animation Control - compute the location for the next refresh
ballX += xSpeed;
ballY += ySpeed;
// Check if the ball exceeds the edges
if (ballX > ballXMax) {
ballX = ballXMax;
xSpeed = -xSpeed;
} else if (ballX < ballXMin) {
ballX = ballXMin;
xSpeed = -xSpeed;
}
if (ballY > ballYMax) {
ballY = ballYMax;
ySpeed = -ySpeed;
} else if (ballY < ballYMin) {
ballY = ballYMin;
ySpeed = -ySpeed;
}
}
/* Call back when the windows is re-sized */
void reshape(GLsizei width, GLsizei height) {
// Compute aspect ratio of the new window
if (height == 0) height = 1; // To prevent divide by 0
GLfloat aspect = (GLfloat)width / (GLfloat)height;
// Set the viewport to cover the new window
glViewport(0, 0, width, height);
// Set the aspect ratio of the clipping area to match the viewport
glMatrixMode(GL_PROJECTION); // To operate on the Projection matrix
glLoadIdentity(); // Reset the projection matrix
if (width >= height) {
clipAreaXLeft = -1.0 * aspect;
clipAreaXRight = 1.0 * aspect;
clipAreaYBottom = -1.0;
clipAreaYTop = 1.0;
} else {
clipAreaXLeft = -1.0;
clipAreaXRight = 1.0;
clipAreaYBottom = -1.0 / aspect;
clipAreaYTop = 1.0 / aspect;
}
gluOrtho2D(clipAreaXLeft, clipAreaXRight, clipAreaYBottom, clipAreaYTop);
ballXMin = clipAreaXLeft + ballRadius;
ballXMax = clipAreaXRight - ballRadius;
ballYMin = clipAreaYBottom + ballRadius;
ballYMax = clipAreaYTop - ballRadius;
}
/* Called back when the timer expired */
void Timer(int value) {
glutPostRedisplay(); // Post a paint request to activate display()
glutTimerFunc(refreshMillis, Timer, 0); // subsequent timer call at milliseconds
}
/* Callback handler for normal-key event */
void keyboard(unsigned char key, int x, int y) {
switch (key) {
case 27: // ESC key
exit(0);
break;
}
}
/* Callback handler for special-key event */
void specialKeys(int key, int x, int y) {
switch (key) {
case GLUT_KEY_F1: // F1: Toggle between full-screen and windowed mode
fullScreenMode = !fullScreenMode; // Toggle state
if (fullScreenMode) { // Full-screen mode
windowPosX = glutGet(GLUT_WINDOW_X); // Save parameters for restoring later
windowPosY = glutGet(GLUT_WINDOW_Y);
windowWidth = glutGet(GLUT_WINDOW_WIDTH);
windowHeight = glutGet(GLUT_WINDOW_HEIGHT);
glutFullScreen(); // Switch into full screen
} else { // Windowed mode
glutReshapeWindow(windowWidth, windowHeight); // Switch into windowed mode
glutPositionWindow(windowPosX, windowPosX); // Position top-left corner
}
break;
case GLUT_KEY_RIGHT: // Right: increase x speed
xSpeed *= 1.05f; break;
case GLUT_KEY_LEFT: // Left: decrease x speed
xSpeed *= 0.95f; break;
case GLUT_KEY_UP: // Up: increase y speed
ySpeed *= 1.05f; break;
case GLUT_KEY_DOWN: // Down: decrease y speed
ySpeed *= 0.95f; break;
case GLUT_KEY_PAGE_UP: // Page-Up: increase ball's radius
ballRadius *= 1.05f;
ballXMin = clipAreaXLeft + ballRadius;
ballXMax = clipAreaXRight - ballRadius;
ballYMin = clipAreaYBottom + ballRadius;
ballYMax = clipAreaYTop - ballRadius;
break;
case GLUT_KEY_PAGE_DOWN: // Page-Down: decrease ball's radius
ballRadius *= 0.95f;
ballXMin = clipAreaXLeft + ballRadius;
ballXMax = clipAreaXRight - ballRadius;
ballYMin = clipAreaYBottom + ballRadius;
ballYMax = clipAreaYTop - ballRadius;
break;
}
}
/* Callback handler for mouse event */
void mouse(int button, int state, int x, int y) {
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { // Pause/resume
paused = !paused; // Toggle state
if (paused) {
xSpeedSaved = xSpeed; // Save parameters for restore later
ySpeedSaved = ySpeed;
xSpeed = 0; // Stop movement
ySpeed = 0;
} else {
xSpeed = xSpeedSaved; // Restore parameters
ySpeed = ySpeedSaved;
}
}
}
/* Main function: GLUT runs as a console application starting at main() */
int main(int argc, char** argv) {
glutInit(&argc, argv); // Initialize GLUT
glutInitDisplayMode(GLUT_DOUBLE); // Enable double buffered mode
glutInitWindowSize(windowWidth, windowHeight); // Initial window width and height
glutInitWindowPosition(windowPosX, windowPosY); // Initial window top-left corner (x, y)
glutCreateWindow(title); // Create window with given title
glutDisplayFunc(display); // Register callback handler for window re-paint
glutReshapeFunc(reshape); // Register callback handler for window re-shape
glutTimerFunc(0, Timer, 0); // First timer call immediately
glutSpecialFunc(specialKeys); // Register callback handler for special-key event
glutKeyboardFunc(keyboard); // Register callback handler for special-key event
glutFullScreen(); // Put into full screen
glutMouseFunc(mouse); // Register callback handler for mouse event
initGL(); // Our own OpenGL initialization
glutMainLoop(); // Enter event-processing loop
return 0;
}
TODO:详细的代码解释
8.2 例子 11:一个简单的绘图程序
TODO:使用鼠标移动事件以及 GL_LINE_STRIP。
Links: OpenGL-使用-GLUT-处理鼠标输入