summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamuel Rødal <sroedal@trolltech.com>2008-06-20 16:20:18 +0200
committerSamuel Rødal <sroedal@trolltech.com>2008-06-20 16:20:18 +0200
commit8b44c875279603745a02ac3b80f83806a4bfbfc6 (patch)
treea1eab71795f67915ce79cf9533e887d25778f7f7
parentf56efe38269f8ed84e7a37e45d4947818889f551 (diff)
Added model loading and rendering.
-rw-r--r--main.cpp164
-rw-r--r--model.cpp135
-rw-r--r--model.h63
-rw-r--r--openglexample.pro2
4 files changed, 342 insertions, 22 deletions
diff --git a/main.cpp b/main.cpp
index 0a7f30f..5110a1e 100644
--- a/main.cpp
+++ b/main.cpp
@@ -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);
+}
diff --git a/model.h b/model.h
new file mode 100644
index 0000000..cde14f8
--- /dev/null
+++ b/model.h
@@ -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