diff --git a/app/package.sh b/app/package.sh deleted file mode 100755 index d51c470..0000000 --- a/app/package.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh -cd ../build -macdeployqt fstl.app -cd fstl.app/Contents/PlugIns -rm -rf accessible audio imageformats mediaservice playlistformats position printsupport qml1tooling sensorgestures sensors -cd ../Frameworks -rm -rf QtDeclarative.framework QtMultimedia.framework QtMultimediaWidgets.framework QtNetwork.framework QtPositioning.framework QtQml.framework QtQuick.framework QtScript.framework QtSensors.framework QtSql.framework QtXmlPatterns.framework -cd ../Resources -rm empty.lproj -cd ../../.. -cp -r fstl.app .. -cd .. -zip -r fstl_mac.zip fstl.app README.md - diff --git a/exe/package.sh b/exe/package.sh deleted file mode 100644 index ef81b62..0000000 --- a/exe/package.sh +++ /dev/null @@ -1,3 +0,0 @@ -cd .. -cp build/release/fstl.exe . -/c/Program\ Files/7-Zip/7z.exe a fstl_win.zip fstl.exe README.md diff --git a/gl/mesh.frag b/gl/mesh.frag index d7a54d5..5949025 100644 --- a/gl/mesh.frag +++ b/gl/mesh.frag @@ -1,18 +1,12 @@ #version 120 -uniform float zoom; - 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))); - ec_normal.z *= zoom; - ec_normal = normalize(ec_normal); - float a = dot(ec_normal, vec3(0.0, 0.0, 1.0)); float b = dot(ec_normal, vec3(-0.57, -0.57, 0.57)); diff --git a/src/app.cpp b/src/app.cpp index b6eb3af..afa4ea7 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -8,10 +8,7 @@ App::App(int argc, char *argv[]) : QApplication(argc, argv), window(new Window()) { window->show(); - if (argc > 1) - window->load_stl(argv[1]); - else - window->load_stl(":gl/sphere.stl"); + window->load_stl(":gl/sphere.stl"); } bool App::event(QEvent* e) diff --git a/src/canvas.cpp b/src/canvas.cpp index dd645a5..03f9376 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -10,7 +10,7 @@ Canvas::Canvas(const QGLFormat& format, QWidget *parent) : QGLWidget(format, parent), mesh(NULL), - scale(1), zoom(1), tilt(90), yaw(0), status(" ") + scale(1), tilt(90), yaw(0), status(" ") { // Nothing to do here } @@ -30,12 +30,6 @@ void Canvas::load_mesh(Mesh* m) pow(m->xmax() - m->xmin(), 2) + pow(m->ymax() - m->ymin(), 2) + pow(m->zmax() - m->zmin(), 2)); - - // Reset other camera parameters - zoom = 1; - yaw = 0; - tilt = 90; - update(); delete m; @@ -95,9 +89,6 @@ void Canvas::draw_mesh() mesh_shader.uniformLocation("view_matrix"), 1, GL_FALSE, view_matrix().data()); - // Compensate for z-flattening when zooming - glUniform1f(mesh_shader.uniformLocation("zoom"), 1/zoom); - // Find and enable the attribute location for vertex position const GLuint vp = mesh_shader.attributeLocation("vertex_position"); glEnableVertexAttribArray(vp); @@ -131,14 +122,12 @@ QMatrix4x4 Canvas::view_matrix() const { m.scale(-1, width() / float(height()), 0.5); } - m.scale(zoom, zoom, 1); return m; } void Canvas::mousePressEvent(QMouseEvent* event) { - if (event->button() == Qt::LeftButton || - event->button() == Qt::RightButton) + if (event->button() == Qt::LeftButton) { mouse_pos = event->pos(); setCursor(Qt::ClosedHandCursor); @@ -147,8 +136,7 @@ void Canvas::mousePressEvent(QMouseEvent* event) void Canvas::mouseReleaseEvent(QMouseEvent* event) { - if (event->button() == Qt::LeftButton || - event->button() == Qt::RightButton) + if (event->button() == Qt::LeftButton) { unsetCursor(); } @@ -156,50 +144,13 @@ void Canvas::mouseReleaseEvent(QMouseEvent* event) void Canvas::mouseMoveEvent(QMouseEvent* event) { - auto p = event->pos(); - auto d = p - mouse_pos; - 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(); } - else if (event->buttons() & Qt::RightButton) - { - center = transform_matrix().inverted() * - view_matrix().inverted() * - QVector3D(-d.x() / (0.5*width()), - d.y() / (0.5*height()), 0); - update(); - } - mouse_pos = p; -} - -void Canvas::wheelEvent(QWheelEvent *event) -{ - // Find GL position before the zoom operation - // (to zoom about mouse cursor) - auto p = event->pos(); - QVector3D v(1 - p.x() / (0.5*width()), - p.y() / (0.5*height()) - 1, 0); - QVector3D a = transform_matrix().inverted() * - view_matrix().inverted() * v; - - if (event->delta() < 0) - { - for (int i=0; i > event->delta(); --i) - zoom *= 1.001; - } - else if (event->delta() > 0) - { - for (int i=0; i < event->delta(); ++i) - zoom /= 1.001; - } - - // Then find the cursor's GL position post-zoom and adjust center. - QVector3D b = transform_matrix().inverted() * - view_matrix().inverted() * v; - center += b - a; - update(); } diff --git a/src/canvas.h b/src/canvas.h index 33313cf..64a8dd5 100644 --- a/src/canvas.h +++ b/src/canvas.h @@ -32,7 +32,6 @@ protected: void mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); - void wheelEvent(QWheelEvent* event); private: @@ -49,7 +48,6 @@ private: QVector3D center; float scale; - float zoom; float tilt; float yaw; diff --git a/src/loader.cpp b/src/loader.cpp index 0d6b165..4131d02 100644 --- a/src/loader.cpp +++ b/src/loader.cpp @@ -1,129 +1,13 @@ #include "loader.h" +#include "mesh.h" Loader::Loader(QObject* parent, const QString& filename) : QThread(parent), filename(filename) { - // Nothing to do here } void Loader::run() { - Mesh* mesh = load_stl(); - if (mesh) - { - emit got_mesh(mesh); - emit loaded_file(filename); - } + emit got_mesh(Mesh::load_stl(filename)); + emit loaded_file(filename); } - - -//////////////////////////////////////////////////////////////////////////////// - -struct Vec3 -{ - GLfloat x, y, z; - bool operator!=(const Vec3& rhs) const - { - return x != rhs.x || y != rhs.y || z != rhs.z; - } - bool operator<(const Vec3& rhs) const - { - if (x != rhs.x) return x < rhs.x; - else if (y != rhs.y) return y < rhs.y; - else if (z != rhs.z) return z < rhs.z; - else return false; - } -}; - -typedef std::pair Vec3i; - -//////////////////////////////////////////////////////////////////////////////// - -Mesh* Loader::load_stl() -{ - QFile file(filename); - file.open(QIODevice::ReadOnly); - if (file.read(5) == "solid") - { - emit error_ascii_stl(); - return NULL; - } - // Skip the rest of the header material - file.read(75); - - QDataStream data(&file); - data.setByteOrder(QDataStream::LittleEndian); - data.setFloatingPointPrecision(QDataStream::SinglePrecision); - - // Load the triangle count from the .stl file - uint32_t tri_count; - data >> tri_count; - - // Verify that the file is the right size - if (file.size() != 84 + tri_count*50) - { - emit error_bad_stl(); - return NULL; - } - - // Extract vertices into an array of xyz, unsigned pairs - QVector verts(tri_count*3); - - // Dummy array, because readRawData is faster than skipRawData - char buffer[sizeof(float)*3]; - - // Store vertices in the array, processing one triangle at a time. - for (auto v=verts.begin(); v != verts.end(); v += 3) - { - // Skip face's normal vector - data.readRawData(buffer, 3*sizeof(float)); - - // Load vertex data from .stl file into vertices - data >> v[0].first.x >> v[0].first.y >> v[0].first.z; - data >> v[1].first.x >> v[1].first.y >> v[1].first.z; - data >> v[2].first.x >> v[2].first.y >> v[2].first.z; - - // Skip face attribute - data.readRawData(buffer, sizeof(uint16_t)); - } - - // Save indicies as the second element in the array - // (so that we can reconstruct triangle order after sorting) - for (size_t i=0; i < tri_count*3; ++i) - { - verts[i].second = i; - } - - // Sort the set of vertices (to deduplicate) - std::sort(verts.begin(), verts.end()); - - // This vector will store triangles as sets of 3 indices - std::vector indices(tri_count*3); - - // Go through the sorted vertex list, deduplicating and creating - // an indexed geometry representation for the triangles. - // Unique vertices are moved so that they occupy the first vertex_count - // positions in the verts array. - size_t vertex_count = 0; - for (auto v : verts) - { - if (!vertex_count || v.first != verts[vertex_count-1].first) - { - verts[vertex_count++] = v; - } - indices[v.second] = vertex_count - 1; - } - verts.resize(vertex_count); - - std::vector flat_verts; - flat_verts.reserve(vertex_count*3); - for (auto v : verts) - { - flat_verts.push_back(v.first.x); - flat_verts.push_back(v.first.y); - flat_verts.push_back(v.first.z); - } - - return new Mesh(flat_verts, indices); -} - diff --git a/src/loader.h b/src/loader.h index fb0c8d8..350985c 100644 --- a/src/loader.h +++ b/src/loader.h @@ -12,16 +12,10 @@ public: explicit Loader(QObject* parent, const QString& filename); void run(); -protected: - Mesh* load_stl(); - signals: void loaded_file(QString filename); void got_mesh(Mesh* m); - void error_ascii_stl(); - void error_bad_stl(); - private: const QString filename; diff --git a/src/mesh.cpp b/src/mesh.cpp index cfb4ca4..36ad10e 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include "mesh.h" @@ -33,3 +34,98 @@ float Mesh::max(size_t start) const } return v; } +//////////////////////////////////////////////////////////////////////////////// + +struct Vec3 +{ + GLfloat x, y, z; + bool operator!=(const Vec3& rhs) const + { + return x != rhs.x || y != rhs.y || z != rhs.z; + } + bool operator<(const Vec3& rhs) const + { + if (x != rhs.x) return x < rhs.x; + else if (y != rhs.y) return y < rhs.y; + else if (z != rhs.z) return z < rhs.z; + else return false; + } +}; + +typedef std::pair Vec3i; + +//////////////////////////////////////////////////////////////////////////////// + +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); + + // Skip .stl file header + data.skipRawData(80); + + // Load the triangle count from the .stl file + uint32_t tri_count; + data >> tri_count; + + // Extract vertices into an array of xyz, unsigned pairs + QVector verts(tri_count*3); + + // Store vertices in the array, processing one triangle at a time. + for (auto v=verts.begin(); v != verts.end(); v += 3) + { + // Skip face's normal vector + data.skipRawData(3*sizeof(float)); + + // Load vertex data from .stl file into vertices + data >> v[0].first.x >> v[0].first.y >> v[0].first.z; + data >> v[1].first.x >> v[1].first.y >> v[1].first.z; + data >> v[2].first.x >> v[2].first.y >> v[2].first.z; + + // Skip face attribute + data.skipRawData(sizeof(uint16_t)); + } + + // Save indicies as the second element in the array + // (so that we can reconstruct triangle order after sorting) + for (size_t i=0; i < tri_count*3; ++i) + { + verts[i].second = i; + } + + // Sort the set of vertices (to deduplicate) + std::sort(verts.begin(), verts.end()); + + // This vector will store triangles as sets of 3 indices + std::vector indices(tri_count*3); + + // Go through the sorted vertex list, deduplicating and creating + // an indexed geometry representation for the triangles. + // Unique vertices are moved so that they occupy the first vertex_count + // positions in the verts array. + size_t vertex_count = 0; + for (auto v : verts) + { + if (!vertex_count || v.first != verts[vertex_count-1].first) + { + verts[vertex_count++] = v; + } + indices[v.second] = vertex_count - 1; + } + verts.resize(vertex_count); + + std::vector flat_verts; + flat_verts.reserve(vertex_count*3); + for (auto v : verts) + { + flat_verts.push_back(v.first.x); + flat_verts.push_back(v.first.y); + flat_verts.push_back(v.first.z); + } + + return new Mesh(flat_verts, indices); +} diff --git a/src/mesh.h b/src/mesh.h index e8a02f0..f83a477 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -10,6 +10,7 @@ class Mesh { public: Mesh(std::vector vertices, std::vector indices); + static Mesh* load_stl(const QString& filename); float min(size_t start) const; float max(size_t start) const; diff --git a/src/window.cpp b/src/window.cpp index 3002ecc..15ffb03 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -14,7 +14,6 @@ Window::Window(QWidget *parent) : { setWindowTitle("fstl"); - setAcceptDrops(true); QFile styleFile(":/qt/style.qss"); styleFile.open( QFile::ReadOnly ); @@ -70,22 +69,6 @@ void Window::on_about() " style=\"color: #93a1a1;\">matt.j.keeter@gmail.com

"); } -void Window::on_ascii_stl() -{ - QMessageBox::critical(this, "Error", - "Error:
" - "Cannot open ASCII .stl file
" - "Please convert to binary .stl and retry"); -} - -void Window::on_bad_stl() -{ - QMessageBox::critical(this, "Error", - "Error:
" - "This .stl file is invalid or corrupted.
" - "Please export it from the original source, verify, and retry."); -} - void Window::enable_open() { open_action->setEnabled(true); @@ -108,10 +91,6 @@ bool Window::load_stl(const QString& filename) connect(loader, &Loader::got_mesh, canvas, &Canvas::load_mesh); - connect(loader, &Loader::error_ascii_stl, - this, &Window::on_ascii_stl); - connect(loader, &Loader::error_bad_stl, - this, &Window::on_bad_stl); connect(loader, &Loader::finished, loader, &Loader::deleteLater); @@ -129,18 +108,3 @@ bool Window::load_stl(const QString& filename) loader->start(); return true; } - -void Window::dragEnterEvent(QDragEnterEvent *event) -{ - if (event->mimeData()->hasUrls()) - { - auto urls = event->mimeData()->urls(); - if (urls.size() == 1 && urls.front().path().endsWith(".stl")) - event->acceptProposedAction(); - } -} - -void Window::dropEvent(QDropEvent *event) -{ - load_stl(event->mimeData()->urls().front().toLocalFile()); -} diff --git a/src/window.h b/src/window.h index cb92ebb..22c35cb 100644 --- a/src/window.h +++ b/src/window.h @@ -12,15 +12,9 @@ public: explicit Window(QWidget* parent=0); bool load_stl(const QString& filename); -protected: - void dragEnterEvent(QDragEnterEvent* event); - void dropEvent(QDropEvent* event); - public slots: void on_open(); void on_about(); - void on_ascii_stl(); - void on_bad_stl(); void enable_open(); void disable_open();