]> git.sur5r.net Git - fstl/commitdiff
Mostly complete from the Python original
authorMatt Keeter <matt.j.keeter@gmail.com>
Tue, 4 Mar 2014 02:14:16 +0000 (21:14 -0500)
committerMatt Keeter <matt.j.keeter@gmail.com>
Tue, 4 Mar 2014 02:14:16 +0000 (21:14 -0500)
18 files changed:
.gitignore [new file with mode: 0644]
src/canvas.cpp [new file with mode: 0644]
src/canvas.h [new file with mode: 0644]
src/fstl.pro [new file with mode: 0644]
src/gl/mesh.frag [new file with mode: 0644]
src/gl/mesh.vert [new file with mode: 0644]
src/gl/quad.frag [new file with mode: 0644]
src/gl/quad.vert [new file with mode: 0644]
src/glmesh.cpp [new file with mode: 0644]
src/glmesh.h [new file with mode: 0644]
src/loader.cpp [new file with mode: 0644]
src/loader.h [new file with mode: 0644]
src/main.cpp [new file with mode: 0644]
src/mesh.cpp [new file with mode: 0644]
src/mesh.h [new file with mode: 0644]
src/resources.qrc [new file with mode: 0644]
src/window.cpp [new file with mode: 0644]
src/window.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..deaad71
--- /dev/null
@@ -0,0 +1,3 @@
+build/*
+*.pro.user
+*.qmake.stash
diff --git a/src/canvas.cpp b/src/canvas.cpp
new file mode 100644 (file)
index 0000000..3468907
--- /dev/null
@@ -0,0 +1,129 @@
+#include <QMouseEvent>
+#include <QDebug>
+
+#include "canvas.h"
+#include "glmesh.h"
+#include "mesh.h"
+
+Canvas::Canvas(const QGLFormat& format, QWidget *parent)
+    : QGLWidget(format, parent), mesh(NULL),
+      scale(1), tilt(90), yaw(0)
+{
+    // Nothing to do here
+}
+
+Canvas::~Canvas()
+{
+    delete mesh;
+}
+
+void Canvas::load_mesh(Mesh* m)
+{
+    mesh = new GLMesh(m);
+    center = QVector3D(m->xmin() + m->xmax(),
+                       m->ymin() + m->ymax(),
+                       m->zmin() + m->zmax()) / 2;
+    scale = 2 / sqrt(
+                pow(m->xmax() - m->xmin(), 2) +
+                pow(m->ymax() - m->ymin(), 2) +
+                pow(m->zmax() - m->zmin(), 2));
+
+    update();
+
+    delete m;
+}
+
+void Canvas::initializeGL()
+{
+    mesh_shader.addShaderFromSourceFile(QGLShader::Vertex, ":/gl/mesh.vert");
+    mesh_shader.addShaderFromSourceFile(QGLShader::Fragment, ":/gl/mesh.frag");
+    mesh_shader.link();
+
+    glClearColor(0.0, 0.0, 0.0, 0.0);
+    glEnable(GL_DEPTH_TEST);
+}
+
+void Canvas::paintGL()
+{
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    if (mesh)  draw_mesh();
+}
+
+void Canvas::draw_mesh()
+{
+    mesh_shader.bind();
+
+    // Load the transform and view matrices into the shader
+    glUniformMatrix4fv(
+                mesh_shader.uniformLocation("transform_matrix"),
+                1, GL_FALSE, transform_matrix().data());
+    glUniformMatrix4fv(
+                mesh_shader.uniformLocation("view_matrix"),
+                1, GL_FALSE, view_matrix().data());
+
+    // Find and enable the attribute location for vertex position
+    const GLuint vp = mesh_shader.attributeLocation("vertex_position");
+    glEnableVertexAttribArray(vp);
+
+    // Then draw the mesh with that vertex position
+    mesh->draw(vp);
+
+    // Clean up state machine
+    glDisableVertexAttribArray(vp);
+    mesh_shader.release();
+}
+
+QMatrix4x4 Canvas::transform_matrix() const
+{
+    QMatrix4x4 m;
+    m.rotate(tilt, QVector3D(1, 0, 0));
+    m.rotate(yaw,  QVector3D(0, 0, 1));
+    m.scale(scale);
+    m.translate(-center);
+    return m;
+}
+
+QMatrix4x4 Canvas::view_matrix() const
+{
+    QMatrix4x4 m;
+    if (width() > height())
+    {
+        m.scale(height() / float(width()), 1, 0.5);
+    }
+    else
+    {
+        m.scale(1, width() / float(height()), 0.5);
+    }
+    return m;
+}
+
+void Canvas::mousePressEvent(QMouseEvent* event)
+{
+    if (event->button() == Qt::LeftButton)
+    {
+        mouse_pos = event->pos();
+        setCursor(Qt::ClosedHandCursor);
+    }
+}
+
+void Canvas::mouseReleaseEvent(QMouseEvent* event)
+{
+    if (event->button() == Qt::LeftButton)
+    {
+        unsetCursor();
+    }
+}
+
+void Canvas::mouseMoveEvent(QMouseEvent* event)
+{
+    if (event->buttons() & Qt::LeftButton)
+    {
+        auto p = event->pos();
+        auto d = p - mouse_pos;
+        yaw = fmod(yaw + d.x(), 360);
+        tilt = fmax(0, fmin(180, tilt - d.y()));
+        mouse_pos = p;
+        update();
+    }
+}
diff --git a/src/canvas.h b/src/canvas.h
new file mode 100644 (file)
index 0000000..e867898
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef CANVAS_H
+#define CANVAS_H
+
+#include <QWidget>
+#include <QtOpenGL/QGLWidget>
+#include <QtOpenGL/QGLShaderProgram>
+#include <QMatrix4x4>
+
+class GLMesh;
+class Mesh;
+
+class Canvas : public QGLWidget
+{
+    Q_OBJECT
+
+public:
+    Canvas(const QGLFormat& format, QWidget* parent=0);
+
+    void initializeGL();
+    void paintGL();
+    ~Canvas();
+
+public slots:
+    void load_mesh(Mesh* m);
+
+
+protected:
+    void mousePressEvent(QMouseEvent* event);
+    void mouseReleaseEvent(QMouseEvent* event);
+    void mouseMoveEvent(QMouseEvent* event);
+
+
+private:
+    void draw_mesh();
+
+    QMatrix4x4 transform_matrix() const;
+    QMatrix4x4 view_matrix() const;
+
+    QGLShaderProgram mesh_shader;
+    QGLShaderProgram quad_shader;
+
+    GLMesh* mesh;
+
+    QVector3D center;
+    float scale;
+    float tilt;
+    float yaw;
+
+    QPoint mouse_pos;
+};
+
+#endif // CANVAS_H
diff --git a/src/fstl.pro b/src/fstl.pro
new file mode 100644 (file)
index 0000000..ac58ee1
--- /dev/null
@@ -0,0 +1,26 @@
+QT       += core gui opengl widgets
+
+TARGET = fstl
+TEMPLATE = app
+
+SOURCES += \
+    main.cpp\
+    canvas.cpp \
+    mesh.cpp \
+    glmesh.cpp \
+    loader.cpp \
+    window.cpp
+
+HEADERS  += \
+    canvas.h \
+    mesh.h \
+    glmesh.h \
+    loader.h \
+    window.h
+
+CONFIG += c++11
+
+INCLUDEPATH += /usr/local/include/eigen3
+
+RESOURCES += \
+    resources.qrc
diff --git a/src/gl/mesh.frag b/src/gl/mesh.frag
new file mode 100644 (file)
index 0000000..5949025
--- /dev/null
@@ -0,0 +1,15 @@
+#version 120
+
+varying vec3 ec_pos;
+
+void main() {
+    vec3 base3 = vec3(0.99, 0.96, 0.89);
+    vec3 base2 = vec3(0.92, 0.91, 0.83);
+    vec3 base00 = vec3(0.40, 0.48, 0.51);
+    vec3 ec_normal = normalize(cross(dFdx(ec_pos), dFdy(ec_pos)));
+    float a = dot(ec_normal, vec3(0.0, 0.0, 1.0));
+    float b = dot(ec_normal, vec3(-0.57, -0.57, 0.57));
+
+    gl_FragColor = vec4((a*base2 + (1-a)*base00)*0.5 +
+                        (b*base3 + (1-b)*base00)*0.5, 1.0);
+}
diff --git a/src/gl/mesh.vert b/src/gl/mesh.vert
new file mode 100644 (file)
index 0000000..e60e76b
--- /dev/null
@@ -0,0 +1,13 @@
+#version 120
+attribute vec3 vertex_position;
+
+uniform mat4 transform_matrix;
+uniform mat4 view_matrix;
+
+varying vec3 ec_pos;
+
+void main() {
+    gl_Position = view_matrix*transform_matrix*
+        vec4(vertex_position, 1.0);
+    ec_pos = gl_Position.xyz;
+}
diff --git a/src/gl/quad.frag b/src/gl/quad.frag
new file mode 100644 (file)
index 0000000..1c02e1b
--- /dev/null
@@ -0,0 +1,7 @@
+#version 120
+
+varying vec3 frag_color;
+
+void main() {
+    gl_FragColor = vec4(frag_color, 1.0);
+}
diff --git a/src/gl/quad.vert b/src/gl/quad.vert
new file mode 100644 (file)
index 0000000..6698297
--- /dev/null
@@ -0,0 +1,10 @@
+#version 120
+attribute vec2 vertex_position;
+attribute vec3 vertex_color;
+
+varying vec3 frag_color;
+
+void main() {
+    gl_Position = vec4(vertex_position, 0.9, 1.0);
+    frag_color = vertex_color;
+}
diff --git a/src/glmesh.cpp b/src/glmesh.cpp
new file mode 100644 (file)
index 0000000..8e0cab3
--- /dev/null
@@ -0,0 +1,35 @@
+#include "glmesh.h"
+#include "mesh.h"
+
+GLMesh::GLMesh(const Mesh* const mesh)
+    : vertices(QGLBuffer::VertexBuffer), indices(QGLBuffer::IndexBuffer)
+{
+    vertices.create();
+    indices.create();
+
+    vertices.setUsagePattern(QGLBuffer::StaticDraw);
+    indices.setUsagePattern(QGLBuffer::StaticDraw);
+
+    vertices.bind();
+    vertices.allocate(mesh->vertices.data(),
+                      mesh->vertices.size() * sizeof(float));
+    vertices.release();
+
+    indices.bind();
+    indices.allocate(mesh->indices.data(),
+                     mesh->indices.size() * sizeof(uint32_t));
+    indices.release();
+}
+
+void GLMesh::draw(GLuint vp)
+{
+    vertices.bind();
+    indices.bind();
+
+    glVertexAttribPointer(vp, 3, GL_FLOAT, false, 3*sizeof(float), NULL);
+    glDrawElements(GL_TRIANGLES, indices.size() / sizeof(uint32_t),
+                   GL_UNSIGNED_INT, NULL);
+
+    vertices.release();
+    indices.release();
+}
diff --git a/src/glmesh.h b/src/glmesh.h
new file mode 100644 (file)
index 0000000..01bf921
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef GLMESH_H
+#define GLMESH_H
+
+#include <QtOpenGL/QGLBuffer>
+
+class Mesh;
+
+class GLMesh
+{
+public:
+    GLMesh(const Mesh* const mesh);
+    void draw(GLuint vp);
+private:
+    QGLBuffer vertices;
+    QGLBuffer indices;
+};
+
+#endif // GLMESH_H
diff --git a/src/loader.cpp b/src/loader.cpp
new file mode 100644 (file)
index 0000000..8666161
--- /dev/null
@@ -0,0 +1,12 @@
+#include "loader.h"
+#include "mesh.h"
+
+Loader::Loader(QObject* parent, const QString& filename)
+    : QThread(parent), filename(filename)
+{
+}
+
+void Loader::run()
+{
+    emit got_mesh(Mesh::load_stl(filename));
+}
diff --git a/src/loader.h b/src/loader.h
new file mode 100644 (file)
index 0000000..29508f0
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef LOADER_H
+#define LOADER_H
+
+#include <QThread>
+
+#include "mesh.h"
+
+class Loader : public QThread
+{
+    Q_OBJECT
+public:
+    explicit Loader(QObject* parent, const QString& filename);
+    void run();
+
+signals:
+    void got_mesh(Mesh* m);
+
+private:
+    const QString filename;
+
+};
+
+#endif // LOADER_H
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644 (file)
index 0000000..35a853a
--- /dev/null
@@ -0,0 +1,16 @@
+#include <QApplication>
+
+#include "window.h"
+#include "mesh.h"
+#include "glmesh.h"
+
+int main(int argc, char *argv[])
+{
+    QApplication a(argc, argv);
+
+    Window window;
+    window.show();
+
+    window.load_stl("../../splitter/cayman.stl");
+    return a.exec();
+}
diff --git a/src/mesh.cpp b/src/mesh.cpp
new file mode 100644 (file)
index 0000000..d7266e7
--- /dev/null
@@ -0,0 +1,87 @@
+#include <QFile>
+#include <QDataStream>
+
+#include <algorithm>
+
+#include "mesh.h"
+
+Mesh::Mesh(const Eigen::Matrix3Xf& v, const Eigen::Matrix3Xi& i)
+    : vertices(v), indices(i)
+{
+    // Nothing to do here
+}
+
+Mesh* Mesh::load_stl(const QString& filename)
+{
+    QFile file(filename);
+    file.open(QIODevice::ReadOnly);
+
+    QDataStream data(&file);
+    data.setByteOrder(QDataStream::LittleEndian);
+    data.setFloatingPointPrecision(QDataStream::SinglePrecision);
+
+    data.skipRawData(80);
+    uint32_t tri_count;
+    data >> tri_count;
+
+    // Extract vertices into a vector of Vector4d objects
+    std::vector<Eigen::Vector4d> verts(tri_count*3);
+    for (unsigned i=0; i < tri_count; ++i)
+    {
+        data.skipRawData(3*sizeof(float));
+        for (int j=0; j < 3; ++j)
+        {
+            float x, y, z;
+            data >> x >> y >> z;
+            verts[3*i + j] << x, y, z, 3*i + j;
+        }
+        data.skipRawData(sizeof(uint16_t));
+    }
+
+    // Sort the set of vertices (to deduplicate)
+    std::sort(verts.begin(), verts.end(),
+              [](const Eigen::Vector4d& lhs, const Eigen::Vector4d& rhs)
+        {
+            if      (lhs[0] != rhs[0])  return lhs[0] < rhs[0];
+            else if (lhs[1] != rhs[1])  return lhs[1] < rhs[1];
+            else if (lhs[2] != rhs[2])  return lhs[2] < rhs[2];
+            else                        return false;
+        }
+    );
+
+    // This list will store unique vertices
+    std::list<Eigen::Vector3f> unique;
+
+    // This vector will store triangles as rows of indices
+    Eigen::Matrix3Xi indices;
+    indices.resize(Eigen::NoChange, tri_count);
+
+    // Go through the sorted vertex list, deduplicating and creating
+    // an indexed geometry representation for the triangles.
+    for (auto v : verts)
+    {
+        if (!unique.size() || v[0] != unique.back()[0] ||
+                              v[1] != unique.back()[1] ||
+                              v[2] != unique.back()[2])
+        {
+            // Switch to a float vector and save in the list.
+            Eigen::Vector3f v_;
+            v_ << v[0], v[1], v[2];
+            unique.push_back(v_);
+        }
+        indices(int(v[3]) % 3, int(v[3]) / 3) = unique.size() - 1;
+    }
+
+    // Finally, pack unique vertices into a matrix.
+    Eigen::Matrix3Xf unique_verts;
+    unique_verts.resize(Eigen::NoChange, unique.size());
+    {
+        auto v = unique.begin();
+        for (unsigned i=0; i < unique.size(); ++i)
+        {
+            unique_verts.col(i) = *(v++);
+        }
+    }
+
+    return new Mesh(unique_verts, indices);
+}
diff --git a/src/mesh.h b/src/mesh.h
new file mode 100644 (file)
index 0000000..064d276
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef MESH_H
+#define MESH_H
+
+#include <QString>
+
+#include <Eigen/Dense>
+
+class Mesh
+{
+public:
+    Mesh(const Eigen::Matrix3Xf &vertices, const Eigen::Matrix3Xi &indices);
+    static Mesh* load_stl(const QString& filename);
+
+    float xmin() const { return vertices.row(0).minCoeff(); }
+    float xmax() const { return vertices.row(0).maxCoeff(); }
+    float ymin() const { return vertices.row(1).minCoeff(); }
+    float ymax() const { return vertices.row(1).maxCoeff(); }
+    float zmin() const { return vertices.row(2).minCoeff(); }
+    float zmax() const { return vertices.row(2).maxCoeff(); }
+
+private:
+    const Eigen::Matrix3Xf vertices;
+    const Eigen::Matrix3Xi indices;
+
+    friend class GLMesh;
+};
+
+#endif // MESH_H
diff --git a/src/resources.qrc b/src/resources.qrc
new file mode 100644 (file)
index 0000000..1cb5c2c
--- /dev/null
@@ -0,0 +1,8 @@
+<RCC>
+    <qresource prefix="/">
+        <file>gl/mesh.frag</file>
+        <file>gl/mesh.vert</file>
+        <file>gl/quad.frag</file>
+        <file>gl/quad.vert</file>
+    </qresource>
+</RCC>
diff --git a/src/window.cpp b/src/window.cpp
new file mode 100644 (file)
index 0000000..55669a6
--- /dev/null
@@ -0,0 +1,26 @@
+#include "window.h"
+#include "canvas.h"
+#include "loader.h"
+
+Window::Window(QWidget *parent) :
+    QMainWindow(parent)
+{
+    setWindowTitle("fstl");
+
+    QGLFormat format;
+    format.setVersion(2, 1);
+    format.setSampleBuffers(true);
+
+    canvas = new Canvas(format, this);
+    setCentralWidget(canvas);
+}
+
+void Window::load_stl(const QString &filename)
+{
+    Loader* loader = new Loader(this, filename);
+    connect(loader, SIGNAL(got_mesh(Mesh*)),
+            canvas, SLOT(load_mesh(Mesh*)));
+    connect(loader, SIGNAL(finished()),
+            loader, SLOT(deleteLater()));
+    loader->start();
+}
diff --git a/src/window.h b/src/window.h
new file mode 100644 (file)
index 0000000..809aeb7
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef WINDOW_H
+#define WINDOW_H
+
+#include <QMainWindow>
+
+class Canvas;
+
+class Window : public QMainWindow
+{
+    Q_OBJECT
+public:
+    explicit Window(QWidget* parent=0);
+    void load_stl(const QString& filename);
+
+private:
+    Canvas* canvas;
+};
+
+#endif // WINDOW_H