#include "../../util/check_error.h" #include "../../util/pick_convert.h" #include "../../view/geometry.h" #include "../../view/state0.h" #include "../actions/process_hits.h" #include "../actions/set_ortho.h" #include "../actions/zoom.h" #include "mouse.h" #include "reshape.h" #include #include #include #include /* * A simple alias to make the code more readable. */ #define S state0 void mouse (int button, int state, int x, int y) { // Release left button. if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) { // Deactive a panning event if one was happening. S.pan.active = false; if (S.selection.active && S.selection.purpose == ZOOM && glutGetModifiers () == GLUT_ACTIVE_CTRL) { /* * NOOP if the mouse was not moved. */ if (x == S.selection.x || y == S.selection.y) return; /* * Convert the selection boundary from window coordinates to * world coordinates. */ glMatrixMode (GL_MODELVIEW); glLoadIdentity (); GLdouble model[16]; glGetDoublev (GL_MODELVIEW_MATRIX, model); GLdouble projection[16]; glGetDoublev (GL_PROJECTION_MATRIX, projection); GLint viewport[4]; glGetIntegerv (GL_VIEWPORT, viewport); check_error (__FILE__, __LINE__); GLdouble start_position[3]; gluUnProject (x, viewport[3] - y, 0, model, projection, viewport, &start_position[0], &start_position[1], &start_position[2]); check_error (__FILE__, __LINE__); GLdouble end_position[3]; gluUnProject (S.selection.x, viewport[3] - S.selection.y, 0, model, projection, viewport, &end_position[0], &end_position[1], &end_position[2]); check_error (__FILE__, __LINE__); zoom (fmin (start_position[0], end_position[0]), fmin (start_position[1], end_position[1]), fmax (start_position[0], end_position[0]), fmax (start_position[1], end_position[1])); } /* * Complete a selection if one was started and not cancelled. */ if (S.selection.active && S.selection.purpose == SET && glutGetModifiers () == GLUT_ACTIVE_CTRL) { /* * "Specify the array to be used for the returned hit records * with glSelectBuffer () [Redbook]." */ GLuint *select_buf = calloc (S.rows, sizeof (GLuint)); glSelectBuffer (S.rows, select_buf); /* * "Enter selection mode by specifying GL_SELECT with * glRenderMode () [Redbook]." */ glRenderMode (GL_SELECT); /* * "Initialize the name stack using glInitNames () and glPush * Names () [Redbook]." */ glInitNames (); glPushName (0); /* * "Define the viewing volume you want to use for selection. * Usually this is different from the viewing volume you * originally used to draw the scene, so you probably want to * save and then restore the current transformation state with * glPushMatrix () and glPopMatrix () [Redbook]." */ glMatrixMode (GL_PROJECTION); glPushMatrix (); glLoadIdentity (); GLint viewport[4]; glGetIntegerv (GL_VIEWPORT, viewport); double c_x = 0.0; double c_y = 0.0; double w = 0.0; double h = 0.0; pick_convert (S.selection.x, S.selection.y, x, y, &c_x, &c_y, &w, &h); gluPickMatrix (c_x, (GLdouble) viewport[3] - c_y, w, h, viewport); set_ortho (); /* * "Alternately issue primitive drawing commands and commands to * manipulate the name stack so that each primitive of interest * has appropriate names assigned [Redbook]." */ geometry (GL_SELECT); glMatrixMode (GL_PROJECTION); glPopMatrix (); glutSwapBuffers (); /* * "Exit selection mode and process the returned selection data * (the hit records) [Redbook]." */ GLint hits = glRenderMode (GL_RENDER); check_error (__FILE__, __LINE__); /* "process hits from selection mode rendering [Angel,2008]." */ process_hits (hits, select_buf); /* "normal render [Angel,2008]." */ glutPostRedisplay (); } } // Begin selection. if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN && glutGetModifiers () == GLUT_ACTIVE_CTRL) { S.selection.active = true; S.selection.x = x; S.selection.y = y; } // Pan. if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN && glutGetModifiers () != GLUT_ACTIVE_CTRL) { /* * Detection of the first point in a panning event. */ if (S.pan.active == false) { S.pan.active = true; S.pan.begin[0] = x; S.pan.begin[1] = y; } } return; }