diff options
Diffstat (limited to 'graphicsscene.cpp')
-rw-r--r-- | graphicsscene.cpp | 332 |
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 ¤t) +{ + 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" |