summaryrefslogtreecommitdiffstats
path: root/graphicsscene.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'graphicsscene.cpp')
-rw-r--r--graphicsscene.cpp332
1 files changed, 332 insertions, 0 deletions
diff --git a/graphicsscene.cpp b/graphicsscene.cpp
new file mode 100644
index 0000000..e26684a
--- /dev/null
+++ b/graphicsscene.cpp
@@ -0,0 +1,332 @@
+#include "graphicsscene.h"
+
+#include "model.h"
+
+#include "GL/gl.h"
+#include "GL/glu.h"
+
+#include <QtGui>
+#include <QGLWidget>
+
+class Controls : public QGroupBox
+{
+ Q_OBJECT
+
+public:
+ Controls(GraphicsScene *scene);
+
+private slots:
+ void loadModel(const QString &model);
+ void modelLoaded();
+ void setModelColor(bool showDialog = true);
+ void setBackgroundColor(bool showDialog = true);
+
+private:
+ GraphicsScene *m_scene;
+ QFutureWatcher<Model *> m_modelLoader;
+ QComboBox *m_models;
+
+ QRgb m_modelColor;
+ QRgb m_backgroundColor;
+};
+
+Controls::Controls(GraphicsScene *scene)
+ : m_scene(scene)
+ , m_models(new QComboBox)
+ , m_modelColor(qRgb(180, 100, 255))
+ , m_backgroundColor(qRgb(0, 0, 0))
+{
+ QVBoxLayout *layout = new QVBoxLayout(this);
+
+ layout->addWidget(new QLabel("Model:"));
+
+ QDir dir("models");
+ dir.setNameFilters(QStringList() << "*.obj");
+ m_models->addItems(dir.entryList());
+ connect(m_models, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(loadModel(const QString &)));
+ connect(&m_modelLoader, SIGNAL(finished()), this, SLOT(modelLoaded()));
+
+ if (m_models->count() > 0)
+ loadModel(m_models->currentText());
+ layout->addWidget(m_models);
+
+ QCheckBox *autoRotate = new QCheckBox("Auto-rotate");
+ autoRotate->setChecked(true);
+ connect(autoRotate, SIGNAL(toggled(bool)), m_scene, SLOT(enableAutoRotate(bool)));
+ layout->addWidget(autoRotate);
+
+ QCheckBox *wireframe = new QCheckBox("Render as wireframe");
+ wireframe->setChecked(true);
+ connect(wireframe, SIGNAL(toggled(bool)), m_scene, SLOT(enableWireframe(bool)));
+ layout->addWidget(wireframe);
+
+ QCheckBox *normals = new QCheckBox("Display normals vectors");
+ wireframe->setChecked(false);
+ connect(normals, SIGNAL(toggled(bool)), m_scene, SLOT(enableNormals(bool)));
+ layout->addWidget(normals);
+
+ layout->addWidget(new QLabel("Light position:"));
+ QSlider *lightPosition = new QSlider(Qt::Horizontal);
+ lightPosition->setRange(-100, 100);
+ connect(lightPosition, SIGNAL(valueChanged(int)), m_scene, SLOT(setLightPosition(int)));
+ layout->addWidget(lightPosition);
+
+ m_scene->setLightPosition(lightPosition->value());
+
+ QPushButton *colorButton = new QPushButton("Choose model color");
+ connect(colorButton, SIGNAL(pressed()), this, SLOT(setModelColor()));
+ layout->addWidget(colorButton);
+ setModelColor(false);
+
+ QPushButton *backgroundButton = new QPushButton("Choose background color");
+ connect(backgroundButton, SIGNAL(pressed()), this, SLOT(setBackgroundColor()));
+ layout->addWidget(backgroundButton);
+ setBackgroundColor(false);
+}
+
+Model *loadModel(const QString &filename)
+{
+ return new Model(QString("models/") + filename);
+}
+
+void Controls::loadModel(const QString &filename)
+{
+ m_modelLoader.setFuture(QtConcurrent::run(::loadModel, filename));
+ m_models->setEnabled(false);
+ QApplication::setOverrideCursor(Qt::BusyCursor);
+}
+
+void Controls::modelLoaded()
+{
+ m_scene->setModel(m_modelLoader.result());
+ m_models->setEnabled(true);
+ QApplication::restoreOverrideCursor();
+}
+
+void Controls::setModelColor(bool showDialog)
+{
+ if (showDialog)
+ m_modelColor = QColorDialog::getRgba(m_modelColor);
+
+ m_scene->setModelColor(m_modelColor);
+}
+
+void Controls::setBackgroundColor(bool showDialog)
+{
+ if (showDialog)
+ m_backgroundColor = QColorDialog::getRgba(m_backgroundColor);
+
+ m_scene->setBackgroundColor(m_backgroundColor);
+}
+
+GraphicsScene::GraphicsScene()
+ : m_wireframeEnabled(true)
+ , m_normalsEnabled(false)
+ , m_autoRotate(true)
+ , m_rotating(false)
+ , m_axis(0.5, 1, 0.1)
+ , m_angle(0.2)
+ , m_distance(1.5)
+ , m_model(0)
+{
+ // set identity matrix
+ for (int i = 0; i < 4; ++i)
+ for (int j = 0; j < 4; ++j)
+ m_matrix[i][j] = (i == j);
+
+ Controls *controls = new Controls(this);
+ controls->setWindowOpacity(0.8);
+
+ QGraphicsProxyWidget *item = addWidget(controls);
+ item->translate(10, 10);
+ item->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
+
+ setSceneRect(QRect(0, 0, 1024, 768));
+}
+
+void GraphicsScene::updateMatrix()
+{
+ if (!QGLContext::currentContext())
+ return;
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+ glRotatef(m_angle, m_axis.x, m_axis.y, m_axis.z);
+ glMultMatrixf(&m_matrix[0][0]);
+ glGetFloatv(GL_MODELVIEW_MATRIX, &m_matrix[0][0]);
+ glPopMatrix();
+}
+
+void GraphicsScene::drawBackground(QPainter *painter, const QRectF &)
+{
+ painter->save();
+
+ glClearColor(qRed(m_backgroundColor)/255.0f, qGreen(m_backgroundColor)/255.0f, qBlue(m_backgroundColor)/255.0f, 1);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ bool useMultisample = static_cast<QGLWidget *>(painter->device())->format().sampleBuffers();
+
+ if (m_model) {
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ gluPerspective(70, painter->device()->width() / float(painter->device()->height()), 0.01, 1000);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+ float pos[] = { m_lightPos, 5, 2, 0 };
+ glLightfv(GL_LIGHT0, GL_POSITION, pos);
+ glColor4f(qRed(m_modelColor)/255.0f, qGreen(m_modelColor)/255.0f, qBlue(m_modelColor)/255.0f, 1.0f);
+
+ if (m_autoRotate && !m_rotating)
+ updateMatrix();
+
+ glLoadIdentity();
+ glTranslatef(0, 0, -m_distance);
+ glMultMatrixf(&m_matrix[0][0]);
+
+ if (useMultisample)
+ glEnable(GL_MULTISAMPLE);
+
+ m_model->render(m_wireframeEnabled, m_normalsEnabled);
+
+ if (useMultisample)
+ glDisable(GL_MULTISAMPLE);
+
+ glPopMatrix();
+
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ }
+
+ painter->restore();
+
+ QTimer::singleShot(20, this, SLOT(update()));
+}
+
+void GraphicsScene::setModel(Model *model)
+{
+ delete m_model;
+ m_model = model;
+ update();
+}
+
+void GraphicsScene::enableAutoRotate(bool enabled)
+{
+ m_autoRotate = enabled;
+ update();
+}
+
+void GraphicsScene::enableWireframe(bool enabled)
+{
+ m_wireframeEnabled = enabled;
+}
+
+void GraphicsScene::enableNormals(bool enabled)
+{
+ m_normalsEnabled = enabled;
+}
+
+void GraphicsScene::setLightPosition(int pos)
+{
+ m_lightPos = pos * 0.05;
+ update();
+}
+
+void GraphicsScene::setModelColor(QRgb color)
+{
+ m_modelColor = color;
+ update();
+}
+
+void GraphicsScene::setBackgroundColor(QRgb color)
+{
+ m_backgroundColor = color;
+ update();
+}
+
+static Point3d spherical(const QPointF &point, qreal w, qreal h)
+{
+ qreal R = qMax(w, h);
+
+ Point3d p;
+ p.x = -(point.x() - w / 2);
+ p.y = point.y() - h / 2;
+ p.z = sqrt(R * R - p.x * p.x - p.y * p.y);
+
+ p.x /= R;
+ p.y /= R;
+ p.z /= R;
+ return p;
+}
+
+void GraphicsScene::updateRotation(const QPointF &last, const QPointF &current)
+{
+ Point3d pos = spherical(current, width(), height());
+ Point3d lastPos = spherical(last, width(), height());
+
+ m_axis.x = lastPos.y * pos.z - lastPos.z * pos.y;
+ m_axis.y = lastPos.z * pos.x - lastPos.x * pos.z;
+ m_axis.z = lastPos.x * pos.y - lastPos.y * pos.x;
+
+ qreal length = sqrt(m_axis.x * m_axis.x + m_axis.y * m_axis.y + m_axis.z * m_axis.z);
+
+ if (length == 0) {
+ m_angle = 0;
+ } else {
+ m_angle = -10 * asin(sqrt(length));
+
+ m_axis.x /= length;
+ m_axis.y /= length;
+ m_axis.z /= length;
+
+ updateMatrix();
+ update();
+ }
+}
+
+void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ QGraphicsScene::mouseMoveEvent(event);
+ if (event->isAccepted() || !m_rotating)
+ return;
+
+ updateRotation(event->lastScenePos(), event->scenePos());
+ event->accept();
+}
+
+void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ QGraphicsScene::mousePressEvent(event);
+ if (event->isAccepted())
+ return;
+
+ event->accept();
+ m_rotating = true;
+}
+
+void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ QGraphicsScene::mouseReleaseEvent(event);
+ if (event->isAccepted())
+ return;
+
+ event->accept();
+ m_rotating = false;
+}
+
+
+void GraphicsScene::wheelEvent(QGraphicsSceneWheelEvent *event)
+{
+ QGraphicsScene::wheelEvent(event);
+ if (event->isAccepted())
+ return;
+
+ m_distance *= qPow(1.2, -event->delta() / 120);
+ event->accept();
+ update();
+}
+
+#include "graphicsscene.moc"