diff options
| author | Samuel Rødal <sroedal@trolltech.com> | 2008-06-20 16:20:18 +0200 |
|---|---|---|
| committer | Samuel Rødal <sroedal@trolltech.com> | 2008-06-20 16:20:18 +0200 |
| commit | 8b44c875279603745a02ac3b80f83806a4bfbfc6 (patch) | |
| tree | a1eab71795f67915ce79cf9533e887d25778f7f7 | |
| parent | f56efe38269f8ed84e7a37e45d4947818889f551 (diff) | |
Added model loading and rendering.
| -rw-r--r-- | main.cpp | 164 | ||||
| -rw-r--r-- | model.cpp | 135 | ||||
| -rw-r--r-- | model.h | 63 | ||||
| -rw-r--r-- | openglexample.pro | 2 |
4 files changed, 342 insertions, 22 deletions
@@ -1,51 +1,173 @@ #include <QtGui> #include <QtOpenGL> -#include <QtDebug> -class Widget : public QGraphicsView +#include <QFuture> +#include <QFutureWatcher> + +#include "model.h" + +class GraphicsScene : public QGraphicsScene { + Q_OBJECT + public: - Widget(); + GraphicsScene(); + + void drawBackground(QPainter *painter, const QRectF &rect); + +public slots: + void setModel(Model *model); + void enableWireframe(bool enabled); + +private: + bool m_wireframeEnabled; - void paintEvent(QPaintEvent *event); + Model *m_model; }; -Widget::Widget() +class Controls : public QGroupBox { - setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers))); + Q_OBJECT - QGraphicsScene *scene = new QGraphicsScene(this); +public: + Controls(GraphicsScene *scene); + +private slots: + void loadModel(const QString &model); + void modelLoaded(); + +private: + GraphicsScene *m_scene; + QFutureWatcher<Model *> m_modelLoader; + QComboBox *m_models; +}; + +Controls::Controls(GraphicsScene *scene) + : m_scene(scene) + , m_models(new QComboBox) +{ + QVBoxLayout *layout = new QVBoxLayout(this); - QComboBox *c = new QComboBox; - c->addItem("Foo"); - c->addItem("Bar"); + 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())); - scene->addWidget(c); + if (m_models->count() > 0) + loadModel(m_models->currentText()); - setScene(scene); -// setBackgroundBrush(Qt::NoBrush); + layout->addWidget(m_models); - viewport()->setAutoFillBackground(false); + QCheckBox *wireframe = new QCheckBox("Wireframe"); + wireframe->setChecked(true); + connect(wireframe, SIGNAL(toggled(bool)), m_scene, SLOT(enableWireframe(bool))); - resize(800, 600); + layout->addWidget(wireframe); } -void Widget::paintEvent(QPaintEvent *event) +Model *loadModel(const QString &filename) { - static_cast<QGLWidget *>(viewport())->makeCurrent(); + 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(); +} + +GraphicsScene::GraphicsScene() + : m_wireframeEnabled(true) + , m_model(0) +{ + Controls *controls = new Controls(this); + controls->setWindowOpacity(0.8); + + addWidget(controls)->translate(10, 10); + setSceneRect(QRect(0, 0, 800, 600)); +} - glClearColor(1, 0, 1, 0); +void GraphicsScene::drawBackground(QPainter *painter, const QRectF &) +{ + painter->save(); + + glClearColor(1, 1, 1, 1); glClear(GL_COLOR_BUFFER_BIT); - QGraphicsView::paintEvent(event); + bool useMultisample = static_cast<QGLWidget *>(painter->device())->format().sampleBuffers(); + + if (m_model) { + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(-1, 1, 1, -1, -10, 10); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + if (useMultisample) + glEnable(GL_MULTISAMPLE); + + static float rot = 0; + glRotatef(rot, 0, 1, 0); + rot += 0.1; + + glColor3f(0, 0, 0); + m_model->render(m_wireframeEnabled); + + if (useMultisample) + glDisable(GL_MULTISAMPLE); + + glPopMatrix(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + } + + painter->restore(); + + update(); +} + +void GraphicsScene::setModel(Model *model) +{ + delete m_model; + m_model = model; + update(); +} + +void GraphicsScene::enableWireframe(bool enabled) +{ + m_wireframeEnabled = enabled; } int main(int argc, char **argv) { QApplication app(argc, argv); - Widget widget; - widget.show(); + QGraphicsView view; + view.setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers))); + + view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + view.setTransformationAnchor(QGraphicsView::NoAnchor); + view.setViewportUpdateMode(QGraphicsView::FullViewportUpdate); + + view.setScene(new GraphicsScene); + view.show(); return app.exec(); } + +#include "main.moc" diff --git a/model.cpp b/model.cpp new file mode 100644 index 0000000..20c91ba --- /dev/null +++ b/model.cpp @@ -0,0 +1,135 @@ +#include "model.h" + +#include <QFile> +#include <QSet> +#include <QTextStream> +#include <QVarLengthArray> + +#include "GL/gl.h" + +static Point3d readPoint(QTextStream &ts) +{ + Point3d p; + ts >> p.x >> p.y >> p.z; + return p; +} + +struct Edge +{ + int pointA; + int pointB; +}; + +uint qHash(const Edge &edge) +{ + return qHash(edge.pointA) ^ qHash(edge.pointB); +} + +bool operator==(const Edge &a, const Edge &b) +{ + return (a.pointA == b.pointA && a.pointB == b.pointB) + || (a.pointA == b.pointB && a.pointB == b.pointA); +} + +Model::Model(const QString &filename) +{ + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) + return; + + QSet<Edge> edges; + + Point3d min( 1e9, 1e9, 1e9); + Point3d max(-1e9,-1e9,-1e9); + + QVector<Point3d> pointData; + QVector<Point3d> normalData; + + QVector<int> pointIndices; + QVector<int> normalIndices; + + QTextStream in(&file); + while (!in.atEnd()) { + QString input = in.readLine(); + + if (input.isEmpty() || input[0] == '#') + continue; + + QTextStream ts(&input); + QString id; + ts >> id; + + if (id == "v") { + Point3d p = readPoint(ts); + p.y = -p.y; + pointData << p; + + min.x = qMin(min.x, p.x); + min.y = qMin(min.y, p.y); + min.z = qMin(min.z, p.z); + + max.x = qMax(max.x, p.x); + max.y = qMax(max.y, p.y); + max.z = qMax(max.z, p.z); + } else if (id == "f" || id == "fo") { + QVarLengthArray<int, 4> p; + + while (!ts.atEnd()) { + QString vertex; + ts >> vertex; + + int index; + QTextStream vertexStream(&vertex); + vertexStream >> index; + if (vertexStream.status() == QTextStream::Ok) + p.append(index - 1); + } + + for (int i = 0; i < p.size(); ++i) { + Edge edge = { p[i], p[(i + 1) % p.size()] }; + edges << edge; + } + + for (int i = 0; i < 3; ++i) + pointIndices << p[i]; + + if (p.size() == 4) + for (int i = 0; i < 3; ++i) + pointIndices << p[(i + 2) % 4]; + } + } + + Point3d bounds = max - min; + qreal scale = 1 / qMax(bounds.x, qMax(bounds.y, bounds.z)); + + for (int i = 0; i < pointData.size(); ++i) { + Point3d &p = pointData[i]; + + p.x -= min.x + bounds.x * 0.5; + p.y -= min.y + bounds.y * 0.5; + p.z -= min.z + bounds.z * 0.5; + + p.x *= scale; + p.y *= scale; + p.z *= scale; + } + + for (int i = 0; i < pointIndices.size(); ++i) + m_points << pointData.at(pointIndices.at(i)); + + foreach(const Edge &edge, QVector<Edge>::fromList(edges.toList())) + m_edges << pointData.at(edge.pointA) << pointData.at(edge.pointB); +} + +void Model::render(bool wireframe) const +{ + glEnableClientState(GL_VERTEX_ARRAY); + if (wireframe) { + glVertexPointer(3, GL_FLOAT, 0, m_edges.data()); + glDrawArrays(GL_LINES, 0, m_edges.size()); + } else { + glVertexPointer(3, GL_FLOAT, 0, m_points.data()); + glDrawArrays(GL_TRIANGLES, 0, m_points.size()); + } + glDisableClientState(GL_VERTEX_ARRAY); +} @@ -0,0 +1,63 @@ +#ifndef MODEL_H +#define MODEL_H + +#include <QVector> + +#include <math.h> + +struct Point3d +{ + float x, y, z; + + Point3d() + : x(0) + , y(0) + , z(0) + { + } + + Point3d(float x_, float y_, float z_) + : x(x_) + , y(y_) + , z(z_) + { + } + + Point3d operator-(const Point3d &p) const + { + return Point3d(x - p.x, y - p.y, z - p.z); + } + + Point3d normalize() const + { + float r = 1. / sqrt(x * x + y * y + z * z); + return Point3d(x * r, y * r, z * r); + } +}; + +inline float dot(const Point3d &a, const Point3d &b) +{ + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +inline Point3d cross(const Point3d &a, const Point3d &b) +{ + return Point3d(a.y * b.z - a.z * b.y, + a.z * b.x - a.x * b.z, + a.x * b.y - a.y * b.x); +} + +class Model +{ +public: + Model(const QString &filename); + + void render(bool wireframe = false) const; + +private: + QVector<Point3d> m_points; + QVector<Point3d> m_normals; + QVector<Point3d> m_edges; +}; + +#endif diff --git a/openglexample.pro b/openglexample.pro index d23cac7..beff809 100644 --- a/openglexample.pro +++ b/openglexample.pro @@ -8,6 +8,6 @@ DEPENDPATH += . INCLUDEPATH += . # Input -SOURCES += main.cpp +SOURCES += main.cpp model.cpp QT += opengl |
