+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<Vec3i>& 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<GLuint> 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<GLfloat> 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);
+}
+