X-Git-Url: https://git.sur5r.net/?p=fstl;a=blobdiff_plain;f=src%2Floader.cpp;fp=src%2Floader.cpp;h=93f5525320ce1f7ddc29dd9493b0d3b8a35852ca;hp=0d6b165ad696d436324a2961d71d6788c6c67c51;hb=ff02a2cf9b32c9cbe26785150d32ccb251149cf4;hpb=0ff67c7a2411f6ebd661f293305d6401d07ec0b5 diff --git a/src/loader.cpp b/src/loader.cpp index 0d6b165..93f5525 100644 --- a/src/loader.cpp +++ b/src/loader.cpp @@ -1,7 +1,9 @@ +#include + #include "loader.h" -Loader::Loader(QObject* parent, const QString& filename) - : QThread(parent), filename(filename) +Loader::Loader(QObject* parent, const QString& filename, bool is_reload) + : QThread(parent), filename(filename), is_reload(is_reload) { // Nothing to do here } @@ -11,12 +13,19 @@ void Loader::run() Mesh* mesh = load_stl(); if (mesh) { - emit got_mesh(mesh); - emit loaded_file(filename); + if (mesh->empty()) + { + emit error_empty_mesh(); + delete mesh; + } + else + { + emit got_mesh(mesh, is_reload); + emit loaded_file(filename); + } } } - //////////////////////////////////////////////////////////////////////////////// struct Vec3 @@ -37,25 +46,116 @@ struct Vec3 typedef std::pair Vec3i; +void parallel_sort(Vec3i* begin, Vec3i* end, int threads) +{ + if (threads < 2 || end - begin < 2) + { + std::sort(begin, end); + } + else + { + const auto mid = begin + (end - begin) / 2; + if (threads == 2) + { + auto future = std::async(parallel_sort, begin, mid, threads / 2); + std::sort(mid, end); + future.wait(); + } + else + { + auto a = std::async(std::launch::async, parallel_sort, begin, mid, threads / 2); + auto b = std::async(std::launch::async, parallel_sort, mid, end, threads / 2); + a.wait(); + b.wait(); + } + std::inplace_merge(begin, mid, end); + } +} + +Mesh* mesh_from_verts(uint32_t tri_count, QVector& verts) +{ + // 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) + parallel_sort(verts.begin(), verts.end(), 8); + + // 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); +} + //////////////////////////////////////////////////////////////////////////////// Mesh* Loader::load_stl() { QFile file(filename); - file.open(QIODevice::ReadOnly); - if (file.read(5) == "solid") + if (!file.open(QIODevice::ReadOnly)) { - emit error_ascii_stl(); + emit error_missing_file(); return NULL; } - // Skip the rest of the header material - file.read(75); + // First, try to read the stl as an ASCII file + if (file.read(6) == "solid ") + { + file.readLine(); // skip solid name + const auto line = file.readLine().trimmed(); + if (line.startsWith("facet") || + line.startsWith("endsolid")) + { + file.seek(0); + return read_stl_ascii(file); + } + confusing_stl = true; + } + else + { + confusing_stl = false; + } + + // Otherwise, skip the rest of the header material and read as binary + file.seek(0); + return read_stl_binary(file); +} + +Mesh* Loader::read_stl_binary(QFile& file) +{ QDataStream data(&file); data.setByteOrder(QDataStream::LittleEndian); data.setFloatingPointPrecision(QDataStream::SinglePrecision); // Load the triangle count from the .stl file + file.seek(80); uint32_t tri_count; data >> tri_count; @@ -70,60 +170,87 @@ Mesh* Loader::load_stl() QVector verts(tri_count*3); // Dummy array, because readRawData is faster than skipRawData - char buffer[sizeof(float)*3]; + uint8_t* buffer = (uint8_t*)malloc(tri_count * 50); + data.readRawData((char*)buffer, tri_count * 50); // Store vertices in the array, processing one triangle at a time. + auto b = buffer; for (auto v=verts.begin(); v != verts.end(); v += 3) { // Skip face's normal vector - data.readRawData(buffer, 3*sizeof(float)); + b += 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; + for (unsigned i=0; i < 3; ++i) + { + memcpy(&v[i].first, b, 3*sizeof(float)); + b += 3 * sizeof(float); + } // Skip face attribute - data.readRawData(buffer, sizeof(uint16_t)); + b += 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) + if (confusing_stl) { - verts[i].second = i; + emit warning_confusing_stl(); } + free(buffer); - // Sort the set of vertices (to deduplicate) - std::sort(verts.begin(), verts.end()); + return mesh_from_verts(tri_count, verts); +} - // This vector will store triangles as sets of 3 indices - std::vector indices(tri_count*3); +Mesh* Loader::read_stl_ascii(QFile& file) +{ + file.readLine(); + uint32_t tri_count = 0; + QVector verts(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) + bool okay = true; + while (!file.atEnd() && okay) { - if (!vertex_count || v.first != verts[vertex_count-1].first) + const auto line = file.readLine().simplified(); + if (line.startsWith("endsolid")) { - verts[vertex_count++] = v; + break; } - indices[v.second] = vertex_count - 1; + else if (!line.startsWith("facet normal") || + !file.readLine().simplified().startsWith("outer loop")) + { + okay = false; + break; + } + + for (int i=0; i < 3; ++i) + { + auto line = file.readLine().simplified().split(' '); + if (line[0] != "vertex") + { + okay = false; + break; + } + const float x = line[1].toFloat(&okay); + const float y = line[2].toFloat(&okay); + const float z = line[3].toFloat(&okay); + verts.push_back({{x, y, z}, 0}); + } + if (!file.readLine().trimmed().startsWith("endloop") || + !file.readLine().trimmed().startsWith("endfacet")) + { + okay = false; + break; + } + tri_count++; } - verts.resize(vertex_count); - std::vector flat_verts; - flat_verts.reserve(vertex_count*3); - for (auto v : verts) + if (okay) { - flat_verts.push_back(v.first.x); - flat_verts.push_back(v.first.y); - flat_verts.push_back(v.first.z); + return mesh_from_verts(tri_count, verts); + } + else + { + emit error_bad_stl(); + return NULL; } - - return new Mesh(flat_verts, indices); }