// // File: mview.cc // // (C) 2000-2006 Helmut Cantzler // // Licensed under the terms of the Lesser General Public License. // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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->setProgress(pos); q++; return pd->wasCancelled(); } void setTotal(int size) { pd->setTotalSteps(size); } MView::MView(int argc, char **argv) : QMainWindow() { //// 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 setCaption("Mesh Viewer"); resize(580, 620); //// Set-up main widgets //// QHBox *q = new QHBox(this); q->setMargin(10); q->setSpacing(10); glmesh = new GLMesh(q, settings); // from, until, step size, start value, orientation, parent, text clipping = new QSlider(-100, 100, 10, 0, Vertical, q); QToolTip::add(clipping, "Clipping"); connect(clipping, SIGNAL(valueChanged(int)), glmesh, SLOT(setClipping(int))); setCentralWidget(q); //// 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 setMviewDefaults(); /* 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(QPixmap(icon_fileopen), tr("&Open model"), Key_O, this); connect(openAct, SIGNAL(activated()), this, SLOT(loadModel()) ); openFeatAct = new QAction(tr("Open &features"), 0, this); connect(openFeatAct, SIGNAL(activated()), this, SLOT(loadFeatures()) ); exitAct = new QAction(tr("&Quit"), Key_Escape, this); connect(exitAct, SIGNAL(activated()), qApp, SLOT( quit())); negNormAct = new QAction(tr("&Negate normals"), 0, this); connect(negNormAct, SIGNAL(activated()), this, SLOT(negateNormals()) ); #ifdef TEXT_MAP textAct = new QAction(QPixmap(icon_mapping), tr("&Calculate texture map"), 0, this); textAct->setToggleAction(true); connect(textAct, SIGNAL(toggled(bool)), this, SLOT(toggleTextureMapping(bool)) ); #endif dTextAct = new QAction(QPixmap(icon_texture), tr("&Texture"), Key_1, this); dTextAct->setToggleAction(true); dSolidAct = new QAction(QPixmap(icon_solid), tr("S&olid"), Key_2, this); dSolidAct->setToggleAction(true); dFrontAct = new QAction(QPixmap(icon_front), tr("F&rontlines"), Key_3, this); dFrontAct->setToggleAction(true); dWireAct = new QAction(QPixmap(icon_wire), tr("&Wireframe"), Key_4, this); dWireAct->setToggleAction(true); dPointAct = new QAction(QPixmap(icon_point), tr("Po&ints"), Key_5, this); dPointAct->setToggleAction(true); dFeatAct = new QAction(QPixmap(icon_feature), tr("F&eature"), Key_6, this); dFeatAct->setToggleAction(true); displayActs = new QActionGroup(this); displayActs->add(dTextAct); displayActs->add(dSolidAct); displayActs->add(dFrontAct); displayActs->add(dWireAct); displayActs->add(dPointAct); displayActs->add(dFeatAct); connect(displayActs, SIGNAL(selected(QAction*)), glmesh, SLOT(setDisplayMode(QAction*)) ); pickAct = new QAction(QPixmap(icon_coordinates), tr("&Pick objects"), Key_P, this); pickAct->setToggleAction(true); connect(pickAct, SIGNAL(toggled(bool)), this, SLOT(togglePick(bool)) ); lightAct = new QAction(tr("Lighting"), Key_L, this); lightAct->setToggleAction(true); connect(lightAct, SIGNAL(toggled(bool)), glmesh, SLOT(setLight(bool)) ); shapeAct = new QAction(tr("Shape colours"), Key_S, this); shapeAct->setToggleAction(true); connect(shapeAct, SIGNAL(toggled(bool)), glmesh, SLOT(setShapeColors(bool)) ); normAct = new QAction(tr("Surface normals"), Key_N, this); normAct->setToggleAction(true); connect(normAct, SIGNAL(toggled(bool)), glmesh, SLOT(setNormals(bool)) ); cutAct = new QAction(tr("Cut back faces"), Key_C, this); cutAct->setToggleAction(true); connect(cutAct, SIGNAL(toggled(bool)), glmesh, SLOT(setBackFaces(bool)) ); filtAct = new QAction(tr("Bilinear filtering"), Key_B, this); filtAct->setToggleAction(true); connect(filtAct, SIGNAL(toggled(bool)), glmesh, SLOT(setTextureFiltering(bool)) ); featAct = new QAction(tr("Features"), Key_F, this); featAct->setToggleAction(true); connect(featAct, SIGNAL(toggled(bool)), glmesh, SLOT(setFeatures(bool)) ); keepAct = new QAction(tr("Keep aspect ratio"), Key_K, this); keepAct->setToggleAction(true); connect(keepAct, SIGNAL(toggled(bool)), glmesh, SLOT(setAspectRatio(bool)) ); backAct = new QAction(tr("Background colour..."), Key_G, this); connect(backAct, SIGNAL(activated()), this, SLOT(setBackgroundColor()) ); vSetAct = new QAction(tr("Set"), 0, this); connect(vSetAct, SIGNAL(activated()), this, SLOT(setView()) ); vResAct = new QAction(tr("Reset"), 0, this); connect(vResAct, SIGNAL(activated()), this, SLOT(resetView()) ); vSaveAct = new QAction(tr("Save"), 0, this); connect(vSaveAct, SIGNAL(activated()), this, SLOT(saveView()) ); vCleaAct = new QAction(tr("Clear"), 0, this); connect(vCleaAct, SIGNAL(activated()), this, SLOT(clearViews()) ); abouAct = new QAction(tr("&About"), 0, this); connect(abouAct, SIGNAL(activated()), this, SLOT(about())); abqtAct = new QAction(tr("About &Qt"), 0, this); connect(abqtAct, SIGNAL(activated()), qApp, SLOT(aboutQt())); } void MView::createMenus() { QStrList format = QImage::outputFormats(); screenshot = new QPopupMenu(this); for ( const char* ff = format.first(); ff; ff = format.next()){ screenshot->insertItem(ff); } connect(screenshot, SIGNAL(activated(int)), this, SLOT(takeShot(int)) ); save = new QPopupMenu(this); save->insertItem("P Mesh"); save->insertItem("GTS"); save->insertItem("Geomview"); save->insertItem("VRML 1"); save->insertItem("VRML 2"); save->insertItem("PLY"); save->insertItem("OBJ"); save->insertItem("VTK"); save->insertItem("X3D"); connect(save, SIGNAL(activated(int)), this, SLOT(saveAs(int)) ); QPopupMenu *file = new QPopupMenu(this); menuBar()->insertItem("&File", file); openAct->addTo(file); openFeatAct->addTo(file); file->insertSeparator(); file->insertItem("&Save as", save); file->insertSeparator(); file->insertItem("&Take Screenshot", screenshot); file->insertSeparator(); exitAct->addTo(file); QPopupMenu *edit = new QPopupMenu(this); menuBar()->insertItem("&Edit", edit); negNormAct->addTo(edit); #ifdef TEXT_MAP textAct->addTo(edit); #endif QPopupMenu *modes = new QPopupMenu(this); menuBar()->insertItem("&Display", modes); dTextAct->addTo(modes); dSolidAct->addTo(modes); dFrontAct->addTo(modes); dWireAct->addTo(modes); dPointAct->addTo(modes); dFeatAct->addTo(modes); modes->insertSeparator(); pickAct->addTo(modes); modes->insertSeparator(); lightAct->addTo(modes); shapeAct->addTo(modes); normAct->addTo(modes); cutAct->addTo(modes); filtAct->addTo(modes); featAct->addTo(modes); keepAct->addTo(modes); modes->insertSeparator(); backAct->addTo(modes); viewpoint = new QPopupMenu(this); menuBar()->insertItem("&Viewpoint", viewpoint); vSetAct->addTo(viewpoint); vResAct->addTo(viewpoint); viewpoint->insertSeparator(); vSaveAct->addTo(viewpoint); vCleaAct->addTo(viewpoint); viewpoint->insertSeparator(); QPopupMenu *help = new QPopupMenu(this); menuBar()->insertItem("&Help", help); abouAct->addTo(help); abqtAct->addTo(help); } void MView::createToolBars() { QToolBar *tools = new QToolBar(this); openAct->addTo(tools); tools->addSeparator(); dTextAct->addTo(tools); dSolidAct->addTo(tools); dFrontAct->addTo(tools); dWireAct->addTo(tools); dPointAct->addTo(tools); dFeatAct->addTo(tools); tools->addSeparator(); pickAct->addTo(tools); #ifdef TEXT_MAP textAct->addTo(tools); #endif tools->addSeparator(); QSpinBox *lighting = new QSpinBox(1, 20, 1, tools); QToolTip::add(lighting, "Light intensity"); lighting->setValue(10); connect(lighting, SIGNAL(valueChanged(int)), glmesh, SLOT(setLightIntensity(int))); QSpinBox *line_width = new QSpinBox(1, 20, 1, tools); QToolTip::add(line_width, "Line width"); line_width->setValue(1); connect(line_width, SIGNAL(valueChanged(int)), glmesh, SLOT(setLineSize(int))); QSpinBox *point_size = new QSpinBox(1, 20, 1, tools); QToolTip::add(point_size, "Point size"); point_size->setValue(1); connect(point_size, SIGNAL(valueChanged(int)), glmesh, SLOT(setPointSize(int))); } void MView::createStatusBar() { label_mesh = new QLabel(statusBar()); statusBar()->addWidget(label_mesh, 1, TRUE); label_features = new QLabel(statusBar()); statusBar()->addWidget(label_features, 1, TRUE); label_fps = new QLabel(" FPS: 0.0 ", statusBar()); statusBar()->addWidget(label_fps, 0, TRUE); } void MView::displayTexture() { dTextAct->toggle(); } void MView::closePick() { pickAct->toggle(); } void MView::closeText() { textAct->toggle(); } void MView::resetView() { glmesh->resetViewpoint(); clipping->setValue(0); } void MView::setView() { GLMesh_Settings s = *settings; ViewpointDialog vpd(this, &s, "Viewpoint"); 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 ) viewpoint->removeItem(it.key()); viewpoints.clear(); } void MView::saveView() { int id; QString name; name.sprintf("View %d", viewpoints.count()+1); id = viewpoint->insertItem(name, this, SLOT(loadView(int)) ); viewpoints.insert(id, *settings); } void MView::loadView(int id) { glmesh->setViewpoint(viewpoints[id]); } void MView::togglePick(bool pick_state) { static CoordinatesWindow *coordinates; #ifdef TEXT_MAP bool text_state = textAct->isOn(); #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; if (!pick_state && !text_state) glmesh->clearSelection(); glmesh->setPick(pick_state || text_state, pick_state ? coordinates : NULL); } void MView::toggleTextureMapping(bool text_state) { #ifdef TEXT_MAP static TextureImageWindow *texture_image; bool pick_state = pickAct->isOn(); 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); glmesh->clearSelection(); delete texture_image; } glmesh->setPick(pick_state || text_state); #endif } void MView::setBackgroundColor() { QColor c = QColorDialog::getColor(white, this, "Color dialog"); glmesh->setBackgroundColor(c); glmesh->updateGL(); } void MView::negateNormals() { if (settings->mesh != NULL) { settings->mesh->negate_surface_normals(); glmesh->updateGL(); } } void MView::timerEvent(QTimerEvent *e) { QString s; s.sprintf(" FPS: %1.1f ", glmesh->getFps()); label_fps->setText(s); } void MView::takeShot(int item) { const char *format = screenshot->text(item); QString filename = QFileDialog::getSaveFileName(QString::null, QString::null, this); if (!filename.isEmpty()) { QImage image = glmesh->renderPixmap().convertToImage(); if (image.save(filename, format) == FALSE) QMessageBox::warning(this, "Save screenshot", "Could not save image!"); } } void MView::saveAs(int item) { QString name = save->text(item); QString filename = QFileDialog::getSaveFileName(QString::null, QString::null, this); if (!filename.isEmpty()) { Mesh *m = NULL; FILE *f; if ((f = fopen(filename, "w")) == NULL) return; if (name == "P Mesh") m = new P_Mesh(); if (name == "GTS") m = new GTS_Mesh(); if (name == "Geomview") m = new Geomview_Mesh(); if (name == "VRML 1") m = new Vrml1_Mesh(); if (name == "VRML 2") m = new Vrml2_Mesh(); if (name == "PLY") m = new PLY_Mesh(); if (name == "OBJ") m = new OBJ_Mesh(); if (name == "VTK") m = new VTK_Mesh(); if (name == "X3D") m = new X3D_Mesh(); if (m != NULL) { m->set_mesh(settings->mesh); m->write(f); delete m; } fclose(f); } } void MView::loadModel() { QString fn = QFileDialog::getOpenFileName( QString::null, QString::null, this); if ( !fn.isEmpty() ) { settings->clear(); name = fn; loadModelFile(fn, &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(); setMviewDefaults(); setStatus(); } } void MView::loadFeatures() { QString fn = QFileDialog::getOpenFileName( QString::null, QString::null, this); if ( !fn.isEmpty() ) { settings->clearFeatures(); loadModelFile(fn, &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, this, "progress", TRUE); pd->setCaption("Please Wait"); if ((*mesh)->read(f, (int (*)(int)) updateProgress, (void (*)(int)) setTotal) != 0) (*mesh)->clear(); delete pd; fclose(f); return 0; } void MView::setMviewDefaults() { // we need to deselect and select to trigger the signal dPointAct->setOn(true); dSolidAct->setOn(true); setOption(lightAct, true); setOption(shapeAct, false); setOption(normAct, false); setOption(cutAct, false); setOption(filtAct, false); setOption(featAct, false); setOption(keepAct, true); } void MView::setOption(QAction *a, bool b) { a->setOn(!b); a->setOn(b); } 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) { s.sprintf(": %d vertices & %d triangles ", settings->mesh->number_of_vertices(), settings->mesh->number_of_triangles()); s.insert(0, name); } 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.sprintf(" Features: %d edges ", edg); else if (ver > 0) s.sprintf(" Features: %d points ", ver); } label_features->setText(s); } } 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 MeshViewer website at:\n" "http://mview.sourceforge.net/\n"); }