]> git.sur5r.net Git - fstl/blob - src/loader.cpp
New upstream version 0.9.3
[fstl] / src / loader.cpp
1 #include <future>
2
3 #include "loader.h"
4
5 Loader::Loader(QObject* parent, const QString& filename, bool is_reload)
6     : QThread(parent), filename(filename), is_reload(is_reload)
7 {
8     // Nothing to do here
9 }
10
11 void Loader::run()
12 {
13     Mesh* mesh = load_stl();
14     if (mesh)
15     {
16         if (mesh->empty())
17         {
18             emit error_empty_mesh();
19             delete mesh;
20         }
21         else
22         {
23             emit got_mesh(mesh, is_reload);
24             emit loaded_file(filename);
25         }
26     }
27 }
28
29 ////////////////////////////////////////////////////////////////////////////////
30
31 struct Vec3
32 {
33     GLfloat x, y, z;
34     bool operator!=(const Vec3& rhs) const
35     {
36         return x != rhs.x || y != rhs.y || z != rhs.z;
37     }
38     bool operator<(const Vec3& rhs) const
39     {
40         if      (x != rhs.x)    return x < rhs.x;
41         else if (y != rhs.y)    return y < rhs.y;
42         else if (z != rhs.z)    return z < rhs.z;
43         else                    return false;
44     }
45 };
46
47 typedef std::pair<Vec3, GLuint> Vec3i;
48
49 void parallel_sort(Vec3i* begin, Vec3i* end, int threads)
50 {
51     if (threads < 2 || end - begin < 2)
52     {
53         std::sort(begin, end);
54     }
55     else
56     {
57         const auto mid = begin + (end - begin) / 2;
58         if (threads == 2)
59         {
60             auto future = std::async(parallel_sort, begin, mid, threads / 2);
61             std::sort(mid, end);
62             future.wait();
63         }
64         else
65         {
66             auto a = std::async(std::launch::async, parallel_sort, begin, mid, threads / 2);
67             auto b = std::async(std::launch::async, parallel_sort, mid, end, threads / 2);
68             a.wait();
69             b.wait();
70         }
71         std::inplace_merge(begin, mid, end);
72     }
73 }
74
75 Mesh* mesh_from_verts(uint32_t tri_count, QVector<Vec3i>& verts)
76 {
77     // Save indicies as the second element in the array
78     // (so that we can reconstruct triangle order after sorting)
79     for (size_t i=0; i < tri_count*3; ++i)
80     {
81         verts[i].second = i;
82     }
83
84     // Sort the set of vertices (to deduplicate)
85     parallel_sort(verts.begin(), verts.end(), 8);
86
87     // This vector will store triangles as sets of 3 indices
88     std::vector<GLuint> indices(tri_count*3);
89
90     // Go through the sorted vertex list, deduplicating and creating
91     // an indexed geometry representation for the triangles.
92     // Unique vertices are moved so that they occupy the first vertex_count
93     // positions in the verts array.
94     size_t vertex_count = 0;
95     for (auto v : verts)
96     {
97         if (!vertex_count || v.first != verts[vertex_count-1].first)
98         {
99             verts[vertex_count++] = v;
100         }
101         indices[v.second] = vertex_count - 1;
102     }
103     verts.resize(vertex_count);
104
105     std::vector<GLfloat> flat_verts;
106     flat_verts.reserve(vertex_count*3);
107     for (auto v : verts)
108     {
109         flat_verts.push_back(v.first.x);
110         flat_verts.push_back(v.first.y);
111         flat_verts.push_back(v.first.z);
112     }
113
114     return new Mesh(flat_verts, indices);
115 }
116
117 ////////////////////////////////////////////////////////////////////////////////
118
119 Mesh* Loader::load_stl()
120 {
121     QFile file(filename);
122     if (!file.open(QIODevice::ReadOnly))
123     {
124         emit error_missing_file();
125         return NULL;
126     }
127
128     // First, try to read the stl as an ASCII file
129     if (file.read(6) == "solid ")
130     {
131         file.readLine(); // skip solid name
132         const auto line = file.readLine().trimmed();
133         if (line.startsWith("facet") ||
134             line.startsWith("endsolid"))
135         {
136             file.seek(0);
137             return read_stl_ascii(file);
138         }
139         confusing_stl = true;
140     }
141     else
142     {
143         confusing_stl = false;
144     }
145
146     // Otherwise, skip the rest of the header material and read as binary
147     file.seek(0);
148     return read_stl_binary(file);
149 }
150
151 Mesh* Loader::read_stl_binary(QFile& file)
152 {
153     QDataStream data(&file);
154     data.setByteOrder(QDataStream::LittleEndian);
155     data.setFloatingPointPrecision(QDataStream::SinglePrecision);
156
157     // Load the triangle count from the .stl file
158     file.seek(80);
159     uint32_t tri_count;
160     data >> tri_count;
161
162     // Verify that the file is the right size
163     if (file.size() != 84 + tri_count*50)
164     {
165         emit error_bad_stl();
166         return NULL;
167     }
168
169     // Extract vertices into an array of xyz, unsigned pairs
170     QVector<Vec3i> verts(tri_count*3);
171
172     // Dummy array, because readRawData is faster than skipRawData
173     uint8_t* buffer = (uint8_t*)malloc(tri_count * 50);
174     data.readRawData((char*)buffer, tri_count * 50);
175
176     // Store vertices in the array, processing one triangle at a time.
177     auto b = buffer;
178     for (auto v=verts.begin(); v != verts.end(); v += 3)
179     {
180         // Skip face's normal vector
181         b += 3 * sizeof(float);
182
183         // Load vertex data from .stl file into vertices
184         for (unsigned i=0; i < 3; ++i)
185         {
186             memcpy(&v[i].first, b, 3*sizeof(float));
187             b += 3 * sizeof(float);
188         }
189
190         // Skip face attribute
191         b += sizeof(uint16_t);
192     }
193
194     if (confusing_stl)
195     {
196         emit warning_confusing_stl();
197     }
198     free(buffer);
199
200     return mesh_from_verts(tri_count, verts);
201 }
202
203 Mesh* Loader::read_stl_ascii(QFile& file)
204 {
205     file.readLine();
206     uint32_t tri_count = 0;
207     QVector<Vec3i> verts(tri_count*3);
208
209     bool okay = true;
210     while (!file.atEnd() && okay)
211     {
212         const auto line = file.readLine().simplified();
213         if (line.startsWith("endsolid"))
214         {
215             break;
216         }
217         else if (!line.startsWith("facet normal") ||
218                  !file.readLine().simplified().startsWith("outer loop"))
219         {
220             okay = false;
221             break;
222         }
223
224         for (int i=0; i < 3; ++i)
225         {
226             auto line = file.readLine().simplified().split(' ');
227             if (line[0] != "vertex")
228             {
229                 okay = false;
230                 break;
231             }
232             const float x = line[1].toFloat(&okay);
233             const float y = line[2].toFloat(&okay);
234             const float z = line[3].toFloat(&okay);
235             verts.push_back({{x, y, z}, 0});
236         }
237         if (!file.readLine().trimmed().startsWith("endloop") ||
238             !file.readLine().trimmed().startsWith("endfacet"))
239         {
240             okay = false;
241             break;
242         }
243         tri_count++;
244     }
245
246     if (okay)
247     {
248         return mesh_from_verts(tri_count, verts);
249     }
250     else
251     {
252         emit error_bad_stl();
253         return NULL;
254     }
255 }
256