// // File: mview.cc // // (C) 2000-2006 Helmut Cantzler // // Licensed under the terms of the Lesser General Public License. // #include #include #include "mesh.h" #include "mview.h" #include "viewpointdialog.h" #include "coordinates.h" #ifdef TEXT_MAP #include "texture_image.h" #endif #include "p_mesh.h" #include "gts_mesh.h" #include "geomview_mesh.h" #include "vrml1_mesh.h" #include "vrml2_mesh.h" #include "list_mesh.h" #include "feature_mesh.h" #include "ply_mesh.h" #include "obj_mesh.h" #include "vtk_mesh.h" #include "x3d_mesh.h" #include "icons/icon_fileopen.xpm" #include "icons/icon_texture.xpm" #include "icons/icon_solid.xpm" #include "icons/icon_wire.xpm" #include "icons/icon_point.xpm" #include "icons/icon_front.xpm" #include "icons/icon_feature.xpm" #include "icons/icon_coordinates.xpm" #ifdef TEXT_MAP #include "icons/icon_mapping.xpm" #endif QProgressDialog *pd; int updateProgress(int pos) { static int q=0; if (q % 100 == 0) pd->setValue(pos); q++; return pd->wasCanceled(); } void setTotal(int size) { pd->setMaximum(size); } MView::MView(int argc, char **argv) { //// Set OpenGL options //// QGLFormat f; f.setDoubleBuffer(TRUE); f.setDepth(TRUE); f.setStencil(TRUE); QGLFormat::setDefaultFormat(f); //// Init mesh display options //// settings = new GLMesh_Settings(); //// Open 3D model file, if specified in command line argument //// if (argc >= 2) { name = argv[1]; loadModelFile(argv[1], &settings->mesh); if (settings->mesh != NULL) { // Map vertices and triangle centroids to mesh centroid settings->mesh->move_to_centre(); settings->mesh->scale_into_normal_sphere(); } } if (argc >= 3) loadModelFile(argv[2], &settings->features); //// Set-up main window setWindowTitle("Mesh Viewer"); resize(580, 620); //// Set-up main widgets //// QWidget *hbox = new QWidget; QHBoxLayout *layout = new QHBoxLayout; layout->setMargin(10); layout->setSpacing(10); hbox->setLayout(layout); glmesh = new GLMesh(hbox, settings); layout->addWidget(glmesh); clipping = new QSlider(hbox); clipping->setMinimum(-100); clipping->setMaximum(100); clipping->setPageStep(5); clipping->setValue(0); clipping->setToolTip("Clipping"); connect(clipping, SIGNAL(valueChanged(int)), glmesh, SLOT(setClipping(int))); layout->addWidget(clipping); setCentralWidget(hbox); //// Set-up menus, toolbar and statusbar //// createActions(); createMenus(); createToolBars(); createStatusBar(); setStatus(); // start timer for updating label_fps every 3 seconds startTimer(3000); // set default display options setDefaultConfig(); /* // Display OpenGL information printf("depth: %d\n", glmesh->format().depth() ? 1 : 0); printf("rgba: %d\n", glmesh->format().rgba() ? 1 : 0); printf("alpha: %d\n", glmesh->format().alpha() ? 1 : 0); printf("accum: %d\n", glmesh->format().accum() ? 1 : 0); printf("stencil: %d\n", glmesh->format().stencil() ? 1 : 0); printf("stereo: %d\n", glmesh->format().stereo() ? 1 : 0); printf("directRendering: %d\n", glmesh->format().directRendering() ? 1 : 0); printf("hasOverlay: %d\n", glmesh->format().hasOverlay() ? 1 : 0); */ } MView::~MView() { settings->clear(); delete settings; } void MView::createActions() { openAct = new QAction(QIcon(icon_fileopen), tr("&Open model"), this); openAct->setShortcut(tr("O")); connect(openAct, SIGNAL(triggered()), this, SLOT(loadModel()) ); openFeatAct = new QAction(tr("Open &features"), this); connect(openFeatAct, SIGNAL(triggered()), this, SLOT(loadFeatures()) ); screenshotActs = new QActionGroup(this); QList formats = QImageWriter::supportedImageFormats(); for (int i = 0; i < formats.size(); ++i) { screenshotActs->addAction(formats.at(i).constData()); } connect(screenshotActs, SIGNAL(triggered(QAction*)), this, SLOT(takeShot(QAction*)) ); saveActs = new QActionGroup(this); saveActs->addAction("P Mesh"); saveActs->addAction("GTS"); saveActs->addAction("Geomview"); saveActs->addAction("VRML 1"); saveActs->addAction("VRML 2"); saveActs->addAction("PLY"); saveActs->addAction("OBJ"); saveActs->addAction("VTK"); saveActs->addAction("X3D"); connect(saveActs, SIGNAL(triggered(QAction*)), this, SLOT(saveAs(QAction*)) ); exitAct = new QAction(tr("&Quit"), this); exitAct->setShortcut(Qt::Key_Escape); connect(exitAct, SIGNAL(triggered()), qApp, SLOT(closeAllWindows())); negNormAct = new QAction(tr("&Negate normals"), this); connect(negNormAct, SIGNAL(triggered()), this, SLOT(negateNormals()) ); #ifdef TEXT_MAP textAct = new QAction(QIcon(icon_mapping), tr("&Calculate texture map"), this); textAct->setCheckable(true); connect(textAct, SIGNAL(triggered(bool)), this, SLOT(toggleTextMap(bool)) ); #endif dTextAct = new QAction(QIcon(icon_texture), tr("&Texture"), this); dTextAct->setCheckable(true); dTextAct->setShortcut(tr("1")); dSolidAct = new QAction(QIcon(icon_solid), tr("S&olid"), this); dSolidAct->setCheckable(true); dSolidAct->setShortcut(tr("2")); dFrontAct = new QAction(QIcon(icon_front), tr("F&rontlines"), this); dFrontAct->setCheckable(true); dFrontAct->setShortcut(tr("3")); dWireAct = new QAction(QIcon(icon_wire), tr("&Wireframe"), this); dWireAct->setCheckable(true); dWireAct->setShortcut(tr("4")); dPointAct = new QAction(QIcon(icon_point), tr("Po&ints"), this); dPointAct->setCheckable(true); dPointAct->setShortcut(tr("5")); dFeatAct = new QAction(QIcon(icon_feature), tr("F&eature"), this); dFeatAct->setCheckable(true); dFeatAct->setShortcut(tr("6")); displayActs = new QActionGroup(this); displayActs->addAction(dTextAct); displayActs->addAction(dSolidAct); displayActs->addAction(dFrontAct); displayActs->addAction(dWireAct); displayActs->addAction(dPointAct); displayActs->addAction(dFeatAct); connect(displayActs, SIGNAL(triggered(QAction*)), glmesh, SLOT(setDisplayMode(QAction*)) ); pickAct = new QAction(QIcon(icon_coordinates), tr("&Pick objects"), this); pickAct->setCheckable(true); pickAct->setShortcut(tr("P")); connect(pickAct, SIGNAL(triggered(bool)), this, SLOT(togglePick(bool)) ); lightAct = new QAction(tr("Lighting"), this); lightAct->setCheckable(true); lightAct->setShortcut(tr("L")); connect(lightAct, SIGNAL(triggered(bool)), glmesh, SLOT(setLight(bool)) ); shapeAct = new QAction(tr("Shape colours"), this); shapeAct->setCheckable(true); shapeAct->setShortcut(tr("S")); connect(shapeAct, SIGNAL(triggered(bool)), glmesh, SLOT(setShapeColors(bool)) ); normAct = new QAction(tr("Surface normals"), this); normAct->setCheckable(true); normAct->setShortcut(tr("N")); connect(normAct, SIGNAL(triggered(bool)), glmesh, SLOT(setNormals(bool)) ); cutAct = new QAction(tr("Cut back faces"), this); cutAct->setCheckable(true); cutAct->setShortcut(tr("C")); connect(cutAct, SIGNAL(triggered(bool)), glmesh, SLOT(setBackFaces(bool)) ); filtAct = new QAction(tr("Bilinear filtering"), this); filtAct->setCheckable(true); filtAct->setShortcut(tr("B")); connect(filtAct, SIGNAL(triggered(bool)), glmesh, SLOT(setTextureFiltering(bool)) ); featAct = new QAction(tr("Features"), this); featAct->setCheckable(true); featAct->setShortcut(tr("F")); connect(featAct, SIGNAL(triggered(bool)), glmesh, SLOT(setFeatures(bool)) ); keepAct = new QAction(tr("Keep aspect ratio"), this); keepAct->setCheckable(true); keepAct->setShortcut(tr("K")); connect(keepAct, SIGNAL(triggered(bool)), glmesh, SLOT(setAspectRatio(bool)) ); backAct = new QAction(tr("Background colour..."), this); backAct->setShortcut(tr("G")); connect(backAct, SIGNAL(triggered()), this, SLOT(setBackgroundColor()) ); vSetAct = new QAction(tr("Set"), this); connect(vSetAct, SIGNAL(triggered()), this, SLOT(setView()) ); vResAct = new QAction(tr("Reset"), this); connect(vResAct, SIGNAL(triggered()), this, SLOT(resetView()) ); vSaveAct = new QAction(tr("Save"), this); connect(vSaveAct, SIGNAL(triggered()), this, SLOT(saveView()) ); vCleaAct = new QAction(tr("Clear"), this); connect(vCleaAct, SIGNAL(triggered()), this, SLOT(clearViews()) ); viewActs = new QActionGroup(this); connect(viewActs, SIGNAL(triggered(QAction*)), this, SLOT(loadView(QAction*)) ); abouAct = new QAction(tr("&About"), this); connect(abouAct, SIGNAL(triggered()), this, SLOT(about())); abqtAct = new QAction(tr("About &Qt"), this); connect(abqtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt())); } void MView::createMenus() { QMenu *saveMenu = new QMenu(tr("&Save as")); saveMenu->addActions(saveActs->actions()); QMenu *screenshotMenu = new QMenu(tr("&Take screenshot")); screenshotMenu->addActions(screenshotActs->actions()); QMenu *fileMenu = menuBar()->addMenu(tr("&File")); fileMenu->addAction(openAct); fileMenu->addAction(openFeatAct); fileMenu->addSeparator(); fileMenu->addMenu(saveMenu); fileMenu->addSeparator(); fileMenu->addMenu(screenshotMenu); fileMenu->addSeparator(); fileMenu->addAction(exitAct); QMenu *editMenu = menuBar()->addMenu(tr("&Edit")); editMenu->addAction(negNormAct); #ifdef TEXT_MAP editMenu->addAction(textAct); #endif QMenu *displayMenu = menuBar()->addMenu(tr("&Display")); displayMenu->addActions(displayActs->actions()); displayMenu->addSeparator(); displayMenu->addAction(pickAct); displayMenu->addSeparator(); displayMenu->addAction(lightAct); displayMenu->addAction(shapeAct); displayMenu->addAction(normAct); displayMenu->addAction(cutAct); displayMenu->addAction(filtAct); displayMenu->addAction(featAct); displayMenu->addAction(keepAct); displayMenu->addSeparator(); displayMenu->addAction(backAct); viewpointMenu = menuBar()->addMenu(tr("&Viewpoint")); viewpointMenu->addAction(vSetAct); viewpointMenu->addAction(vResAct); viewpointMenu->addSeparator(); viewpointMenu->addAction(vSaveAct); viewpointMenu->addAction(vCleaAct); viewpointMenu->addSeparator(); QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(abouAct); helpMenu->addAction(abqtAct); } void MView::createToolBars() { QToolBar *tools = addToolBar(tr("Mesh Viewer Toolbar")); tools->addAction(openAct); tools->addSeparator(); tools->addActions(displayActs->actions()); tools->addSeparator(); tools->addAction(pickAct); #ifdef TEXT_MAP tools->addAction(textAct); #endif tools->addSeparator(); QSpinBox *lighting = new QSpinBox(tools); lighting->setToolTip("Light intensity"); lighting->setSingleStep(1); lighting->setMinimum(1); lighting->setMaximum(20); lighting->setValue(10); connect(lighting, SIGNAL(valueChanged(int)), glmesh, SLOT(setLightIntensity(int))); tools->addWidget(lighting); QSpinBox *line_width = new QSpinBox(tools); line_width->setToolTip("Line width"); line_width->setSingleStep(1); line_width->setMinimum(1); line_width->setMaximum(20); line_width->setValue(1); connect(line_width, SIGNAL(valueChanged(int)), glmesh, SLOT(setLineSize(int))); tools->addWidget(line_width); QSpinBox *point_size = new QSpinBox(tools); point_size->setToolTip("Point size"); point_size->setSingleStep(1); point_size->setMinimum(1); point_size->setMaximum(20); point_size->setValue(1); connect(point_size, SIGNAL(valueChanged(int)), glmesh, SLOT(setPointSize(int))); tools->addWidget(point_size); } void MView::createStatusBar() { label_mesh = new QLabel(statusBar()); statusBar()->addWidget(label_mesh, 1); label_features = new QLabel(statusBar()); statusBar()->addWidget(label_features, 1); label_fps = new QLabel(" FPS: 0.0 ", statusBar()); statusBar()->addPermanentWidget(label_fps, 0); } void MView::loadModel() { QString fn = QFileDialog::getOpenFileName(this, "Select a model file"); if ( !fn.isEmpty() ) { settings->clear(); name = fn; loadModelFile(fn.toAscii().data(), &settings->mesh); if (settings->mesh != NULL) { // Map vertices and triangle centroids to mesh centroid settings->mesh->move_to_centre(); settings->mesh->scale_into_normal_sphere(); } glmesh->newMesh(); setDefaultConfig(); setStatus(); } } void MView::loadFeatures() { QString fn = QFileDialog::getOpenFileName(this, "Select a model file"); if ( !fn.isEmpty() ) { settings->clearFeatures(); loadModelFile(fn.toAscii().data(), &settings->features); glmesh->newFeatures(); setStatus(); } } int MView::loadModelFile(const char *fileName, Mesh **mesh) { FILE *f; if ((f = fopen(fileName, "r")) == NULL) return 1; switch (Mesh::type(f)) { case LIST_MESH: *mesh = new List_Mesh(); break; case P_MESH: *mesh = new P_Mesh(); break; case OBJ_MESH: *mesh = new OBJ_Mesh(); break; case VTK_MESH: *mesh = new VTK_Mesh(); break; case GTS_MESH: *mesh = new GTS_Mesh(); break; case GEOMVIEW_MESH: *mesh = new Geomview_Mesh(); break; case PLY_MESH: *mesh = new PLY_Mesh(); break; case VRML1_MESH: *mesh = new Vrml1_Mesh(); break; case VRML2_MESH: *mesh = new Vrml2_Mesh(); break; case FEATURES_MESH: *mesh = new Feature_Mesh(); break; default: fclose(f); return 2; } pd=new QProgressDialog("Loading...", "Cancel", 0, 0, this); pd->setWindowTitle("Please wait"); if ((*mesh)->read(f, (int (*)(int)) updateProgress, (void (*)(int)) setTotal) != 0) (*mesh)->clear(); delete pd; fclose(f); return 0; } void MView::saveAs(QAction *a) { QString format = a->text(); QString filename = QFileDialog::getSaveFileName( this, "Choose a filename to save under"); if (!filename.isEmpty()) { Mesh *m = NULL; FILE *f; if ((f = fopen(filename.toAscii().data(), "w")) == NULL) return; if (format == "P Mesh") m = new P_Mesh(); if (format == "GTS") m = new GTS_Mesh(); if (format == "Geomview") m = new Geomview_Mesh(); if (format == "VRML 1") m = new Vrml1_Mesh(); if (format == "VRML 2") m = new Vrml2_Mesh(); if (format == "PLY") m = new PLY_Mesh(); if (format == "OBJ") m = new OBJ_Mesh(); if (format == "VTK") m = new VTK_Mesh(); if (format == "X3D") m = new X3D_Mesh(); if (m != NULL) { m->set_mesh(settings->mesh); m->write(f); delete m; } fclose(f); } } void MView::takeShot(QAction *a) { QString format = a->text(); QString filename = QFileDialog::getSaveFileName( this, "Choose a filename to save under", "", "*." + format); if (!filename.isEmpty()) { QImage image = glmesh->renderPixmap().toImage(); if (image.save(filename, format.toAscii().data()) == FALSE) QMessageBox::warning(this, "Save screenshot", "Could not save image!"); } } void MView::negateNormals() { if (settings->mesh != NULL) { settings->mesh->negate_surface_normals(); glmesh->updateGL(); } } void MView::closeText(void) { textAct->trigger(); } void MView::toggleTextMap(bool text_state) { #ifdef TEXT_MAP static TextureImageWindow *texture_image; bool pick_state = pickAct->isChecked(); if (text_state) { QDesktopWidget desktop; glmesh->setKeepPick(1); texture_image = new TextureImageWindow(this, glmesh, settings->mesh); if ( frameGeometry().topRight().x() + texture_image->width() <= desktop.screen( desktop.primaryScreen() )->width() ) texture_image->move( frameGeometry().topRight().x() + 1, frameGeometry().topRight().y() ); else if ( frameGeometry().topLeft().x() - texture_image->width() > 0 ) texture_image->move( frameGeometry().topLeft().x() - texture_image->frameGeometry().width() - 8, frameGeometry().topLeft().y() ); texture_image->show(); } else { glmesh->setKeepPick(0); delete texture_image; } glmesh->setPick(pick_state || text_state); #endif } void MView::displayTexture(void) { dTextAct->trigger(); } void MView::closePick(void) { pickAct->trigger(); } void MView::togglePick(bool pick_state) { static CoordinatesWindow *coordinates; #ifdef TEXT_MAP bool text_state = textAct->isChecked(); #else bool text_state = FALSE; #endif if (pick_state) { QDesktopWidget desktop; coordinates = new CoordinatesWindow(this, settings->mesh); if ( frameGeometry().topRight().x() + coordinates->width() <= desktop.screen( desktop.primaryScreen() )->width() ) coordinates->move( frameGeometry().topRight().x() + 1, frameGeometry().topRight().y() ); else if ( frameGeometry().topLeft().x() - coordinates->width() >= 0 ) coordinates->move( frameGeometry().topLeft().x() - coordinates->frameGeometry().width() - 8, frameGeometry().topLeft().y() ); coordinates->show(); } else delete coordinates; glmesh->setPick(pick_state || text_state, pick_state ? coordinates : NULL); } void MView::setBackgroundColor() { QColor c = QColorDialog::getColor(); glmesh->setBackgroundColor(c); } void MView::resetView() { glmesh->resetViewpoint(); clipping->setValue(0); } void MView::setView() { GLMesh_Settings s = *settings; ViewpointDialog vpd(this, &s); if (vpd.exec() == QDialog::Accepted) { glmesh->setViewpoint(s); clipping->setValue( (int) (-s.clipping*25) ); } } void MView::clearViews() { QMap::Iterator it; for(it = viewpoints.begin(); it != viewpoints.end(); ++it ) { viewActs->removeAction(it.key()); viewpointMenu->removeAction(it.key()); delete it.key(); } viewpoints.clear(); } void MView::saveView() { QString name( tr("View %1").arg(viewpoints.count()+1) ); QAction *a = new QAction(name, this); viewActs->addAction(a); viewpointMenu->addAction(a); viewpoints.insert(a, *settings); } void MView::loadView(QAction *a) { glmesh->setViewpoint( viewpoints[a] ); clipping->setValue( (int) (-viewpoints[a].clipping*25) ); } void MView::about() { QMessageBox::about(this, "Mesh Viewer 0.3.1", "\nCopyright (c) 2001-2006 Helmut Cantzler < cantzler at gmx dot net >\n\n" "This program can be freely used and distributed according\n" "to the terms of the GNU Lesser General Public License.\n\n" "For more information visit the Mesh Viewer web site at:\n" "http://mview.sourceforge.net/\n"); } void MView::setDefaultConfig() { setOption(lightAct, true); setOption(shapeAct, false); setOption(normAct, false); setOption(cutAct, false); setOption(filtAct, false); setOption(featAct, false); setOption(keepAct, true); setOption(dSolidAct, true); } void MView::setOption(QAction *a, bool b) { a->setChecked(!b); a->trigger(); } void MView::setStatus() { QString s; if (!name.isEmpty()) { // remove directory path of the filename if ( name.contains('/') ) name = name.section('/', -1); else if ( name.contains('\\') ) name = name.section('\\', -1); s = ""; if (settings->mesh != NULL) { int ver = settings->mesh->number_of_vertices(); int tri = settings->mesh->number_of_triangles(); s = name + QString(": %1 vertices & %2 triangles ").arg(ver).arg(tri); } label_mesh->setText(s); s = ""; if (settings->features != NULL) { int ver = settings->features->number_of_vertices(); int edg = settings->features->number_of_edges(); if (edg > 0) s = QString(" Features: %1 edges ").arg(edg); else if (ver > 0) s = QString(" Features: %1 points ").arg(ver); } label_features->setText(s); } } void MView::timerEvent(QTimerEvent *e) { QString s; s.sprintf(" FPS: %1.1f ", glmesh->getFps()); label_fps->setText(s); }