mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-18 12:17:54 -06:00
Speed up QEC by move error directly into MutablePriorityQueue
Memory optimization: Change size_t to 32bit int Lightweight Error structure for faster PriorityQueue
This commit is contained in:
parent
35a906139e
commit
c00dca7810
2 changed files with 179 additions and 176 deletions
|
@ -12,73 +12,83 @@ namespace QuadricEdgeCollapse {
|
||||||
using Indices = std::vector<stl_triangle_vertex_indices>;
|
using Indices = std::vector<stl_triangle_vertex_indices>;
|
||||||
using SymMat = SimplifyMesh::implementation::SymetricMatrix<double>;
|
using SymMat = SimplifyMesh::implementation::SymetricMatrix<double>;
|
||||||
|
|
||||||
|
// smallest error caused by edges, identify smallest edge in triangle
|
||||||
struct Error
|
struct Error
|
||||||
{
|
{
|
||||||
float value;
|
float value = -1.;
|
||||||
// range(0 .. 2),
|
// range(0 .. 2),
|
||||||
unsigned char min_index;
|
uint32_t triangle_index = 0;
|
||||||
Error(float value, unsigned char min_index): value(value), min_index(min_index) {
|
Error(float value, uint32_t triangle_index)
|
||||||
|
: value(value)
|
||||||
|
, triangle_index(triangle_index)
|
||||||
|
{
|
||||||
assert(min_index < 3);
|
assert(min_index < 3);
|
||||||
}
|
}
|
||||||
Error() = default;
|
Error() = default;
|
||||||
};
|
};
|
||||||
using Errors = std::vector<Error>;
|
using Errors = std::vector<Error>;
|
||||||
|
|
||||||
// merge information together - faster access during processing
|
// merge information together - faster access during processing
|
||||||
struct TriangleInfo
|
struct TriangleInfo {
|
||||||
{
|
|
||||||
Vec3f n; // normalized normal - speed up calcualtion of q and check flip
|
Vec3f n; // normalized normal - speed up calcualtion of q and check flip
|
||||||
Error e; // smallest error caused by edges, identify smallest edge in triangle
|
unsigned char min_index = 0; // identify edge for minimal Error -> lightweight Error structure
|
||||||
TriangleInfo() = default;
|
TriangleInfo() = default;
|
||||||
bool is_deleted() const { return e.min_index > 2; }
|
bool is_deleted() const { return n.x() > 2.f; }
|
||||||
void set_deleted() { e.min_index = 3; }
|
void set_deleted() { n.x() = 3.f; }
|
||||||
};
|
};
|
||||||
using TriangleInfos = std::vector<TriangleInfo>;
|
using TriangleInfos = std::vector<TriangleInfo>;
|
||||||
struct VertexInfo
|
struct VertexInfo {
|
||||||
{
|
|
||||||
SymMat q; // sum quadric of surround triangles
|
SymMat q; // sum quadric of surround triangles
|
||||||
size_t start = 0, count = 0; // vertex neighbor triangles
|
uint32_t start = 0, count = 0; // vertex neighbor triangles
|
||||||
VertexInfo() = default;
|
VertexInfo() = default;
|
||||||
bool is_deleted() const { return count == 0; }
|
bool is_deleted() const { return count == 0; }
|
||||||
};
|
};
|
||||||
using VertexInfos = std::vector<VertexInfo>;
|
using VertexInfos = std::vector<VertexInfo>;
|
||||||
struct EdgeInfo
|
struct EdgeInfo {
|
||||||
{
|
uint32_t t_index=0; // triangle index
|
||||||
size_t t_index=0; // triangle index
|
|
||||||
unsigned char edge = 0; // 0 or 1 or 2
|
unsigned char edge = 0; // 0 or 1 or 2
|
||||||
EdgeInfo() = default;
|
EdgeInfo() = default;
|
||||||
};
|
};
|
||||||
using EdgeInfos = std::vector<EdgeInfo>;
|
using EdgeInfos = std::vector<EdgeInfo>;
|
||||||
|
|
||||||
|
// DTO for change neighbors
|
||||||
|
struct CopyEdgeInfo {
|
||||||
|
uint32_t start;
|
||||||
|
uint32_t count;
|
||||||
|
uint32_t move;
|
||||||
|
CopyEdgeInfo(uint32_t start, uint32_t count, uint32_t move)
|
||||||
|
: start(start), count(count), move(move)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
using CopyEdgeInfos = std::vector<CopyEdgeInfo>;
|
||||||
|
|
||||||
Vec3f create_normal(const Triangle &triangle, const Vertices &vertices);
|
Vec3f create_normal(const Triangle &triangle, const Vertices &vertices);
|
||||||
double calculate_error(size_t id_v1, size_t id_v2, SymMat & q, const Vertices &vertices);
|
double calculate_error(uint32_t id_v1, uint32_t id_v2, SymMat & q, const Vertices &vertices);
|
||||||
Vec3f calculate_vertex(size_t id_v1, size_t id_v2, SymMat & q, const Vertices &vertices);
|
Vec3f calculate_vertex(uint32_t id_v1, uint32_t id_v2, SymMat & q, const Vertices &vertices);
|
||||||
// calculate error for vertex and quadrics, triangle quadrics and triangle vertex give zero, only pozitive number
|
// calculate error for vertex and quadrics, triangle quadrics and triangle vertex give zero, only pozitive number
|
||||||
double vertex_error(const SymMat &q, const Vec3d &vertex);
|
double vertex_error(const SymMat &q, const Vec3d &vertex);
|
||||||
SymMat create_quadric(const Triangle &t, const Vec3f& normal, const Vertices &vertices);
|
SymMat create_quadric(const Triangle &t, const Vec3f& normal, const Vertices &vertices);
|
||||||
std::tuple<TriangleInfos, VertexInfos, EdgeInfos> init(const indexed_triangle_set &its);
|
std::tuple<TriangleInfos, VertexInfos, EdgeInfos, Errors> init(const indexed_triangle_set &its);
|
||||||
size_t find_triangle_index1(size_t vi, const VertexInfo& v_info, size_t ti, const EdgeInfos& e_infos, const Indices& indices);
|
uint32_t find_triangle_index1(uint32_t vi, const VertexInfo& v_info, uint32_t ti, const EdgeInfos& e_infos, const Indices& indices);
|
||||||
bool is_flipped(const Vec3f &new_vertex, size_t ti0, size_t ti1, const VertexInfo& v_info,
|
bool is_flipped(const Vec3f &new_vertex, uint32_t ti0, uint32_t ti1, const VertexInfo& v_info,
|
||||||
const TriangleInfos &t_infos, const EdgeInfos &e_infos, const indexed_triangle_set &its);
|
const TriangleInfos &t_infos, const EdgeInfos &e_infos, const indexed_triangle_set &its);
|
||||||
|
|
||||||
// find edge with smallest error in triangle
|
// find edge with smallest error in triangle
|
||||||
Error calculate_error(const Triangle& t,const Vertices &vertices, const VertexInfos& v_infos);
|
Error calculate_error(uint32_t ti, const Triangle& t,const Vertices &vertices, const VertexInfos& v_infos, unsigned char& min_index);
|
||||||
// subtract quadric of one triangle from triangle vertex
|
void remove_triangle(EdgeInfos &e_infos, VertexInfo &v_info, uint32_t ti);
|
||||||
void sub_quadric(const Triangle &t, const Vec3f& normal, VertexInfos &v_infos, const Vertices &vertices);
|
void change_neighbors(EdgeInfos &e_infos, VertexInfos &v_infos, uint32_t ti0, uint32_t ti1,
|
||||||
|
uint32_t vi0, uint32_t vi1, uint32_t vi_top0,
|
||||||
void remove_triangle(EdgeInfos &e_infos, VertexInfo &v_info, size_t ti);
|
const Triangle &t1, CopyEdgeInfos& infos, EdgeInfos &e_infos1);
|
||||||
void change_neighbors(EdgeInfos &e_infos, VertexInfos &v_infos, size_t ti0, size_t ti1,
|
|
||||||
size_t vi0, size_t vi1, size_t vi_top0, const Triangle &t1);
|
|
||||||
|
|
||||||
void compact(const VertexInfos &v_infos, const TriangleInfos &t_infos, const EdgeInfos &e_infos, indexed_triangle_set &its);
|
void compact(const VertexInfos &v_infos, const TriangleInfos &t_infos, const EdgeInfos &e_infos, indexed_triangle_set &its);
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace QuadricEdgeCollapse;
|
using namespace QuadricEdgeCollapse;
|
||||||
|
|
||||||
|
#ifdef NDEBUG
|
||||||
bool check_neighbors(TriangleInfos &t_infos,
|
bool check_neighbors(TriangleInfos &t_infos,
|
||||||
Indices& indices,
|
Indices& indices,
|
||||||
VertexInfos & v_infos)
|
VertexInfos & v_infos)
|
||||||
{
|
{
|
||||||
std::vector<size_t> t_counts(v_infos.size(), 0);
|
std::vector<uint32_t> t_counts(v_infos.size(), 0);
|
||||||
for (size_t i = 0; i < indices.size(); i++) {
|
for (size_t i = 0; i < indices.size(); i++) {
|
||||||
TriangleInfo &t_info = t_infos[i];
|
TriangleInfo &t_info = t_infos[i];
|
||||||
if (t_info.is_deleted()) continue;
|
if (t_info.is_deleted()) continue;
|
||||||
|
@ -114,41 +124,53 @@ bool check_new_vertex(const Vec3f& nv, const Vec3f& v0, const Vec3f& v1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its,
|
#endif // NDEBUG
|
||||||
size_t triangle_count)
|
|
||||||
|
void Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its,
|
||||||
|
uint32_t triangle_count,
|
||||||
|
float * max_error)
|
||||||
{
|
{
|
||||||
TriangleInfos t_infos;
|
TriangleInfos t_infos; // only normals with information about deleted triangle
|
||||||
VertexInfos v_infos;
|
VertexInfos v_infos;
|
||||||
EdgeInfos e_infos;
|
EdgeInfos e_infos;
|
||||||
std::tie(t_infos, v_infos, e_infos) = init(its);
|
Errors errors;
|
||||||
|
std::tie(t_infos, v_infos, e_infos, errors) = init(its);
|
||||||
|
|
||||||
static constexpr float max_error = std::numeric_limits<float>::max();
|
float max_float = std::numeric_limits<float>::max();
|
||||||
|
float last_collapsed_error = 0.f;
|
||||||
|
if (max_error == nullptr) {
|
||||||
|
max_error = &max_float;
|
||||||
|
}
|
||||||
|
|
||||||
auto cmp = [&t_infos](size_t ti0, size_t ti1) -> bool {
|
// convert from triangle index to mutable priority queue index
|
||||||
const Error &e0 = t_infos[ti0].e;
|
std::vector<uint32_t> ti_2_mpqi(its.indices.size(), {0});
|
||||||
const Error &e1 = t_infos[ti1].e;
|
auto setter = [&ti_2_mpqi](const Error &e, size_t index) { ti_2_mpqi[e.triangle_index] = index; };
|
||||||
return e0.value < e1.value;
|
auto less = [](const Error &e1, const Error &e2) -> bool { return e1.value < e2.value; };
|
||||||
};
|
MutablePriorityQueue<Error, decltype(setter), decltype(less)> mpq(std::move(setter), std::move(less));
|
||||||
// convert triangle index to priority queue index
|
|
||||||
std::vector<size_t> i_convert(its.indices.size(), {0});
|
|
||||||
auto setter = [&i_convert](size_t it, size_t index) { i_convert[it] = index; };
|
|
||||||
MutablePriorityQueue<size_t, decltype(setter), decltype(cmp)> mpq(std::move(setter), std::move(cmp));
|
|
||||||
mpq.reserve(its.indices.size());
|
mpq.reserve(its.indices.size());
|
||||||
for (size_t i = 0; i < its.indices.size(); i++) mpq.push(i);
|
for (Error &error :errors) mpq.push(error);
|
||||||
|
|
||||||
size_t actual_triangle_count = its.indices.size();
|
const size_t max_triangle_count_for_one_vertex = 50;
|
||||||
|
CopyEdgeInfos ceis;
|
||||||
|
ceis.reserve(max_triangle_count_for_one_vertex);
|
||||||
|
EdgeInfos e_infos_swap;
|
||||||
|
e_infos_swap.reserve(max_triangle_count_for_one_vertex);
|
||||||
|
std::vector<uint32_t> changed_triangle_indices;
|
||||||
|
changed_triangle_indices.reserve(2 * max_triangle_count_for_one_vertex);
|
||||||
|
|
||||||
|
uint32_t actual_triangle_count = its.indices.size();
|
||||||
while (actual_triangle_count > triangle_count && !mpq.empty()) {
|
while (actual_triangle_count > triangle_count && !mpq.empty()) {
|
||||||
// triangle index 0
|
// triangle index 0
|
||||||
size_t ti0 = mpq.top();
|
Error e = mpq.top(); // copy
|
||||||
|
if (e.value >= *max_error) break; // Too big error
|
||||||
mpq.pop();
|
mpq.pop();
|
||||||
|
uint32_t ti0 = e.triangle_index;
|
||||||
TriangleInfo &t_info0 = t_infos[ti0];
|
TriangleInfo &t_info0 = t_infos[ti0];
|
||||||
if (t_info0.is_deleted()) continue;
|
if (t_info0.is_deleted()) continue;
|
||||||
Error &e = t_info0.e;
|
|
||||||
if (e.value >= max_error) return false; // only flipped triangles
|
|
||||||
|
|
||||||
const Triangle &t0 = its.indices[ti0];
|
const Triangle &t0 = its.indices[ti0];
|
||||||
size_t vi0 = t0[e.min_index];
|
uint32_t vi0 = t0[t_info0.min_index];
|
||||||
size_t vi1 = t0[(e.min_index+1) %3];
|
uint32_t vi1 = t0[(t_info0.min_index+1) %3];
|
||||||
// Need by move of neighbor edge infos in function: change_neighbors
|
// Need by move of neighbor edge infos in function: change_neighbors
|
||||||
if (vi0 > vi1) std::swap(vi0, vi1);
|
if (vi0 > vi1) std::swap(vi0, vi1);
|
||||||
VertexInfo &v_info0 = v_infos[vi0];
|
VertexInfo &v_info0 = v_infos[vi0];
|
||||||
|
@ -161,7 +183,7 @@ bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its,
|
||||||
Vec3f new_vertex0 = calculate_vertex(vi0, vi1, q, its.vertices);
|
Vec3f new_vertex0 = calculate_vertex(vi0, vi1, q, its.vertices);
|
||||||
//assert(check_new_vertex(new_vertex0, its.vertices[vi0], its.vertices[vi1]));
|
//assert(check_new_vertex(new_vertex0, its.vertices[vi0], its.vertices[vi1]));
|
||||||
// set of triangle indices that change quadric
|
// set of triangle indices that change quadric
|
||||||
size_t ti1 = (v_info0.count < v_info1.count)?
|
uint32_t ti1 = (v_info0.count < v_info1.count)?
|
||||||
find_triangle_index1(vi1, v_info0, ti0, e_infos, its.indices) :
|
find_triangle_index1(vi1, v_info0, ti0, e_infos, its.indices) :
|
||||||
find_triangle_index1(vi0, v_info1, ti0, e_infos, its.indices) ;
|
find_triangle_index1(vi0, v_info1, ti0, e_infos, its.indices) ;
|
||||||
|
|
||||||
|
@ -169,73 +191,63 @@ bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its,
|
||||||
is_flipped(new_vertex0, ti0, ti1, v_info1, t_infos, e_infos, its)) {
|
is_flipped(new_vertex0, ti0, ti1, v_info1, t_infos, e_infos, its)) {
|
||||||
// IMPROVE1: what about other edges in triangle?
|
// IMPROVE1: what about other edges in triangle?
|
||||||
// IMPROVE2: check mpq top if it is ti1 with same edge
|
// IMPROVE2: check mpq top if it is ti1 with same edge
|
||||||
e.value = max_error;
|
e.value = std::numeric_limits<float>::max();
|
||||||
// error is changed when surround edge is reduced
|
// error is changed when surround edge is reduced
|
||||||
mpq.push(ti0);
|
mpq.push(e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
last_collapsed_error = e.value;
|
||||||
std::vector<size_t> changed_triangle_indices;
|
changed_triangle_indices.clear();
|
||||||
changed_triangle_indices.reserve(v_info0.count + v_info1.count - 4);
|
changed_triangle_indices.reserve(v_info0.count + v_info1.count - 4);
|
||||||
|
|
||||||
sub_quadric(t0, t_info0.n, v_infos, its.vertices);
|
|
||||||
TriangleInfo &t_info1 = t_infos[ti1];
|
|
||||||
const Triangle &t1 = its.indices[ti1];
|
|
||||||
sub_quadric(t1, t_info1.n, v_infos, its.vertices);
|
|
||||||
// for each vertex0 triangles
|
// for each vertex0 triangles
|
||||||
size_t v_info0_end = v_info0.start + v_info0.count;
|
uint32_t v_info0_end = v_info0.start + v_info0.count;
|
||||||
for (size_t di = v_info0.start; di < v_info0_end; ++di) {
|
for (uint32_t di = v_info0.start; di < v_info0_end; ++di) {
|
||||||
assert(di < e_infos.size());
|
assert(di < e_infos.size());
|
||||||
size_t ti = e_infos[di].t_index;
|
uint32_t ti = e_infos[di].t_index;
|
||||||
if (ti == ti0) continue; // ti0 will be deleted
|
if (ti == ti0) continue; // ti0 will be deleted
|
||||||
if (ti == ti1) continue; // ti1 will be deleted
|
if (ti == ti1) continue; // ti1 will be deleted
|
||||||
sub_quadric(its.indices[ti], t_infos[ti].n, v_infos, its.vertices);
|
|
||||||
changed_triangle_indices.emplace_back(ti);
|
changed_triangle_indices.emplace_back(ti);
|
||||||
}
|
}
|
||||||
|
|
||||||
// for each vertex1 triangles
|
// for each vertex1 triangles
|
||||||
size_t v_info1_end = v_info1.start + v_info1.count;
|
uint32_t v_info1_end = v_info1.start + v_info1.count;
|
||||||
for (size_t di = v_info1.start; di < v_info1_end; ++di) {
|
for (uint32_t di = v_info1.start; di < v_info1_end; ++di) {
|
||||||
assert(di < e_infos.size());
|
assert(di < e_infos.size());
|
||||||
EdgeInfo &e_info = e_infos[di];
|
EdgeInfo &e_info = e_infos[di];
|
||||||
size_t ti = e_info.t_index;
|
uint32_t ti = e_info.t_index;
|
||||||
if (ti == ti0) continue; // ti0 will be deleted
|
if (ti == ti0) continue; // ti0 will be deleted
|
||||||
if (ti == ti1) continue; // ti1 will be deleted
|
if (ti == ti1) continue; // ti1 will be deleted
|
||||||
Triangle &t = its.indices[ti];
|
Triangle &t = its.indices[ti];
|
||||||
sub_quadric(t, t_infos[ti].n, v_infos, its.vertices);
|
|
||||||
t[e_info.edge] = vi0; // change index
|
t[e_info.edge] = vi0; // change index
|
||||||
changed_triangle_indices.emplace_back(ti);
|
changed_triangle_indices.emplace_back(ti);
|
||||||
}
|
}
|
||||||
|
v_info0.q = q;
|
||||||
|
|
||||||
// fix neighbors
|
// fix neighbors
|
||||||
|
|
||||||
// vertex index of triangle 0 which is not vi0 nor vi1
|
// vertex index of triangle 0 which is not vi0 nor vi1
|
||||||
size_t vi_top0 = t0[(e.min_index + 2) % 3];
|
uint32_t vi_top0 = t0[(t_info0.min_index + 2) % 3];
|
||||||
change_neighbors(e_infos, v_infos, ti0, ti1, vi0, vi1, vi_top0, t1);
|
const Triangle &t1 = its.indices[ti1];
|
||||||
|
change_neighbors(e_infos, v_infos, ti0, ti1, vi0, vi1,
|
||||||
|
vi_top0, t1, ceis, e_infos_swap);
|
||||||
|
|
||||||
// Change vertex
|
// Change vertex
|
||||||
// Has to be set after subtract quadric
|
// Has to be set after subtract quadric
|
||||||
its.vertices[vi0] = new_vertex0;
|
its.vertices[vi0] = new_vertex0;
|
||||||
|
|
||||||
// add new quadrics
|
// fix errors - must be after set neighbors - v_infos
|
||||||
v_info0.q = SymMat(); // zero value
|
mpq.remove(ti_2_mpqi[ti1]);
|
||||||
for (size_t ti : changed_triangle_indices) {
|
for (uint32_t ti : changed_triangle_indices) {
|
||||||
const Triangle& t = its.indices[ti];
|
size_t priority_queue_index = ti_2_mpqi[ti];
|
||||||
|
auto iterator = mpq.begin() + priority_queue_index;
|
||||||
TriangleInfo& t_info = t_infos[ti];
|
TriangleInfo& t_info = t_infos[ti];
|
||||||
t_info.n = create_normal(t, its.vertices); // new normal
|
*iterator = calculate_error(ti, its.indices[ti], its.vertices, v_infos, t_info.min_index);
|
||||||
SymMat q = create_quadric(t, t_info.n, its.vertices);
|
mpq.update(priority_queue_index);
|
||||||
for (const size_t vi: t) v_infos[vi].q += q;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fix errors - must be after calculate all quadric
|
|
||||||
mpq.remove(i_convert[ti1]);
|
|
||||||
for (size_t ti : changed_triangle_indices) {
|
|
||||||
const Triangle &t = its.indices[ti];
|
|
||||||
t_infos[ti].e = calculate_error(t, its.vertices, v_infos);
|
|
||||||
mpq.update(i_convert[ti]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set triangle(0 + 1) indices as deleted
|
// set triangle(0 + 1) indices as deleted
|
||||||
|
TriangleInfo &t_info1 = t_infos[ti1];
|
||||||
t_info0.set_deleted();
|
t_info0.set_deleted();
|
||||||
t_info1.set_deleted();
|
t_info1.set_deleted();
|
||||||
// triangle counter decrementation
|
// triangle counter decrementation
|
||||||
|
@ -246,7 +258,7 @@ bool Slic3r::its_quadric_edge_collapse(indexed_triangle_set &its,
|
||||||
|
|
||||||
// compact triangle
|
// compact triangle
|
||||||
compact(v_infos, t_infos, e_infos, its);
|
compact(v_infos, t_infos, e_infos, its);
|
||||||
return true;
|
*max_error = last_collapsed_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3f QuadricEdgeCollapse::create_normal(const Triangle &triangle,
|
Vec3f QuadricEdgeCollapse::create_normal(const Triangle &triangle,
|
||||||
|
@ -261,8 +273,8 @@ Vec3f QuadricEdgeCollapse::create_normal(const Triangle & triangle,
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
double QuadricEdgeCollapse::calculate_error(size_t id_v1,
|
double QuadricEdgeCollapse::calculate_error(uint32_t id_v1,
|
||||||
size_t id_v2,
|
uint32_t id_v2,
|
||||||
SymMat & q,
|
SymMat & q,
|
||||||
const Vertices &vertices)
|
const Vertices &vertices)
|
||||||
{
|
{
|
||||||
|
@ -288,8 +300,8 @@ double QuadricEdgeCollapse::calculate_error(size_t id_v1,
|
||||||
}
|
}
|
||||||
|
|
||||||
// similar as calculate error but focus on new vertex without calculation of error
|
// similar as calculate error but focus on new vertex without calculation of error
|
||||||
Vec3f QuadricEdgeCollapse::calculate_vertex(size_t id_v1,
|
Vec3f QuadricEdgeCollapse::calculate_vertex(uint32_t id_v1,
|
||||||
size_t id_v2,
|
uint32_t id_v2,
|
||||||
SymMat & q,
|
SymMat & q,
|
||||||
const Vertices &vertices)
|
const Vertices &vertices)
|
||||||
{
|
{
|
||||||
|
@ -331,13 +343,13 @@ SymMat QuadricEdgeCollapse::create_quadric(const Triangle & t,
|
||||||
return SymMat(n.x(), n.y(), n.z(), -n.dot(v0));
|
return SymMat(n.x(), n.y(), n.z(), -n.dot(v0));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<TriangleInfos, VertexInfos, EdgeInfos> QuadricEdgeCollapse::init(
|
std::tuple<TriangleInfos, VertexInfos, EdgeInfos, Errors>
|
||||||
const indexed_triangle_set &its)
|
QuadricEdgeCollapse::init(const indexed_triangle_set &its)
|
||||||
{
|
{
|
||||||
TriangleInfos t_infos(its.indices.size());
|
TriangleInfos t_infos(its.indices.size());
|
||||||
VertexInfos v_infos(its.vertices.size());
|
VertexInfos v_infos(its.vertices.size());
|
||||||
EdgeInfos e_infos(its.indices.size() * 3);
|
EdgeInfos e_infos(its.indices.size() * 3);
|
||||||
|
Errors errors(its.indices.size());
|
||||||
// calculate normals
|
// calculate normals
|
||||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, its.indices.size()),
|
tbb::parallel_for(tbb::blocked_range<size_t>(0, its.indices.size()),
|
||||||
[&](const tbb::blocked_range<size_t> &range) {
|
[&](const tbb::blocked_range<size_t> &range) {
|
||||||
|
@ -361,7 +373,7 @@ std::tuple<TriangleInfos, VertexInfos, EdgeInfos> QuadricEdgeCollapse::init(
|
||||||
}
|
}
|
||||||
|
|
||||||
// set offseted starts
|
// set offseted starts
|
||||||
size_t triangle_start = 0;
|
uint32_t triangle_start = 0;
|
||||||
for (VertexInfo &v_info : v_infos) {
|
for (VertexInfo &v_info : v_infos) {
|
||||||
v_info.start = triangle_start;
|
v_info.start = triangle_start;
|
||||||
triangle_start += v_info.count;
|
triangle_start += v_info.count;
|
||||||
|
@ -376,7 +388,7 @@ std::tuple<TriangleInfos, VertexInfos, EdgeInfos> QuadricEdgeCollapse::init(
|
||||||
for (size_t i = range.begin(); i < range.end(); ++i) {
|
for (size_t i = range.begin(); i < range.end(); ++i) {
|
||||||
const Triangle &t = its.indices[i];
|
const Triangle &t = its.indices[i];
|
||||||
TriangleInfo & t_info = t_infos[i];
|
TriangleInfo & t_info = t_infos[i];
|
||||||
t_info.e = calculate_error(t, its.vertices, v_infos);
|
errors[i] = calculate_error(i, t, its.vertices, v_infos, t_info.min_index);
|
||||||
}
|
}
|
||||||
}); // END parallel for
|
}); // END parallel for
|
||||||
|
|
||||||
|
@ -393,18 +405,18 @@ std::tuple<TriangleInfos, VertexInfos, EdgeInfos> QuadricEdgeCollapse::init(
|
||||||
++v_info.count;
|
++v_info.count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {t_infos, v_infos, e_infos};
|
return {t_infos, v_infos, e_infos, errors};
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t QuadricEdgeCollapse::find_triangle_index1(size_t vi,
|
uint32_t QuadricEdgeCollapse::find_triangle_index1(uint32_t vi,
|
||||||
const VertexInfo &v_info,
|
const VertexInfo &v_info,
|
||||||
size_t ti0,
|
uint32_t ti0,
|
||||||
const EdgeInfos & e_infos,
|
const EdgeInfos & e_infos,
|
||||||
const Indices & indices)
|
const Indices & indices)
|
||||||
{
|
{
|
||||||
coord_t vi_coord = static_cast<coord_t>(vi);
|
coord_t vi_coord = static_cast<coord_t>(vi);
|
||||||
size_t end = v_info.start + v_info.count;
|
uint32_t end = v_info.start + v_info.count;
|
||||||
for (size_t ei = v_info.start; ei < end; ++ei) {
|
for (uint32_t ei = v_info.start; ei < end; ++ei) {
|
||||||
const EdgeInfo &e_info = e_infos[ei];
|
const EdgeInfo &e_info = e_infos[ei];
|
||||||
if (e_info.t_index == ti0) continue;
|
if (e_info.t_index == ti0) continue;
|
||||||
const Triangle& t = indices[e_info.t_index];
|
const Triangle& t = indices[e_info.t_index];
|
||||||
|
@ -418,8 +430,8 @@ size_t QuadricEdgeCollapse::find_triangle_index1(size_t vi,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex,
|
bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex,
|
||||||
size_t ti0,
|
uint32_t ti0,
|
||||||
size_t ti1,
|
uint32_t ti1,
|
||||||
const VertexInfo & v_info,
|
const VertexInfo & v_info,
|
||||||
const TriangleInfos & t_infos,
|
const TriangleInfos & t_infos,
|
||||||
const EdgeInfos & e_infos,
|
const EdgeInfos & e_infos,
|
||||||
|
@ -456,37 +468,29 @@ bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error QuadricEdgeCollapse::calculate_error(const Triangle & t,
|
Error QuadricEdgeCollapse::calculate_error(uint32_t ti,
|
||||||
|
const Triangle & t,
|
||||||
const Vertices & vertices,
|
const Vertices & vertices,
|
||||||
const VertexInfos &v_infos)
|
const VertexInfos &v_infos,
|
||||||
|
unsigned char & min_index)
|
||||||
{
|
{
|
||||||
Vec3d error;
|
Vec3d error;
|
||||||
for (size_t j = 0; j < 3; ++j) {
|
for (size_t j = 0; j < 3; ++j) {
|
||||||
size_t j2 = (j == 2) ? 0 : (j + 1);
|
size_t j2 = (j == 2) ? 0 : (j + 1);
|
||||||
size_t vi0 = t[j];
|
uint32_t vi0 = t[j];
|
||||||
size_t vi1 = t[j2];
|
uint32_t vi1 = t[j2];
|
||||||
SymMat q(v_infos[vi0].q); // copy
|
SymMat q(v_infos[vi0].q); // copy
|
||||||
q += v_infos[vi1].q;
|
q += v_infos[vi1].q;
|
||||||
error[j] = calculate_error(vi0, vi1, q, vertices);
|
error[j] = calculate_error(vi0, vi1, q, vertices);
|
||||||
}
|
}
|
||||||
unsigned char min_index = (error[0] < error[1]) ?
|
min_index = (error[0] < error[1]) ? ((error[0] < error[2]) ? 0 : 2) :
|
||||||
((error[0] < error[2]) ? 0 : 2) :
|
|
||||||
((error[1] < error[2]) ? 1 : 2);
|
((error[1] < error[2]) ? 1 : 2);
|
||||||
return Error(static_cast<float>(error[min_index]), min_index);
|
return Error(static_cast<float>(error[min_index]), ti);
|
||||||
}
|
|
||||||
|
|
||||||
void QuadricEdgeCollapse::sub_quadric(const Triangle &t,
|
|
||||||
const Vec3f& normal,
|
|
||||||
VertexInfos &v_infos,
|
|
||||||
const Vertices &vertices)
|
|
||||||
{
|
|
||||||
SymMat quadric = create_quadric(t, normal, vertices);
|
|
||||||
for (auto vi: t) v_infos[vi].q -= quadric;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuadricEdgeCollapse::remove_triangle(EdgeInfos & e_infos,
|
void QuadricEdgeCollapse::remove_triangle(EdgeInfos & e_infos,
|
||||||
VertexInfo &v_info,
|
VertexInfo &v_info,
|
||||||
size_t ti)
|
uint32_t ti)
|
||||||
{
|
{
|
||||||
auto e_info = e_infos.begin() + v_info.start;
|
auto e_info = e_infos.begin() + v_info.start;
|
||||||
auto e_info_end = e_info + v_info.count - 1;
|
auto e_info_end = e_info + v_info.count - 1;
|
||||||
|
@ -504,19 +508,20 @@ void QuadricEdgeCollapse::remove_triangle(EdgeInfos & e_infos,
|
||||||
|
|
||||||
void QuadricEdgeCollapse::change_neighbors(EdgeInfos & e_infos,
|
void QuadricEdgeCollapse::change_neighbors(EdgeInfos & e_infos,
|
||||||
VertexInfos & v_infos,
|
VertexInfos & v_infos,
|
||||||
size_t ti0,
|
uint32_t ti0,
|
||||||
size_t ti1,
|
uint32_t ti1,
|
||||||
size_t vi0,
|
uint32_t vi0,
|
||||||
size_t vi1,
|
uint32_t vi1,
|
||||||
size_t vi_top0,
|
uint32_t vi_top0,
|
||||||
const Triangle &t1)
|
const Triangle &t1,
|
||||||
|
CopyEdgeInfos& infos,
|
||||||
|
EdgeInfos & e_infos1)
|
||||||
{
|
{
|
||||||
// have to copy Edge info from higher vertex index into smaller
|
// have to copy Edge info from higher vertex index into smaller
|
||||||
assert(vi0 < vi1);
|
assert(vi0 < vi1);
|
||||||
|
|
||||||
|
|
||||||
// vertex index of triangle 1 which is not vi0 nor vi1
|
// vertex index of triangle 1 which is not vi0 nor vi1
|
||||||
size_t vi_top1 = t1[0];
|
uint32_t vi_top1 = t1[0];
|
||||||
if (vi_top1 == vi0 || vi_top1 == vi1) {
|
if (vi_top1 == vi0 || vi_top1 == vi1) {
|
||||||
vi_top1 = t1[1];
|
vi_top1 = t1[1];
|
||||||
if (vi_top1 == vi0 || vi_top1 == vi1) vi_top1 = t1[2];
|
if (vi_top1 == vi0 || vi_top1 == vi1) vi_top1 = t1[2];
|
||||||
|
@ -528,15 +533,15 @@ void QuadricEdgeCollapse::change_neighbors(EdgeInfos & e_infos,
|
||||||
VertexInfo &v_info0 = v_infos[vi0];
|
VertexInfo &v_info0 = v_infos[vi0];
|
||||||
VertexInfo &v_info1 = v_infos[vi1];
|
VertexInfo &v_info1 = v_infos[vi1];
|
||||||
|
|
||||||
size_t new_triangle_count = v_info0.count + v_info1.count - 4;
|
uint32_t new_triangle_count = v_info0.count + v_info1.count - 4;
|
||||||
remove_triangle(e_infos, v_info0, ti0);
|
remove_triangle(e_infos, v_info0, ti0);
|
||||||
remove_triangle(e_infos, v_info0, ti1);
|
remove_triangle(e_infos, v_info0, ti1);
|
||||||
|
|
||||||
// copy second's edge infos out of e_infos, to free size
|
// copy second's edge infos out of e_infos, to free size
|
||||||
EdgeInfos e_infos1;
|
e_infos1.clear();
|
||||||
e_infos1.reserve(v_info1.count - 2);
|
e_infos1.reserve(v_info1.count - 2);
|
||||||
size_t v_info_s_end = v_info1.start + v_info1.count;
|
uint32_t v_info_s_end = v_info1.start + v_info1.count;
|
||||||
for (size_t ei = v_info1.start; ei < v_info_s_end; ++ei) {
|
for (uint32_t ei = v_info1.start; ei < v_info_s_end; ++ei) {
|
||||||
const EdgeInfo &e_info = e_infos[ei];
|
const EdgeInfo &e_info = e_infos[ei];
|
||||||
if (e_info.t_index == ti0) continue;
|
if (e_info.t_index == ti0) continue;
|
||||||
if (e_info.t_index == ti1) continue;
|
if (e_info.t_index == ti1) continue;
|
||||||
|
@ -544,33 +549,25 @@ void QuadricEdgeCollapse::change_neighbors(EdgeInfos & e_infos,
|
||||||
}
|
}
|
||||||
v_info1.count = 0;
|
v_info1.count = 0;
|
||||||
|
|
||||||
size_t need = (new_triangle_count < v_info0.count)? 0:
|
uint32_t need = (new_triangle_count < v_info0.count)? 0:
|
||||||
(new_triangle_count - v_info0.count);
|
(new_triangle_count - v_info0.count);
|
||||||
|
|
||||||
size_t act_vi = vi0 + 1;
|
uint32_t act_vi = vi0 + 1;
|
||||||
VertexInfo *act_v_info = &v_infos[act_vi];
|
VertexInfo *act_v_info = &v_infos[act_vi];
|
||||||
size_t act_start = act_v_info->start;
|
uint32_t act_start = act_v_info->start;
|
||||||
size_t last_end = v_info0.start + v_info0.count;
|
uint32_t last_end = v_info0.start + v_info0.count;
|
||||||
|
|
||||||
|
infos.clear();
|
||||||
|
infos.reserve(need);
|
||||||
|
|
||||||
struct CopyEdgeInfo
|
|
||||||
{
|
|
||||||
size_t start;
|
|
||||||
size_t count;
|
|
||||||
unsigned char move;
|
|
||||||
CopyEdgeInfo(size_t start, size_t count, unsigned char move)
|
|
||||||
: start(start), count(count), move(move)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
std::vector<CopyEdgeInfo> c_infos;
|
|
||||||
c_infos.reserve(need);
|
|
||||||
while (true) {
|
while (true) {
|
||||||
size_t save = act_start - last_end;
|
uint32_t save = act_start - last_end;
|
||||||
if (save > 0) {
|
if (save > 0) {
|
||||||
if (save >= need) break;
|
if (save >= need) break;
|
||||||
need -= save;
|
need -= save;
|
||||||
c_infos.emplace_back(act_v_info->start, act_v_info->count, need);
|
infos.emplace_back(act_v_info->start, act_v_info->count, need);
|
||||||
} else {
|
} else {
|
||||||
c_infos.back().count += act_v_info->count;
|
infos.back().count += act_v_info->count;
|
||||||
}
|
}
|
||||||
last_end = act_v_info->start + act_v_info->count;
|
last_end = act_v_info->start + act_v_info->count;
|
||||||
act_v_info->start += need;
|
act_v_info->start += need;
|
||||||
|
@ -583,15 +580,15 @@ void QuadricEdgeCollapse::change_neighbors(EdgeInfos & e_infos,
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy by c_infos
|
// copy by c_infos
|
||||||
for (size_t i = c_infos.size(); i > 0; --i) {
|
for (uint32_t i = infos.size(); i > 0; --i) {
|
||||||
const CopyEdgeInfo &c_info = c_infos[i - 1];
|
const CopyEdgeInfo &c_info = infos[i - 1];
|
||||||
for (size_t ei = c_info.start + c_info.count - 1; ei >= c_info.start; --ei)
|
for (uint32_t ei = c_info.start + c_info.count - 1; ei >= c_info.start; --ei)
|
||||||
e_infos[ei + c_info.move] = e_infos[ei]; // copy
|
e_infos[ei + c_info.move] = e_infos[ei]; // copy
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy triangle from first info into second
|
// copy triangle from first info into second
|
||||||
for (size_t ei_s = 0; ei_s < e_infos1.size(); ++ei_s) {
|
for (uint32_t ei_s = 0; ei_s < e_infos1.size(); ++ei_s) {
|
||||||
size_t ei_f = v_info0.start + v_info0.count;
|
uint32_t ei_f = v_info0.start + v_info0.count;
|
||||||
e_infos[ei_f] = e_infos1[ei_s]; // copy
|
e_infos[ei_f] = e_infos1[ei_s]; // copy
|
||||||
++v_info0.count;
|
++v_info0.count;
|
||||||
}
|
}
|
||||||
|
@ -602,12 +599,12 @@ void QuadricEdgeCollapse::compact(const VertexInfos & v_infos,
|
||||||
const EdgeInfos & e_infos,
|
const EdgeInfos & e_infos,
|
||||||
indexed_triangle_set &its)
|
indexed_triangle_set &its)
|
||||||
{
|
{
|
||||||
size_t vi_new = 0;
|
uint32_t vi_new = 0;
|
||||||
for (size_t vi = 0; vi < v_infos.size(); vi++) {
|
for (uint32_t vi = 0; vi < v_infos.size(); ++vi) {
|
||||||
const VertexInfo &v_info = v_infos[vi];
|
const VertexInfo &v_info = v_infos[vi];
|
||||||
if (v_info.is_deleted()) continue; // deleted
|
if (v_info.is_deleted()) continue; // deleted
|
||||||
size_t e_info_end = v_info.start + v_info.count;
|
uint32_t e_info_end = v_info.start + v_info.count;
|
||||||
for (size_t ei = v_info.start; ei < e_info_end; ei++) {
|
for (uint32_t ei = v_info.start; ei < e_info_end; ++ei) {
|
||||||
const EdgeInfo &e_info = e_infos[ei];
|
const EdgeInfo &e_info = e_infos[ei];
|
||||||
// change vertex index
|
// change vertex index
|
||||||
its.indices[e_info.t_index][e_info.edge] = vi_new;
|
its.indices[e_info.t_index][e_info.edge] = vi_new;
|
||||||
|
@ -618,8 +615,8 @@ void QuadricEdgeCollapse::compact(const VertexInfos & v_infos,
|
||||||
// remove vertices tail
|
// remove vertices tail
|
||||||
its.vertices.erase(its.vertices.begin() + vi_new, its.vertices.end());
|
its.vertices.erase(its.vertices.begin() + vi_new, its.vertices.end());
|
||||||
|
|
||||||
size_t ti_new = 0;
|
uint32_t ti_new = 0;
|
||||||
for (size_t ti = 0; ti < t_infos.size(); ti++) {
|
for (uint32_t ti = 0; ti < t_infos.size(); ti++) {
|
||||||
const TriangleInfo &t_info = t_infos[ti];
|
const TriangleInfo &t_info = t_infos[ti];
|
||||||
if (t_info.is_deleted()) continue;
|
if (t_info.is_deleted()) continue;
|
||||||
its.indices[ti_new++] = its.indices[ti];
|
its.indices[ti_new++] = its.indices[ti];
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// sum up: https://users.csc.calpoly.edu/~zwood/teaching/csc570/final06/jseeba/
|
// sum up: https://users.csc.calpoly.edu/~zwood/teaching/csc570/final06/jseeba/
|
||||||
// inspiration: https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification
|
// inspiration: https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include "TriangleMesh.hpp"
|
#include "TriangleMesh.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
@ -10,8 +11,13 @@ namespace Slic3r {
|
||||||
/// Simplify mesh by Quadric metric
|
/// Simplify mesh by Quadric metric
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="its">IN/OUT triangle mesh to be simplified.</param>
|
/// <param name="its">IN/OUT triangle mesh to be simplified.</param>
|
||||||
/// <param name="triangle_count">wanted triangle count.</param>
|
/// <param name="triangle_count">Wanted triangle count.</param>
|
||||||
/// <returns>TRUE on success otherwise FALSE</returns>
|
/// <param name="max_error">Maximal Quadric for reduce.
|
||||||
bool its_quadric_edge_collapse(indexed_triangle_set &its, size_t triangle_count);
|
/// When nullptr then max float is used
|
||||||
|
/// Output: Last used ErrorValue to collapse edge
|
||||||
|
/// </param>
|
||||||
|
void its_quadric_edge_collapse(indexed_triangle_set &its,
|
||||||
|
uint32_t triangle_count = 0,
|
||||||
|
float * max_error = nullptr);
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue