//
// File: mview.cc
//
// (C) 2000-2008 Helmut Cantzler
//
// Licensed under the terms of the Lesser General Public License.
//
#include <QtGui>
#include <QString>
#include "mesh.h"
#include "mview.h"
#include "viewpointdialog.h"
#include "coordinates.h"
#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 "shallo_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"
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 GLMeshSettings();
//// Open 3D model file, if specified in command line argument ////
if (argc >= 2)
{
loadModelFile(argv[1], &settings->mesh);
if (settings->mesh != NULL)
{
// Map vertices and triangle centroids to mesh centroid
settings->mesh->moveToCentre();
settings->mesh->scaleIntoNormalSphere();
}
}
if (argc >= 3)
{
loadModelFile(argv[2], &settings->features);
if (settings->features != NULL)
{
settings->features->scaleAccordingToReferenceMesh(settings->mesh);
}
}
//// Set-up main window
setWindowTitle("Mesh Viewer");
resize(580, 620);
//// Set-up main widgets ////
QWidget *box = new QWidget;
QGridLayout *layout = new QGridLayout;
layout->setMargin(10);
layout->setSpacing(10);
glmesh = new GLMesh(box, settings, true, -1);
layout->addWidget(glmesh, 0, 0, 2, 4);
clipping = new QSlider(box);
clipping->setMinimum(-20);
clipping->setMaximum(60);
clipping->setValue(0);
clipping->setToolTip("Clipping");
connect(clipping, SIGNAL(valueChanged(int)), glmesh, SLOT(setClipping(int)));
layout->addWidget(clipping, 0, 4, 2, 1);
int xs[6] = { 0, 1, 1, 1, 2, 3 };
int ys[6] = { 1, 0, 1, 2, 1, 1 };
for (int i = 0; i < 6; i++)
{
glmeshCubeSides[i] = new GLMesh(box, settings, false, i);
glmeshCubeSides[i]->setFixedSize(256, 256);
layout->addWidget(glmeshCubeSides[i], ys[i] + 2, xs[i], 1, 1);
}
box->setLayout(layout);
setCentralWidget(box);
//// Set-up menus, toolbar and statusbar ////
createActions();
createMenus();
createToolBars();
createStatusBar();
setStatus();
// start timer for updating labelFPS every 3 seconds
startTimer(3000);
qWarning("here\n");
// 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);
*/
qWarning("here\n");
}
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<QByteArray> 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");
saveActs->addAction("Shallo");
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()));
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)) );
polyAct = new QAction(tr("Show polygons"), this);
polyAct->setCheckable(true);
polyAct->setShortcut(tr("Y"));
connect(polyAct, SIGNAL(triggered(bool)),
glmesh, SLOT(setPolygons(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)) );
boundingAct = new QAction(tr("Bounding Box"), this);
boundingAct->setCheckable(true);
boundingAct->setShortcut(tr("B"));
connect(boundingAct, SIGNAL(triggered(bool)),
glmesh, SLOT(setBoundingBox(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("T"));
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 *displayMenu = menuBar()->addMenu(tr("&Display"));
displayMenu->addActions(displayActs->actions());
displayMenu->addSeparator();
displayMenu->addAction(pickAct);
displayMenu->addSeparator();
displayMenu->addAction(lightAct);
displayMenu->addAction(polyAct);
displayMenu->addAction(shapeAct);
displayMenu->addAction(normAct);
displayMenu->addAction(boundingAct);
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);
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 *lineWidth = new QSpinBox(tools);
lineWidth->setToolTip("Line width");
lineWidth->setSingleStep(1);
lineWidth->setMinimum(1);
lineWidth->setMaximum(20);
lineWidth->setValue(1);
connect(lineWidth, SIGNAL(valueChanged(int)),
glmesh, SLOT(setLineSize(int)));
tools->addWidget(lineWidth);
QSpinBox *pointSize = new QSpinBox(tools);
pointSize->setToolTip("Point size");
pointSize->setSingleStep(1);
pointSize->setMinimum(1);
pointSize->setMaximum(20);
pointSize->setValue(1);
connect(pointSize, SIGNAL(valueChanged(int)),
glmesh, SLOT(setPointSize(int)));
tools->addWidget(pointSize);
}
void MView::createStatusBar()
{
labelMesh = new QLabel(statusBar());
statusBar()->addWidget(labelMesh, 1);
labelFeatures = new QLabel(statusBar());
statusBar()->addWidget(labelFeatures, 1);
labelFPS = new QLabel(" FPS: 0.0 ", statusBar());
statusBar()->addPermanentWidget(labelFPS, 0);
}
void MView::loadModel()
{
QString fn = QFileDialog::getOpenFileName(this, "Select a model file");
if ( !fn.isEmpty() )
{
settings->clear();
loadModelFile(fn.toAscii().constData(), &settings->mesh);
if (settings->mesh != NULL)
{
// Map vertices and triangle centroids to mesh centroid
settings->mesh->moveToCentre();
settings->mesh->scaleIntoNormalSphere();
}
glmesh->newMesh();
for (int i = 0; i < 6; i++)
{
glmeshCubeSides[i]->newMesh();
}
setDefaultConfig();
setStatus();
}
}
void MView::loadFeatures()
{
if (settings->mesh != NULL)
{
QString fn = QFileDialog::getOpenFileName(this, "Select a model file");
if ( !fn.isEmpty() )
{
settings->clearFeatures();
loadModelFile(fn.toAscii().constData(), &settings->features);
if (settings->features != NULL)
{
settings->features->scaleAccordingToReferenceMesh(settings->mesh);
}
glmesh->newFeatures();
for (int i = 0; i < 6; i++)
{
glmeshCubeSides[i]->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 ListMesh();
break;
case P_MESH:
*mesh = new PMesh();
break;
case OBJ_MESH:
*mesh = new OBJMesh();
break;
case VTK_MESH:
*mesh = new VTKMesh();
break;
case GTS_MESH:
*mesh = new GTSMesh();
break;
case GEOMVIEW_MESH:
*mesh = new GeomviewMesh();
break;
case PLY_MESH:
*mesh = new PLYMesh();
break;
case VRML1_MESH:
*mesh = new Vrml1Mesh();
break;
case VRML2_MESH:
*mesh = new Vrml2Mesh();
break;
case SHALLO_MESH:
*mesh = new ShalloMesh();
break;
case FEATURES_MESH:
*mesh = new FeatureMesh();
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);
setName(fileName, *mesh);
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 PMesh();
if (format == "GTS")
m = new GTSMesh();
if (format == "Geomview")
m = new GeomviewMesh();
if (format == "VRML 1")
m = new Vrml1Mesh();
if (format == "VRML 2")
m = new Vrml2Mesh();
if (format == "PLY")
m = new PLYMesh();
if (format == "OBJ")
m = new OBJMesh();
if (format == "VTK")
m = new VTKMesh();
if (format == "X3D")
m = new X3DMesh();
if (format == "Shallo")
m = new ShalloMesh();
if (m != NULL)
{
m->setMesh(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!");
}
for (int i = 0; i < 6; i++)
{
QImage image = glmeshCubeSides[i]->renderPixmap().toImage();
QString file = filename + QString("side_%1_").arg(i);
image.save(file, format.toAscii().data());
}
}
void MView::closeText(void)
{
textAct->trigger();
}
void MView::displayTexture(void)
{
dTextAct->trigger();
}
void MView::closePick(void)
{
pickAct->trigger();
}
void MView::togglePick(bool pickState)
{
static CoordinatesWindow *coordinates;
if (pickState)
{
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(pickState, pickState ? coordinates : NULL);
}
void MView::setBackgroundColor()
{
QColor c = QColorDialog::getColor();
glmesh->setBackgroundColor(c);
}
void MView::resetView()
{
glmesh->resetViewpoint();
for (int i = 0; i < 6; i++)
{
glmeshCubeSides[i]->resetViewpoint();
}
clipping->setValue(0);
}
void MView::setView()
{
GLMeshSettings s = *settings;
ViewpointDialog vpd(this, &s);
if (vpd.exec() == QDialog::Accepted)
{
glmesh->setViewpoint(s);
for (int i = 0; i < 6; i++)
{
glmeshCubeSides[i]->setViewpoint(s);
}
clipping->setValue( (int) (-s.clipping*10) );
}
}
void MView::clearViews()
{
QMap<QAction*, GLMeshSettings>::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] );
for (int i = 0; i < 6; i++)
{
glmeshCubeSides[i]->setViewpoint(viewpoints[a]);
}
clipping->setValue( (int) (-viewpoints[a].clipping*25) );
}
void MView::about()
{
QMessageBox::about(this, "Mesh Viewer 0.3.3",
"\nCopyright (c) 2001-2010 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(polyAct, true);
setOption(shapeAct, false);
setOption(normAct, false);
setOption(boundingAct, false);
setOption(cutAct, false);
setOption(filtAct, false);
setOption(featAct, false);
setOption(keepAct, true);
setOption(dSolidAct, true);
}
void MView::setOption(QAction *a, bool b)
{
if ( a )
{
a->setChecked(!b);
a->trigger();
}
}
void MView::setStatus()
{
QString s;
if (!name.isEmpty())
{
s = "";
if (settings->mesh != NULL)
{
int ver = settings->mesh->numberOfVertices();
int tri = settings->mesh->numberOfTriangles();
s = name + QString(": %1 vertices & %2 triangles ").arg(ver).arg(tri);
}
labelMesh->setText(s);
s = "";
if (settings->features != NULL)
{
int ver = settings->features->numberOfVertices();
int edg = settings->features->numberOfEdges();
if (edg > 0)
s = QString(" Features: %1 edges ").arg(edg);
else
if (ver > 0)
s = QString(" Features: %1 points ").arg(ver);
}
labelFeatures->setText(s);
}
}
void MView::setName(QString fileName, Mesh *mesh)
{
QFileInfo fi(fileName);
name = fi.fileName();
fileName.chop(name.length());
mesh->setName(name.toAscii().constData());
mesh->setPath(fileName.toAscii().constData());
}
void MView::timerEvent(QTimerEvent *e)
{
QString s;
s.sprintf(" FPS: %1.1f ", glmesh->getFps());
labelFPS->setText(s);
}