// // File: glmesh.cc // // (C) 2000-2006 Helmut Cantzler // // Licensed under the terms of the Lesser General Public License. // #include "timeval.h" #ifndef WIN32 #include #endif #include #include "glmesh.h" #include "mview.h" #include "coordinates.h" // for FPS unsigned long time_counter; unsigned int frame_counter; GLMesh_Settings::GLMesh_Settings() { init(); } void GLMesh_Settings::init() { mesh = features = NULL; mesh_displayMode = features_displayMode = NOTHING; point_size = 1.1; line_size = light_brightness = 1.0; red = green = blue = 0.0; f_normals = f_different_colors_for_shapes = 0; f_texture_filter = f_back_faces = 0; f_features = f_keep_pick = 0; f_lighting = f_aspect_ratio = 0; pick_x = pick_y = -1; pick = NULL; resetViewpoint(); } void GLMesh_Settings::clear() { delete mesh; delete features; init(); } void GLMesh_Settings::clearFeatures() { delete features; features = NULL; features_displayMode = NOTHING; } void GLMesh_Settings::resetViewpoint() { xShift = yShift = zShift = 0.0; clipping = 0.0; // create identity matrix for (int i=0; i < 4; i++) for (int j=0; j < 4; j++) tb_transform[i][j]= i == j ? 1.0 : 0.0; } void GLMesh_Settings::setViewpoint(GLMesh_Settings &s) { xShift = s.xShift; yShift = s.yShift; zShift = s.zShift; clipping = s.clipping; for (int i=0; i < 4; i++) for (int j=0; j < 4; j++) tb_transform[i][j] = s.tb_transform[i][j]; } GLMesh::GLMesh(QWidget *parent, GLMesh_Settings *s) : QGLWidget(parent) { GLMesh::parent=parent; settings = s; // Initialise variables initMesh(); initFeatures(); } GLMesh::~GLMesh() { // delete texture glDeleteTextures(number_of_shapes, textureList); delete textureList; } float GLMesh::getFps(void) { static float fps = 0.0; if (time_counter != 0) { fps = frame_counter * 1000000.0 / (float) time_counter; time_counter = frame_counter = 0; } return fps; } void GLMesh::resetViewpoint(void) { settings->resetViewpoint(); updateGL(); } void GLMesh::setViewpoint(GLMesh_Settings &vp) { settings->setViewpoint(vp); updateGL(); } void GLMesh::displayPickObjects(void) { Mesh *mesh = settings->mesh; if (mesh != NULL) { list::iterator iv; list::iterator ie; list::iterator it; // Purple, Green, Yellow, White // const int colors[4] = {5, 2, 4, 7}; const int colors[4] = {19, 16, 18, 21}; int nr; glPointSize(8.0); for (nr=-1, iv=mesh->selected_vertices.begin(); iv != mesh->selected_vertices.end(); iv++) { // Set color nr = nr+1 <= 3 ? nr+1 : 0; setMaterialColor(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, colors[nr]); glBegin(GL_POINTS); glVertex3fv( (GLfloat*) (*iv)->float_data() ); glEnd(); } glPointSize(settings->point_size); glLineWidth(10); for (nr=-1, ie=mesh->selected_edges.begin(); ie != mesh->selected_edges.end(); ie++) { // Set color nr = nr+1 <= 3 ? nr+1 : 0; setMaterialColor(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, colors[nr]); glBegin(GL_LINES); glVertex3fv((GLfloat*) (*ie)->vertices[0]->float_data() ); glVertex3fv((GLfloat*) (*ie)->vertices[1]->float_data() ); glEnd(); } glLineWidth(settings->line_size); for (nr=-1, it=mesh->selected_triangles.begin(); it != mesh->selected_triangles.end(); it++) { // Set color nr = nr+1 <= 3 ? nr+1 : 0; setMaterialColor(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, colors[nr]); glBegin(GL_TRIANGLES); glNormal3fv( (GLfloat*) (*it)->float_normal() ); glVertex3fv( (GLfloat*) (*it)->vertices[0]->float_data() ); glVertex3fv( (GLfloat*) (*it)->vertices[1]->float_data() ); glVertex3fv( (GLfloat*) (*it)->vertices[2]->float_data() ); glEnd(); } } } void GLMesh::pickObject(int x, int y) { Mesh *mesh = settings->mesh; GLuint *records, name, select_buffer[1000]; GLint hits; // set the coordinates & size for the pick window settings->pick_x = x; settings->pick_y = y; // vertices & lines are harder to pick => they get a bigger pick window switch (settings->mesh_displayMode) { case EDGES: case VERTICES: settings->pick_size_x = settings->pick_size_y = 20.0; break; default: settings->pick_size_x = settings->pick_size_y = 10.0; break; } glSelectBuffer(1000, select_buffer); // start with a big window size // gradually decrease the size until we only get one hit do { // "select" rendering glRenderMode(GL_SELECT); glInitNames(); glPushName(0); updateGL(); // back to normal rendering hits = glRenderMode(GL_RENDER); settings->pick_size_x--; settings->pick_size_y--; } while (hits > 1 && settings->pick_size_x > 0); // we might have made the window to small??? if (hits < 1) { settings->pick_size_x+=2; settings->pick_size_y+=2; // "select" rendering glRenderMode(GL_SELECT); glInitNames(); glPushName(0); updateGL(); // back to normal rendering hits = glRenderMode(GL_RENDER); } // disable pick mode settings->pick_x = settings->pick_y = 0; if (!settings->f_keep_pick) settings->mesh->clear_selection(); records = (GLuint*) select_buffer; // process results // we are only interested in one selection, only take the 1st one if (hits >= 1) { Vertex *v = NULL; Edge *e = NULL; Triangle *t = NULL; // number of names in the record stack records++; // min & max z values records++; records++; // names (well the 1st one) are: name = *records; records++; switch (settings->mesh_displayMode) { case VERTICES: v = mesh->select_vertex(name); break; case EDGES: e = mesh->select_edge(name); break; case SOLID: case TEXTURE: case FRONTLINES: case WIRE: t = mesh->select_triangle(name); break; case FEATURES: switch (settings->features_displayMode) { case VERTICES: v = mesh->select_vertex(name); break; case EDGES: e = mesh->select_edge(name); break; } break; } if (settings->pick != NULL) settings->pick->printResult(v, e, t); } } void GLMesh::clearSelection(void) { if (settings->mesh != NULL) { settings->mesh->clear_selection(); updateGL(); } } // Mouse Events void GLMesh::mousePressEvent(QMouseEvent *e) { int x = e->x(); int y = e->y(); setFocus(); switch (e->button()) { case Qt::LeftButton: if (settings->pick_x == 0 && settings->pick_y == 0) { if (settings->mesh != NULL) { pickObject(x, y); updateGL(); } } else { actionMode=ROTATE; setCursor( QCursor(Qt::CrossCursor) ); tbPointToVector(x, y, tb_lastposition); } break; case Qt::MidButton: actionMode=TRANSLATE; setCursor( QCursor(Qt::SizeAllCursor) ); lastMouseX = x; lastMouseY = y; break; case Qt::RightButton: actionMode=ZOOM; setCursor( QCursor(Qt::SizeVerCursor) ); lastMouseX = x; lastMouseY = y; break; } } void GLMesh::mouseMoveEvent(QMouseEvent *e) { if (actionMode == NONE) return; int x = e->x(); int y = e->y(); switch (actionMode) { case ZOOM: settings->zShift += (y-lastMouseY)/(double)height()*3; lastMouseX = x; lastMouseY = y; break; case TRANSLATE: settings->xShift += (x-lastMouseX)/(double)width()*3; settings->yShift -= (y-lastMouseY)/(double)height()*3; lastMouseX = x; lastMouseY = y; break; case ROTATE: float current_position[3], angle, dx, dy, dz; tbPointToVector(x, y, current_position); dx = current_position[0] - tb_lastposition[0]; dy = current_position[1] - tb_lastposition[1]; dz = current_position[2] - tb_lastposition[2]; angle = 180.0 * sqrt(dx * dx + dy * dy + dz * dz); tb_axis[0] = tb_lastposition[1] * current_position[2] - tb_lastposition[2] * current_position[1]; tb_axis[1] = tb_lastposition[2] * current_position[0] - tb_lastposition[0] * current_position[2]; tb_axis[2] = tb_lastposition[0] * current_position[1] - tb_lastposition[1] * current_position[0]; tb_lastposition[0] = current_position[0]; tb_lastposition[1] = current_position[1]; tb_lastposition[2] = current_position[2]; glPushMatrix(); glLoadIdentity(); glRotatef(angle, tb_axis[0], tb_axis[1], tb_axis[2]); glMultMatrixf((GLfloat*) settings->tb_transform); glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)settings->tb_transform); glPopMatrix(); break; } updateGL(); } void GLMesh::mouseReleaseEvent(QMouseEvent *e) { actionMode = NONE; setCursor( QCursor(Qt::ArrowCursor) ); } // public slots void GLMesh::newMesh() { if (settings->f_lighting) glEnable(GL_LIGHTING); else glDisable(GL_LIGHTING); if (settings->f_back_faces) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); // delete old texture glDeleteTextures(number_of_shapes, textureList); delete textureList; // initialise new mesh initMesh(); initFeatures(); initTexture(); updateGL(); } void GLMesh::newFeatures() { initFeatures(); updateGL(); } void GLMesh::setDisplayMode(QAction *a) { if (settings->mesh != NULL) { QString name = a->text(); if (name == "Texture") { settings->mesh_displayMode = TEXTURE; } if (name == "Solid") { settings->mesh_displayMode = SOLID; } if (name == "Frontlines") { settings->mesh_displayMode = FRONTLINES; } if (name == "Wireframe") { if (settings->mesh->number_of_triangles() == 0) { settings->mesh_displayMode = EDGES; } else { settings->mesh_displayMode = WIRE; } } if (name == "Points") { settings->mesh_displayMode = VERTICES; } if (name == "Feature") { settings->mesh_displayMode = FEATURES; } clearSelection(); } } void GLMesh::setClipping(int c) { settings->clipping = -c/25.0; updateGL(); } void GLMesh::setPick(bool state, CoordinatesWindow *cw) { clearSelection(); settings->pick_x = settings->pick_y = state ? 0 : -1; settings->pick = cw; } void GLMesh::setPick(bool state) { setPick(state, settings->pick); } void GLMesh::setLightIntensity(int l) // l = 1..20 { settings->light_brightness = 0.4 + (l-10) * 0.02; GLfloat source[] = {settings->light_brightness, settings->light_brightness, settings->light_brightness, settings->light_brightness}; glLightfv(GL_LIGHT0, GL_DIFFUSE, source); glLightfv(GL_LIGHT1, GL_DIFFUSE, source); glLightfv(GL_LIGHT2, GL_DIFFUSE, source); glLightfv(GL_LIGHT3, GL_DIFFUSE, source); updateGL(); } void GLMesh::setPointSize(int l) // 1.0 1.2 1.4 ... { settings->point_size = 1.0 + l * 0.2; glPointSize(settings->point_size); updateGL(); } void GLMesh::setLineSize(int l) // 1.0 2.0 3.0 ... { settings->line_size = l; glLineWidth(settings->line_size); updateGL(); } void GLMesh::setLight(bool l) { settings->f_lighting = l; if (settings->f_lighting) glEnable(GL_LIGHTING); else glDisable(GL_LIGHTING); updateGL(); } void GLMesh::setShapeColors(bool sc) { settings->f_different_colors_for_shapes = sc; updateGL(); } void GLMesh::setNormals(bool n) { settings->f_normals = n; updateGL(); } void GLMesh::setBackFaces(bool bf) { settings->f_back_faces = bf; if (settings->f_back_faces) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); updateGL(); } void GLMesh::setTextureFiltering(bool tf) { GLint filter; int id; settings->f_texture_filter = tf; filter = settings->f_texture_filter ? GL_LINEAR : GL_NEAREST; for (int i=0; i < number_of_shapes; i++) { id=settings->mesh->get_shape(i)->get_texture_id(); if (id != -1) { glBindTexture(GL_TEXTURE_2D, id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); } } updateGL(); } void GLMesh::setFeatures(bool f) { settings->f_features = f; updateGL(); } void GLMesh::setAspectRatio(bool a) { settings->f_aspect_ratio = a; resizeGL(width(), height()); updateGL(); } void GLMesh::setBackgroundColor(QColor c) { settings->red = c.red()/255.0; settings->green = c.green()/255.0; settings->blue = c.blue()/255.0; qglClearColor(c); updateGL(); } void GLMesh::setKeepPick(int k) { settings->f_keep_pick = k; } // Initialise OpenGL void GLMesh::initFeatures(void) { if (settings->features == NULL) settings->features_displayMode=NOTHING; else if (settings->features->number_of_edges() > 0) settings->features_displayMode=EDGES; else if (settings->features->number_of_vertices() > 0) settings->features_displayMode=VERTICES; } void GLMesh::initMesh(void) { if (settings->mesh == NULL) { number_of_shapes = 0; settings->mesh_displayMode=NOTHING; } else { number_of_shapes = settings->mesh->number_of_shapes(); surface_normal_length = 0.02 + settings->mesh->average_triangle_size()/10; } time_counter = frame_counter = 0; actionMode=NONE; } void GLMesh::initializeGL(void) { // Set background colour and clear glClearColor(settings->red, settings->green, settings->blue, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Initialise light source initLighting(); // Other initialisations glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glLineWidth(1.0); glPointSize(1.0); glEnable(GL_POINT_SMOOTH); if (settings->f_back_faces) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); // Enable Smooth Shading //glShadeModel(GL_SMOOTH); // Really Nice Perspective Calculations //glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Initialise the modelview transformation glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Texture stuff initTexture(); } void GLMesh::paintGL() { static struct timeval start, end; gettimeofday(&start, NULL); // Clear the current display glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // save matrix glPushMatrix(); // Change the modelview transformation glMultMatrixf((GLfloat*) settings->tb_transform); // Change the projection transformation glMatrixMode(GL_PROJECTION); glLoadIdentity(); // for object picking if (settings->pick_x > 0 && settings->pick_y > 0) { int viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); gluPickMatrix(settings->pick_x, viewport[3] + 2*viewport[1] - settings->pick_y, settings->pick_size_x, settings->pick_size_y, viewport); } glOrtho(-2.1 - settings->xShift - settings->zShift, 2.1 - settings->xShift + settings->zShift, -2.1 - settings->yShift - settings->zShift, 2.1 - settings->yShift + settings->zShift, -2.1 - settings->zShift - settings->clipping, 2.1); glMatrixMode(GL_MODELVIEW); // display main object (typically a mesh) switch (settings->mesh_displayMode) { case VERTICES: setMaterialColor(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, CYAN); displayShapePoints(); break; case EDGES: setMaterialColor(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, CYAN*4+3); //setMaterialColor(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, BLUE); displayEdges(settings->mesh->get_edges()); break; case SOLID: case TEXTURE: // Set polygon mode glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); displayMesh(); break; case FRONTLINES: displayMeshWithStencil(); break; case WIRE: // Set polygon mode glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); displayMesh(); break; case FEATURES: displayFeatures(); break; // case FRONTLINES2: // draw the solid // glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // display_mesh(); // draw the the wireframe on top of the solid // glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // glDepthFunc(GL_LEQUAL); // display_mesh(); // glDepthFunc(GL_LESS); // break; } checkForTraps("Draw Polygons", 1); // Draw features like points and edges additionally if (settings->f_features && settings->mesh_displayMode != FEATURES) displayFeatures(); checkForTraps("Draw Features", 1); // Draw the surface normals if (settings->f_normals) displayNormals(); checkForTraps("Draw Surface Normals",1); displayPickObjects(); glPopMatrix(); glFlush(); gettimeofday(&end, NULL); time_counter += (end.tv_sec - start.tv_sec)*1000000 + end.tv_usec - start.tv_usec; frame_counter++; } void GLMesh::resizeGL(int width, int height) { int max = width > height ? width : height; if (settings->f_aspect_ratio) glViewport((width-max)/2, (height-max)/2, max, max); else glViewport(0, 0, width, height); }