Mostly complete from the Python original
This commit is contained in:
commit
97cf902b87
18 changed files with 527 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
build/*
|
||||
*.pro.user
|
||||
*.qmake.stash
|
129
src/canvas.cpp
Normal file
129
src/canvas.cpp
Normal 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
52
src/canvas.h
Normal 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
26
src/fstl.pro
Normal 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
15
src/gl/mesh.frag
Normal 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
13
src/gl/mesh.vert
Normal 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
7
src/gl/quad.frag
Normal 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
10
src/gl/quad.vert
Normal 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
35
src/glmesh.cpp
Normal 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
18
src/glmesh.h
Normal 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
12
src/loader.cpp
Normal 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
23
src/loader.h
Normal 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
16
src/main.cpp
Normal 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
87
src/mesh.cpp
Normal 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
28
src/mesh.h
Normal 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
8
src/resources.qrc
Normal 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
26
src/window.cpp
Normal 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
19
src/window.h
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue