Mostly complete from the Python original

This commit is contained in:
Matt Keeter 2014-03-03 21:14:16 -05:00
commit 97cf902b87
18 changed files with 527 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
build/*
*.pro.user
*.qmake.stash

129
src/canvas.cpp Normal file
View file

@ -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();
}
}

52
src/canvas.h Normal file
View file

@ -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

26
src/fstl.pro Normal file
View file

@ -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

15
src/gl/mesh.frag Normal file
View file

@ -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);
}

13
src/gl/mesh.vert Normal file
View file

@ -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;
}

7
src/gl/quad.frag Normal file
View file

@ -0,0 +1,7 @@
#version 120
varying vec3 frag_color;
void main() {
gl_FragColor = vec4(frag_color, 1.0);
}

10
src/gl/quad.vert Normal file
View file

@ -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;
}

35
src/glmesh.cpp Normal file
View file

@ -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();
}

18
src/glmesh.h Normal file
View file

@ -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

12
src/loader.cpp Normal file
View file

@ -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));
}

23
src/loader.h Normal file
View file

@ -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

16
src/main.cpp Normal file
View file

@ -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();
}

87
src/mesh.cpp Normal file
View file

@ -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);
}

28
src/mesh.h Normal file
View file

@ -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

8
src/resources.qrc Normal file
View file

@ -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>

26
src/window.cpp Normal file
View file

@ -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();
}

19
src/window.h Normal file
View file

@ -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